import { Router } from 'express' import { registerSchema, loginSchema, refreshSchema, resetPasswordSchema, forgotPasswordSchema, verifyCodeSchema } from '../schemas/auth.schema' import { register, login, refresh, resetPassword } from '../services/auth.service' import { authLimiter, loginLimiter } from '../middleware/rateLimit' import prisma from '../lib/prisma' import bcrypt from 'bcryptjs' const router = Router() const codeStore = new Map() router.post('/register', authLimiter, async (req, res, next) => { try { const data = registerSchema.parse(req.body) const result = await register(data.orgName, data.phone, data.password) res.json({ success: true, data: result }) } catch (err) { next(err) } }) router.post('/login', loginLimiter, async (req, res, next) => { try { const data = loginSchema.parse(req.body) const result = await login(data.phone, data.password) res.json({ success: true, data: result }) } catch (err) { next(err) } }) router.post('/refresh', async (req, res, next) => { try { const data = refreshSchema.parse(req.body) const result = await refresh(data.refreshToken) res.json({ success: true, data: result }) } catch (err) { next(err) } }) // 发送重置验证码 router.post('/forgot-password/send-code', authLimiter, async (req, res, next) => { try { const data = forgotPasswordSchema.parse(req.body) const user = await prisma.user.findUnique({ where: { phone: data.phone } }) if (!user) { return res.status(400).json({ success: false, error: { code: 'NOT_FOUND', message: '该手机号未注册' } }) } const code = Math.random().toString().slice(2, 8) codeStore.set(data.phone, { code, expiresAt: Date.now() + 5 * 60 * 1000 }) res.json({ success: true, data: { code, message: '验证码已生成(开发阶段直接返回,生产环境将发送短信)' } }) } catch (err) { next(err) } }) // 验证码重置密码 router.post('/forgot-password/verify', authLimiter, async (req, res, next) => { try { const data = verifyCodeSchema.parse(req.body) const stored = codeStore.get(data.phone) if (!stored || stored.expiresAt < Date.now()) { return res.status(400).json({ success: false, error: { code: 'CODE_EXPIRED', message: '验证码已过期,请重新获取' } }) } if (stored.code !== data.code) { return res.status(400).json({ success: false, error: { code: 'CODE_WRONG', message: '验证码错误' } }) } codeStore.delete(data.phone) const passwordHash = await bcrypt.hash(data.newPassword, 10) await prisma.user.updateMany({ where: { phone: data.phone }, data: { passwordHash }, }) res.json({ success: true, data: { message: '密码重置成功' } }) } catch (err) { next(err) } }) router.post('/reset-password', authLimiter, async (req, res, next) => { try { const data = resetPasswordSchema.parse(req.body) const result = await resetPassword(data.phone, data.newPassword) res.json({ success: true, data: result }) } catch (err) { next(err) } }) export default router