fix(admin): cascade delete user tokens; test invite cleanup

This commit is contained in:
root
2026-10-17 13:40:45 +08:00
parent 5452611a63
commit 78bfe188b6
2 changed files with 24 additions and 1 deletions
+22
View File
@@ -86,6 +86,28 @@ ok(tSet.body?.ok === true, 'attachment_set_thumb 回填缩略图', tSet.body);
const tGet = await api(A, 'POST', '/api/attachment_thumb', { id: rawUp.b?.data?.item?.id }, T);
ok(tGet.body?.data?.data_b64 === Buffer.from('RAWSUMB').toString('base64'), 'attachment_thumb 读回 raw 缩略图', tGet.body);
// ---- admin 用户删除级联 ----
const invDel = await api(A, 'POST', '/admin/invites/generate', { count: 1 }, T);
const delCode = invDel.body?.data?.codes?.[0];
const regDel = await api(A, 'POST', '/auth/register', { username: 'deltgt', password: 'password123', invite_code: delCode });
ok(regDel.status === 200, 'deltgt 注册', regDel.body);
const usersBefore = await api(A, 'GET', '/admin/users', undefined, T);
const delUser = usersBefore.body?.data?.users?.find((u) => u.username === 'deltgt');
ok(!!delUser, 'deltgt 出现在用户列表', delUser);
const selfUser = usersBefore.body?.data?.users?.find((u) => u.username === 'demo');
const selfDel = await api(A, 'POST', '/admin/users/delete', { user_id: selfUser.id }, T);
ok(selfDel.status !== 200, '不能删除自己(高危保护)', selfDel.status);
const delResp = await api(A, 'POST', '/admin/users/delete', { user_id: delUser.id }, T);
ok(delResp.status === 200, 'admin_user_delete 断言:删除成功', delResp.body);
const usersAfter = await api(A, 'GET', '/admin/users', undefined, T);
ok(!usersAfter.body?.data?.users?.some((u) => u.username === 'deltgt'), 'deltgt 已从列表移除');
ok(!fs.existsSync(path.join(dataA, 'users', delUser.id + '.db')), '用户库文件已删除');
ok(!fs.existsSync(path.join(dataA, 'users', String(delUser.id))), '用户附件目录已删除');
// 清理测试邀请码,恢复邀请码计数
const delInv = await api(A, 'GET', '/admin/invites', undefined, T);
const invRow = delInv.body?.data?.invites?.find((i) => i.code === delCode);
if (invRow) { await api(A, 'POST', '/admin/invites/delete', { id: invRow.id }, T); }
// 个人信息:upsert → get 往返
const profileContent = { info: { name: '张三', school: '重庆城市科技学院', major: '网络工程' }, resume: { selfIntro: '热爱安全研究' } };
const pUp = await api(A, 'POST', '/api/profile_upsert', { content: profileContent }, T);
+2 -1
View File
@@ -180,7 +180,7 @@ const server = http.createServer(async (req, res) => {
// 用户删除:高危操作,级联清理(图床文件 → 用户库 → 附件目录 → 认证数据)
if (req.method === 'POST' && route === '/admin/users/delete') {
const body = await readJson(req);
const uid = Number(body.user_id);
const uid = String(body.user_id ?? '').trim();
const target = db.prepare('SELECT id, username, is_admin FROM users WHERE id=?').get(uid);
if (!target) return send(404, false, '用户不存在');
if (target.id === auth.user_id) return send(400, false, '不能删除当前登录的账号自己');
@@ -208,6 +208,7 @@ const server = http.createServer(async (req, res) => {
// 认证库级联清理
db.exec('BEGIN');
try {
db.prepare('DELETE FROM tokens WHERE user_id=?').run(target.id);
db.prepare('DELETE FROM current WHERE user_id=?').run(target.id);
db.prepare('DELETE FROM attachments WHERE user_id=?').run(target.id);
db.prepare('DELETE FROM devices WHERE user_id=?').run(target.id);