/** * This is a user authentication API route. * Handle user registration, login, token management, etc. */ import { Router, type Request, type Response } from 'express'; import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; import { query, queryOne, execute } from '../config/database.js'; const router = Router(); // JWT密钥 const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; /** * User Registration * POST /api/auth/register */ router.post('/register', async (req: Request, res: Response): Promise => { try { const { username, email, password } = req.body; // 验证输入 if (!username || !email || !password) { res.status(400).json({ success: false, message: '用户名、邮箱和密码都是必填项' }); return; } // 检查用户是否已存在 const existingUser = await queryOne( 'SELECT id FROM users WHERE username = ? OR email = ?', [username, email] ); if (existingUser) { res.status(400).json({ success: false, message: '用户名或邮箱已存在' }); return; } // 加密密码 const hashedPassword = await bcrypt.hash(password, 10); // 插入新用户 const result = await execute( 'INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, datetime("now"))', [username, email, hashedPassword] ); res.status(201).json({ success: true, message: '用户注册成功', data: { id: result.lastID, username, email } }); } catch (error) { console.error('注册失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); /** * User Login * POST /api/auth/login */ router.post('/login', async (req: Request, res: Response): Promise => { try { const { username, password } = req.body; // 验证输入 if (!username || !password) { res.status(400).json({ success: false, message: '用户名和密码都是必填项' }); return; } // 查找用户 const user = await queryOne( 'SELECT id, username, email, password, role FROM users WHERE username = ? OR email = ?', [username, username] ); if (!user) { res.status(401).json({ success: false, message: '用户名或密码错误' }); return; } // 验证密码 const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { res.status(401).json({ success: false, message: '用户名或密码错误' }); return; } // 生成JWT token const token = jwt.sign( { id: user.id, username: user.username, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '24h' } ); res.json({ success: true, message: '登录成功', data: { token, user: { id: user.id, username: user.username, email: user.email, role: user.role } } }); } catch (error) { console.error('登录失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); /** * Token Refresh * POST /api/auth/refresh */ router.post('/refresh', async (req: Request, res: Response): Promise => { try { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { res.status(401).json({ success: false, message: '访问令牌缺失' }); return; } // 验证当前token(即使过期也要能解析出用户信息) let decoded: any; try { decoded = jwt.verify(token, JWT_SECRET); } catch (error: any) { // 如果是过期错误,尝试解析过期的token if (error.name === 'TokenExpiredError') { decoded = jwt.decode(token); if (!decoded) { res.status(401).json({ success: false, message: '无效的访问令牌' }); return; } } else { res.status(401).json({ success: false, message: '无效的访问令牌' }); return; } } // 验证用户是否仍然存在 const user = await queryOne( 'SELECT id, username, email, role FROM users WHERE id = ?', [decoded.id] ); if (!user) { res.status(401).json({ success: false, message: '用户不存在' }); return; } // 生成新的JWT token const newToken = jwt.sign( { id: user.id, username: user.username, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '24h' } ); res.json({ success: true, message: 'Token刷新成功', data: { token: newToken, user: { id: user.id, username: user.username, email: user.email, role: user.role } } }); } catch (error) { console.error('Token刷新失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); /** * Get Captcha * GET /api/auth/captcha */ router.get('/captcha', async (req: Request, res: Response): Promise => { try { // 生成简单的数学验证码 const num1 = Math.floor(Math.random() * 10) + 1; const num2 = Math.floor(Math.random() * 10) + 1; const answer = num1 + num2; // 生成验证码ID const captchaId = Math.random().toString(36).substring(2, 15); res.json({ success: true, data: { captchaId, question: `${num1} + ${num2} = ?`, answer // 在实际生产环境中,这应该存储在服务器端而不是返回给客户端 } }); } catch (error) { console.error('获取验证码失败:', error); res.status(500).json({ success: false, message: '服务器内部错误' }); } }); /** * User Logout * POST /api/auth/logout */ router.post('/logout', async (req: Request, res: Response): Promise => { // 由于使用JWT,logout主要在前端处理(删除token) res.json({ success: true, message: '退出登录成功' }); }); export default router;