add sms
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -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
125
functions/utils/sms.ts
Normal 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-SHA256(V3 使用 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
220
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function Login() {
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<PhoneLogin onSuccess={handleLoginSuccess} />
|
||||
<PhoneLogin handleLoginSuccess={handleLoginSuccess} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user