feat: refactor role system to is_admin flag, add Badge component, fix native dialogs
- Separate admin identity from role: viewer|model + is_admin boolean flag - DB migration 0001_is_admin: adds column, migrates former admin role users - Update ACL helpers, auth session, GraphQL types and all resolvers - Admin layout guard and header links check is_admin instead of role - Admin users table: show Admin badge next to name, remove admin from role select - Admin user edit page: is_admin checkbox toggle - Install shadcn Badge component; use in admin users table - Fix duplicate photo keys in adminGetUser resolver - Replace confirm() in /me recordings with Dialog component Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
files,
|
||||
} from "../../db/schema/index";
|
||||
import { eq, and, lte, desc, inArray, count } from "drizzle-orm";
|
||||
import { requireRole } from "../../lib/acl";
|
||||
import { requireAdmin } from "../../lib/acl";
|
||||
|
||||
async function enrichVideo(db: any, video: any) {
|
||||
// Fetch models
|
||||
@@ -432,7 +432,7 @@ builder.queryField("adminListVideos", (t) =>
|
||||
t.field({
|
||||
type: [VideoType],
|
||||
resolve: async (_root, _args, ctx) => {
|
||||
requireRole(ctx, "admin");
|
||||
requireAdmin(ctx);
|
||||
const rows = await ctx.db.select().from(videos).orderBy(desc(videos.upload_date));
|
||||
return Promise.all(rows.map((v: any) => enrichVideo(ctx.db, v)));
|
||||
},
|
||||
@@ -454,7 +454,7 @@ builder.mutationField("createVideo", (t) =>
|
||||
uploadDate: t.arg.string(),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
requireRole(ctx, "admin");
|
||||
requireAdmin(ctx);
|
||||
const inserted = await ctx.db
|
||||
.insert(videos)
|
||||
.values({
|
||||
@@ -491,7 +491,7 @@ builder.mutationField("updateVideo", (t) =>
|
||||
uploadDate: t.arg.string(),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
requireRole(ctx, "admin");
|
||||
requireAdmin(ctx);
|
||||
const updates: Record<string, unknown> = {};
|
||||
if (args.title !== undefined && args.title !== null) updates.title = args.title;
|
||||
if (args.slug !== undefined && args.slug !== null) updates.slug = args.slug;
|
||||
@@ -522,7 +522,7 @@ builder.mutationField("deleteVideo", (t) =>
|
||||
id: t.arg.string({ required: true }),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
requireRole(ctx, "admin");
|
||||
requireAdmin(ctx);
|
||||
await ctx.db.delete(videos).where(eq(videos.id, args.id));
|
||||
return true;
|
||||
},
|
||||
@@ -537,7 +537,7 @@ builder.mutationField("setVideoModels", (t) =>
|
||||
userIds: t.arg.stringList({ required: true }),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
requireRole(ctx, "admin");
|
||||
requireAdmin(ctx);
|
||||
await ctx.db.delete(video_models).where(eq(video_models.video_id, args.videoId));
|
||||
if (args.userIds.length > 0) {
|
||||
await ctx.db.insert(video_models).values(
|
||||
|
||||
Reference in New Issue
Block a user