This commit is contained in:
maojindao55
2025-03-25 19:58:39 +08:00
parent 487621a645
commit 051a7da4c6
12 changed files with 482 additions and 66 deletions

View File

@@ -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: {

View File

@@ -1,6 +1,7 @@
interface Env {
bgkv: KVNamespace;
JWT_SECRET: string;
DB: D1Database;
}
export const onRequestPost: PagesFunction<Env> = async (context) => {
@@ -61,19 +62,56 @@ export const onRequestPost: PagesFunction<Env> = 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
}
}),
{

View File

@@ -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<Env> = 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' },
}
);
}

125
functions/utils/sms.ts Normal file
View File

@@ -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-SHA256V3 使用 SHA256
async function calculateHmacSha256(message: string, secret: string): Promise<string> {
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<string> {
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)));
}

220
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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 <Navigate to="/login" state={{ from: location }} replace />;
}
if (!token && location.pathname !== '/login') {
return <Navigate to="/login" state={{ from: location }} replace />;
if (token && location.pathname === '/login') {
return <Navigate to="/" replace />;
}
}
if (token && location.pathname === '/login') {
return <Navigate to="/" replace />;
}
return children;
}

View File

@@ -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',

View File

@@ -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<PhoneLoginProps> = ({ onLogin }) => {
const PhoneLogin: React.FC<PhoneLoginProps> = ({ handleLoginSuccess }) => {
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [countdown, setCountdown] = useState(0);
@@ -22,7 +21,7 @@ const PhoneLogin: React.FC<PhoneLoginProps> = ({ 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<PhoneLoginProps> = ({ 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<PhoneLoginProps> = ({ 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);

View File

@@ -11,7 +11,7 @@ export default function Login() {
return (
<div className="login-container">
<PhoneLogin onSuccess={handleLoginSuccess} />
<PhoneLogin handleLoginSuccess={handleLoginSuccess} />
</div>
);
}

View File

@@ -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);

View File

@@ -1,4 +1,9 @@
[[kv_namespaces]]
binding = "bgkv"
id = "cbd11575c3504e6bb043c1c250d2f7ed"
preview_id = "cbd11575c3504e6bb043c1c250d2f7ed" # 本地开发环境使用
preview_id = "cbd11575c3504e6bb043c1c250d2f7ed" # 本地开发环境使用
[[d1_databases]]
binding = "bgdb" # available in your Worker on env.DB
database_name = "friends"
database_id = "f68e9fa5-4aea-45db-9516-2d7052e936fa"