fix: account credentials, record deletion refresh and backup recovery

This commit is contained in:
陈煜 committed 2026-10-03 11:23:19 +08:00
1 parent 9adf7fdbc5
commit e77650f0c0
22 files changed
+711 -115

No files matched your search

+45 -2
View File
@@ -3,6 +3,8 @@ import {
Controller,
Get,
Post,
Patch,
BadRequestException,
Body,
Req,
Res,
@@ -18,7 +20,8 @@ import { Request, Response } from 'express';
import { randomBytes, createHash } from 'node:crypto';
import { hash, compare } from 'bcryptjs';
import { Database } from './database';
import { credentials } from './validation';
import { credentials, credentialChange } from './validation';
import { Prisma } from '@prisma/client';
export type UserRequest = Request & { userId: string; sessionId: string; revealed: boolean };
const Public = () => SetMetadata('public', true);
const digest = (s: string) => createHash('sha256').update(s).digest('hex');
@@ -58,6 +61,9 @@ export class AuthService {
const token = randomBytes(32).toString('hex'),
expiresAt = new Date(Date.now() + 7 * 86400000);
await this.db.session.create({ data: { id: digest(token), userId, expiresAt } });
this.cookie(token, expiresAt, res);
}
cookie(token: string, expiresAt: Date, res: Response) {
res.cookie('wp_session', token, {
httpOnly: true,
sameSite: 'strict',
@@ -166,7 +172,44 @@ export class AuthController {
idleMinutes: true,
},
});
return { ...user, hiddenMenus: user.hiddenMenus.split(',').filter(Boolean) };
const session = await this.db.session.findUniqueOrThrow({ where: { id: req.sessionId } });
return {
...user,
hiddenMenus: user.hiddenMenus.split(',').filter(Boolean),
revealed: req.revealed,
revealUntil: session.revealUntil,
lastActivity: session.lastActivity,
};
}
@Patch('auth/credentials') async changeCredentials(
@Req() r: UserRequest,
@Body() body: unknown,
@Res({ passthrough: true }) res: Response,
) {
this.auth.limit(r);
const v = credentialChange.parse(body);
const user = await this.db.user.findUniqueOrThrow({ where: { id: r.userId } });
if (!(await compare(v.currentPassword, user.passwordHash)))
throw new ForbiddenException('当前密码错误');
if ((!v.username || v.username === user.username) && !v.newPassword)
throw new BadRequestException('请填写新的账号或密码');
const passwordHash = v.newPassword ? await hash(v.newPassword, 12) : user.passwordHash;
const token = randomBytes(32).toString('hex'),
expiresAt = new Date(Date.now() + 7 * 86400000);
await this.db.serial(async (tx) => {
await tx.$queryRaw(Prisma.sql`SELECT id FROM User WHERE id = ${r.userId} FOR UPDATE`);
const current = await tx.user.findUniqueOrThrow({ where: { id: r.userId } });
if (current.passwordHash !== user.passwordHash || current.username !== user.username)
throw new ForbiddenException('账号已变更,请重新登录后操作');
await tx.user.update({
where: { id: r.userId },
data: { username: v.username, passwordHash },
});
await tx.session.deleteMany({ where: { userId: r.userId } });
await tx.session.create({ data: { id: digest(token), userId: r.userId, expiresAt } });
});
this.auth.cookie(token, expiresAt, res);
return { ok: true };
}
@Post('auth/activity') async activity(@Req() r: UserRequest) {
await this.db.session.update({