Files
BRETT/shared/utils/permissions.ts
T
kibiandClaude Opus 5.5 06ac414457 Add audit log for moderation and admin actions
New audit_log table records actor, action, target and a JSON snapshot
of the details, so entries stay readable after the target is deleted;
the actor is set to NULL when the user is removed. logAudit() is called
from upload delete (single and bulk), comment delete, tag removal, role
create/delete, role permission changes, role assign/revoke and MOTD
updates. Logging failures only warn and never fail the action itself.

The admin page gets an audit card behind the new audit.view permission
with action filter, click-to-filter by user, readable summaries per
action and server-side pagination. Action catalog lives in
shared/utils/audit.ts, labels under admin.auditActions (de/en).

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:27:28 +02:00

43 lines
1.6 KiB
TypeScript

export const PERMISSIONS = [
{ key: 'admin.access', label: 'Admin-Bereich öffnen' },
{ key: 'roles.manage', label: 'Rollen & Berechtigungen verwalten' },
{ key: 'users.manage', label: 'Benutzer & Rollen verwalten' },
{ key: 'site.manage', label: 'Seiten-Einstellungen verwalten' },
{ key: 'comment.delete', label: 'Kommentare löschen' },
{ key: 'upload.delete', label: 'Uploads löschen' },
{ key: 'tag.delete', label: 'Tags löschen' },
{ key: 'audit.view', label: 'Audit-Log einsehen' },
] as const
export type PermissionKey = (typeof PERMISSIONS)[number]['key']
export const PERMISSION_KEYS: string[] = PERMISSIONS.map(p => p.key)
// System-Rollen: immer vorhanden, nicht löschbar
export const SYSTEM_ROLES = ['admin', 'moderator'] as const
interface Queryable {
query: (sql: string, params?: unknown[]) => Promise<unknown>
}
// Permission-Katalog idempotent einpflegen. Läuft sowohl im Nitro-Plugin
// (Dev-Start) als auch im manuellen migrate-Script. Die bestehende
// moderator-Rolle erhält dabei die bisherigen Moderationsrechte.
export async function seedPermissions(db: Queryable) {
for (const role of SYSTEM_ROLES) {
await db.query('INSERT IGNORE INTO roles (name) VALUES (?)', [role])
}
for (const p of PERMISSIONS) {
await db.query(
'INSERT INTO permissions (`key`, `label`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `label` = VALUES(`label`)',
[p.key, p.label],
)
}
await db.query(
`INSERT IGNORE INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id FROM roles r
JOIN permissions p ON p.\`key\` IN ('comment.delete', 'upload.delete', 'tag.delete')
WHERE r.name = 'moderator'`,
)
}