diff --git a/functions/api/_middleware.js b/functions/api/_middleware.js index de21585..fe638db 100644 --- a/functions/api/_middleware.js +++ b/functions/api/_middleware.js @@ -47,6 +47,14 @@ async function verifyToken(token, env) { // 中间件函数 export async function onRequest(context) { try { + //获取环境变量中的AUTH_ACCESS + const authAccess = context.env.AUTH_ACCESS; + console.log('authAccess', authAccess); + //如果AUTH_ACCESS为0则跳过权限校验 + if (!authAccess || authAccess === '0') { + console.log('跳过权限校验'); + return await context.next(); + } const request = context.request; const env = context.env; //跳过登录页面 @@ -66,6 +74,7 @@ export async function onRequest(context) { return await context.next(); } catch (error) { + console.error(error.message, context.request.url); return new Response(JSON.stringify({ error: error.message }), { status: 401, headers: { diff --git a/functions/api/login.ts b/functions/api/login.ts index d89b6c5..b97c933 100644 --- a/functions/api/login.ts +++ b/functions/api/login.ts @@ -1,6 +1,7 @@ interface Env { bgkv: KVNamespace; JWT_SECRET: string; + DB: D1Database; } export const onRequestPost: PagesFunction = async (context) => { @@ -61,19 +62,56 @@ export const onRequestPost: PagesFunction = async (context) => { ); } - // 验证成功,生成 JWT token,传入 env + // 验证成功后,处理用户数据 + const db = env.DB; // 假设你的 D1 数据库实例名为 DB + + // 查询用户是否存在 + const existingUser = await db.prepare( + "SELECT id, phone, nickname FROM users WHERE phone = ?" + ).bind(phone).first(); + + let userId; + if (!existingUser) { + // 用户不存在,创建新用户 + const result = await db.prepare(` + INSERT INTO users (phone, nickname, status, created_at, updated_at, last_login_at) + VALUES (?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + `).bind(phone, `用户${phone.substring(7)}`).run(); + + userId = result.lastRowId; + } else { + // 用户存在,更新登录时间 + await db.prepare(` + UPDATE users + SET last_login_at = CURRENT_TIMESTAMP, + updated_at = CURRENT_TIMESTAMP + WHERE phone = ? + `).bind(phone).run(); + + userId = existingUser.id; + } + + // 获取完整的用户信息 + const userInfo = await db.prepare(` + SELECT id, phone, nickname, avatar_url, status + FROM users + WHERE phone = ? + `).bind(phone).first(); + + // 生成 token const token = await generateToken(phone, env); // 删除验证码 await env.bgkv.delete(`sms:${phone}`); + // 返回用户信息和token return new Response( JSON.stringify({ success: true, message: '登录成功', data: { token, - phone + user: userInfo } }), { diff --git a/functions/api/sendCode.ts b/functions/api/sendCode.ts index c3990b1..3710215 100644 --- a/functions/api/sendCode.ts +++ b/functions/api/sendCode.ts @@ -1,71 +1,85 @@ +import { sendSMS } from '../utils/sms'; + interface Env { - bgkv: KVNamespace; + ALIYUN_ACCESS_KEY_ID: string; + ALIYUN_ACCESS_KEY_SECRET: string; + ALIYUN_SMS_SIGN_NAME: string; + ALIYUN_SMS_TEMPLATE_CODE: string; + bgkv: KVNamespace; } export const onRequestPost: PagesFunction = async (context) => { + const { request, env } = context; + try { - const { request, env } = context; + const { phone } = await request.json(); - // 获取请求体 - const body = await request.json(); - const { phone } = body; - - // 验证手机号格式 if (!phone || !/^1[3-9]\d{9}$/.test(phone)) { return new Response( JSON.stringify({ - success: false, - message: '无效的手机号码' - }), + success: false, + message: '请输入正确的手机号' + }), { status: 400, - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Content-Type': 'application/json' }, } ); } - // 生成6位随机验证码 - let verificationCode = Math.floor(100000 + Math.random() * 900000).toString(); - // 使用 CF_PAGES_ENVIRONMENT 判断环境 - // 值为 'production' 或 'preview' - if (env.CF_PAGES_ENVIRONMENT !== 'production') { - verificationCode = '123456'; + // 开发环境使用固定验证码 + const verificationCode = env.CF_PAGES_ENVIRONMENT === 'preview' + ? '123456' + : Math.random().toString().slice(-6); + + if (env.CF_PAGES_ENVIRONMENT !== 'preview') { + try { + await sendSMS(phone, verificationCode, { + accessKeyId: env.ALIYUN_ACCESS_KEY_ID, + accessKeySecret: env.ALIYUN_ACCESS_KEY_SECRET, + signName: env.ALIYUN_SMS_SIGN_NAME, + templateCode: env.ALIYUN_SMS_TEMPLATE_CODE + }); + } catch (error) { + console.error('SMS Error:', error); + return new Response( + JSON.stringify({ + success: false, + message: error instanceof Error ? error.message : '发送验证码失败,请重试' + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' }, + } + ); + } } - // 将验证码存储到 KV 中,设置5分钟过期 + + // 存储验证码到 KV,设置 5 分钟过期 await env.bgkv.put(`sms:${phone}`, verificationCode, { - expirationTtl: 300 // 5分钟过期 + expirationTtl: 5 * 60 // 5分钟 }); - console.log(env.CF_PAGES_ENVIRONMENT, await env.bgkv.get(`sms:${phone}`)); return new Response( JSON.stringify({ - success: true, - message: '验证码发送成功', - // 注意:实际生产环境不应该返回验证码 - code: verificationCode // 仅用于测试 - }), + success: true, + message: '验证码发送成功' + }), { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Content-Type': 'application/json' }, } ); } catch (error) { - console.error(error); + console.error('Request Error:', error); return new Response( JSON.stringify({ - success: false, - message: '服务器错误' - }), + success: false, + message: '请求格式错误' + }), { - status: 500, - headers: { - 'Content-Type': 'application/json', - }, + status: 400, + headers: { 'Content-Type': 'application/json' }, } ); } diff --git a/functions/utils/sms.ts b/functions/utils/sms.ts new file mode 100644 index 0000000..9e4e8a9 --- /dev/null +++ b/functions/utils/sms.ts @@ -0,0 +1,125 @@ +interface AliyunSMSConfig { + accessKeyId: string; + accessKeySecret: string; + signName: string; + templateCode: string; +} + +// 辅助函数:生成随机字符串 +function generateNonce(length: number): string { + const array = new Uint8Array(length); + crypto.getRandomValues(array); + return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join(''); +} + +// 辅助函数:计算 HMAC-SHA256(V3 使用 SHA256) +async function calculateHmacSha256(message: string, secret: string): Promise { + const encoder = new TextEncoder(); + const keyData = encoder.encode(secret); + const messageData = encoder.encode(message); + + const key = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['sign'] + ); + + const signature = await crypto.subtle.sign( + 'HMAC', + key, + messageData + ); + + return btoa(String.fromCharCode(...new Uint8Array(signature))); +} + +export async function sendSMS(phone: string, code: string, config: AliyunSMSConfig) { + const API_URL = 'https://dysmsapi.aliyuncs.com/'; + const API_VERSION = '2017-05-25'; + + // 准备请求参数 + const params = { + AccessKeyId: config.accessKeyId, + Action: 'SendSms', + Format: 'JSON', // 明确指定返回 JSON 格式 + PhoneNumbers: phone, + SignName: config.signName, + TemplateCode: config.templateCode, + TemplateParam: JSON.stringify({ code }), + Version: API_VERSION, + SignatureMethod: 'HMAC-SHA1', + SignatureVersion: '1.0', + SignatureNonce: generateNonce(16), + Timestamp: new Date().toISOString() + }; + + // 参数排序 + const sortedParams = Object.keys(params) + .sort() + .reduce((acc, key) => ({ + ...acc, + [key]: params[key] + }), {}); + + // 构建签名字符串 + const canonicalizedQueryString = Object.entries(sortedParams) + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`) + .join('&'); + + const stringToSign = `GET&${encodeURIComponent('/')}&${encodeURIComponent(canonicalizedQueryString)}`; + + // 计算签名 + const signature = await calculateHmacSha1(stringToSign, `${config.accessKeySecret}&`); + + // 构建最终的 URL + const finalUrl = `${API_URL}?${canonicalizedQueryString}&Signature=${encodeURIComponent(signature)}`; + + // 发送请求 + const response = await fetch(finalUrl, { + method: 'GET', + headers: { + 'Accept': 'application/json' + } + }); + + const responseText = await response.text(); + let responseData; + + try { + responseData = JSON.parse(responseText); + } catch (e) { + console.error('Response:', responseText); + throw new Error('Invalid response format from SMS service'); + } + //console.log(responseData, finalUrl) + if (!response.ok || responseData.Code !== 'OK') { + throw new Error(responseData.Message || 'SMS send failed'); + } + + return responseData; +} + +// 辅助函数:计算 HMAC-SHA1 +async function calculateHmacSha1(message: string, secret: string): Promise { + const encoder = new TextEncoder(); + const keyData = encoder.encode(secret); + const messageData = encoder.encode(message); + + const key = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-1' }, + false, + ['sign'] + ); + + const signature = await crypto.subtle.sign( + 'HMAC', + key, + messageData + ); + + return btoa(String.fromCharCode(...new Uint8Array(signature))); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cfea23a..becc9b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "chat-ui", "version": "1.0.0", "dependencies": { + "@alicloud/credentials": "^2.4.2", + "@alicloud/dysmsapi20170525": "^3.1.1", "@fontsource/audiowide": "^5.1.1", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", @@ -55,6 +57,114 @@ "vite": "^5.0.0" } }, + "node_modules/@alicloud/credentials": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.4.2.tgz", + "integrity": "sha512-UbqUYlwOWKNxOemXM545HzQyCaChhyrne9cab4f67EqAkgrTjeMiTA7QK6sHtmcmJYowYQxxXoKPSe5GZstvbA==", + "dependencies": { + "@alicloud/tea-typescript": "^1.8.0", + "httpx": "^2.3.3", + "ini": "^1.3.5", + "kitx": "^2.0.0" + } + }, + "node_modules/@alicloud/dysmsapi20170525": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@alicloud/dysmsapi20170525/-/dysmsapi20170525-3.1.1.tgz", + "integrity": "sha512-UvrQo9p1b7A/JH209jPFLdtuYGywMrn4vWl48LwGxgZOH21i/LQXJKGhIUkeN9/CbdWsW709lkJ9kWvzmQZ5gQ==", + "dependencies": { + "@alicloud/endpoint-util": "^0.0.1", + "@alicloud/openapi-client": "^0.4.12", + "@alicloud/openapi-util": "^0.3.2", + "@alicloud/tea-typescript": "^1.7.1", + "@alicloud/tea-util": "^1.4.9" + } + }, + "node_modules/@alicloud/endpoint-util": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/@alicloud/endpoint-util/-/endpoint-util-0.0.1.tgz", + "integrity": "sha512-+pH7/KEXup84cHzIL6UJAaPqETvln4yXlD9JzlrqioyCSaWxbug5FUobsiI6fuUOpw5WwoB3fWAtGbFnJ1K3Yg==", + "dependencies": { + "@alicloud/tea-typescript": "^1.5.1", + "kitx": "^2.0.0" + } + }, + "node_modules/@alicloud/gateway-spi": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/@alicloud/gateway-spi/-/gateway-spi-0.0.8.tgz", + "integrity": "sha512-KM7fu5asjxZPmrz9sJGHJeSU+cNQNOxW+SFmgmAIrITui5hXL2LB+KNRuzWmlwPjnuA2X3/keq9h6++S9jcV5g==", + "dependencies": { + "@alicloud/credentials": "^2", + "@alicloud/tea-typescript": "^1.7.1" + } + }, + "node_modules/@alicloud/openapi-client": { + "version": "0.4.13", + "resolved": "https://registry.npmmirror.com/@alicloud/openapi-client/-/openapi-client-0.4.13.tgz", + "integrity": "sha512-APi5NwzY6IMxGxM3bTehCzfFVsvY11ljbERhuO2Q4Lfemo4yF23ph38nXLmpv92exDmc4Xe93qKDhnVYMVEdiQ==", + "dependencies": { + "@alicloud/credentials": "^2.4.2", + "@alicloud/gateway-spi": "^0.0.8", + "@alicloud/openapi-util": "^0.3.2", + "@alicloud/tea-typescript": "^1.7.1", + "@alicloud/tea-util": "1.4.9", + "@alicloud/tea-xml": "0.0.3" + } + }, + "node_modules/@alicloud/openapi-client/node_modules/@alicloud/tea-util": { + "version": "1.4.9", + "resolved": "https://registry.npmmirror.com/@alicloud/tea-util/-/tea-util-1.4.9.tgz", + "integrity": "sha512-S0wz76rGtoPKskQtRTGqeuqBHFj8BqUn0Vh+glXKun2/9UpaaaWmuJwcmtImk6bJZfLYEShDF/kxDmDJoNYiTw==", + "dependencies": { + "@alicloud/tea-typescript": "^1.5.1", + "kitx": "^2.0.0" + } + }, + "node_modules/@alicloud/openapi-util": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@alicloud/openapi-util/-/openapi-util-0.3.2.tgz", + "integrity": "sha512-EC2JvxdcOgMlBAEG0+joOh2IB1um8CPz9EdYuRfTfd1uP8Yc9D8QRUWVGjP6scnj6fWSOaHFlit9H6PrJSyFow==", + "dependencies": { + "@alicloud/tea-typescript": "^1.7.1", + "@alicloud/tea-util": "^1.3.0", + "kitx": "^2.1.0", + "sm3": "^1.0.3" + } + }, + "node_modules/@alicloud/tea-typescript": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/@alicloud/tea-typescript/-/tea-typescript-1.8.0.tgz", + "integrity": "sha512-CWXWaquauJf0sW30mgJRVu9aaXyBth5uMBCUc+5vKTK1zlgf3hIqRUjJZbjlwHwQ5y9anwcu18r48nOZb7l2QQ==", + "dependencies": { + "@types/node": "^12.0.2", + "httpx": "^2.2.6" + } + }, + "node_modules/@alicloud/tea-typescript/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/@alicloud/tea-util": { + "version": "1.4.10", + "resolved": "https://registry.npmmirror.com/@alicloud/tea-util/-/tea-util-1.4.10.tgz", + "integrity": "sha512-VEsXWP2dlJLvsY2THj+sH++zwxQRz3Y5BQ8EkfnFems36RkngQKYOLsoto5nR6ej1Gf6I+0IOgBXrkRdpNCQ1g==", + "dependencies": { + "@alicloud/tea-typescript": "^1.5.1", + "@darabonba/typescript": "^1.0.0", + "kitx": "^2.0.0" + } + }, + "node_modules/@alicloud/tea-xml": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/@alicloud/tea-xml/-/tea-xml-0.0.3.tgz", + "integrity": "sha512-+/9GliugjrLglsXVrd1D80EqqKgGpyA0eQ6+1ZdUOYCaRguaSwz44trX3PaxPu/HhIPJg9PsGQQ3cSLXWZjbAA==", + "dependencies": { + "@alicloud/tea-typescript": "^1", + "@types/xml2js": "^0.4.5", + "xml2js": "^0.6.0" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -440,6 +550,19 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@darabonba/typescript": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@darabonba/typescript/-/typescript-1.0.3.tgz", + "integrity": "sha512-/y2y6wf5TsxD7pCPIm0OvTC+5qV0Tk7HQYxwpIuWRLXQLB0CRDvr6qk4bR6rTLO/JglJa8z2uCGZsaLYpQNqFQ==", + "dependencies": { + "@alicloud/tea-typescript": "^1.5.1", + "httpx": "^2.3.2", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "moment-timezone": "^0.5.45", + "xml2js": "^0.6.2" + } + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -2539,6 +2662,14 @@ "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmmirror.com/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -3994,6 +4125,28 @@ "node": ">=8.0.0" } }, + "node_modules/httpx": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/httpx/-/httpx-2.3.3.tgz", + "integrity": "sha512-k1qv94u1b6e+XKCxVbLgYlOypVP9MPGpnN5G/vxFf6tDO4V3xpz3d6FUOY/s8NtPgaq5RBVVgSB+7IHpVxMYzw==", + "dependencies": { + "@types/node": "^20", + "debug": "^4.1.1" + } + }, + "node_modules/httpx/node_modules/@types/node": { + "version": "20.17.27", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.17.27.tgz", + "integrity": "sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/httpx/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, "node_modules/human-signals": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", @@ -4057,6 +4210,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/inline-style-parser": { "version": "0.2.4", "resolved": "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz", @@ -4315,6 +4473,14 @@ "node": ">= 12" } }, + "node_modules/kitx": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/kitx/-/kitx-2.2.0.tgz", + "integrity": "sha512-tBMwe6AALTBQJb0woQDD40734NKzb0Kzi3k7wQj9ar3AbP9oqhoVrdXPh7rk2r00/glIgd0YbToIUJsnxWMiIg==", + "dependencies": { + "@types/node": "^22.5.4" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4340,6 +4506,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmmirror.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -5392,6 +5563,25 @@ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6484,6 +6674,11 @@ } ] }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -6606,6 +6801,11 @@ "node": ">=8" } }, + "node_modules/sm3": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sm3/-/sm3-1.0.3.tgz", + "integrity": "sha512-KyFkIfr8QBlFG3uc3NaljaXdYcsbRy1KrSfc4tsQV8jW68jAktGeOcifu530Vx/5LC+PULHT0Rv8LiI8Gw+c1g==" + }, "node_modules/sonner": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/sonner/-/sonner-2.0.0.tgz", @@ -7902,6 +8102,26 @@ } } }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 27240bc..bb6a51a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "preview": "vite preview" }, "dependencies": { + "@alicloud/credentials": "^2.4.2", + "@alicloud/dysmsapi20170525": "^3.1.1", "@fontsource/audiowide": "^5.1.1", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", diff --git a/src/components/AuthGuard.jsx b/src/components/AuthGuard.jsx index 738133e..c85aeaf 100644 --- a/src/components/AuthGuard.jsx +++ b/src/components/AuthGuard.jsx @@ -1,16 +1,21 @@ import { Navigate, useLocation } from 'react-router-dom'; export default function AuthGuard({ children }) { - const location = useLocation(); - const token = localStorage.getItem('token'); + //判断环境变量中的AUTH_ACCESS是否为1开启权限校验 + const authAccess = import.meta.env.AUTH_ACCESS; + if (authAccess === '1') { + const location = useLocation(); + const token = localStorage.getItem('token'); + + if (!token && location.pathname !== '/login') { + return ; + } - if (!token && location.pathname !== '/login') { - return ; + if (token && location.pathname === '/login') { + return ; + } } - if (token && location.pathname === '/login') { - return ; - } return children; } \ No newline at end of file diff --git a/src/pages/chat/components/ChatUI.tsx b/src/pages/chat/components/ChatUI.tsx index 89541d8..9b2206a 100644 --- a/src/pages/chat/components/ChatUI.tsx +++ b/src/pages/chat/components/ChatUI.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; - +import { request } from '@/utils/request'; import { Tooltip, TooltipContent, @@ -137,8 +137,6 @@ const KaTeXStyle = () => ( `}} /> ); -// Vite环境变量访问方式 -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; const ChatUI = () => { //获取url参数 @@ -182,7 +180,7 @@ const ChatUI = () => { const initData = async () => { try { - const response = await fetch(`${API_BASE_URL}/api/init`); + const response = await request(`/api/init`); if (!response.ok) { throw new Error('初始化数据失败'); } @@ -297,7 +295,7 @@ const ChatUI = () => { })); let selectedGroupAiCharacters = groupAiCharacters; if (!isGroupDiscussionMode) { - const shedulerResponse = await fetch(`${API_BASE_URL}/api/scheduler`, { + const shedulerResponse = await request(`/api/scheduler`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -325,7 +323,7 @@ const ChatUI = () => { setMessages(prev => [...prev, aiMessage]); try { - const response = await fetch(`${API_BASE_URL}/api/chat`, { + const response = await request(`/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/src/pages/login/comonents/PhoneLogin.tsx b/src/pages/login/comonents/PhoneLogin.tsx index 44c49e4..d599124 100644 --- a/src/pages/login/comonents/PhoneLogin.tsx +++ b/src/pages/login/comonents/PhoneLogin.tsx @@ -1,13 +1,12 @@ import React, { useState } from 'react'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; - +import { request } from '@/utils/request'; interface PhoneLoginProps { onLogin: (phone: string, code: string) => void; } -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; -const PhoneLogin: React.FC = ({ onLogin }) => { +const PhoneLogin: React.FC = ({ handleLoginSuccess }) => { const [phone, setPhone] = useState(''); const [code, setCode] = useState(''); const [countdown, setCountdown] = useState(0); @@ -22,7 +21,7 @@ const PhoneLogin: React.FC = ({ onLogin }) => { //setIsLoading(true); try { - const response = await fetch(`${API_BASE_URL}/api/sendcode`, { + const response = await fetch(`/api/sendcode`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -65,7 +64,7 @@ const PhoneLogin: React.FC = ({ onLogin }) => { setIsLoading(true); try { - const response = await fetch(`${API_BASE_URL}/api/login`, { + const response = await request(`/api/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -80,9 +79,8 @@ const PhoneLogin: React.FC = ({ onLogin }) => { throw new Error(data.message || '登录失败'); } - // 保存 token 到 localStorage - localStorage.setItem('token', data.data.token); - localStorage.setItem('phone', data.data.phone); + //执行登录成功回调 + handleLoginSuccess(data.data.token); } catch (error) { console.error('登录失败:', error); diff --git a/src/pages/login/index.jsx b/src/pages/login/index.jsx index b5aef5d..35f7b8b 100644 --- a/src/pages/login/index.jsx +++ b/src/pages/login/index.jsx @@ -11,7 +11,7 @@ export default function Login() { return (
- +
); } \ No newline at end of file diff --git a/src/utils/request.ts b/src/utils/request.ts index b309ad7..90d542b 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,3 +1,4 @@ +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; export async function request(url: string, options: RequestInit = {}) { const token = localStorage.getItem('token'); @@ -10,8 +11,9 @@ export async function request(url: string, options: RequestInit = {}) { headers['Authorization'] = `Bearer ${token}`; } + try { - const response = await fetch(url, { + const response = await fetch(`${API_BASE_URL}${url}`, { ...options, headers, }); @@ -27,7 +29,7 @@ export async function request(url: string, options: RequestInit = {}) { throw new Error('Request failed'); } - return response.json(); + return response; } catch (error) { // 如果是网络错误或其他错误,也可以处理 console.error('Request error:', error); diff --git a/wrangler.toml b/wrangler.toml index 9d9d2a5..4812b4f 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,4 +1,9 @@ [[kv_namespaces]] binding = "bgkv" id = "cbd11575c3504e6bb043c1c250d2f7ed" -preview_id = "cbd11575c3504e6bb043c1c250d2f7ed" # 本地开发环境使用 \ No newline at end of file +preview_id = "cbd11575c3504e6bb043c1c250d2f7ed" # 本地开发环境使用 + +[[d1_databases]] +binding = "bgdb" # available in your Worker on env.DB +database_name = "friends" +database_id = "f68e9fa5-4aea-45db-9516-2d7052e936fa" \ No newline at end of file