# Provisionner une base MyInfoMate — préprod ou production Le flux complet, du serveur nu à une base Postgres remplie avec les données MongoDB. Éprouvé le **2026-09-07** sur la préprod `51.77.222.154` : 4 instances, 315 sections, 2383 ressources migrées sans perte. > Ces scripts sont en PowerShell mais s'exécutent depuis `cmd` via `run.cmd` — > dans `cmd`, un `.ps1` n'est pas exécuté, il est passé à l'association de fichier > et ne fait rien. > > ``` > run.cmd 1-bootstrap-superadmin.ps1 -Email moi@exemple.be > ``` --- ## 0. Prérequis sur le serveur ```bash docker image prune -f # 7,5 Go récupérés sur la préprod : 3 ans de builds df -h / # il faut ~1 Go pour les images ``` ## 1. La base ```bash cp .env.example .env # puis remplir POSTGRES_PASSWORD docker-compose -p myim-preprod -f docker-compose.postgres.yml up -d ``` **Le `-p` n'est pas optionnel.** Compose est en 1.21.0 (2018) sur ce serveur : il ne connaît pas la clé `name:`, et sans `-p` il déduit le projet du dossier courant — lancé depuis `/home/debian`, il réutiliserait le volume `debian_postgres-data`, c'est-à-dire la base cassée d'octobre 2025. Vérifier que le port n'est **pas** exposé : ```bash docker port myim_pg # doit afficher 127.0.0.1:5432, jamais 0.0.0.0 ``` ## 2. Le schéma Aucun `Migrate()` au démarrage : les migrations s'appliquent explicitement. Depuis le poste de dev, à travers un tunnel SSH : ```bash ssh -f -N -L 5433:127.0.0.1:5432 -p 55522 user@serveur ``` ```powershell $env:MIGRATIONS_CONNECTION = "Host=127.0.0.1;Port=5433;Database=my_info_mate;Username=myim;Password=..." dotnet ef database update --project ManagerService ``` `MIGRATIONS_CONNECTION` est prévue pour ça dans `MyInfoMateDbContextFactory`. Contrôler ensuite que `postgis` **et** `vector` sont bien installées : ```sql select extname, extversion from pg_extension; ``` ## 3. Le Mongo source `MigrationController` lit un MongoDB **vivant**, pas des fichiers. Un export JSON ne suffit donc pas : il faut le restaurer dans un Mongo jetable. ```bash docker run -d --name myim_mongo_src -p 27018:27017 mongo:6 docker run --rm --network container:myim_mongo_src -v "$PWD:/src:ro" mongo:6 \ mongorestore --uri mongodb://localhost:27017 --gzip --archive=/src/dump.gz --drop ``` ⚠️ **Ne jamais pointer sur le Mongo de production.** Les `DatabaseService` exposent `InsertOne`/`ReplaceOne`/`DeleteOne`, et `dryRun` ne protège que Postgres. ## 4. Le compte SuperAdmin `MigrationController` est `[Authorize(Policy = SuperAdmin)]`, et la migration n'en crée aucun — MongoDB n'a pas de champ `Role`. C'est donc le préalable obligé. ``` run.cmd 1-bootstrap-superadmin.ps1 -Email moi@exemple.be ``` Il affiche deux `INSERT` à exécuter dans le client SQL (**penser à commiter**), puis la commande de l'étape suivante. Les mots de passe étant hachés en scrypt, on laisse l'application faire le hachage via `set-password` : personne d'autre que toi ne connaît le mot de passe. ## 5. Le service, en local ``` run.cmd 2-run-service-local.ps1 ``` Le brancher en local évite de builder une image, donc d'affronter au même moment le `Dockerfile` de manager-app (Flutter 3.7 = Dart 2.19, alors que le `pubspec` exige `>= 3.1`) et la source NuGet `git.dev-espaces-naturels.lu` qui répond 401. Puis, dans une autre fenêtre : ``` run.cmd 3-set-password.ps1 -Token run.cmd 4-get-jwt.ps1 -Email moi@exemple.be ``` ## 6. La migration ``` run.cmd 5-migrate.ps1 # simulation run.cmd 5-migrate.ps1 -Apply -InstanceId # pour de vrai, une instance ``` **Commencer par la plus petite instance.** Chaque appel est une transaction unique : en cas d'échec, rien n'est écrit. > ⚠️ **Un dry run ne peut détecter aucune violation de contrainte** : il n'écrit rien, > donc `SaveChanges` n'est jamais appelé. Un `NOT NULL` violé ne sort qu'au run réel. > C'est exactement ce qui est arrivé le 07/09/2026 — `LastName` null sur > `test@email.be` — et commencer par la plus petite instance l'a fait apparaître > en 3 secondes sur 1 utilisateur au lieu de 10. Compter 2 à 5 min : la sonde HEAD interroge chaque blob Firebase, 30 à la fois. Elle tourne **aussi** en dry run. ## 7. Le backfill des tailles ``` POST /api/Resource/backfill-storage?dryRun=true puis dryRun=false ``` La sonde HEAD est **instable sous charge** : une ressource peut arriver à `SizeBytes = 0` alors que son blob existe. Le 07/09, le backfill a rattrapé une ressource sur 2383 et confirmé 3 vrais 404. Un `SizeBytes` à 0 n'est donc jamais une preuve de blob manquant. ## 8. Nettoyage et vérifications ```sql -- rattacher le SuperAdmin à une vraie instance, puis supprimer l'échafaudage update "Users" set "InstanceId" = '' where "Id" = 'sa-bootstrap-0001'; delete from "Instances" where "Id" = 'sa-bootstrap'; ``` Puis `pg_dump -Fc` (voir `../backup/`), et les vérifications qui ne se voient qu'à l'œil : login manager, une app visiteur avec sa clé API, un parcours, une carte, un PDF, un quiz avec ses questions. --- ## Ce qui a mal tourné, et qu'on ne refait pas | | | |---|---| | **`5432:5432`** sur le Postgres précédent | exposé à l'internet avec `mym`/`mym` du 06/06 au 22/10/2025 : **1 074 465 échecs d'authentification** sur autant de noms distincts. Le compte `mym` n'a jamais été tenté — c'est le seul hasard qui a sauvé la base. D'où le `127.0.0.1` du compose | | **Aucune limite de log** | un seul conteneur avait atteint 516 Mo sur un disque de 20 Go | | **Image `postgres:16` nue** | sans PostGIS, la migration `UpdateCoordinatesToGeom` ne passe pas : le schéma était bloqué à 14 migrations sur 68 | | **`mym`/`mym`** | ne jamais réutiliser ce couple |