# 1quinquies. La bascule elle-même — Mongo → Postgres en prod (ajouté le 2026-08-09) > ← Section **§1quinquies** du tableau de bord : [STATUS.md](../STATUS.md) > **Trou identifié le 2026-08-09** : le §1ter dit *ce qui doit entrer dans le schéma avant que la base se remplisse*. Il ne dit nulle part **comment on remplit la base**. L'opération de bascule n'était écrite ni ici, ni dans le kanban, ni dans aucun plan de `v2/`. > > Rappel : la prod tourne **encore sur MongoDB**. Le transfert se fait par `MigrationController` (`POST /api/migration/run`, paramètres `dryRun` et `instanceId`), qui rejoue Instances → Resources → Users → Configurations → ApplicationInstances → Sections → Devices, puis relie les menus et enrichit les ressources PDF. ### ⚠️ `MigrationController` a pris du retard sur le modèle Écrit avant l'essentiel des chantiers de 2026 (`SectionEvent`, `SectionParcours`, abonnements, onboarding, guide IA, colonnes médias). Relevé le 2026-08-09 sur [ManagerService/Controllers/MigrationController.cs](../../manager-service/ManagerService/Controllers/MigrationController.cs) : | # | Écart | Où | Conséquence si on bascule tel quel | |---|---|---|---| | a | `BuildSection` ne couvre que **11 types** (Map, Slider, Video, Web, Menu, Quiz, Article, PDF, Game, Agenda, Weather) | `:458-613` | **`SectionEvent` et `SectionParcours` tombent dans le `default`** → ligne d'erreur dans le rapport, section non migrée, silencieusement | | b | Les collections filles ne sont jamais remplies : `QuizQuestions = new()`, `EventAgendas = new()`, aucun `GuidedPath`/`GuidedStep` | `:526`, `:591` | Un quiz arrive **sans ses questions**, un agenda sans ses événements | | c | `Instance` : **4 champs migrés sur ~35** | `:106-112` | `SubscriptionPlanId` null → instance sans plan (quotas à 0, `HasStats` false) ; `PublicApiKey` null → **les apps visiteur ne s'authentifient plus** (`X-Api-Key`) ; `WebSlug` null → visitapp-web injoignable ; `IsMobile`/`IsTablet`/`IsWeb`/`IsAssistant` à false | | d | `Role = UserRole.ContentEditor` en dur | `:225` | **Plus aucun administrateur** après la bascule | | e | `Resource` : `StoragePath`, `FileName`, `IncludeInAiKnowledge`, `AiIndexStatus` non renseignés ; `SizeBytes` déduit d'un `HEAD` sur l'URL Firebase, échec avalé → 0 | `:172-181` | C'est **le point 5 du §1ter** (backfill) : à faire *dans* la migration, pas après | | f | `IsQRCode` / `IsSearchText` / `IsSearchNumber` forcés à `false` | `:276-278` | Réglages de configuration perdus | | g | `MapResourceId = dto?.iconResourceId` | `:469` | À répercuter le jour où la colonne est renommée en `IconResourceId` (§6bis) | | h | Pas de transaction globale : 9 étapes, un `SaveChangesAsync` chacune, `catch` qui renvoie **200 OK** avec `FatalError` dans le corps | `:68-86` | Un échec au milieu laisse la base à moitié remplie. La reprise ne tient qu'aux tests `AnyAsync` d'idempotence — qui existent, eux, et sont corrects | > Le point **c** est le plus dangereux : il ne produit **aucune erreur**. La migration se déclare réussie et le lieu est inaccessible aux visiteurs. ### La bascule, dans l'ordre Numérotation continue avec le §1ter (1-13). | # | Quoi | Note | |---|---|---| | 14 | **Remettre `MigrationController` à niveau** — les 8 écarts a→h ci-dessus | Le plus gros morceau. À faire avant tout le reste | | 15 | **Dry run** sur un export Mongo de prod, puis comparer les comptages par entité aux comptages Mongo | `dryRun=true` n'écrit rien. Un export existe déjà dans `manager-service/migration-data/` | | 16 | **Créer l'environnement prod Postgres** : `Deployment/Dockerfile.postgres` (épinglé par digest), compose prod, Traefik, **port 5432 fermé** | Dépend de la rotation des secrets (§3) — ne pas créer la prod avec les clés committées | | 17 | `dotnet ef database update` sur la base vide, puis vérifier `postgis` **et** `vector` présents | Le §1quater rappelle qu'une base en retard d'une migration fait planter l'API dès le login | | 18 | **`pg_dump` avant et après**, puis cron quotidien | Déjà noté en §4 comme non fait. Ici il devient bloquant : c'est le seul filet du jour J | | 19 | **Rejouer pour de vrai, instance par instance** (`instanceId=…`), en commençant par la plus petite | Le paramètre existe précisément pour ça | | 20 | **Vérifications post-bascule** : login manager-app, une app visiteur avec sa clé API, un parcours, une carte, un PDF, un quiz avec ses questions | Les points b et c ne se voient qu'ici | | 21 | **Bascule DNS / API**, Mongo gardé en lecture seule quelques jours | | | 22 | **Plan de retour arrière écrit avant le jour J** | Un rollback qu'on improvise à 23 h n'est pas un rollback | ### Après la bascule — relationnel, pas technique | Quoi | Note | |---|---| | **Reprendre contact avec Louise Smets — Musée d'Ixelles** | À faire **une fois la prod stabilisée**, pas avant : on n'ouvre pas une conversation en montrant un produit en cours de bascule. Objectif d'abord d'écoute — comprendre ce qu'est réellement son travail au quotidien et sur quoi elle bute — avant de parler de MyInfoMate. L'entraide se décide après, pas dans le premier message. Rien n'est su de son rôle exact à ce stade : à ne pas supposer. | **Rappel de la porte** : rien de tout ça ne démarre avant que le reste du backlog soit terminé (§1quater). La base Postgres vide est une fenêtre — chaque chantier de schéma passé maintenant est un chantier qui n'exigera pas de fenêtre de maintenance plus tard. ---