// Central permission registry. This is the single source of truth for every // granular permission key in the dashboard. Adding a key here automatically // surfaces it in the GET /admin/permissions endpoint and the Roles matrix UI. // To add a new permission: add an entry to PERMISSIONS below, then (optionally) // grant it to roles by default in DEFAULT_ROLE_PERMISSIONS and the migration. export type PermissionKey = string; export interface PermissionDef { key: PermissionKey; label: string; group: string; } // Ordered list of every permission, grouped by feature area for the matrix UI. export const PERMISSIONS: PermissionDef[] = [ { key: 'events.create', label: 'Create events', group: 'Events' }, { key: 'events.edit', label: 'Edit events', group: 'Events' }, { key: 'events.delete', label: 'Delete events', group: 'Events' }, { key: 'organizers.manage', label: 'Manage organizers', group: 'Organizers' }, { key: 'gallery.upload', label: 'Upload media', group: 'Gallery' }, { key: 'gallery.delete', label: 'Delete media', group: 'Gallery' }, { key: 'blog.draft', label: 'Draft and edit posts', group: 'Blog' }, { key: 'blog.publish', label: 'Publish posts', group: 'Blog' }, { key: 'blog.delete', label: 'Delete posts', group: 'Blog' }, { key: 'faq.manage', label: 'Manage FAQ', group: 'FAQ' }, { key: 'submissions.review', label: 'Review submissions', group: 'Submissions' }, { key: 'board.manage', label: 'Moderate the message board', group: 'Board' }, { key: 'moderation.act', label: 'Hide content and block pubkeys', group: 'Moderation' }, { key: 'categories.manage', label: 'Manage categories', group: 'Categories' }, { key: 'users.assign_role', label: 'Assign user roles', group: 'Users' }, { key: 'nip05.assign', label: 'Assign NIP-05 usernames', group: 'Users' }, { key: 'relays.manage', label: 'Manage relays', group: 'Relays' }, { key: 'settings.edit', label: 'Edit site settings', group: 'Settings' }, { key: 'roles.edit_permissions', label: 'Edit roles and permissions', group: 'Roles' }, { key: 'api_keys.manage', label: 'Create and manage API keys', group: 'API Keys' }, { key: 'nostr_tools.use', label: 'Use Nostr tools', group: 'Nostr Tools' }, ]; // Assignable roles, highest to lowest. SuperAdmin is env-sourced and never stored // or assignable, so it is not part of this list. Guest is the absence of a role. export const ASSIGNABLE_ROLES = ['admin', 'moderator', 'writer'] as const; export type AssignableRole = (typeof ASSIGNABLE_ROLES)[number]; export type EffectiveRole = 'superadmin' | AssignableRole | 'guest'; // Hierarchy ranks used for safeguard comparisons. export const ROLE_RANK: Record = { superadmin: 4, admin: 3, moderator: 2, writer: 1, guest: 0, }; export const ALL_PERMISSION_KEYS: ReadonlySet = new Set( PERMISSIONS.map((p) => p.key) ); export function isValidPermissionKey(key: string): boolean { return ALL_PERMISSION_KEYS.has(key); } export function isAssignableRole(role: unknown): role is AssignableRole { return typeof role === 'string' && (ASSIGNABLE_ROLES as readonly string[]).includes(role); } // Default permission sets seeded per role. Admin gets everything except the // roles editor and API key management, which stay SuperAdmin-only by default. // Guest gets nothing and is therefore not represented here. export const DEFAULT_ROLE_PERMISSIONS: Record = { admin: PERMISSIONS.map((p) => p.key).filter( (k) => k !== 'roles.edit_permissions' && k !== 'api_keys.manage' ), moderator: [ 'events.create', 'events.edit', 'events.delete', 'submissions.review', 'moderation.act', 'gallery.upload', 'board.manage', 'categories.manage', 'faq.manage', ], writer: ['blog.draft', 'gallery.upload', 'events.create'], };