Design-Tokens aus docs/10-design-system.md als CSS-Variablen: Grundpalette, die sechs Klassifizierungsfarben, Eckenradius durchgehend 2 px, keine Schatten ausser dem Fokusring. Der Schwärzungsbalken mit dem einmaligen Durchwischen respektiert prefers-reduced-motion. Vier Tests halten die Tokens gegen das Dokument. IBM Plex selbst gehostet als woff2, latin und latin-ext getrennt über unicode-range, font-display: swap, immutable-Cache. Alle Oberflächentexte in lib/i18n/de.ts. Ein Test durchsucht app/ und components/ nach Umlauten im JSX und schlägt fehl, wenn dort deutscher Text steht. Er hat beim ersten Lauf gleich einen Verstoss gefunden. /api/health prüft Datenbank, Discord-Gateway und ClamAV. Die Gesamtbewertung fällt nur auf "down", wenn die Datenbank weg ist: die anderen Dienste haben eigene Healthchecks im Stack, und ein ausgefallener Virenscanner soll nicht den Web-Container als tot markieren. Strikte CSP mit Nonce, kein unsafe-inline für Skripte. Die erste Route liegt bei 100 kB JavaScript nach Kompression, Budget sind 120 kB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
/**
|
|
* Rechte-Enum aus docs/03-auth-und-rechte.md. RoleMapping.permissions ist in der
|
|
* Datenbank ein String[], diese Liste ist die verbindliche Menge gültiger Werte
|
|
* und die Grundlage der Kästchen im Adminbereich.
|
|
*/
|
|
export const PERMISSIONS = [
|
|
'case.read',
|
|
'case.create',
|
|
'case.edit',
|
|
'case.close',
|
|
'case.reopen',
|
|
'case.delete',
|
|
'case.edit_closed',
|
|
'entry.create',
|
|
'entry.correct',
|
|
'entry.edit_own',
|
|
'entry.edit_any',
|
|
'entry.retract',
|
|
'personnel.read',
|
|
'personnel.edit',
|
|
'template.manage',
|
|
'roles.manage',
|
|
'audit.read',
|
|
'export.approve',
|
|
'admin',
|
|
] as const
|
|
|
|
export type Permission = (typeof PERMISSIONS)[number]
|
|
|
|
/**
|
|
* Diese drei sind im Auslieferungszustand keiner Rolle zugeordnet. Der Leiter
|
|
* schaltet sie bewusst frei. Der Adminbereich weist beim Zuweisen darauf hin.
|
|
*/
|
|
export const OPT_IN_PERMISSIONS: readonly Permission[] = [
|
|
'entry.edit_own',
|
|
'entry.edit_any',
|
|
'entry.retract',
|
|
]
|
|
|
|
export function isPermission(value: string): value is Permission {
|
|
return (PERMISSIONS as readonly string[]).includes(value)
|
|
}
|
|
|
|
/** Aufgelöste Rechte eines Nutzers für genau einen Request. */
|
|
export type EffectivePermissions = {
|
|
userId: string
|
|
/** Höchste Stufe aller Rollen. */
|
|
clearanceLevel: number
|
|
/** Vereinigung aller Rollen. */
|
|
permissions: Set<Permission>
|
|
/** Aus BOOTSTRAP_ADMIN_IDS oder Recht 'admin'. */
|
|
isAdmin: boolean
|
|
}
|
|
|
|
export function can(p: EffectivePermissions, permission: Permission): boolean {
|
|
return p.isAdmin || p.permissions.has(permission)
|
|
}
|