import type { z } from 'zod'; import { AuthResponseSchema, DemoAccountsResponseSchema, DemoLoginRequestSchema, LoginRequestSchema, } from './schemas/auth'; import { MeResponseSchema } from './schemas/users'; import { HealthResponseSchema } from './schemas/health'; /** * Contrat d'API R0 — source unique de vérité (règle d'or ADR-001). * `scripts/generate-openapi.ts` en dérive `docs/openapi.json` (committée) ; * les clients web/mobile sont générés depuis cette spec, dans le même commit. */ export interface ApiOperation { operationId: string; method: 'get' | 'post' | 'put' | 'patch' | 'delete'; path: string; summary: string; tags: string[]; /** Route annotée @Public() côté API (pas de JWT requis). */ isPublic?: boolean; /** ADR-002 : la route N'EXISTE PAS (404) si DEMO_MODE n'est pas actif. */ demoOnly?: boolean; request?: { name: string; schema: z.ZodType }; responses: Record< number, { description: string; name?: string; schema?: z.ZodType } >; } export const API_CONTRACT: ApiOperation[] = [ { operationId: 'login', method: 'post', path: '/auth/login', summary: 'Connexion par e-mail et mot de passe', tags: ['auth'], isPublic: true, request: { name: 'LoginRequest', schema: LoginRequestSchema }, responses: { 200: { description: 'Jeton émis', name: 'AuthResponse', schema: AuthResponseSchema }, 401: { description: 'Identifiants invalides ou compte inactif' }, }, }, { operationId: 'listDemoAccounts', method: 'get', path: '/auth/demo-accounts', summary: 'Comptes de démonstration (ADR-002 — jamais de secret)', tags: ['auth', 'demo'], isPublic: true, demoOnly: true, responses: { 200: { description: 'Comptes isDemo actifs', name: 'DemoAccountsResponse', schema: DemoAccountsResponseSchema, }, }, }, { operationId: 'demoLogin', method: 'post', path: '/auth/demo-login', summary: 'Connexion 1 clic sur un compte de démonstration (ADR-002)', tags: ['auth', 'demo'], isPublic: true, demoOnly: true, request: { name: 'DemoLoginRequest', schema: DemoLoginRequestSchema }, responses: { 200: { description: 'Jeton émis', name: 'AuthResponse', schema: AuthResponseSchema }, 403: { description: 'Le compte n’est pas un compte de démonstration' }, 404: { description: 'Compte inconnu' }, }, }, { operationId: 'getMe', method: 'get', path: '/users/me', summary: 'Profil courant + matrice de permissions du rôle', tags: ['users'], responses: { 200: { description: 'Profil', name: 'MeResponse', schema: MeResponseSchema }, 401: { description: 'Non authentifié' }, }, }, { operationId: 'getHealth', method: 'get', path: '/health', summary: 'État des dépendances (base, Redis, stockage)', tags: ['health'], isPublic: true, responses: { 200: { description: 'État agrégé', name: 'HealthResponse', schema: HealthResponseSchema, }, }, }, ];