MIGRATION — agrimeca → capbe-meca

📄 Source : CAPBE-MECA/docs/MIGRATION.md 🕒 Généré le 06/08/2026 09:56
Sommaire

Document technique qui explique comment et pourquoi l'application agrimeca (pile autonome : Caddy local + FastAPI + Postgres + Odoo dans un seul conteneur) a été transformée en capbe-meca dans l'architecture CAPBE.

Le script source de cette migration est playbook/12-migrate-projects.sh. Ce kit en contient un wrapper spécialisé : deploy/migrate-meca.sh.


1. L'architecture cible (CAPBE)

CAPBE mutualise les backends dans des conteneurs dédiés, connectés sur le réseau privé capbe-br0 (10.10.10.0/24, NAT sortant). Un seul point d'entrée public : capbe-caddy (TLS auto, On-Demand).

Backend partagé Conteneur Rôle
PostgreSQL 18 + PostGIS capbe-pgis base de données + hébergement des API migrées (uvicorn)
Oracle AI DB Free capbe-oracle stack #2 (non utilisé par meca)
Odoo 19 multi-tenant capbe-odoo ERP (un tenant = une base, dbfilter ^%d$)
Messagerie Stalwart capbe-mail SMTP/IMAP/JMAP + webmail

Le projet meca y est découpé en 3 morceaux :

agrimeca (1 conteneur autonome)          capbe-meca (architecture CAPBE)
┌──────────────────────────┐      ┌──────────────────────────────────────┐
│ Caddy local (TLS)        │  →   │ capbe-caddy : https://meca.app.ci.capbe.ovh
│ Front Flutter            │  →   │ capbe-meca : nginx :80 (/var/www/html)
│ FastAPI + base Postgres  │  →   │ capbe-pgis :8000 → /srv/meca/api + base meca_api
│ Odoo + base              │  →   │ capbe-odoo :8069 → base meca (tenant)
└──────────────────────────┘      └──────────────────────────────────────┘
        (INTACT — jamais touché)

2. Les 3 phases de la migration (détail réel)

Phase 1 — Frontend statique → capbe-meca

  • Création du conteneur capbe-meca (profil front, réseau capbe-br0).
  • Provisionnement nginx :80 (root /var/www/html, try_files ... /index.html).
  • Détection de la racine web de l'héritage : /var/www/html (nginx) ou /usr/share/caddy (racine par défaut de Caddy — piège rencontré avec agroworoba : le script ne cherchait que /var/www/html → front 403).
  • Copie du build via tar | incus exec tar (ne dépend pas de scp/rsync).

Phase 2 — API FastAPI + base → capbe-pgis

  • Détection du code API : /opt/<legacy>/api (layout historique) ou /app (layout supervisor/venv — piège : étape silencieusement ignorée sinon).
  • Base : pg_dump --no-owner --no-privileges de la base source (nom lu dans alembic.inisqlalchemy.url), restore sur pgis en base <id>_api.
  • Port : un service uvicorn par port sur pgis — meca a pris le 8000 (1re API migrée), agro le 8001. Routage Caddy même-origine : meca.app.ci.capbe.ovh/api/* → pgis:8000.
  • .env : recopié vers /srv/meca/api/.env (chmod 600) avec DATABASE_URL/DB_PASSWORD réécrits vers pgis (rôle app, mot de passe APP_DB_PASS). Les clés de paiement (Wave/Orange/MTN/Moov) sont conservées. Si aucun .env hérité : généré avec les connexions capbe-pgis.
  • alembic.ini (tous, racine + sous-dossiers) : sqlalchemy.url repointé vers capbe-pgis (sinon une copie orpheline garde une URL morte).
  • Service systemd meca-api sur pgis : uvicorn app.main:app --port 8000.
  • Redis + Celery déployés automatiquement si l'app déclare un celery_app.

Phase 3 — Odoo → tenant de capbe-odoo

  • Base Odoo source détectée via db_name de la config (/etc/odoo/odoo.conf, /etc/odoo.conf, …) — repli convention <legacy>_odoo.
  • pg_dump --no-owner → base <id> sur pgis (rôle odoo), servie par capbe-odoo avec dbfilter ^%d$https://meca.ci.capbe.ovh.
  • Filestore (pièces jointes) : copié depuis l'héritage (/var/lib/odoo/filestore, /opt/odoo*/.local/share/Odoo/filestore, …) vers capbe-odoo:/var/lib/odoo/filestore/<tenant>.
  • Tenant vierge (odoo -i base) si la base source n'avait jamais été initialisée (pas de ir_module_module) — aucune donnée ERP à attendre.

3. Pièges rencontrés en réel (corrections incluses dans le script)

  1. Propriété PostgreSQL (PG15+) : les objets restaurés avec --no-owner appartiennent à postgres. REASSIGN OWNED échoue sur PG15+ (postgres possède des objets système). → fix_owner + sweep_owner (DO blocks) réattribuent schémas utilisateur, tables, vues, séquences (hors séquences de colonne liées), fonctions.
  2. Tables orm_signaling_* (Odoo 19) : restaurées possédées par postgres, elles deviennent invisibles au rôle odoo dans information_schema → Odoo tente de les recréer (DuplicateTable) → registre KO (KeyError: 'meca', login 500). Le sweep de propriété est indispensable (constaté en réel sur meca).
  3. /tmp/ et non /root/ : le restore tourne via su postgres, qui ne peut pas lire /root/ (700) → « Permission denied » sinon.
  4. Dump sensible nettoyé : le .sql intermédiaire est supprimé dès son push vers pgis (même si le restore échoue ensuite).
  5. Rewrite API /api/* → /api/v2/* : la convention d'URL de l'API meca est /api/v2/<ressource> mais le front appelle /api/<ressource>. Caddy réécrit uniquement les ressources listées dans la regex du bloc meca.app.ci.capbe.ovh (health, dashboard, clients, parcelles, engins, conducteurs, interventions, factures, prestations, paiements, geographie, echeancier, sites, transferts, photos, notifications, global-search, stock). ⚠️ Tout nouvel endpoint doit y être ajouté (voir EVOLUTIONS.md).
  6. Héritage jamais touché : agrimeca n'est ni arrêté ni supprimé — son sort est décidé manuellement après validation (playbook/POST-DEPLOY.md).

4. État réel constaté (validation du 03/08/2026)

Vérification Résultat
https://meca.app.ci.capbe.ovh ✅ 200 (front Flutter)
https://meca.app.ci.capbe.ovh/api/health ✅ 200 (même-origine → pgis:8000)
https://meca.ci.capbe.ovh/web/login?db=meca ✅ 200 (tenant Odoo)
meca-api sur pgis ✅ actif, uvicorn :8000
Base meca_api ✅ 247 tables
Tenant meca ✅ 676 modules Odoo (ir_module_module)
Héritage agrimeca ⏳ RUNNING (sort différé)

5. Rejouer la migration

sudo ./deploy/migrate-meca.sh      # wrapper : playbook 12-migrate-projects.sh agrimeca=meca
sudo ./playbook/09-caddy-config.sh # (re)déploie le Caddyfile si besoin
sudo ./ops/smoke-meca.sh           # validation

Idempotent : les composants déjà migrés sont détectés et ignorés (warn) ; les manquants sont complétés.