Files
siop2/docs/openapi.json
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

480 lines
14 KiB
JSON
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.

{
"openapi": "3.1.0",
"info": {
"title": "SIOP V2 API",
"version": "0.1.0",
"description": "GMAO ascenseurs — contrat R0 (auth, démo-login ADR-002, profil, santé). Généré depuis packages/shared/src/contract.ts — NE PAS ÉDITER À LA MAIN."
},
"paths": {
"/auth/login": {
"post": {
"operationId": "login",
"summary": "Connexion par e-mail et mot de passe",
"tags": [
"auth"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginRequest"
}
}
}
},
"responses": {
"200": {
"description": "Jeton émis",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AuthResponse"
}
}
}
},
"401": {
"description": "Identifiants invalides ou compte inactif"
}
}
}
},
"/auth/demo-accounts": {
"get": {
"operationId": "listDemoAccounts",
"summary": "Comptes de démonstration (ADR-002 — jamais de secret)",
"tags": [
"auth",
"demo"
],
"x-demo-only": true,
"description": "ADR-002 : cette route est absente (404) quand DEMO_MODE nest pas actif.",
"responses": {
"200": {
"description": "Comptes isDemo actifs",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DemoAccountsResponse"
}
}
}
}
}
}
},
"/auth/demo-login": {
"post": {
"operationId": "demoLogin",
"summary": "Connexion 1 clic sur un compte de démonstration (ADR-002)",
"tags": [
"auth",
"demo"
],
"x-demo-only": true,
"description": "ADR-002 : cette route est absente (404) quand DEMO_MODE nest pas actif.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DemoLoginRequest"
}
}
}
},
"responses": {
"200": {
"description": "Jeton émis",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AuthResponse"
}
}
}
},
"403": {
"description": "Le compte nest pas un compte de démonstration"
},
"404": {
"description": "Compte inconnu"
}
}
}
},
"/users/me": {
"get": {
"operationId": "getMe",
"summary": "Profil courant + matrice de permissions du rôle",
"tags": [
"users"
],
"security": [
{
"bearerAuth": []
}
],
"responses": {
"200": {
"description": "Profil",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MeResponse"
}
}
}
},
"401": {
"description": "Non authentifié"
}
}
}
},
"/health": {
"get": {
"operationId": "getHealth",
"summary": "État des dépendances (base, Redis, stockage)",
"tags": [
"health"
],
"responses": {
"200": {
"description": "État agrégé",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HealthResponse"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"AuthResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"accessToken": {
"type": "string"
},
"user": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
},
"displayName": {
"type": "string"
},
"role": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"enum": [
"Administrateur",
"Dispatcher",
"Technicien",
"Technicien limité",
"Gestionnaire",
"Demandeur",
"Vue seule"
]
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"isDemo": {
"type": "boolean"
}
},
"required": [
"id",
"email",
"displayName",
"role",
"isDemo"
],
"additionalProperties": false
}
},
"required": [
"accessToken",
"user"
],
"additionalProperties": false
},
"LoginRequest": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
},
"password": {
"type": "string",
"minLength": 1
}
},
"required": [
"email",
"password"
],
"additionalProperties": false
},
"DemoAccountsResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"accounts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"displayName": {
"type": "string"
},
"roleName": {
"type": "string",
"enum": [
"Administrateur",
"Dispatcher",
"Technicien",
"Technicien limité",
"Gestionnaire",
"Demandeur",
"Vue seule"
]
},
"initials": {
"type": "string",
"minLength": 1,
"maxLength": 3
}
},
"required": [
"id",
"displayName",
"roleName",
"initials"
],
"additionalProperties": false
}
}
},
"required": [
"accounts"
],
"additionalProperties": false
},
"DemoLoginRequest": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"userId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"userId"
],
"additionalProperties": false
},
"MeResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
},
"displayName": {
"type": "string"
},
"role": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"enum": [
"Administrateur",
"Dispatcher",
"Technicien",
"Technicien limité",
"Gestionnaire",
"Demandeur",
"Vue seule"
]
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"isDemo": {
"type": "boolean"
},
"permissions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"objectCategory": {
"type": "string",
"enum": [
"WORK_ORDERS",
"REQUESTS",
"ASSETS",
"LOCATIONS",
"METERS",
"PARTS",
"PURCHASE_ORDERS",
"PEOPLE_TEAMS",
"ANALYTICS",
"SETTINGS"
]
},
"canView": {
"type": "boolean"
},
"canViewOther": {
"type": "boolean"
},
"canCreate": {
"type": "boolean"
},
"canEdit": {
"type": "boolean"
},
"canDelete": {
"type": "boolean"
}
},
"required": [
"objectCategory",
"canView",
"canViewOther",
"canCreate",
"canEdit",
"canDelete"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"email",
"displayName",
"role",
"isDemo",
"permissions"
],
"additionalProperties": false
},
"HealthResponse": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"ok",
"degraded"
]
},
"services": {
"type": "object",
"properties": {
"database": {
"type": "string",
"enum": [
"up",
"down"
]
},
"redis": {
"type": "string",
"enum": [
"up",
"down"
]
},
"storage": {
"type": "string",
"enum": [
"up",
"down"
]
}
},
"required": [
"database",
"redis",
"storage"
],
"additionalProperties": false
}
},
"required": [
"status",
"services"
],
"additionalProperties": false
}
},
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}