Manuel utilisateur ← App

⚙️ Incidents & Casse — Manuel technique

MAIDEO SERVICES · POC v1.2 · Septembre 2026 · Spec de référence : /opt/poc/poc-casse/SPEC.md

Sommaire
  1. Architecture
  2. Infrastructure & déploiement
  3. Sécurité
  4. Modèle de données
  5. Moteur de règles & machine d'états
  6. Référence API
  7. Intégration Maideo (sync)
  8. Frontend
  9. Exploitation
  10. Limites & passage en prod

1. Architecture

Navigateur (SPA Vue 3)
      │  HTTPS (TLS Let's Encrypt)
      ▼
Caddy (droplet maideo-lab)  poc-casse.lab.ikki.io
      ├── /api/*  → reverse_proxy 127.0.0.1:4701
      └── /*      → fichiers statiques /opt/poc/poc-casse/web/
                      ▼
              Fastify (conteneur poc-casse-api, port 4701)
                      │  driver mongodb
                      ▼
              MongoDB 7 (conteneur poc-casse-mongo, NON exposé)

Sync entrante (sens unique, 1×/heure) :
Mongo PROD Maideo (readonly) ──► script sync-poc-casse-missions.mjs
(droplet maideo-intelligence)  ──► POST /api/maideo/index (Bearer)

Deux principes structurants : le POC ne se connecte jamais à la production (la donnée prod est poussée vers lui, en lecture seule côté prod) et chaque conteneur Mongo est dédié au POC (aucune BDD partagée).

2. Infrastructure & déploiement

ComposantDétail
HôteDroplet maideo-lab — jamais de POC sur un droplet de prod
Racine/opt/poc/poc-casse/ (api/, web/, docker-compose.yml, .env, SPEC.md)
Conteneurspoc-casse-api (Node 22 alpine, Fastify 5, mongodb 6) · poc-casse-mongo (mongo:7, volume mongo_data)
RéseauAPI publiée sur 127.0.0.1:4701 uniquement ; Mongo sans clef ports (jamais exposée)
Reverse proxy/etc/caddy/poc/poc-casse.caddy — TLS automatique Let's Encrypt sur poc-casse.lab.ikki.io
Config.env : API_TOKEN (Bearer) · MONGO_URL et PORT injectés par compose

Commandes usuelles :

ssh maideo-lab
cd /opt/poc/poc-casse
docker compose up -d --build     # (re)déployer après modif du code api/
docker compose logs -f api       # logs applicatifs
docker compose restart api       # redémarrage simple
curl https://poc-casse.lab.ikki.io/api/health   # sonde publique

3. Sécurité

4. Modèle de données (MongoDB poc_casse)

Collection dossiers

{
  ref: "INC-2026-0001",            // compteur annuel (collection counters)
  status: "declare|instruction|decision|execution|cloture",
  level: "N1|N2|N3",               // calculé à la déclaration (refAmount)
  clientName: "…",
  missionRef: "2026-…",            // n° dossier Maideo (numeroDossier)
  maideoMissionId: "…", maideoClientId: "…",   // ids prod (lecture seule)
  workerName: "…", channel: "telephone|email|client_app|worker",
  description: "…", refAmount: 250 | null,
  owner: "…",                      // jamais vide si ouvert (invariant I1)
  insurance: { suggested, status, claimRef, declaredAt,
               ganContract: "314879172001", ganAgency: "Dreux" },
  decision: null | { type, amount, proposedBy, proposedAt,
                     directionRequired, status: "en_attente_direction|validee",
                     validatedBy, validatedAt },
  nextAction: { label, dueDate } | null,       // null seulement si clôturé (I2)
  messages: [{ at, actor, body }],
  history:  [{ at, actor, action, note }],
  createdAt, updatedAt, closedAt, outcome
}

Autres collections

CollectionRôleIndex
missions_indexMiroir des missions prod (numeroDossier, clientName, workerNames, status, prestation, isAbonnement, syncedAt) alimenté par la sync horairenumeroDossier unique, clientName
notificationsJournal de TOUTE notification émise (dossierId, type, recipient, channel, body, sentAt). Canal externe email/SMS hors scope : channel: "log"dossierId + sentAt
countersSéquence annuelle des références INC-{année}-{nnnn} (findOneAndUpdate atomique)

5. Moteur de règles & machine d'états

Seuils (constantes en tête de api/index.js)

ConstanteValeurChamp viséEffet
REF_THRESHOLD_N2150 €ref_amount (réclamé)< 150 → N1, sinon N2
REF_THRESHOLD_N3300 €ref_amount> 300 → N3 + assurance suggérée
DIRECTION_THRESHOLD300 €decision_amount (décision)> 300 → validation Direction requise, quel que soit le niveau

Montant réclamé inconnu (null) → N2 par prudence.

Machine d'états

declare → instruction → decision → execution → cloture

Invariants (vérifiés à chaque écriture d'un dossier non clôturé — HTTP 422)

Garde-fou formulations (spec §11)

Tant que decision.status !== 'validee', tout message client matchant /rembours|indemnis|dédommag|dedommage|compensation financière/i est rejeté (HTTP 422 formulation_bloquee). Protection contre la reconnaissance de responsabilité prématurée.

6. Référence API

Base : https://poc-casse.lab.ikki.io — header Authorization: Bearer <API_TOKEN> partout sauf /api/health.

Méthode · pathRôleCodes d'erreur notables
GET /api/healthSonde publique { ok, service, time }
GET /api/metaSeuils, infos Gan, liste des statuts (consommés par le front)401
GET /api/dossiers?status=&level=Liste (max 500, tri récent d'abord)401
POST /api/dossiersCréation. Body : numeroDossier? (préremplit depuis l'index), clientName, description, refAmount?, channel?, workerName?, owner?, actor?400 champs requis · 422 numeroDossier inconnu / invariant
GET /api/dossiers/:idDétail complet400 id · 404
POST /api/dossiers/:id/transitionChangement d'étape. Body : to, actor, note?, nextAction?422 transition interdite / décision non validée / invariant
POST /api/dossiers/:id/decisionProposer une décision. Body : type (remboursement | geste_commercial | refus | renvoi_assurance), amount, actor. > 300 € → en_attente_direction + notif Direction400 type/montant · 422 mauvaise étape
POST /api/dossiers/:id/validateValidation Direction. Body : actor, role: "direction"403 rôle · 422 rien en attente
POST /api/dossiers/:id/insuranceStatut Gan. Body : status (a_declarer | declaree | en_cours | acceptee | refusee), claimRef?, actor?400 statut
POST /api/dossiers/:id/messagesMessage client consigné. Body : body, actor?400 vide · 422 formulation_bloquee
POST /api/dossiers/:id/updateRéassignation owner / nextAction manuelle (invariants appliqués)422 invariant
GET /api/dossiers/:id/notificationsJournal des notifications du dossier (100 dernières)400 id
GET /api/statsCompteurs par statut/niveau, actions en retard, créations du mois401
POST /api/maideo/indexRéception d'un lot de missions (upsert par numeroDossier) — appelé par la sync uniquement400 items[]
GET /api/maideo/missions/search?q=Autocomplete déclaration (n° dossier ou nom client, 10 résultats, q ≥ 2 car.)401

7. Intégration Maideo (sens unique)

8. Frontend

9. Exploitation

BesoinCommande / emplacement
Santé de l'appcurl https://poc-casse.lab.ikki.io/api/health
Logs APIdocker compose logs -f api (dans /opt/poc/poc-casse)
Logs Caddyjournalctl -u caddy -f
DonnéesVolume Docker poc-casse_mongo_data — backup : docker exec poc-casse-mongo mongodump --out /tmp/dump
Rotation tokenModifier API_TOKEN dans .env puis docker compose up -d ; redistribuer le token aux utilisateurs
Modifier la config CaddyÉditer /etc/caddy/poc/poc-casse.caddycaddy validatesystemctl reload caddy
Forcer une sync missionsRelancer manuellement sync-poc-casse-missions.mjs sur maideo-intelligence

10. Limites connues & passage en prod