fix
This commit is contained in:
		| @@ -1,5 +1,4 @@ | ||||
| import { app } from './app.ts'; | ||||
| import './demo-route.ts'; | ||||
| import './routes/wx/login.ts'; | ||||
|  | ||||
| export { app }; | ||||
							
								
								
									
										18
									
								
								app/src/modules/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/src/modules/config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import dotenv from 'dotenv'; | ||||
| import path from 'path'; | ||||
| export const env = dotenv.config({ | ||||
|   path: [path.resolve(process.cwd(), '.env'), path.resolve(process.cwd(), '.env.wxopen')], | ||||
| }); | ||||
| console.log(env.parsed); | ||||
|  | ||||
| export const config = { | ||||
|   domain: env.parsed?.DOMAIN, | ||||
|   wx: { | ||||
|     appId: env.parsed?.WX_MP_APP_ID, | ||||
|     appSecret: env.parsed?.WX_MP_APP_SECRET, | ||||
|   }, | ||||
|   wxOpen: { | ||||
|     appId: env.parsed?.WX_OPEN_APP_ID, | ||||
|     appSecret: env.parsed?.WX_OPEN_APP_SECRET, | ||||
|   }, | ||||
| }; | ||||
| @@ -1,11 +1,5 @@ | ||||
| import { useConfig } from '@kevisual/use-config'; | ||||
|  | ||||
| type WxConfig = { | ||||
|   appId: string; | ||||
|   appSecret: string; | ||||
| }; | ||||
|  | ||||
| const config = useConfig<{ wx: WxConfig }>(); | ||||
| import { CustomError } from '@kevisual/router'; | ||||
| import { config } from './config.ts'; | ||||
|  | ||||
| export type WxTokenResponse = { | ||||
|   access_token: string; | ||||
| @@ -25,12 +19,26 @@ export type WxToken = { | ||||
|   unionid: string; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * 根据code获取token | ||||
|  * @param code | ||||
|  * @returns | ||||
|  */ | ||||
| export const fetchToken = async (code: string): Promise<WxToken> => { | ||||
|   const { appId, appSecret } = config.wx; | ||||
|   let appId = config.wxOpen.appId; | ||||
|   let appSecret = config.wxOpen.appSecret; | ||||
|   if (!appId && !appSecret) { | ||||
|     appId = config.wx.appId; | ||||
|     appSecret = config.wx.appSecret; | ||||
|   } | ||||
|   if (!appId || !appSecret) { | ||||
|     throw new CustomError(500, 'appId or appSecret is not set'); | ||||
|   } | ||||
|   console.log('fetchToken===', appId, appSecret, code); | ||||
|   const wxUrl = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code`; | ||||
|   const res = await fetch(wxUrl); | ||||
|   const data = await res.json(); | ||||
|   // console.log(data) | ||||
|   console.log('query token', data); | ||||
|   return data; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,74 +0,0 @@ | ||||
| import { WxServices } from '@/routes/wx/services.ts' | ||||
| import { simple } from './simple.ts' | ||||
| export const createLoginHtml = (wxService: WxServices) => { | ||||
|   const redirectUrl = wxService.isNew ? '/user/info' : '/' | ||||
|   return ` | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|   <title>Wx Login</title> | ||||
|   <style> | ||||
|     body { | ||||
|       font-family: Arial, sans-serif; | ||||
|       text-align: center; | ||||
|       margin-top: 50px; | ||||
|     } | ||||
|     #loading { | ||||
|       font-size: 1.2rem; | ||||
|       color: #555; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <div>Login Success</div> | ||||
|   <div id="loading">Redirecting, please wait...</div> | ||||
|   <script> | ||||
|     (function() { | ||||
|       // Save the token to localStorage | ||||
|       localStorage.setItem('token', '${wxService.webToken}'); | ||||
|        | ||||
|       // Redirect after 2 seconds | ||||
|       setTimeout(() => { | ||||
|         window.location.href = '${redirectUrl}'; | ||||
|       }, 2000); | ||||
|     })(); | ||||
|   </script> | ||||
| </body> | ||||
| </html> | ||||
| ` | ||||
| } | ||||
| simple.get('/api/wx/login', async (req, res) => { | ||||
|   try { | ||||
|     const url = req.url | ||||
|     const query = new URLSearchParams(url.split('?')[1]) | ||||
|     const code = query.get('code') | ||||
|     const state = query.get('state') | ||||
|     if (!code) { | ||||
|       res.end('code is required') | ||||
|       return | ||||
|     } | ||||
|     const wxService = new WxServices() | ||||
|     await wxService.login(code) | ||||
|     if (wxService.isNew) { | ||||
|       await wxService.getUserInfo() | ||||
|     } | ||||
|     res.setHeader('Content-Type', 'text/html') | ||||
|     res.end(createLoginHtml(wxService)) | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|     res.end('error') | ||||
|   } | ||||
| }) | ||||
| simple.get('/api/wx/on-ai/login', async (req, res) => { | ||||
|   const url = req.url | ||||
|   const query = new URLSearchParams(url.split('?')[1]) | ||||
|   const code = query.get('code') | ||||
|   const state = query.get('state') | ||||
|   const onAIBaseUrl = 'https://note.on-ai.ai' | ||||
|   const newUrl = `${onAIBaseUrl}/api/wx/login?code=${code}&state=${state}` | ||||
|   res.setHeader('Content-Type', 'text/html') | ||||
|   res.end(`<script>window.location.href='${newUrl}'</script>`) | ||||
| }) | ||||
| @@ -1,6 +1,40 @@ | ||||
| import { app } from '@/app.ts'; | ||||
| import { useContextKey } from '@kevisual/use-config/context'; | ||||
| import { WxServices } from './services.ts'; | ||||
| import { config } from '@/modules/config.ts'; | ||||
| export const createCookie = async (token: any, ctx: any) => { | ||||
|   if (!config.domain) { | ||||
|     return; | ||||
|   } | ||||
|   //TODO, 获取访问的 hostname, 如果访问的和 domain 的不一致,也创建cookie | ||||
|   const browser = ctx.req.headers['user-agent']; | ||||
|   const isBrowser = browser.includes('Mozilla'); // 浏览器 | ||||
|   if (isBrowser && ctx.res.cookie) { | ||||
|     // const reqDomain = ctx.req?.headers?.host; | ||||
|     // if (reqDomain !== config.domain) { | ||||
|     //   const redis = await useContextKey('redis'); | ||||
|     //   if (!redis) { | ||||
|     //     console.error('redis is not set'); | ||||
|     //     return; | ||||
|     //   } | ||||
|     //   const getCacheToken = await redis.get(`login:check:domain:${reqDomain}`); | ||||
|     //   if (getCacheToken) { | ||||
|     //     ctx.res.cookie('token', getCacheToken, { | ||||
|     //       maxAge: 7 * 24 * 60 * 60 * 1000, // 过期时间, 设置7天 | ||||
|     //       domain: config.domain, | ||||
|     //       sameSite: 'lax', | ||||
|     //       httpOnly: true, | ||||
|     //     }); | ||||
|     //   } | ||||
|     // } | ||||
|     ctx.res.cookie('token', token.accessToken || token?.token, { | ||||
|       maxAge: 7 * 24 * 60 * 60 * 1000, // 过期时间, 设置7天 | ||||
|       domain: config.domain, | ||||
|       sameSite: 'lax', | ||||
|       httpOnly: true, | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
| app | ||||
|   .route({ | ||||
|     path: 'wx', | ||||
| @@ -8,6 +42,10 @@ app | ||||
|   }) | ||||
|   .define(async (ctx) => { | ||||
|     const state = ctx.query.state; | ||||
|     if (!state) { | ||||
|       ctx.throw(400, 'state is required'); | ||||
|       return; | ||||
|     } | ||||
|     const redis = useContextKey('redis'); | ||||
|     const token = await redis.get(`wx:mp:login:${state}`); | ||||
|     if (!token) { | ||||
| @@ -42,3 +80,29 @@ app | ||||
|     } | ||||
|   }) | ||||
|   .addTo(app); | ||||
|  | ||||
| app | ||||
|   .route({ | ||||
|     path: 'wx', | ||||
|     key: 'open-login', | ||||
|     isDebug: true, | ||||
|   }) | ||||
|   .define(async (ctx) => { | ||||
|     const code = ctx.query.code; | ||||
|     const wx = new WxServices(); | ||||
|     if (!code) { | ||||
|       ctx.throw(400, 'code is required'); | ||||
|       return; | ||||
|     } | ||||
|     try { | ||||
|       const token = await wx.login(code); | ||||
|       ctx.body = token; | ||||
|       if (!token.accessToken) { | ||||
|         ctx.throw(500, 'Invalid code'); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error(error); | ||||
|       ctx.throw(500, 'Invalid code'); | ||||
|     } | ||||
|   }) | ||||
|   .addTo(app); | ||||
|   | ||||
| @@ -2,12 +2,14 @@ import { WxTokenResponse, fetchToken, getUserInfo } from '@/modules/wx.ts'; | ||||
| import { useContextKey } from '@kevisual/use-config/context'; | ||||
| import { UserModel } from '@kevisual/code-center-module'; | ||||
| import { Buffer } from 'buffer'; | ||||
|  | ||||
| import { CustomError } from '@kevisual/router'; | ||||
| const User = useContextKey<typeof UserModel>('UserModel'); | ||||
| export class WxServices { | ||||
|   token: WxTokenResponse; | ||||
|   // 创建一个webToken,用户登录 | ||||
|   webToken: string; | ||||
|   accessToken: string; | ||||
|   refreshToken: string; | ||||
|   isNew: boolean; | ||||
|   // @ts-ignore | ||||
|   user: User; | ||||
| @@ -16,9 +18,9 @@ export class WxServices { | ||||
|   } | ||||
|   async login(code: string) { | ||||
|     const token = await fetchToken(code); | ||||
|     this.token = token; | ||||
|     console.log('login token', token); | ||||
|     if (!token.unionid) { | ||||
|       throw new Error('unionid is required'); | ||||
|       throw new CustomError(400, 'code is invalid, wxdata can not be found'); | ||||
|     } | ||||
|     const unionid = token.unionid; | ||||
|     let user = await User.findOne({ | ||||
| @@ -32,14 +34,29 @@ export class WxServices { | ||||
|       user = await User.createUser(unionid, unionid.slice(0, 8)); | ||||
|       user.data = { | ||||
|         ...user.data, | ||||
|         // @ts-ignore | ||||
|         wxOpenid: token.openid, | ||||
|         wxUnionId: unionid, | ||||
|       }; | ||||
|       this.isNew = true; | ||||
|     } | ||||
|     const tokenInfo = await user.createToken(null, 'plugin'); | ||||
|     this.webToken = tokenInfo.token; | ||||
|     console.log('mp-user login=============', token.openid, token.unionid); | ||||
|     const tokenInfo = await user.createToken(null, 'plugin', { | ||||
|       wx: { | ||||
|         openid: token.openid, | ||||
|         unionid: unionid, | ||||
|       }, | ||||
|     }); | ||||
|     this.webToken = tokenInfo.accessToken; | ||||
|  | ||||
|     this.accessToken = tokenInfo.accessToken; | ||||
|     this.refreshToken = tokenInfo.refreshToken; | ||||
|     this.user = user; | ||||
|     return this.webToken; | ||||
|     return { | ||||
|       accessToken: this.accessToken, | ||||
|       refreshToken: this.refreshToken, | ||||
|       isNew: this.isNew, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   async checkHasUser() {} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user