Wiki/bridge/src/lib/errors.ts
Corentin JOGUET 571f5c3426
Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
feat(auth): Bloc 4 — middleware OIDC-ready avec dual mode service-token + Authentik JWT
- Support JWT OIDC Authentik via jose + JWKS (cache 10min)
- Lookup Personne via PersonneRepo.findByEmail + cache Redis 60s
- Mapping groups Authentik + roles formation-hub vers scopes
- Mode OIDC active uniquement si AUTHENTIK_ISSUER + JWKS_URI + AUDIENCE set
- Service tokens brg_* inchanges, restent voie principale en local
2026-05-07 21:17:56 +02:00

62 lines
2.1 KiB
TypeScript

/**
* Erreurs metier typees pour le bridge.
* Chaque erreur a un code stable + statut HTTP + details serializables.
*/
export type ErrorCode =
| 'AUTH_REQUIRED'
| 'AUTH_INVALID'
| 'FORBIDDEN'
| 'FORBIDDEN_SCOPE'
| 'NOT_FOUND'
| 'VALIDATION_ERROR'
| 'RG_VIOLATION'
| 'CONFLICT'
| 'RATE_LIMITED'
| 'BASEROW_UNAVAILABLE'
| 'DOCMOST_UNAVAILABLE'
| 'INTERNAL';
export class BridgeError extends Error {
constructor(
public readonly code: ErrorCode,
public readonly status: number,
message: string,
public readonly details?: Record<string, unknown>,
) {
super(message);
this.name = 'BridgeError';
}
toJSON() {
return {
error: {
code: this.code,
message: this.message,
...(this.details ? { details: this.details } : {}),
},
};
}
}
export const errors = {
authRequired: () => new BridgeError('AUTH_REQUIRED', 401, 'Token absent'),
authInvalid: () => new BridgeError('AUTH_INVALID', 401, 'Token invalide'),
forbidden: (scope: string) =>
new BridgeError('FORBIDDEN_SCOPE', 403, `Scope requis : ${scope}`, { scope }),
forbiddenIdentity: (reason: string, details?: Record<string, unknown>) =>
new BridgeError('FORBIDDEN', 403, reason, details),
notFound: (entity: string, id: string | number) =>
new BridgeError('NOT_FOUND', 404, `${entity} introuvable`, { entity, id }),
validation: (issues: unknown) =>
new BridgeError('VALIDATION_ERROR', 400, 'Body invalide', { issues }),
rgViolation: (rule: string, message: string, details?: Record<string, unknown>) =>
new BridgeError('RG_VIOLATION', 422, message, { rule, ...details }),
conflict: (message: string, details?: Record<string, unknown>) =>
new BridgeError('CONFLICT', 409, message, details),
rateLimited: (retryAfter: number) =>
new BridgeError('RATE_LIMITED', 429, 'Too many requests', { retry_after: retryAfter }),
baserowDown: () => new BridgeError('BASEROW_UNAVAILABLE', 502, 'Baserow API unreachable'),
docmostDown: () => new BridgeError('DOCMOST_UNAVAILABLE', 502, 'Docmost API unreachable'),
internal: (message: string) => new BridgeError('INTERNAL', 500, message),
};