Introduce ApiKey model, CRUD endpoints, and admin UI so agents can authenticate with permission-scoped keys. Normalize pubkeys to hex on login, dedupe legacy npub/hex user rows, and ignore .cursor in git. Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
// 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<EffectiveRole, number> = {
|
|
superadmin: 4,
|
|
admin: 3,
|
|
moderator: 2,
|
|
writer: 1,
|
|
guest: 0,
|
|
};
|
|
|
|
export const ALL_PERMISSION_KEYS: ReadonlySet<PermissionKey> = 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<AssignableRole, PermissionKey[]> = {
|
|
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'],
|
|
};
|