feat(auth): make upload review a separate grant instead of an admin privilege

Reviewing the upload queue is now a per-user flag (reviews_uploads), not part
of the admin role: several people can hold it, a moderator can have it, and an
admin may not. Only admins grant or revoke it; existing admins keep it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-21 10:07:05 +03:00
1 parent 066ba24834
commit 74da457362
27 files changed
+503 -319

No files matched your search

+17
View File
@@ -56,6 +56,7 @@ export function makeUser(username: string, role: Role, extra: Partial<FakeUser>
is_blocked: false,
must_change_password: false,
must_change_credentials: false,
reviews_uploads: false,
created_at: '2026-01-01T00:00:00Z',
last_login_at: null,
password: `${username}-password`,
@@ -80,6 +81,7 @@ export function fakeApi(initial: FakeUser[]) {
username: u.username,
display_name: u.display_name,
role: u.role,
reviews_uploads: u.reviews_uploads,
locale: u.locale,
ui_prefs: prefs.get(u.id) ?? {},
must_change_password: u.must_change_password,
@@ -179,6 +181,7 @@ export function fakeApi(initial: FakeUser[]) {
display_name: (body?.display_name as string) ?? null,
must_change_password: true,
password: 'Temp-Pass-123',
reviews_uploads: body?.reviews_uploads === true && user.role === 'admin',
});
users.set(u.id, u);
return json({ user: admin(u), temporary_password: u.password }, 201);
@@ -235,6 +238,20 @@ export function fakeApi(initial: FakeUser[]) {
}
if (req.method === 'POST' && /^\/api\/v1\/admin\/users\/[^/]+\/reset-recommendations$/.test(path))
return new Response(null, { status: 204 });
const userPath = /^\/api\/v1\/admin\/users\/([^/]+)$/.exec(path);
if (userPath && req.method === 'PATCH') {
if (!user || user.role === 'user') return problem(403, 'auth.forbidden');
const target = users.get(userPath[1]!);
if (!target) return problem(404, 'common.not_found');
if (typeof body?.reviews_uploads === 'boolean') {
if (user.role !== 'admin') return problem(403, 'auth.forbidden');
target.reviews_uploads = body.reviews_uploads;
}
if (typeof body?.role === 'string' && user.role === 'admin') target.role = body.role as Role;
if (typeof body?.is_blocked === 'boolean') target.is_blocked = body.is_blocked;
users.set(target.id, target);
return json(admin(target));
}
const reactionPath = /^\/api\/v1\/me\/reactions\/tracks\/([^/]+)$/.exec(path);
if (reactionPath) {
if (req.method === 'PUT') reactions.set(reactionPath[1]!, body?.reaction as 'like' | 'dislike');