// 用户认证路由 import express from 'express' import jwt from 'jsonwebtoken' import bcrypt from 'bcryptjs' import { v4 as uuidv4 } from 'uuid' import { db } from '../db/database.js' const router = express.Router() // JWT 密钥(生产环境应使用环境变量) const JWT_SECRET = process.env.JWT_SECRET || 'trip-planner-secret-key-2024' const JWT_EXPIRES_IN = '7d' // Token 7天过期 // 中间件:验证 JWT Token function authenticateToken(req, res, next) { const authHeader = req.headers['authorization'] const token = authHeader && authHeader.split(' ')[1] // Bearer TOKEN if (!token) { return res.status(401).json({ error: '未提供认证令牌' }) } try { const user = jwt.verify(token, JWT_SECRET) req.user = user next() } catch (err) { return res.status(403).json({ error: '无效的认证令牌' }) } } // 中间件:可选认证(有 token 则验证,没有则继续) function optionalAuth(req, res, next) { const authHeader = req.headers['authorization'] const token = authHeader && authHeader.split(' ')[1] if (token) { try { const user = jwt.verify(token, JWT_SECRET) req.user = user } catch (err) { // Token 无效但继续,视为游客 } } next() } // 中间件:管理员权限检查 function requireAdmin(req, res, next) { if (!req.user || req.user.role !== 'admin') { return res.status(403).json({ error: '需要管理员权限' }) } next() } // 注册新用户 router.post('/register', async (req, res) => { try { const { username, email, password } = req.body if (!username || !password) { return res.status(400).json({ error: '用户名和密码不能为空' }) } if (password.length < 6) { return res.status(400).json({ error: '密码至少需要6个字符' }) } // 检查用户名是否已存在 const existing = db.prepare('SELECT id FROM users WHERE username = ?').get(username) if (existing) { return res.status(409).json({ error: '用户名已被使用' }) } // 检查是否是第一个注册用户(自动成为管理员) const userCount = db.prepare('SELECT COUNT(*) as count FROM users').get() const role = userCount.count === 0 ? 'admin' : 'user' // 哈希密码 const passwordHash = await bcrypt.hash(password, 10) // 创建用户 const userId = uuidv4() db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?, ?, ?, ?, ?)').run( userId, username, email || null, passwordHash, role ) // 生成 JWT const token = jwt.sign( { id: userId, username, role }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN } ) // 记录统计 db.prepare('INSERT INTO stats (user_id, event_type, event_data) VALUES (?, ?, ?)').run( userId, 'user_register', JSON.stringify({ username, role }) ) res.status(201).json({ message: '注册成功', user: { id: userId, username, role }, token }) } catch (err) { console.error('注册失败:', err) res.status(500).json({ error: '注册失败,请稍后重试' }) } }) // 用户登录 router.post('/login', async (req, res) => { try { const { username, password } = req.body if (!username || !password) { return res.status(400).json({ error: '用户名和密码不能为空' }) } // 查找用户 const user = db.prepare('SELECT * FROM users WHERE username = ?').get(username) if (!user) { return res.status(401).json({ error: '用户名或密码错误' }) } // 验证密码 const validPassword = await bcrypt.compare(password, user.password_hash) if (!validPassword) { return res.status(401).json({ error: '用户名或密码错误' }) } // 生成 JWT const token = jwt.sign( { id: user.id, username: user.username, role: user.role }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN } ) // 记录统计 db.prepare('INSERT INTO stats (user_id, event_type) VALUES (?, ?)').run( user.id, 'user_login' ) res.json({ message: '登录成功', user: { id: user.id, username: user.username, role: user.role }, token }) } catch (err) { console.error('登录失败:', err) res.status(500).json({ error: '登录失败,请稍后重试' }) } }) // 获取当前用户信息 router.get('/me', authenticateToken, (req, res) => { const user = db.prepare('SELECT id, username, email, role, created_at FROM users WHERE id = ?').get(req.user.id) if (!user) { return res.status(404).json({ error: '用户不存在' }) } res.json({ user }) }) // 生成游客 ID router.post('/guest', (req, res) => { const guestId = uuidv4() const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 24小时后过期 res.json({ guestId, expiresAt, message: '游客会话已创建,数据将保留24小时' }) }) // 迁移游客数据到登录用户 router.post('/migrate-guest-data', authenticateToken, (req, res) => { try { const { guestId } = req.body if (!guestId) { return res.status(400).json({ error: '缺少游客ID' }) } // 获取游客的所有计划 const guestPlans = db.prepare('SELECT * FROM plans WHERE guest_id = ?').all(guestId) // 将游客计划转移到用户 let migrated = 0 const stmt = db.prepare('UPDATE plans SET user_id = ?, guest_id = NULL WHERE id = ?') for (const plan of guestPlans) { stmt.run(req.user.id, plan.id) migrated++ } // 同时迁移游客计划 const guestTempPlans = db.prepare('SELECT * FROM guest_plans WHERE guest_id = ?').all(guestId) const insertPlan = db.prepare('INSERT INTO plans (id, user_id, name, description, data) VALUES (?, ?, ?, ?, ?)') for (const gp of guestTempPlans) { insertPlan.run(uuidv4(), req.user.id, gp.name, gp.description, gp.data) migrated++ } // 删除游客临时数据 db.prepare('DELETE FROM guest_plans WHERE guest_id = ?').run(guestId) // 记录统计 db.prepare('INSERT INTO stats (user_id, event_type, event_data) VALUES (?, ?, ?)').run( req.user.id, 'guest_data_migrated', JSON.stringify({ guestId, migrated }) ) res.json({ message: '数据迁移成功', migrated }) } catch (err) { console.error('数据迁移失败:', err) res.status(500).json({ error: '数据迁移失败' }) } }) export default router export { authenticateToken, optionalAuth, requireAdmin }