iboard/server/routes/auth.js
2026-08-03 18:05:50 +08:00

46 lines
1.8 KiB
JavaScript

import { Router } from 'express'
import crypto from 'node:crypto'
import { prisma } from '../db.js'
import { authRequired, signToken } from '../middleware.js'
const router = Router()
// 校验密码: 接受 scrypt$salt$hash 格式
function verifyPassword(plain, stored) {
if (!stored || !stored.startsWith('scrypt$')) return false
const parts = stored.split('$')
if (parts.length !== 3) return false
const [, salt, hash] = parts
const computed = crypto.scryptSync(plain, salt, 64).toString('hex')
// 常数时间比较
const a = Buffer.from(computed, 'hex')
const b = Buffer.from(hash, 'hex')
if (a.length !== b.length) return false
return crypto.timingSafeEqual(a, b)
}
router.post('/login', async (req, res) => {
const { email, password } = req.body || {}
if (!email || !password) return res.status(400).json({ error: '邮箱或密码不能为空' })
const user = await prisma.user.findUnique({ where: { email: String(email).toLowerCase() } })
if (!user) return res.status(401).json({ error: '邮箱或密码错误' })
if (!verifyPassword(String(password), user.password)) {
return res.status(401).json({ error: '邮箱或密码错误' })
}
const payload = { sub: user.id, email: user.email, name: user.name }
const token = signToken(payload)
res.json({ token, user: { id: user.id, name: user.name, email: user.email, createdAt: user.createdAt } })
})
router.get('/me', authRequired, async (req, res) => {
const user = await prisma.user.findUnique({ where: { id: req.user.sub } })
if (!user) return res.status(401).json({ error: '用户不存在' })
res.json({ user: { id: user.id, name: user.name, email: user.email, createdAt: user.createdAt } })
})
router.post('/logout', (_req, res) => {
// JWT 无状态, 前端清 token 即可; 这里返回 200 让前端不用特判
res.json({ ok: true })
})
export default router