Files
siop2/packages/shared/src/contract.ts
pr-daaif 8adb561b63 feat(r0.10): apps/api — auth fermée par défaut, matrice en base, démo-login ADR-002, seed
- packages/shared : rôles/catégories, schémas Zod, contrat d'API ;
  pnpm contract → docs/openapi.json committée (règle d'or ADR-001)
- apps/api : NestJS 11 + Prisma 6, migration r0_identity (Role/Permission/User) ;
  guard JWT global + @Public() ; PermissionsGuard (@RequirePermission,
  matrice relue en base, cache 60 s) ; FileStorage (seul import MinIO) ; /health
- démo-login ADR-002 : module conditionnel DEMO_MODE (404 sinon, testé e2e),
  double verrou production, refus des comptes isDemo=false
- seed idempotent : 7 rôles, matrice complète (70 lignes), 7 comptes démo
- 19 tests Jest (unit + e2e) ; smoke test sur build de prod

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:55:04 +01:00

105 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 nest 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,
},
},
},
];