mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 13:02:49 +08:00
feat(auth): two-role permission system (admin/member)
Adds an admin/member role to WebUI auth. /api/users and /api/audit are now gated by a requireAdmin middleware; all other authenticated endpoints accept both roles. Schema migration defaults all existing users to admin to preserve access. POST /api/users defaults new users to member; first-run setup always creates an admin. Adds PATCH /api/users/:id/role with last-admin demotion and deletion guards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b0b61f8fce
commit
a73f797bcb
18 files changed
+292
-60
No files matched your search
@@ -7,6 +7,7 @@ export const SESSION_TOUCH_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
|
||||
export interface SessionValidation {
|
||||
userId: string;
|
||||
username: string;
|
||||
role: "admin" | "member";
|
||||
}
|
||||
|
||||
export interface SessionStore {
|
||||
@@ -26,7 +27,7 @@ export function createSessionStore(db: Database.Database): SessionStore {
|
||||
"INSERT INTO sessions (id, userId, createdAt, expiresAt, lastSeenAt) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
const selectStmt = db.prepare(`
|
||||
SELECT s.id, s.userId, s.expiresAt, s.lastSeenAt, u.username
|
||||
SELECT s.id, s.userId, s.expiresAt, s.lastSeenAt, u.username, u.role
|
||||
FROM sessions s INNER JOIN users u ON u.id = s.userId
|
||||
WHERE s.id = ?
|
||||
`);
|
||||
@@ -54,7 +55,7 @@ export function createSessionStore(db: Database.Database): SessionStore {
|
||||
if (!rawToken) return null;
|
||||
const id = hashToken(rawToken);
|
||||
const row = selectStmt.get(id) as
|
||||
| { id: string; userId: string; expiresAt: number; lastSeenAt: number; username: string }
|
||||
| { id: string; userId: string; expiresAt: number; lastSeenAt: number; username: string; role: string }
|
||||
| undefined;
|
||||
if (!row) return null;
|
||||
const now = Date.now();
|
||||
@@ -65,7 +66,7 @@ export function createSessionStore(db: Database.Database): SessionStore {
|
||||
if (now - row.lastSeenAt > SESSION_TOUCH_INTERVAL_MS) {
|
||||
touchStmt.run(now, now + SESSION_TTL_MS, id);
|
||||
}
|
||||
return { userId: row.userId, username: row.username };
|
||||
return { userId: row.userId, username: row.username, role: row.role as "admin" | "member" };
|
||||
},
|
||||
|
||||
deleteSession(rawToken) {
|
||||
|
||||
Reference in new issue
Block a user