feat: role-based ACL + admin management UI
Backend: - Add acl.ts with requireAuth/requireRole/requireOwnerOrAdmin helpers - Gate premium videos from unauthenticated users in videos query/resolver - Fix updateVideoPlay to verify ownership before updating - Add admin mutations: adminListUsers, adminUpdateUser, adminDeleteUser - Add admin mutations: createVideo, updateVideo, deleteVideo, setVideoModels, adminListVideos - Add admin mutations: createArticle, updateArticle, deleteArticle, adminListArticles - Add deleteComment mutation (owner or admin only) - Add AdminUserListType to GraphQL types - Fix featured filter on articles query Frontend: - Install marked for markdown rendering - Add /admin/* section with sidebar layout and admin-only guard - Admin users page: paginated table with search, role filter, inline role change, delete - Admin videos pages: list, create form, edit form with file upload and model assignment - Admin articles pages: list, create form, edit form with split-pane markdown editor - Add admin nav link in header (desktop + mobile) for admin users - Render article content through marked in magazine detail page - Add all admin GraphQL service functions to services.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { CommentType } from "../types/index";
|
||||
import { comments, users } from "../../db/schema/index";
|
||||
import { eq, and, desc } from "drizzle-orm";
|
||||
import { awardPoints, checkAchievements } from "../../lib/gamification";
|
||||
import { requireOwnerOrAdmin } from "../../lib/acl";
|
||||
|
||||
builder.queryField("commentsForVideo", (t) =>
|
||||
t.field({
|
||||
@@ -78,3 +79,23 @@ builder.mutationField("createCommentForVideo", (t) =>
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
builder.mutationField("deleteComment", (t) =>
|
||||
t.field({
|
||||
type: "Boolean",
|
||||
args: {
|
||||
id: t.arg.int({ required: true }),
|
||||
},
|
||||
resolve: async (_root, args, ctx) => {
|
||||
const comment = await ctx.db
|
||||
.select()
|
||||
.from(comments)
|
||||
.where(eq(comments.id, args.id))
|
||||
.limit(1);
|
||||
if (!comment[0]) throw new GraphQLError("Comment not found");
|
||||
requireOwnerOrAdmin(ctx, comment[0].user_id);
|
||||
await ctx.db.delete(comments).where(eq(comments.id, args.id));
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user