Files
sexy/packages/backend/src/lib/acl.ts
Sebastian Krüger c1770ab9c9 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>
2026-03-06 12:31:33 +01:00

21 lines
654 B
TypeScript

import { GraphQLError } from "graphql";
import type { Context } from "../graphql/builder";
type UserRole = "viewer" | "model" | "admin";
export function requireAuth(ctx: Context): void {
if (!ctx.currentUser) throw new GraphQLError("Unauthorized");
}
export function requireRole(ctx: Context, ...roles: UserRole[]): void {
requireAuth(ctx);
if (!roles.includes(ctx.currentUser!.role)) throw new GraphQLError("Forbidden");
}
export function requireOwnerOrAdmin(ctx: Context, ownerId: string): void {
requireAuth(ctx);
if (ctx.currentUser!.id !== ownerId && ctx.currentUser!.role !== "admin") {
throw new GraphQLError("Forbidden");
}
}