diff --git a/STATUS.md b/STATUS.md index df9091c..2bb0e79 100644 --- a/STATUS.md +++ b/STATUS.md @@ -131,10 +131,19 @@ Ordre conseillé : - [x] ✅ **Travail non committé — résorbé. Revérifié le 2026-08-11 (2e passe) : plus rien en attente.** Les **9 repos** (DOCS compris) sont propres (`git status --porcelain` vide partout) **et à jour avec leur remote** (aucun `ahead`/`behind`). L'alerte du 2026-08-09 et sa révision du matin du 11/08 (« ~41 fichiers en working tree dont 9 non suivis ») sont toutes deux **périmées** : le lot 3 RAG est dans `18e4240`, le socle visuel + Guide IA dans `eb8e849`. **L'étape 1 du lot A est terminée** — ne pas la re-planifier - Derniers commits par repo : `manager-service` `18e4240` (RAG : pipeline d'ingestion, endpoints du guide IA, journalisation RGPD) · `manager-app` `eb8e849` (socle visuel et écran Guide IA à deux onglets) · `visitapp-web` `473fc3a` · `mymuseum-visitapp` `af44927` · `tablet-app` `5a6701d` (drapeaux du migrateur Flutter) · `myinfomate-landing` `d91cf7f` · `unov-landing` `cfa70c4` · `OpenGlasses` `1d887d7` - Rappel de l'état des branches : `manager-service` → `MyInfoMate_3.0`, `manager-app` → `Interface-refacto`, `mymuseum-visitapp` → `Meta-Rayban-Test` (23 commits d'avance sur `master`, voir §5bis), `tablet-app` → `AI-Assistant-test`, `visitapp-web` et les deux landings → `master` -- [ ] **pg_dump quotidien** pour `manager-service` (Postgres `my_info_mate`), en complément du snapshot OVH — voir mémoire `project_pgdump_todo`, pas encore fait au 2026-07-15. ⚠️ **Devient bloquant au moment de la bascule** : c'est le point 18 du [§1quinquies](#1quinquies-la-bascule-elle-même--mongo--postgres-en-prod-ajouté-le-2026-08-09), le seul filet du jour J - - ⛔ **Deuxième chose qu'il débloque** : le job `visit-events-purge` (purge des stats au-delà de 13 mois) est codé et enregistré mais **volontairement inactif** — il ne supprimera rien tant que `Stats:RetentionDays` n'est pas défini. Ne l'activer (`Stats__RetentionDays=395`) qu'une fois ce pg_dump en place +- [ ] **pg_dump quotidien** pour `manager-service` (Postgres `my_info_mate`), en complément du snapshot OVH. ⚠️ **Bloquant au moment de la bascule** : point 18 du [§1quinquies](#1quinquies-la-bascule-elle-même--mongo--postgres-en-prod-ajouté-le-2026-08-09), le seul filet du jour J + - 🔨 **Scripts livrés et testés le 2026-09-07** — `manager-service/ManagerService/Deployment/backup/`, installation et restauration documentées dans `README.md` et **`RESTORE.md`**. Cycle complet validé sur la base de dev : dump → vérification de relecture → restauration dans une base jetable → **25 tables aux comptages identiques**. Extraction par instance validée aussi (les deux extractions totalisent exactement le global). Embeddings exclus : dump de 12 Mo → **1,7 Mo** + - ✅ **Destination créée le 2026-09-07** — projet GCP `myinfomate-backups` (séparé de `mymuseum-3b97f`), bucket `gs://unov-myinfomate-backups` en `EU` multi-région, versioning activé, accès public interdit, lifecycle 400 jours sous `pg/` seulement. Service account `backup-writer` en `objectCreator` + `objectViewer` : **incapable de supprimer, et vérifié** (l'écriture passe, le `rm` renvoie `403 storage.objects.delete denied`). Détail et test rejouable dans `Deployment/backup/README.md` + - ⛔ **Ce n'est pas encore une sauvegarde** : `rclone` n'est ni installé ni configuré sur le VPS, donc rien ne part encore hors site. Et `notify.sh` n'est branché sur aucun canal — carte kanban « Brancher `notify.sh` sur un canal réel » + - 📌 **Volume des médias : 1,43 Go** (`gcloud storage du -s gs://mymuseum-3b97f.appspot.com`, 2026-09-07). Le bucket connaissait la réponse que le plan attendait du backfill. Assez petit pour tenir dans le même bucket de sauvegarde sous `media/` — **pas besoin d'OVH Object Storage**, la disposition croisée envisagée le 07/09 est abandonnée pour cette raison + - ✅ **Versioning activé sur le bucket des médias le 2026-09-07** — `versioning_enabled: True` sur `gs://mymuseum-3b97f.appspot.com`, plus une lifecycle rule qui purge les versions périmées à 30 jours (le bucket n'avait **aucune** règle auparavant, rien n'a été écrasé). L'ordre importait : le filet est en place **avant** que `Firebase:StorageBucket` soit renseignée (carte « Poser `Firebase:StorageBucket` »), jour où la suppression de blob devient réelle. Le bucket portait déjà une soft delete de 7 jours depuis mars 2024 + - 📌 **Décision de conception** : **un dump global, pas un par client.** Une restauration par client se fait par extraction depuis le dump global (`extract-instance.sh`) — 14 tables portent `InstanceId`, 7 tables filles se rattachent par leur parent. Des sauvegardes par client multiplieraient par N les jobs à planifier, les échecs à surveiller et les restaurations à tester, pour la même capacité de récupération + - ✅ **Médias copiés le 2026-09-07** — 1,43 Go, 2407 objets, `gcloud storage rsync` serveur à serveur vers `gs://unov-myinfomate-backups/media/`. **Taille identique à l'octet**, zéro erreur. C'est la première sauvegarde des médias depuis le début du projet. ⛔ **Pas encore planifiée** : c'est une copie unique. **Storage Transfer Service envisagé puis écarté le 2026-09-07** — 5 attributions IAM et un agent de service avec droit de suppression, pour automatiser une commande à relancer toutes les quelques semaines sur des données qui bougent de ~2 ressources par mois : disproportionné. Le mécanisme reste le `rsync`, qui tourne avec un compte humain sans aucun changement de permission, et sera ajouté aux timers du VPS avec la sauvegarde Postgres + - 🔍 **L'inventaire des orphelins est à moitié fait, gratuitement** — 27 objets sur 2408 n'appartiennent à aucun client : 19 sous un id à un caractère de celui de MyInfoMate (`…f1` vs `…f2`), 2 sous MNAHA (qui n'existe qu'en base de dev), et 6 fichiers de test à la racine du bucket (`giphy.gif`, une vidéo Cybertruck, des `file_example_*`) qui ne suivent pas le schéma `pictures/{instanceId}/{id}` et sont donc invisibles au quota. Détail et réserves dans [v2/media-storage-plan.md](v2/media-storage-plan.md) + - ⛔ **Deuxième chose qu'il débloque** : le job `visit-events-purge` (purge des stats au-delà de 13 mois) est codé et enregistré mais **volontairement inactif** — il ne supprimera rien tant que `Stats:RetentionDays` n'est pas défini. Ne l'activer (`Stats__RetentionDays=395`) qu'une fois ce pg_dump réellement hors site - [ ] **Fermer le port PostgreSQL exposé publiquement** via Traefik/Docker (détail dans [todo-features.md](todo-features.md), section "Infrastructure — Sécurité réseau") — accès DBeaver via tunnel SSH uniquement -- [ ] **Backup Mongo automatique en cron** — voir [todo-landing-deploy.md](todo-landing-deploy.md), section 5 (dump + `docker cp` vers l'hôte, pas seulement dans le conteneur) +- [x] 🔨 **Sauvegarde de la prod Mongo — faite le 2026-09-07.** `Deployment/backup/dump-mongo-prod.sh`, lecture seule (aucun `mongorestore` dans le script, volontairement). L'export de référence datait du 1er avril. Delta sur 5 mois : 11 sections créées, 3 supprimées, 17 modifiées, 9 ressources créées. **VisitNamur crée du contenu, MDLF retouche l'existant, Fort Saint Héribert n'a rien touché** — utile à savoir avant de reprendre contact avec eux. ⚠️ Piège : le formatage des dates change entre versions de `mongoexport` (`.420Z` → `.42Z`) et fait apparaître 239 fausses modifications ; normaliser avant de comparer +- [ ] **Backup Mongo automatique en cron** — le script existe désormais, reste à le planifier. Voir [todo-landing-deploy.md](todo-landing-deploy.md), section 5 (dump + `docker cp` vers l'hôte, pas seulement dans le conteneur) - [x] Déploiement landing page (`docker-compose-landing.yml` séparé, ne jamais toucher au compose principal) — voir [todo-landing-deploy.md](todo-landing-deploy.md) pour les règles à respecter --- diff --git a/kanban/cards/6-bascule-prod/040-pg-dump-avant-apres-cron-quotidien.md b/kanban/cards/6-bascule-prod/040-pg-dump-avant-apres-cron-quotidien.md index 90fcfb9..722a8b0 100644 --- a/kanban/cards/6-bascule-prod/040-pg-dump-avant-apres-cron-quotidien.md +++ b/kanban/cards/6-bascule-prod/040-pg-dump-avant-apres-cron-quotidien.md @@ -2,7 +2,10 @@ title: pg_dump avant / après + cron quotidien area: infra tags: étape 18 -flag: critical | Filet du jour J +flag: warn | scripts + destination prêts, attend la prod Postgres src: STATUS.md §4 · §1quinquies — étape 18 --- -
Noté comme « à faire » depuis juillet et jamais fait. Ici ça cesse d'être une bonne pratique : c'est la seule chose qui permette de recommencer si la bascule tourne mal.
+Scripts écrits et testés le 07/09 dans manager-service/ManagerService/Deployment/backup/ : dump -Fc avec vérification de relecture et rotation, test de restauration comparant les comptages table par table (25 tables identiques), extraction par instance, contrôle de fraîcheur, units systemd, et une procédure de restauration écrite (RESTORE.md).
La destination existe depuis le 07/09 : BACKUP_DEST=gcs:unov-myinfomate-backups/pg, service account incapable de supprimer (vérifié par un 403). Voir la carte close « Trancher la destination des sauvegardes hors site ».
Ce qui reste : rclone n'est ni installé ni configuré sur le VPS, et il n'y a de toute façon pas encore de base Postgres en prod à sauvegarder. Cette carte est donc une étape à l'intérieur de « Créer l'environnement prod Postgres », pas un chantier parallèle — une vingtaine de minutes le jour venu, plus l'acheminement de la clé du service account en chmod 600.
Deux constats du test : les embeddings exclus font passer le dump de 12 Mo à 1,7 Mo, et le « collation version mismatch » du README s'est reproduit — il bloque createdb, donc toute restauration.
notify.sh sur un canal réel
+area: infra
+tags: étape 18
+flag: warn | une alerte que personne ne lit n'est pas une alerte
+src: conversation 07/09 — chantier sauvegardes
+---
+Trois surveillances sont en place — échec du script (OnFailure=), sauvegarde plus vieille que 48 h, test de restauration mensuel — et toutes appellent backup/notify.sh, qui ne fait aujourd'hui qu'un logger.
Le mode de panne réel n'est pas « le script plante » : c'est « le script ne tourne plus depuis trois semaines et personne ne l'a vu ». C'est précisément celui qu'un OnFailure n'attrape pas et que le contrôle de fraîcheur attrape — à condition que l'alerte sorte de la machine. Renseigner BACKUP_ALERT_WEBHOOK, ou brancher le canal mail du service.
Arrêté et créé le 07/09 : projet GCP myinfomate-backups, bucket gs://unov-myinfomate-backups en EU multi-région, versioning, accès public interdit, lifecycle 400 jours sous pg/. Service account backup-writer en objectCreator + objectViewer — incapable de supprimer, vérifié par un 403.
Les médias pèsent 1,43 Go (mesuré, pas estimé) : ils tiennent dans le même bucket sous media/, donc pas d'OVH Object Storage. La disposition croisée envisagée le matin même est abandonnée — elle se justifiait sur un volume supposé de 45 Go, qui venait en réalité du stockage Google One personnel.