update
This commit is contained in:
@@ -47,32 +47,27 @@ 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') {
|
||||
|
||||
if (!authAccess || authAccess === '0' || context.request.url.includes('/login') || context.request.url.includes('/sendcode') || context.request.url.includes('/test-db')) {
|
||||
console.log('跳过权限校验');
|
||||
return await context.next();
|
||||
context.data = { user: null };
|
||||
return context.next();
|
||||
}
|
||||
const request = context.request;
|
||||
const env = context.env;
|
||||
//跳过登录页面
|
||||
if (request.url.includes('/login') || request.url.includes('/sendcode') || request.url.includes('/login') || request.url.includes('/test-db')) {
|
||||
return await context.next();
|
||||
}
|
||||
const authHeader = request.headers.get('Authorization');
|
||||
|
||||
const authHeader = context.request.headers.get('Authorization');
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new Error('No token provided');
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
const payload = await verifyToken(token, env);
|
||||
const payload = await verifyToken(token, context.env);
|
||||
|
||||
// 将用户信息添加到上下文中
|
||||
context.user = payload;
|
||||
|
||||
return await context.next();
|
||||
// 直接在原有的 request 对象上添加 context
|
||||
context.data = { user: payload };
|
||||
console.log('context.request.user', context.data);
|
||||
return context.next();
|
||||
} catch (error) {
|
||||
console.error(error.message, context.request.url);
|
||||
return new Response(JSON.stringify({ error: error.message }), {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {generateAICharacters } from '../../src/config/aiCharacters';
|
||||
import { groups } from '../../src/config/groups';
|
||||
export async function onRequestGet({ env, request }) {
|
||||
console.log('init');
|
||||
export async function onRequestGet(context) {
|
||||
try {
|
||||
return Response.json({
|
||||
code: 200,
|
||||
data: {
|
||||
groups: groups,
|
||||
characters: generateAICharacters('#groupName#', '#allTags#'),
|
||||
user: context.data.user || null
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -98,7 +98,7 @@ export const onRequestPost: PagesFunction<Env> = async (context) => {
|
||||
`).bind(phone).first();
|
||||
|
||||
// 生成 token
|
||||
const token = await generateToken(phone, env);
|
||||
const token = await generateToken(userId, env);
|
||||
|
||||
// 删除验证码
|
||||
await env.bgkv.delete(`sms:${phone}`);
|
||||
@@ -139,14 +139,14 @@ export const onRequestPost: PagesFunction<Env> = async (context) => {
|
||||
};
|
||||
|
||||
// 修改为 async 函数
|
||||
async function generateToken(phone: string, env: Env): Promise<string> {
|
||||
async function generateToken(userId: string, env: Env): Promise<string> {
|
||||
const header = {
|
||||
alg: 'HS256',
|
||||
typ: 'JWT'
|
||||
};
|
||||
|
||||
const payload = {
|
||||
phone,
|
||||
userId,
|
||||
exp: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60), // 7天过期
|
||||
iat: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
60
functions/api/user/info.ts
Normal file
60
functions/api/user/info.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
interface Env {
|
||||
bgdb: D1Database;
|
||||
}
|
||||
|
||||
export const onRequestGet: PagesFunction<Env> = async (context) => {
|
||||
try {
|
||||
const { env, data } = context;
|
||||
|
||||
// 从数据库获取用户信息
|
||||
const db = env.bgdb;
|
||||
const userInfo = await db.prepare(`
|
||||
SELECT id, phone, nickname, avatar_url, status
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`).bind(data.user.userId).first();
|
||||
|
||||
if (!userInfo) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
}),
|
||||
{
|
||||
status: 404,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
data: userInfo
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
88
functions/api/user/update.ts
Normal file
88
functions/api/user/update.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
interface Env {
|
||||
bgdb: D1Database;
|
||||
}
|
||||
|
||||
export const onRequestPost: PagesFunction<Env> = async (context) => {
|
||||
try {
|
||||
const { env, data, request } = context;
|
||||
|
||||
// 解析请求体
|
||||
const body = await request.json();
|
||||
const { nickname } = body;
|
||||
|
||||
// 验证昵称
|
||||
if (!nickname || typeof nickname !== 'string' || nickname.length > 32) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: '昵称格式不正确'
|
||||
}),
|
||||
{
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 更新数据库中的昵称
|
||||
const db = env.bgdb;
|
||||
const result = await db.prepare(`
|
||||
UPDATE users
|
||||
SET nickname = ?,
|
||||
updated_at = DATETIME('now')
|
||||
WHERE id = ?
|
||||
`).bind(nickname, data.user.userId).run();
|
||||
|
||||
if (!result.success) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: '更新失败'
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 获取更新后的用户信息
|
||||
const userInfo = await db.prepare(`
|
||||
SELECT id, phone, nickname, avatar_url, status
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`).bind(data.user.userId).first();
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
data: userInfo
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: '服务器错误'
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -145,7 +145,7 @@ const Sidebar = ({ isOpen, toggleSidebar, selectedGroupIndex = 0, onSelectGroup,
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="flex items-center justify-left">
|
||||
<div className="flex items-center justify-left h-8">
|
||||
<GitHubButton
|
||||
href="https://github.com/maojindao55/botgroup.chat"
|
||||
data-color-scheme="no-preference: light; light: light; dark: light;"
|
||||
|
||||
@@ -1,20 +1,71 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Edit2Icon, LogOutIcon } from 'lucide-react';
|
||||
import { Edit2Icon, LogOutIcon, CheckIcon, XIcon } from 'lucide-react';
|
||||
import { request } from '@/utils/request';
|
||||
|
||||
interface UserSectionProps {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
// 添加用户信息接口
|
||||
interface UserInfo {
|
||||
nickname: string;
|
||||
avatar_url?: string;
|
||||
}
|
||||
|
||||
export const UserSection: React.FC<UserSectionProps> = ({ isOpen }) => {
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
|
||||
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [newNickname, setNewNickname] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserInfo = async () => {
|
||||
if (!isOpen) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await request('/api/user/info');
|
||||
const { data } = await response.json();
|
||||
console.log('data', data);
|
||||
setUserInfo(data);
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserInfo();
|
||||
}, [isOpen]);
|
||||
|
||||
// 添加更新昵称的函数
|
||||
const updateNickname = async () => {
|
||||
if (!newNickname.trim()) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await request('/api/user/update', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ nickname: newNickname.trim() })
|
||||
});
|
||||
const { data } = await response.json();
|
||||
setUserInfo(data);
|
||||
setIsEditing(false);
|
||||
} catch (error) {
|
||||
console.error('更新昵称失败:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"px-3 py-3 border-t border-b border-border/40",
|
||||
"px-3 py-3 border-t border-b border-border/40 h-20",
|
||||
"flex items-center gap-3 hover:bg-accent/50 transition-colors"
|
||||
)}
|
||||
onMouseEnter={() => setIsHovering(true)}
|
||||
@@ -23,7 +74,9 @@ export const UserSection: React.FC<UserSectionProps> = ({ isOpen }) => {
|
||||
{/* 头像区域 */}
|
||||
<div className="relative group cursor-pointer">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-orange-400 to-rose-400 flex items-center justify-center shadow-sm">
|
||||
<span className="text-base font-medium text-white">游</span>
|
||||
<span className="text-base font-medium text-white">
|
||||
{isLoading ? '...' : userInfo?.nickname?.[0] || '我'}
|
||||
</span>
|
||||
</div>
|
||||
{/* 头像hover效果 */}
|
||||
<div className="absolute inset-0 rounded-full bg-black/40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
@@ -33,40 +86,81 @@ export const UserSection: React.FC<UserSectionProps> = ({ isOpen }) => {
|
||||
|
||||
{/* 用户信息区域 */}
|
||||
<div className="flex flex-col relative flex-1">
|
||||
<div className="flex items-center gap-1 group cursor-pointer">
|
||||
<span className="text-sm font-semibold group-hover:text-primary transition-colors">
|
||||
游客用户
|
||||
</span>
|
||||
<Edit2Icon
|
||||
className={cn(
|
||||
"w-3 h-3 text-muted-foreground/50",
|
||||
"opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center group cursor-pointer">
|
||||
{isEditing ? (
|
||||
<div className="flex flex-col">
|
||||
<input
|
||||
type="text"
|
||||
value={newNickname}
|
||||
onChange={(e) => setNewNickname(e.target.value)}
|
||||
className="text-sm px-2 border rounded-md w-full"
|
||||
placeholder={userInfo?.nickname || '输入新昵称'}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') updateNickname();
|
||||
if (e.key === 'Escape') setIsEditing(false);
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={updateNickname}
|
||||
className="p-1 hover:bg-emerald-50 rounded-md transition-colors"
|
||||
title="保存"
|
||||
>
|
||||
<CheckIcon className="w-4 h-4 text-emerald-600 hover:text-emerald-500" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsEditing(false)}
|
||||
className="p-1 hover:bg-rose-50 rounded-md transition-colors"
|
||||
title="取消"
|
||||
>
|
||||
<XIcon className="w-4 h-4 text-rose-600 hover:text-rose-500" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-sm font-semibold group-hover:text-primary transition-colors">
|
||||
{isLoading ? '加载中...' : userInfo?.nickname || '游客用户'}
|
||||
</span>
|
||||
<Edit2Icon
|
||||
className={cn(
|
||||
"w-3 h-3 text-muted-foreground/50",
|
||||
"opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer"
|
||||
)}
|
||||
onClick={() => {
|
||||
setIsEditing(true);
|
||||
setNewNickname(userInfo?.nickname || '');
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 退出登录按钮 */}
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-0.5 mt-0.5 text-xs text-muted-foreground/70",
|
||||
"hover:text-rose-500 transition-all duration-200 group",
|
||||
"rounded-md cursor-pointer"
|
||||
)}
|
||||
onClick={() => {
|
||||
// 这里添加退出登录的处理逻辑
|
||||
console.log('退出登录');
|
||||
}}
|
||||
>
|
||||
<LogOutIcon
|
||||
{!isEditing && (
|
||||
<div
|
||||
className={cn(
|
||||
"w-3 h-3",
|
||||
"group-hover:animate-pulse"
|
||||
)}
|
||||
/>
|
||||
<span className="group-hover:tracking-wide transition-all duration-200">
|
||||
退出登录
|
||||
</span>
|
||||
</div>
|
||||
"flex items-center gap-0.5 mt-1 text-xs text-muted-foreground/70",
|
||||
"hover:text-rose-500 transition-all duration-200 group",
|
||||
"rounded-md cursor-pointer"
|
||||
)}
|
||||
onClick={() => {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
}}
|
||||
>
|
||||
<LogOutIcon
|
||||
className={cn(
|
||||
"w-3 h-3",
|
||||
"group-hover:animate-pulse"
|
||||
)}
|
||||
/>
|
||||
<span className="group-hover:tracking-wide transition-all duration-200">
|
||||
退出登录
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PhoneLogin from './comonents/PhoneLogin';
|
||||
|
||||
@@ -9,6 +10,13 @@ export default function Login() {
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const isLogin = localStorage.getItem('token');
|
||||
if (isLogin) {
|
||||
window.location.href = '/'; // 由于是 Vite 多页面,这里使用 window.location.href
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<PhoneLogin handleLoginSuccess={handleLoginSuccess} />
|
||||
|
||||
Reference in New Issue
Block a user