mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
fix(auth): atomic last-admin guards on role-change and delete
This commit is contained in:
1 parent
c0504d65a5
commit
1a11489f2e
3 files changed
+120
-25
No files matched your search
@@ -25,7 +25,9 @@ export interface UserStore {
|
||||
verifyPassword(plain: string, hash: string): Promise<boolean>;
|
||||
changePassword(userId: string, newPassword: string): Promise<void>;
|
||||
setRole(userId: string, role: UserRole): boolean;
|
||||
setRoleIfNotLastAdmin(id: string, newRole: UserRole): "ok" | "not_found" | "would_orphan";
|
||||
deleteUser(id: string): boolean;
|
||||
deleteUserIfNotLastAdmin(id: string): "ok" | "not_found" | "would_orphan";
|
||||
listUsers(): Array<{ id: string; username: string; createdAt: number; role: UserRole }>;
|
||||
}
|
||||
|
||||
@@ -125,6 +127,21 @@ export function createUserStore(db: Database.Database): UserStore {
|
||||
return result.changes > 0;
|
||||
},
|
||||
|
||||
setRoleIfNotLastAdmin(id, newRole) {
|
||||
const tx = db.transaction(() => {
|
||||
const row = findByIdStmt.get(id) as UserRow | undefined;
|
||||
if (!row) return "not_found" as const;
|
||||
if (row.role === newRole) return "ok" as const; // no-op
|
||||
if (row.role === "admin" && newRole === "member") {
|
||||
const adminCount = (countAdminsStmt.get() as { n: number }).n;
|
||||
if (adminCount <= 1) return "would_orphan" as const;
|
||||
}
|
||||
updateRoleStmt.run(newRole, Date.now(), id);
|
||||
return "ok" as const;
|
||||
});
|
||||
return tx();
|
||||
},
|
||||
|
||||
listUsers() {
|
||||
return listUsersStmt.all() as Array<{ id: string; username: string; createdAt: number; role: UserRole }>;
|
||||
},
|
||||
@@ -133,5 +150,19 @@ export function createUserStore(db: Database.Database): UserStore {
|
||||
const result = deleteUserStmt.run(id);
|
||||
return result.changes > 0;
|
||||
},
|
||||
|
||||
deleteUserIfNotLastAdmin(id) {
|
||||
const tx = db.transaction(() => {
|
||||
const row = findByIdStmt.get(id) as UserRow | undefined;
|
||||
if (!row) return "not_found" as const;
|
||||
if (row.role === "admin") {
|
||||
const adminCount = (countAdminsStmt.get() as { n: number }).n;
|
||||
if (adminCount <= 1) return "would_orphan" as const;
|
||||
}
|
||||
deleteUserStmt.run(id);
|
||||
return "ok" as const;
|
||||
});
|
||||
return tx();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user