44 lines
2.3 KiB
TypeScript
44 lines
2.3 KiB
TypeScript
|
|
import { Router, type Request, type Response } from 'express';
|
||
|
|
import { database } from '../database.js';
|
||
|
|
import { clearSession, createSession, hashPassword, sessionCookie, type AuthRequest, verifyPassword } from '../auth.js';
|
||
|
|
|
||
|
|
const router = Router();
|
||
|
|
|
||
|
|
router.post('/login', async (req: Request, res: Response) => {
|
||
|
|
const username = String(req.body?.username ?? '').trim();
|
||
|
|
const password = String(req.body?.password ?? '');
|
||
|
|
const row = await database.one<{ id: number; password_hash: string }>('SELECT id, password_hash FROM users WHERE username = ? AND status = ?', [username, 'active']);
|
||
|
|
if (!row || !verifyPassword(password, row.password_hash)) { res.status(401).json({ error: '账号或密码错误' }); return; }
|
||
|
|
await database.execute('UPDATE users SET last_login_at = ? WHERE id = ?', [new Date().toISOString(), row.id]);
|
||
|
|
const token = await createSession(row.id);
|
||
|
|
res.setHeader('Set-Cookie', sessionCookie(token));
|
||
|
|
res.json({ success: true });
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/logout', async (req: Request, res: Response) => {
|
||
|
|
const token = req.headers.cookie?.match(/(?:^|; )proofing_session=([^;]+)/)?.[1];
|
||
|
|
await clearSession(token);
|
||
|
|
res.setHeader('Set-Cookie', 'proofing_session=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0');
|
||
|
|
res.status(204).end();
|
||
|
|
});
|
||
|
|
|
||
|
|
router.get('/me', (req: AuthRequest, res: Response) => {
|
||
|
|
if (!req.authUser) { res.status(401).json({ error: '未登录' }); return; }
|
||
|
|
res.json(req.authUser);
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/change-password', async (req: AuthRequest, res: Response) => {
|
||
|
|
if (!req.authUser) { res.status(401).json({ error: '未登录' }); return; }
|
||
|
|
const currentPassword = String(req.body?.currentPassword ?? '');
|
||
|
|
const newPassword = String(req.body?.newPassword ?? '');
|
||
|
|
if (newPassword.length < 8 || !/[A-Za-z]/.test(newPassword) || !/\d/.test(newPassword)) {
|
||
|
|
res.status(400).json({ error: '新密码至少 8 位,并同时包含字母和数字' }); return;
|
||
|
|
}
|
||
|
|
const row = await database.one<{ password_hash: string }>('SELECT password_hash FROM users WHERE id = ?', [req.authUser.id]);
|
||
|
|
if (!row || !verifyPassword(currentPassword, row.password_hash)) { res.status(400).json({ error: '当前密码错误' }); return; }
|
||
|
|
await database.execute('UPDATE users SET password_hash = ?, must_change_password = ? WHERE id = ?', [hashPassword(newPassword), false, req.authUser.id]);
|
||
|
|
res.json({ success: true });
|
||
|
|
});
|
||
|
|
|
||
|
|
export default router;
|