143 lines
11 KiB
Markdown
143 lines
11 KiB
Markdown
# Migration cin-search-tt : hors-Docker vers Docker + SMTP OVH -> Infomaniak
|
|
|
|
## Contexte
|
|
Le 10/07/2026, Nabil signale que la migration SMTP globale vers Infomaniak (faite la veille)
|
|
n'a pas couvert cin-search-tt car cette app tournait en dehors de la stack Docker, dans
|
|
/volume1/web/cin-search-tt (Node.js standalone, jamais containerise).
|
|
|
|
Audit etendu a tout /volume1/web/ : 4 apps Node y etaient hebergees hors-Docker.
|
|
|
|
## Etat trouve
|
|
| App | Etat | Action |
|
|
|---|---|---|
|
|
| cin-search-tt | SMTP sur ssl0.ovh.net, Baserow sur ancien alias nd.i234.me, jamais dockerise | Migre vers Docker |
|
|
| rla | Copie **obsolete** — la vraie version tourne deja proprement dans /volume1/docker/rla-api (Gotenberg/pptx-tt-api deja purges le 04/07) | Archivee (doublon perime) |
|
|
| club-ia | Projet termine (jamais deploye en prod) | Archivee |
|
|
| weblinks | Page HTML statique simple, aucune dependance | Conservee en l'etat dans /volume1/web |
|
|
|
|
## Actions effectuees — cin-search-tt
|
|
1. Copie du code (hors node_modules/zip) vers /volume1/docker/cin-search-tt
|
|
2. `.env` corrige :
|
|
- SMTP_HOST ssl0.ovh.net -> mail.infomaniak.com (port 587, STARTTLS, meme compte nabil.derouiche@ik.me que Baserow/Vaultwarden)
|
|
- BASEROW_API_URL https://baserow.nd.i234.me -> https://baserow.bolbol.tn (alias reseau Docker, regle absolue)
|
|
3. Dockerfile ecrit (node:20-alpine, npm install --omit=dev)
|
|
4. Image buildee via API Docker de Portainer (`POST /api/endpoints/2/docker/build` avec tar de contexte) car `docker` CLI absent du container mcp-nas (hardening 08/07)
|
|
5. Stack deploye via API Portainer (`POST /api/stacks/create/standalone/string`) — **pas de `env_file:`** dans le compose (Portainer ne voit pas les chemins hote arbitraires), variables passees en `environment:` inline a la place
|
|
6. Container `cin-search-tt` : port 3094->3000, reseau `n8n`, volume `/volume1/docker/cin-search-tt/data:/app/data` (persistance database.json)
|
|
7. `ports-registry.md` mis a jour (edite via un container alpine ephemere + bind mount, car le fichier appartient a uid 1026 et mcp-nas n'a plus CAP_DAC_OVERRIDE)
|
|
8. Reverse-proxy DSM `cin.bolbol.tn` -> `localhost:3094` **NON fait** (hors scope API/Docker, a faire manuellement dans DSM Control Panel)
|
|
9. Anciens dossiers archives dans `/volume1/web/.archive/` : `cin-search-tt.archived-20260710-migrated-to-docker`, `rla.archived-20260710-obsolete-superseded-by-docker-rla-api`, `club-ia.archived-20260710`
|
|
|
|
## Pieges decouverts (a capitaliser)
|
|
- **mcp-nas ne peut pas lancer `docker`/`docker compose` directement** (binaire absent, SSH host indisponible depuis le hardening). Solution : API REST Portainer (`/api/endpoints/{id}/docker/build` pour build, `/api/stacks/create/standalone/string` pour deploy).
|
|
- **Portainer ne resout pas `env_file:` avec un chemin hote** dans un stack deploye par API string — le moteur compose de Portainer tourne dans son propre filesystem (`/data/compose/<id>/`), pas celui de l'hote. Toujours utiliser `environment:` inline pour les stacks deployes ainsi.
|
|
- **Volume bind mounts en chemin absolu hote fonctionnent normalement** (resolus par le daemon Docker, pas par Portainer) — contrairement a `env_file:`.
|
|
- **Ecriture de fichiers appartenant a uid 1026 depuis mcp-nas (root) : impossible**, meme en root, a cause de `cap_drop: [ALL]` (pas de CAP_DAC_OVERRIDE). Contournement : creer un container ephemere (alpine) avec bind mount du dossier concerne via l'API Portainer, executer la commande d'ecriture dedans en root reel (container non-hardened), puis le supprimer.
|
|
- **Token Gitea `GITEA_DEPLOY_TOKEN` n'a pas le scope `write:organization` ni `read:user`** — peut lire/ecrire du contenu de repos existants, mais pas creer de nouveaux repos ni lister les users/orgs. Creation de nouveaux repos = a faire manuellement dans l'UI Gitea.
|
|
- **`/volume1/docker/ports-registry.md` n'est pas versionne dans Gitea** (pas de `.git` a la racine de `/volume1/docker`) — mise a jour uniquement in-place sur le NAS.
|
|
|
|
## Reste a faire (hors scope automatisable)
|
|
- Creer la regle reverse-proxy DSM `cin.bolbol.tn` -> `localhost:3094`
|
|
- Creer le repo Gitea `bolbol/cin-search-tt` manuellement (token actuel insuffisant) et y pousser le code
|
|
|
|
## Correctif post-deploiement (10/07/2026, meme session)
|
|
|
|
Nabil a signale "Echec de l'envoi email" apres mise en prod. Deux bugs distincts trouves via les logs container :
|
|
|
|
1. **SMTP code en dur dans server.js** — le transporter nodemailer n'utilisait PAS `config.smtp`
|
|
(qui lit le `.env`) mais des valeurs litterales : `host: 'ssl0.ovh.net'`, `user: 'contact@bolbol.tn'`,
|
|
`pass: '2L2u519wbolbol'`. Corriger le `.env` seul ne changeait donc rien. Patch : les 3 champs
|
|
(host/port/secure/auth) passes en `process.env.SMTP_*`. Le `From` des 2 `sendMail()` (etait
|
|
`contact@bolbol.tn`, adresse OVH desormais coupee) change pour `nabil.derouiche@ik.me` —
|
|
**Infomaniak rejette un From qui ne correspond pas au compte authentifie** (meme contrainte
|
|
deja documentee pour Baserow/Vaultwarden).
|
|
2. **BASEROW_API_URL en https:// echouait en interne** (`ECONNREFUSED 172.27.x.x:443`) —
|
|
sur le reseau Docker `n8n`, l'alias `baserow.bolbol.tn` resout directement vers le container
|
|
Baserow qui n'ecoute qu'en HTTP (port 80, cf. `ports-registry.md` : `3888->80`). Pas de TLS
|
|
en interne. Corrige en `http://baserow.bolbol.tn`.
|
|
|
|
**Piege a capitaliser** : toujours verifier qu'un service qui lit un `.env` via `config.js`
|
|
utilise vraiment ce module partout — un `require('dotenv').config()` en tete de fichier
|
|
n'empeche pas des valeurs codees en dur ailleurs dans le meme fichier. Grep systematique
|
|
sur host/user/pass/api url avant toute migration de credentials.
|
|
|
|
**Piege reseau** : `https://<alias>.bolbol.tn` ne fonctionne QUE depuis l'exterieur (WAN) ou
|
|
un service hors du reseau Docker `n8n` (TLS termine par le reverse-proxy DSM). Un container
|
|
sur le meme reseau `n8n` qu'un alias doit taper en `http://` sur le port interne reel du
|
|
service cible (verifier `ports-registry.md`), jamais supposer que le HTTPS est disponible
|
|
en interne.
|
|
|
|
|
|
## Regression post-migration decouverte le 24/07/2026 : recherche toujours "RAS"
|
|
|
|
### Symptome
|
|
Nabil signale que l'app renvoie systematiquement "RAS" (aucun resultat) meme pour des CIN
|
|
presents dans la base Baserow. L'envoi mail fonctionnait, seul le contenu etait toujours vide.
|
|
|
|
### Cause racine
|
|
Le correctif du 10/07 (point 2 ci-dessus, "BASEROW_API_URL en https:// echouait en interne")
|
|
n'avait ete applique qu'a la fonction `searchByCIN()` du fichier `baserow.js` — **module jamais
|
|
importe ni appele nulle part dans `server.js`** (code mort). La vraie fonction utilisee par la
|
|
route `/api/search`, `searchBaserow()` **dans `server.js` lui-meme**, contenait sa propre paire
|
|
URL/token codee en dur, distincte et jamais touchee par le grep du 10/07 :
|
|
```js
|
|
const TOKEN = 'zJaDdkttN1gr6oPvd3cxfCXNwzvvwMMF'; // token perime, non lie a .env
|
|
...
|
|
'https://baserow.bolbol.tn/api/database/rows/table/' + TABLE_ID + '/' // https en dur
|
|
```
|
|
Logs container : `Erreur pagination page 1: connect ECONNREFUSED 172.27.0.15:443` a chaque
|
|
recherche → `allRows` reste vide → 0 resultat → email "RAS" systematique, quel que soit le CIN.
|
|
|
|
**Lecon** : un meme fichier peut contenir plusieurs implementations paralleles de la meme
|
|
fonctionnalite (ici deux clients Baserow distincts). Grep sur `https://`/token corrige *une*
|
|
occurrence ne garantit pas d'avoir corrige *la* occurrence reellement appelee par les routes.
|
|
Toujours verifier par un `grep -n "require(" server.js` + trace des appels effectifs avant de
|
|
declarer un correctif complet, pas seulement un grep sur le pattern fautif.
|
|
|
|
### Correctif applique
|
|
Dans `searchBaserow()` (server.js), remplacement des valeurs en dur par lecture `.env`,
|
|
coherent avec le reste du fichier (qui lit deja `process.env.*` partout ailleurs) :
|
|
```js
|
|
const TABLE_ID = process.env.BASEROW_TABLE_ID || process.env.BASEROW_TABLE_SUD || '860';
|
|
const BASEROW_URL = process.env.BASEROW_API_URL || 'http://baserow.bolbol.tn';
|
|
const TOKEN = process.env.BASEROW_TOKEN;
|
|
```
|
|
Rebuild image (`docker build -t cin-search-tt:latest .`) + `docker compose up -d` (recreate).
|
|
Verifie par recherche reelle via l'API (`/api/login` puis `/api/search`) : CIN existant → trouve
|
|
avec le bon nombre d'occurrences, CIN absent → correctement liste dans `notFoundCins`, mail envoye.
|
|
Backup de l'ancien `server.js` conserve sur le NAS (`server.js.bak_<date>`), fichier `baserow.js`
|
|
(code mort) laisse en l'etat — a supprimer ou reutiliser lors d'un futur nettoyage.
|
|
|
|
### Note infra corrigee : SSH host de nouveau disponible depuis mcp-nas
|
|
Contrairement a ce que disait ce runbook ("SSH host indisponible depuis le hardening"), l'alias
|
|
`nas-host` (cle `mcp-nas-bestof`, cree le 11/07, posterieur a cette migration) permet desormais
|
|
`ssh nas-host` puis `/usr/local/bin/docker ...` directement depuis mcp-nas — plus besoin du
|
|
contournement API Portainer pour un simple build/recreate. Le detour Portainer reste utile
|
|
uniquement pour des cas d'ecriture de fichiers appartenant a un uid hors de portee de `Best0f`.
|
|
|
|
### Reste a faire (toujours hors scope automatisable)
|
|
- Repo Gitea `bolbol/cin-search-tt` toujours pas cree (token `GITEA_DEPLOY_TOKEN` insuffisant
|
|
pour creer un repo) — code non versionne a ce jour, uniquement sur disque NAS.
|
|
|
|
|
|
## Nettoyage et repo Gitea, meme session (24/07/2026)
|
|
|
|
- `baserow.js` (module mort identifie ci-dessus) **supprime** du NAS (archive locale
|
|
`baserow.js.removed_20260724`) et du repo Git — il n'apportait rien et a directement cause
|
|
la fausse resolution du 10/07 (correctif applique au mauvais fichier).
|
|
- **Lien casse trouve et corrige** : le mail de bienvenue (nouveau compte) pointait vers
|
|
`https://bolbol.tn/cin/` (verifie : 404) au lieu de `https://cin.bolbol.tn/` (verifie : 200) —
|
|
reliquat du routage par chemin pre-migration, jamais mis a jour lors du passage a la
|
|
sous-domaine `cin.bolbol.tn`. Corrige dans `generateWelcomeEmail()`.
|
|
- Repo **`bolbol/cin-search-tt` cree** (prive) via `GITEA_TOKEN_TT` (le `GITEA_DEPLOY_TOKEN`
|
|
n'a toujours pas le scope creation de repo, cf. piege deja documente ci-dessus). Code source
|
|
pousse en excluant `.env` et `data/` (`.gitignore`) ; `docker-compose.yml` reecrit sans
|
|
secrets inline (`env_file: .env` au lieu de `environment:` — le contournement Portainer du
|
|
10/07 n'est plus necessaire, `ssh nas-host` + `docker compose` fonctionnent directement) ;
|
|
`.env.example` ajoute comme template.
|
|
- **Point releve mais volontairement pas corrige** : `express-session` tourne encore avec le
|
|
`MemoryStore` par defaut (warning au demarrage : non concu pour la prod, fuite memoire au
|
|
long cours). Juge basse priorite ici (3 utilisateurs, appli peu sollicitee, redemarrages
|
|
reguliers de toute facon) — a traiter seulement si l'usage augmente. Pas d'action prise pour
|
|
eviter d'ajouter une dependance et un risque sur un correctif qui n'etait pas demande.
|