import express, { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { config } from '../config'; import { MongoError } from 'mongodb'; import { User, type IUser, type UserStats } from '../models/User'; import bcrypt from 'bcrypt'; import { Session } from 'express-session'; import mongoose from 'mongoose'; interface CustomSession extends Session { userId?: string; isAuthenticated?: boolean; } // 扩展 Request 类型 interface CustomRequest extends Request { session: CustomSession; } // 定义 JWT payload 的类型 interface UserPayload { _id: string; username: string; fullname: string; email: string; isAdmin: boolean; exp?: number; } // 扩展 Express 的 Request 类型 declare global { namespace Express { interface Request { user?: UserPayload; } } } const router = express.Router(); // 定义路由处理函数类型 type RouteHandler = ( req: Request, res: Response, next: NextFunction ) => Promise; // 允许任何返回值 // 登录处理函数 const loginHandler: RouteHandler = async (req: CustomRequest, res) => { try { const { username, password } = req.body; console.log('Login attempt:', { username }); const user = await User.findOne({ username }); if (!user) { console.log('User not found'); res.status(401).json({ message: '用户名不存在' }); return; } const isPasswordValid = await user.comparePassword(password); if (!isPasswordValid) { console.log('Password mismatch'); return res.status(401).json({ message: '密码错误' }); } // 确保设置这些session值 req.session.userId = (user._id as mongoose.Types.ObjectId).toString(); req.session.isAuthenticated = true; // 如果有重定向参数,则重定向回原始OAuth2请求 const redirectUrl = req.query.redirect || '/'; // 生成 JWT token const token = jwt.sign( { _id: user._id, username: user.username, fullname: user.fullname, email: user.email, isAdmin: user.isAdmin }, config.JWT_SECRET, { expiresIn: '24h' } ); // 调试:打印 token 信息(不打印完整 token) console.log('Generated token info:', { username: user.username, tokenLength: token.length, tokenStart: token.substring(0, 20) + '...' }); console.log('Login successful:', { username, fullname:user.fullname, isAdmin: user.isAdmin, hasToken: !!token }); res.json({ token, user: { _id: user._id, username: user.username, fullname: user.fullname, email: user.email, isAdmin: user.isAdmin }, success: true, redirectUrl }); } catch (error) { console.error('Login error:', error); res.status(500).json({ message: '登录失败' }); } }; // 注册处理函数 const registerHandler: RouteHandler = async (req, res) => { try { const { username, password, email,fullname } = req.body; console.log('Registration attempt:', { username, email,fullname }); if (!email) { return res.status(400).json({ message: '邮箱是必填项' }); } // 检查用户名是否存在 const existingUsername = await User.findOne({ username }); if (existingUsername) { return res.status(400).json({ message: '用户名已存在' }); } const existingEmail = await User.findOne({ email }); if (existingEmail) { return res.status(400).json({ message: '邮箱已被使用' }); } const userCount = await User.countDocuments(); const isAdmin = userCount === 0; const user = new User({ username, password, email,fullname , isAdmin }); await user.save(); const token = jwt.sign( { _id: user._id, username: user.username, fullname: user.fullname, email: user.email, isAdmin: user.isAdmin }, config.JWT_SECRET, { expiresIn: '24h' } ); console.log('Registration successful:', { username: user.username, email: user.email, isAdmin: user.isAdmin, hasToken: !!token }); res.status(201).json({ token, user: { _id: user._id, username: user.username, fullname:user.fullname, email: user.email, isAdmin: user.isAdmin } }); } catch (error) { console.error('Registration error:', error); if (error instanceof MongoError && error.code === 11000) { res.status(400).json({ message: '用户名或邮箱已存在' }); } else { res.status(500).json({ message: '注册失败:' + (error instanceof Error ? error.message : '未知错误') }); } } }; // 中间件:验证 token const authMiddleware: RouteHandler = async (req, res, next) => { try { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ message: '未登录' }); } const token = authHeader.substring(7); const decoded = jwt.verify(token, config.JWT_SECRET) as UserPayload; req.user = decoded; next(); } catch (error) { res.status(401).json({ message: '登录已过期,请重新登录' }); } }; // 修改密码处理函数 const changePasswordHandler: RouteHandler = async (req, res) => { try { if (!req.user?._id) { return res.status(401).json({ message: '未登录' }); } const { oldPassword, newPassword } = req.body; // 参数验证 if (!oldPassword || !newPassword) { return res.status(400).json({ message: '请提供原密码和新密码' }); } if (newPassword.length < 6) { return res.status(400).json({ message: '新密码长度至少6个字符' }); } // 查找用户 const user = await User.findById(req.user._id); if (!user) { return res.status(404).json({ message: '用户不存在' }); } // 验证原密码 const isPasswordValid = await user.comparePassword(oldPassword); if (!isPasswordValid) { console.log('Old password mismatch for user:', user.username); return res.status(401).json({ message: '原密码错误' }); } // 更新密码 user.password = newPassword; await user.save(); console.log('Password changed successfully for user:', user.username); res.json({ message: '密码修改成功' }); } catch (error) { console.error('Change password error:', error); res.status(500).json({ message: '修改密码失败:' + (error instanceof Error ? error.message : '未知错误') }); } }; // 登出处理函数 const logoutHandler: RouteHandler = async (req: CustomRequest, res) => { try { console.log('Logout request received'); // 清除 session if (req.session) { req.session.destroy((err) => { if (err) { console.error('Session destruction error:', err); return res.status(500).json({ message: '登出失败' }); } // 清除 cookie res.clearCookie('connect.sid'); console.log('Logout successful'); res.json({ success: true, message: '登出成功' }); }); } else { console.log('No session to destroy'); res.json({ success: true, message: '登出成功' }); } } catch (error) { console.error('Logout error:', error); res.status(500).json({ message: '登出失败' }); } }; // 登录路由 router.post('/login', loginHandler); // 注册路由 router.post('/register', registerHandler); // 登出路由 router.post('/logout', logoutHandler); // 修改密码路由 router.post('/change-password', authMiddleware, changePasswordHandler); export default router;