Commit fff82bab authored by Kourser's avatar Kourser
Browse files

Wiki : rattraper deux semaines de fonctionnalités, et refaire les captures

Le site de documentation n'avait pas bougé depuis le renommage du 28 août,
alors que vingt-neuf commits ont livré des fonctionnalités depuis.

LES DIX CAPTURES DATAIENT DU 23 JUILLET. Elles montraient encore
« Cythin », son logo « C » et une interface sombre qui n'existe plus : le
nettoyage du nom avait été textuel, et les PNG y ont échappé. Elles sont
toutes refaites, au même format que les originales — 1440×900 en densité
2 — et quatre s'y ajoutent : choix du scénario, séance partagée, espace
joueur d'une découverte, et la mise en situation à l'arrivée.

Deux d'entre elles ne montraient rien : la console d'animation et
l'espace joueur étaient vides, l'exercice de démonstration n'ayant ni
injects ni messages. Elles sont prises sur un exercice installé depuis un
kit, démarré, avec des stimuli déjà partis.

UN OUTIL POUR LES REFAIRE, plutôt qu'une demi-journée de clics tous les
six mois — c'est la seule façon qu'elles ne se périment plus :

    node scripts/captures/capturer.mjs

Chrome est piloté par le protocole DevTools, sans aucune dépendance :
Node 22+ fournit un client WebSocket.

LA CONSOLE D'INSTANCE N'ÉTAIT DÉCRITE NULLE PART. Supervision, annonce,
adresse publique, courriel, inscription publique, suspension, bac à
sable et ses séances, réarmement d'un second facteur : rien. Elle a
désormais sa page.

L'installation ne parlait ni de l'installateur, ni de l'amorçage du
premier compte, ni du relais de courriel — elle menait au jeu de
démonstration comme seule porte d'entrée, dont les mots de passe sont
publiés.

LA SAUVEGARDE ÉTAIT FAUSSE, et c'est le plus coûteux : le README
affirmait qu'« aucun volume de fichiers séparé n'est requis ». Les
fichiers téléversés — pièces jointes, avatars, logos — sont écrits sur
disque dans un volume distinct depuis qu'ils ne sont plus stockés en
base. Qui suivait cette procédure sauvegardait la base et perdait tout le
reste.

La page de référence promettait qu'« une variable non renseignée est
laissée telle quelle » : c'est l'inverse depuis qu'elles ont toutes un
repli. Le tableau était par ailleurs amputé de cinq variables, et le rôle
d'administrateur d'instance n'y figurait pas.

Le guide joueur ignorait la mise en situation, le décompte de séance et
les appels vocaux. Le journal des nouveautés s'arrêtait au 21 août.

Co-Authored-By: default avatarClaude Opus 5 <noreply@anthropic.com>
parent 93c5e8c2
Loading
Loading
Loading
Loading
+15 −6
Changes for README.md: 15 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -205,18 +205,27 @@ quiconque atteint l'instance — l'écran de rédaction le dit au-dessus du cham

## Sauvegarde & restauration

Les données sont dans le volume PostgreSQL `db_data`.
Les données vivent dans **deux volumes**, et sauvegarder l'un sans l'autre laisse une
restauration incomplète :

- `db_data` — la base PostgreSQL ;
- `uploads_data` — les fichiers téléversés (pièces jointes de la GED, avatars, logos), écrits
  sur disque dans `UPLOADS_DIR` et *non* stockés en base.

```bash
# Sauvegarde
# Base
docker compose exec -T db pg_dump -U enkadenn enkadenn > sauvegarde.sql

# Restauration (base vide)
cat sauvegarde.sql | docker compose exec -T db psql -U enkadenn enkadenn
# Fichiers téléversés
docker run --rm -v enkadenn_uploads_data:/data -v "$PWD":/sortie alpine   tar czf /sortie/televerses.tar.gz -C /data .
```

Les documents/pièces jointes sont stockés en base (texte) au stade actuel ; aucun volume de
fichiers séparé n'est requis.
Le nom réel du volume est préfixé par celui du projet Compose ; `docker volume ls` le donne.

```bash
# Restauration de la base (base vide)
cat sauvegarde.sql | docker compose exec -T db psql -U enkadenn enkadenn
```

## Découverte sans compte (bac à sable public)

+83 −0
Changes for docs-site/docs/administration-instance.md: 83 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
sidebar_position: 6
title: Console d'instance
---

# Console d'instance

Réservée au rôle **`INSTANCE_ADMIN`**, qui n'existe que dans l'organisation *système*. Il ne s'agit pas d'administrer une organisation cliente — c'est l'objet de la page [Administration](administration.md) — mais **l'instance elle-même** : ce qu'elle expose au public, qui peut y entrer, et ce qui y tourne.

:::caution Second facteur obligatoire
L'accès à cette console exige un second facteur, et l'exigence est immédiate : le compte doit l'enrôler à sa première connexion, sans délai de grâce.
:::

## Supervision

Des comptages seuls — organisations, comptes, exercices, participants —, **jamais de donnée personnelle**. C'est ce qui permet de suivre la charge d'une instance sans lire le contenu de ses clients.

## Annonce aux utilisateurs

Un bandeau visible de tous, **y compris avant connexion** : message, niveau (information, avertissement, alerte) et date d'expiration. Utile pour prévenir d'une maintenance. **Retirer l'annonce** le fait disparaître immédiatement.

## Adresse publique de l'instance

Préfixe **tous** les liens envoyés par courriel — accès joueur, écran de salle, activation, réinitialisation. Réglable ici sans redéploiement ; ce réglage **prime** sur la variable `APP_URL`. L'origine réellement appliquée est affichée, ce qui évite de chercher pourquoi un lien pointe ailleurs.

## Envoi de courriel

Les réglages SMTP saisis ici **priment en bloc** sur les variables d'environnement dès qu'un hôte est renseigné, et le mot de passe est **chiffré avant d'être stocké** — contrairement à `.env`, où il est lisible par quiconque peut faire un `docker inspect`. **Envoyer un test** propage l'erreur du serveur telle quelle : c'est l'objet d'un test.

:::note
Sans serveur de courriel, les **invitations sont impossibles** et le formulaire disparaît : l'interface ne propose pas un envoi qui n'aurait pas lieu. Les comptes se créent alors directement, avec un mot de passe temporaire affiché une seule fois.
:::

## Inscription publique

Trois modes : **fermée** (défaut), **modérée** (une demande est déposée, l'administrateur l'approuve ou la refuse avec un motif obligatoire), ou **libre-service**. Le visiteur ne saisit que le nom de son organisation : l'identifiant public en est déduit automatiquement.

## Organisations

Créer, régler les plafonds d'usage (exercices, membres, stockage, rétention propre), **suspendre** et **supprimer**.

- Une organisation **suspendue** bloque aussi ses joueurs et ses écrans de salle, avec le motif affiché. L'effet est différé de **30 secondes au plus** : l'état est mis en cache, arbitrage assumé pour ne pas relire la base à chaque requête.
- **Observer / Intervenir** bascule dans une organisation cliente. La bascule est **tracée**, et en lecture seule par défaut.
- La **suppression** redemande l'identifiant public en confirmation, et efface tout en cascade.
- **Réarmer le second facteur** d'un membre demande un motif, qui est tracé.

## Bac à sable public

**Fermé par défaut.** L'ouvrir expose une route publique et non authentifiée : la confirmation est explicite, et l'action est tracée.

Deux réglages, bornés pour qu'ils ne puissent pas vider de leur sens la purge et le plafond : la **durée de vie d'une séance** (5 minutes à 24 heures) et le **nombre de séances simultanées** (50 au plus). Un champ vide applique la valeur par défaut.

:::note
La valeur est enregistrée quand vous **quittez le champ**, pas à chaque frappe — sans quoi il serait impossible de saisir un nombre dont le début est hors bornes.
:::

### Les séances en cours

Une organisation éphémère est exclue de la liste des organisations clientes — ce n'en est pas une. Ce tableau est donc le **seul endroit** où l'on voit ce que le bac à sable fait tourner : scénario, identifiant public cliquable, rôles pris sur le total, dernière activité, temps restant. Il se relit tout seul.

« Jamais ouverte » distingue une séance abandonnée d'une séance active : un rôle peut avoir été pris sans que personne n'ait suivi son lien.

Deux gestes, **tracés tous les deux** :

- **+30 min** repousse l'échéance. Les accès des joueurs sont repoussés **avec elle** — sans cela, la page de séance resterait ouverte pendant qu'ils se feraient refuser à la porte. Jamais au-delà de 24 heures au total.
- **Fermer** supprime la séance sur-le-champ, avec tout son contenu, après confirmation : des joueurs sont peut-être en train de jouer.

Ces gestes ne peuvent atteindre **que** des organisations éphémères : une organisation cliente y est hors de portée.

## Journal d'instance

Toutes les actions sensibles y sont consignées avec leur auteur et, le cas échéant, leur motif : ouverture du bac à sable, suspension, bascule dans une organisation, réarmement d'un second facteur, prolongation et fermeture d'une séance. Il est purgé après 730 jours par défaut.

## Réarmer un second facteur perdu

L'administrateur d'organisation peut être réarmé depuis cette console. L'administrateur **d'instance**, lui, n'a personne au-dessus de lui : la reprise en main passe par le serveur.

```bash
docker compose exec app node apps/api/dist/scripts/reset-mfa.js \
  --email=root@exemple.org --confirm
```

La commande demande un accès au serveur et à la base — c'est précisément ce qui en fait un privilège acceptable.
+18 −1
Changes for docs-site/docs/administration.md: 18 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@ Les fonctions d'administration sont réservées au rôle **Administrateur** (`TE
| **ANIMATOR** | Créer et animer des exercices. |
| **OBSERVER** | Suivre un exercice et prendre des notes (RETEX). |

Un rôle de plus existe, hors de ce tableau : **`INSTANCE_ADMIN`**, qui n'administre pas une organisation mais l'instance entière. Voir [Console d'instance](administration-instance.md).

Le rôle est porté par l'**appartenance** à une organisation, pas par le compte : la même personne peut être animatrice ici et conceptrice ailleurs, et bascule de l'une à l'autre depuis le sélecteur d'organisation.

## Gérer les utilisateurs

Depuis **Utilisateurs**, un administrateur peut :
@@ -23,12 +27,25 @@ Depuis **Utilisateurs**, un administrateur peut :
- **Ajouter un utilisateur** (nom, e-mail, rôle, mot de passe initial) ;
- **Modifier** un utilisateur (nom, e-mail, rôle) ;
- **Réinitialiser le mot de passe** — un mot de passe temporaire est affiché une seule fois, à transmettre à l'utilisateur ;
- **Supprimer** un utilisateur.
- **Supprimer** un utilisateur ;
- **Inviter** quelqu'un par courriel — il choisit lui-même son mot de passe en suivant le lien reçu.

:::note Sans serveur de courriel
Le formulaire d'invitation **disparaît** : l'interface ne propose pas un envoi qui n'aurait pas lieu. Il reste l'ajout direct, avec un mot de passe temporaire affiché une seule fois, à transmettre autrement.
:::

:::caution Moindre privilège
Attribuez le rôle le plus restreint suffisant à chaque personne. Réservez `TENANT_ADMIN` à un petit nombre de responsables.
:::

## Mon compte

Chacun gère son propre compte, sans passer par un administrateur : **nom affiché**, **mot de passe** (le mot de passe actuel est redemandé) et **second facteur**.

L'**adresse de courriel** est modifiable, et le changement invalide les liens d'activation et de réinitialisation encore en attente pour l'ancienne adresse — un lien encore valable serait une porte laissée ouverte.

Le **second facteur** (TOTP, application d'authentification) s'enrôle depuis cette page. Des **codes de secours** à usage unique sont remis une seule fois à l'enrôlement : notez-les ailleurs que dans le téléphone qui porte l'application.

## Archiver et supprimer un exercice

Le cycle recommandé est **Clôturer → Archiver → (éventuellement) Supprimer**.
+43 −3
Changes for docs-site/docs/guide-joueur.md: 43 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -26,14 +26,32 @@ L'espace joueur regroupe, par onglets, les canaux de la crise :
| **Documents** | L'espace documentaire (procédures, kits de communication, pièces jointes). |
| **Annuaire** | Les contacts de l'exercice (données fictives). |
| **Main courante** | Le journal de bord partagé. |
| **Fiches réflexes** | Les attendus de chaque rôle — visible si l'animateur ouvre le volet. |
| **Téléphone** | Les appels vocaux, si l'animateur les a ouverts sur cet exercice. |
| **RETEX** | Le questionnaire de retour d'expérience — visible uniquement quand l'animateur ouvre la collecte. |

## En arrivant

À l'ouverture de votre lien, une fenêtre d'accueil vous présente **la situation, puis votre rôle** :

- le **dossier de mise en situation** — qui est l'organisation, ce qu'elle fait, de quoi elle dépend. C'est le contexte que tout le monde partage ;
- votre **fiche réflexe** — votre mission, les risques de votre poste, et les réflexes attendus de vous.

![Mise en situation et fiche réflexe à l'ouverture de l'espace joueur](/img/14-mise-en-situation.png)

Le bouton **ⓘ La situation**, dans l'en-tête, rouvre ce dossier à tout moment : c'est en pleine crise qu'on doute du contexte.

:::note Séance limitée dans le temps
Si votre accès a une échéance — c'est le cas des séances de découverte —, un **décompte** est affiché en permanence dans l'en-tête, et passe au rouge dans le dernier quart d'heure.
:::

## Bien démarrer

1. Ouvrez le **lien** reçu de l'animateur. Votre rôle et votre identité s'affichent en haut.
2. Surveillez la **Messagerie** et le **Chat** : les stimuli y arrivent au fil de l'eau.
3. Gardez un œil sur les **Réseaux sociaux** et la **Presse** : la pression médiatique fait partie de la crise.
4. Consignez vos décisions et actions dans la **Main courante**.
2. Lisez la **mise en situation** et votre **fiche réflexe** avant de vous lancer.
3. Surveillez la **Messagerie** et le **Chat** : les stimuli y arrivent au fil de l'eau.
4. Gardez un œil sur les **Réseaux sociaux** et la **Presse** : la pression médiatique fait partie de la crise.
5. Consignez vos décisions et actions dans la **Main courante**.

## Répondre

@@ -64,6 +82,28 @@ Prenez le réflexe de **tracer** vos décisions dans la main courante : elles al

Avant le lancement, l'animateur peut lancer une **vérification des accès**. Un bandeau apparaît alors en haut de votre espace : confirmez d'un clic que tout fonctionne, et signalez au besoin un souci dans le champ prévu (son, affichage, canal manquant…). Rien ne quitte l'environnement d'exercice.

## Appels vocaux

Quand l'animateur les a ouverts, un onglet **Téléphone** apparaît : l'animation peut vous appeler, et vous décrochez depuis le navigateur. La sonnerie s'affiche **par-dessus l'onglet courant** — un appel qui ne sonnerait que sur son propre onglet passerait inaperçu — et s'arrête d'elle-même au bout de 45 secondes sans réponse.

:::note
Sans serveur ICE/TURN configuré sur l'instance, aucune donnée ne sort : la liaison ne s'établit qu'entre navigateurs d'un même réseau. Les appels sont toujours **fermés** sur une séance de découverte.
:::

## Découvrir sans compte

Si l'instance l'a ouvert, une **séance de découverte** se lance sans aucun compte, depuis la page de connexion. Vous choisissez un scénario, puis vous recevez **l'adresse de la séance** : transmettez-la à vos collègues, chacun l'ouvre et prend un rôle libre de la cellule. Les rôles déjà pris apparaissent grisés.

![Choix du scénario](/img/11-decouverte-scenarios.png)

Les rôles déjà pris apparaissent grisés, et le décompte de la séance est visible en permanence.

![Page de séance partagée : rôles pris et rôles libres](/img/12-decouverte-seance.png)

Tout est supprimé à l'échéance de la séance.

![Espace joueur d'une séance de découverte, avec son décompte](/img/13-decouverte-espace-joueur.png)

## Le questionnaire de RETEX

À la fin de l'exercice, l'animateur peut ouvrir un **questionnaire de retour d'expérience** : un onglet **RETEX** apparaît dans votre espace.
+90 −21
Changes for docs-site/docs/installation.md: 90 added lines, 21 removed lines.
Original line number Diff line number Diff line
@@ -5,57 +5,109 @@ title: Installation & déploiement

# Installation & déploiement

Enkadenn se déploie en une commande avec **Docker Compose** : un conteneur PostgreSQL (`db`) et l'application monolithe (`app`, API + interface web) servie sur le port `3000`.
Enkadenn se déploie avec **Docker Compose** : un conteneur PostgreSQL (`db`) et l'application monolithe (`app`, API + interface web) servie sur le port `3000`.

## Prérequis

- Docker et Docker Compose
- Docker et Docker Compose v2
- Un accès au dépôt du projet

## 1. Configurer l'environnement
## Chemin recommandé : l'installateur

Copiez le modèle et renseignez vos valeurs :
```bash
./install.sh
```

Le script pose les questions dont la réponse ne se devine pas, écrit le fichier `.env`, lance la pile, attend que l'API réponde, puis **crée le premier compte d'administration**. Il est **bilingue** : la langue est devinée depuis la locale du système, puis confirmée à la première question. `ENKADENN_LANGUE=en ./install.sh` (ou `=fr`) l'impose sans la demander.

Ce qu'il vérifie avant d'écrire quoi que ce soit :

- que Docker répond et que Compose v2 est présent ;
- qu'une source d'aléa existe — `openssl`, ou `/dev/urandom` à défaut. Découvrir à mi-chemin qu'on ne sait pas produire de secret laisserait un `.env` à moitié écrit ;
- que le port demandé n'est pas déjà pris sur la machine ;
- **qu'aucune base de données de ce projet n'existe déjà**. PostgreSQL ne lit `POSTGRES_PASSWORD` qu'à la *création* de son volume : sur une base existante, un mot de passe neuf serait écrit dans `.env` sans jamais être appliqué, et l'application ne pourrait plus s'y connecter. Le script propose alors de ressaisir le mot de passe d'origine, ou de détruire la base en connaissance de cause.

Si un `.env` existe déjà, trois issues sont offertes : le **compléter** (vos valeurs sont conservées, seules les variables manquantes sont ajoutées — c'est le cas d'une mise à jour), le **remplacer** avec sauvegarde horodatée, ou renoncer.

:::note Installation sans questions
L'installateur est interactif. Pour une installation automatisée, écrivez `.env` vous-même à partir de `.env.example`, puis `docker compose up --build -d`.
:::

## À la main

```bash
cp .env.example .env
# Secret de session, obligatoire :
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env
docker compose up -d --build
```

Variables importantes :
Au démarrage, le conteneur `app` **applique automatiquement les migrations** de base de données, puis lance l'API. L'application est ensuite disponible sur `http://localhost:3000`.

![Page de connexion](/img/01-connexion.png)

### Variables

| Variable | Rôle |
|----------|------|
| `JWT_SECRET` | **Obligatoire.** Secret de signature des sessions. Générez-le, par ex. `openssl rand -hex 32`. |
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | Identifiants de la base. |
| `APP_URL` | URL publique de l'application (utilisée dans les liens joueurs). |
| `JWT_SECRET` | **Obligatoire.** Signe les sessions **et** dérive la clé de chiffrement des secrets stockés. Le changer déconnecte tout le monde et rend illisible le mot de passe SMTP enregistré en base. |
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | Identifiants de la base. Le mot de passe n'est lu qu'à la création du volume. |
| `APP_URL` | Adresse publique, préfixant tous les liens envoyés (joueur, activation, mot de passe). Réglable ensuite depuis la console d'instance, qui prime. |
| `PORT` | Port exposé (défaut `3000`). |
| `TRUSTED_PROXIES` | Nombre de proxys inverses devant l'API. **À renseigner dès qu'il y en a un**, sinon le plafonnement compte l'IP du proxy pour tout le monde. |
| `SECRETS_KEY` | Clé de chiffrement des secrets stockés. Absente, elle est dérivée de `JWT_SECRET` ; la définir séparément permet de faire tourner `JWT_SECRET` sans rendre ces secrets illisibles. |
| `RETENTION_DAYS` | Purge automatique des exercices archivés après N jours (`0` = désactivé). |
| `AUDIT_RETENTION_DAYS` / `SIGNUP_RETENTION_DAYS` | Rétention du journal d'audit (730 j) et des demandes d'inscription traitées (90 j). |
| `DOCS_URL` | Adresse d'un wiki public, liée depuis la page de connexion. Vide = aucun lien. |
| `WEBRTC_ICE_SERVERS` | Serveurs ICE/TURN pour les appels vocaux, en JSON. Vide = aucune donnée ne sort de l'instance, la liaison ne s'établit qu'au sein d'un même réseau. |

:::caution
Ne committez jamais votre fichier `.env`. Choisissez un `JWT_SECRET` fort et un mot de passe PostgreSQL robuste.
Ne committez jamais votre fichier `.env`. Il porte vos secrets, et l'installateur lui applique les permissions `600`.
:::

## 2. Démarrer
## Le premier compte d'administration

Une base neuve ne contient **aucun compte**, et l'inscription publique est fermée par défaut : sans cette étape, personne ne peut se connecter. `install.sh` s'en charge. À la main :

```bash
docker compose up -d --build
printf '%s' 'votre-mot-de-passe' | docker compose exec -T app \
  node apps/api/dist/scripts/amorcer-instance.js \
  --email=admin@mon-domaine.fr --nom='Prénom Nom'
```

Au démarrage, le conteneur `app` **applique automatiquement les migrations** de base de données, puis lance l'API. L'application est ensuite disponible sur `http://localhost:3000`.
Le mot de passe se transmet **par un tube, jamais en argument** : la ligne de commande d'un processus est lisible par tout utilisateur de la machine, et resterait dans l'historique du shell.

![Page de connexion](/img/01-connexion.png)
La commande refuse de s'exécuter si l'instance a déjà un administrateur — elle sert à amorcer, pas à s'ajouter à une instance en service. Les comptes suivants se créent depuis la console d'instance, qui trace qui les a créés.

## 3. Comptes de démonstration
:::caution Second facteur obligatoire
L'administration d'instance exige un second facteur, et l'exigence est immédiate : prévoyez l'appareil qui le portera dès la première connexion.
:::

## Envoi de courriel

Sans relais, l'instance fonctionne mais **n'envoie rien** : ni invitation, ni activation de compte, ni réinitialisation de mot de passe. Les comptes doivent alors être créés à la main et les accès transmis autrement.

Deux façons de le déclarer, et elles ne se valent pas :

- **depuis la console d'instance** — recommandé : le mot de passe est **chiffré** avant d'être stocké ;
- **dans `.env`** (`SMTP_HOST`, `SMTP_PORT`, `SMTP_SECURE`, `SMTP_USER`, `SMTP_PASSWORD`, `MAIL_FROM`) — le mot de passe y est en clair, lisible par quiconque peut faire un `docker inspect`. C'est le chemin d'amorçage, que l'installateur propose. Dès qu'un hôte est saisi en console, **ses réglages priment en bloc** sur ceux de l'environnement.

:::caution TLS exigé, sans dérogation
Le produit refuse une session SMTP en clair, même quand le relais l'accepte : STARTTLS est exigé (TLS 1.2 minimum). Un relais interne sur le port 25 sans STARTTLS ne conviendra pas.
:::

L'authentification, elle, est facultative : un relais qui reconnaît ses clients à leur adresse réseau fonctionne sans identifiants.

## Jeu de données de démonstration

En environnement de développement, le script de **seed** crée des organisations de démonstration, chacune avec un compte administrateur :
Deux organisations cloisonnées, leurs comptes et un exercice prêt à jouer :

```bash
pnpm --filter @enkadenn/api db:seed
docker compose exec app node_modules/.bin/ts-node apps/api/prisma/seed.ts
```

Les adresses et le **mot de passe par défaut** sont définis dans `apps/api/prisma/seed.ts`.

:::danger À faire dès la première connexion
Changez immédiatement le mot de passe par défaut des comptes créés par le seed. Ces identifiants ne doivent jamais servir en production.
:::danger Identifiants publics
Le mot de passe de ces comptes est publié dans le dépôt. Réservez ce jeu à un poste de test, **jamais à une instance accessible**.
:::

## Mettre à jour
@@ -66,4 +118,21 @@ Après avoir récupéré une nouvelle version du code :
docker compose up -d --build
```

Les nouvelles migrations sont appliquées au redémarrage. Les kits fournis sont importés dans chaque organisation au premier chargement de la bibliothèque.
Les nouvelles migrations sont appliquées au redémarrage. Si la nouvelle version attend des variables absentes de votre `.env`, relancez `./install.sh` et choisissez **compléter** : vos valeurs sont conservées, seules les manquantes sont ajoutées.

## Sauvegarder

Les données vivent dans **deux volumes**, et sauvegarder l'un sans l'autre laisse une restauration incomplète : la base (`db_data`) et les fichiers téléversés (`uploads_data` — pièces jointes de la GED, avatars, logos, écrits sur disque et non stockés en base).

```bash
# Base
docker compose exec -T db pg_dump -U enkadenn enkadenn > sauvegarde.sql

# Fichiers téléversés (le nom du volume est préfixé par celui du projet Compose)
docker run --rm -v enkadenn_uploads_data:/data -v "$PWD":/sortie alpine \
  tar czf /sortie/televerses.tar.gz -C /data .
```

:::danger
`docker compose down -v` détruit la base **et** les fichiers téléversés, sans confirmation.
:::
Loading