import { PermissionKey } from '../auth/permissions/permission-catalog'; /** * Permissions an API key may carry. Deliberately a short allowlist, not the * whole catalog: a key is a long-lived secret stored in another system, so it * only ever gets what a machine integration needs. shop-sync:write lets the * shop book its own sales and returns - nothing else - and every booking is * idempotent and kept in the ledger, so a leaked key cannot erase history. * erp-inventory:read lets ERPNext read the inventory mirror (articles, * batches with remaining stock, stock movements, harvest totals) - read * only, no people, no notes. Removing an entry here also takes it away from * existing keys (see ApiKeysService.authenticate). */ export const API_KEY_SCOPES = [ 'shop-sync:read', 'shop-sync:write', 'erp-inventory:read', ] as const satisfies readonly PermissionKey[]; export type ApiKeyScope = (typeof API_KEY_SCOPES)[number]; /** * Scopes that book (change data). Every other scope only reads. A new key * never mixes both kinds: a leaked read key cannot book, a leaked booking * key cannot read (ARCHITECTURE.md, Annahme 14). */ export const API_KEY_WRITE_SCOPES = [ 'shop-sync:write', ] as const satisfies readonly ApiKeyScope[]; export function isApiKeyScope(value: string): value is ApiKeyScope { return (API_KEY_SCOPES as readonly string[]).includes(value); } function isWriteScope(scope: string): boolean { return (API_KEY_WRITE_SCOPES as readonly string[]).includes(scope); } /** True if the scopes hold at least one reading and one booking right. */ export function combinesReadAndWrite(scopes: readonly string[]): boolean { return scopes.some(isWriteScope) && scopes.some((s) => !isWriteScope(s)); }