**Motivations:** - Align master with current codebase (token from projects/<id>/.secrets/<env>/ia_token) - Id resolution by mail To or by API token; no slug **Root causes:** - Token moved from conf.json to .secrets/<env>/ia_token; env from directory name **Correctifs:** - Server and scripts resolve project+env by scanning all projects and envs **Evolutions:** - tickets-fetch-inbox routes by To address; notary-ai agents and API doc updated **Pages affectées:** - ai_working_help/server.js, docs, project_config.py, lib/project_config.sh - projects/README.md, lecoffreio/docs/API.md, gitea-issues/tickets-fetch-inbox.py
882 lines
33 KiB
Markdown
882 lines
33 KiB
Markdown
# Migration - Documentation Complète
|
|
|
|
**Auteur** : Équipe 4NK
|
|
**Date** : 2026-01-XX
|
|
**Version** : 2.0.0
|
|
|
|
**Référence unique (checks de déploiement)** : [`docs/DEPLOYMENT.md#cartographie-des-checks-de-déploiement-source-unique`](./DEPLOYMENT.md#cartographie-des-checks-de-déploiement-source-unique)
|
|
|
|
## 📋 Vue d'Ensemble
|
|
|
|
Ce document regroupe toutes les informations relatives aux migrations dans LeCoffre.io :
|
|
|
|
- Migration des données V1 vers V2
|
|
- Variables d'environnement V2
|
|
- Chargement de configuration V2
|
|
- Utilisation des variables V2
|
|
- Nettoyage des variables V1
|
|
- Migration des classes vers factories (pattern de code)
|
|
|
|
---
|
|
|
|
## 1. Migration V1 → V2
|
|
|
|
### Vue d'Ensemble
|
|
|
|
Ce processus décrit la migration complète des données V1 vers V2, incluant le reset de la base de données, l'import des données, la récupération des documents pour leur réancrage et re-chiffrement, ainsi que le traitement spécifique des RIB.
|
|
|
|
### Processus Global (centralisé)
|
|
|
|
Les checks de déploiement et leur distinction automatique/manuelle sont centralisés dans :
|
|
[`docs/DEPLOYMENT.md#cartographie-des-checks-de-déploiement-source-unique`](./DEPLOYMENT.md#cartographie-des-checks-de-déploiement-source-unique)
|
|
|
|
Pour la migration V1 → V2 :
|
|
|
|
- Reset de schéma et baseline Prisma via `deploy/scripts_v2/deploy.sh`
|
|
- Import V1 initial (destructif) via `deploy/scripts_v2/deploy.sh --importV1Data`
|
|
- Imports incrémentaux (merge non destructif) via `deploy/scripts_v2/merge-v1-data-into-v2.sh <env>`
|
|
|
|
**Dépendances détaillées** : voir [`docs/IMPORT_V1_DEPENDENCIES.md`](./IMPORT_V1_DEPENDENCIES.md) pour les prérequis, fichiers et scripts par phase.
|
|
|
|
### Récupération des Documents pour Réancrage et Re-Chiffrement
|
|
|
|
#### Problème Identifié
|
|
|
|
Lors de l'import V1→V2, les fichiers sont importés avec :
|
|
|
|
- **Clés V1** : UUID individuelles stockées dans `files.key` et `files_notary.key`
|
|
- **Algorithme V1** : AES-256-CTR (IV 16 bytes intégré)
|
|
- **Algorithme V2** : AES-256-GCM (IV 12 bytes séparé, authTag 16 bytes)
|
|
|
|
Les fichiers V1 ne peuvent pas être déchiffrés directement avec la clé maître V2 car :
|
|
|
|
1. Les clés sont différentes (UUID individuelles vs clé maître)
|
|
2. Les algorithmes sont différents (CTR vs GCM)
|
|
3. La structure est différente (IV intégré vs IV séparé)
|
|
|
|
#### Solution : Script de Récupération
|
|
|
|
**Script** : `recover-files-from-v1.ts`
|
|
|
|
**Processus** :
|
|
|
|
1. **Déchiffrement V1** : Utilise la clé UUID stockée en base pour déchiffrer le fichier V1
|
|
2. **Re-chiffrement V2** : Rechiffre avec la clé maître V2 (AES-256-GCM)
|
|
3. **Réancrage** : Recalcule le hash et réancre sur Bitcoin Signet
|
|
4. **Mise à jour** : Met à jour les métadonnées en base (hash, tx_id, etc.)
|
|
|
|
**Utilisation** :
|
|
```bash
|
|
npm run recover-files-from-v1
|
|
```
|
|
|
|
### Traitement des RIB
|
|
|
|
Les RIB en V1 étaient stockés en clair sur S3. En V2, ils sont chiffrés avec la clé maître.
|
|
|
|
**Processus** :
|
|
|
|
1. Téléchargement depuis S3 (V1)
|
|
2. Chiffrement avec la clé maître V2
|
|
3. Stockage en base de données (V2)
|
|
|
|
### Variables d'Environnement V2
|
|
|
|
#### Variables V2 (Application Production)
|
|
|
|
Les variables suivantes sont définies dans `.secrets/<env>/infos_v2.json` et sont **utilisées par l'application V2 en production**.
|
|
|
|
**Base de Données** :
|
|
|
|
- `DATABASE_HOST` : Hôte de la base de données (toujours `localhost` en V2)
|
|
- `DATABASE_PORT` : Port de la base de données
|
|
- `DATABASE_USERNAME` : Nom d'utilisateur PostgreSQL
|
|
- `DATABASE_PASSWORD` : Mot de passe PostgreSQL
|
|
- `DATABASE_NAME` : Nom de la base de données
|
|
- `ENV` : Environnement (test, pprod, prod)
|
|
|
|
**Application** :
|
|
|
|
- `API_ROOT_URL` : Racine de l'API
|
|
- `APP_HOST` : URL de l'application
|
|
- `APP_LABEL` : Label de l'application
|
|
- `APP_PORT` : Port de l'application backend
|
|
- `APP_ROOT_URL` : Racine de l'application
|
|
- `BACK_API_HOST` : URL de l'API backend
|
|
|
|
**Frontend (NEXT_PUBLIC_*)** :
|
|
|
|
- `NEXT_PUBLIC_ADMIN_ID` : ID administrateur
|
|
- `NEXT_PUBLIC_BACK_API_HOST` : Hôte de l'API backend
|
|
- `NEXT_PUBLIC_BACK_API_PROTOCOL` : Protocole de l'API
|
|
- `NEXT_PUBLIC_BACK_API_ROOT_URL` : Racine de l'API
|
|
- `NEXT_PUBLIC_BACK_API_VERSION` : Version de l'API
|
|
- `NEXT_PUBLIC_FRONT_APP_HOST` : URL de l'application frontend
|
|
- `NEXT_PUBLIC_FRONT_APP_PORT` : Port de l'application frontend
|
|
- `NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT` : Endpoint d'autorisation IdNot
|
|
- `NEXT_PUBLIC_IDNOT_BASE_URL` : URL de base IdNot
|
|
- `NEXT_PUBLIC_IDNOT_CLIENT_ID` : Client ID IdNot
|
|
|
|
**Intégrations** :
|
|
|
|
- **IdNot** : `IDNOT_API_BASE_URL`, `IDNOT_API_KEY`, `IDNOT_BASE_URL`, `IDNOT_CLIENT_ID`, `IDNOT_CLIENT_SECRET`, `IDNOT_CONNEXION_URL`, `IDNOT_PROD_BASE_URL`, `IDNOT_REDIRECT_URL`
|
|
- **Mailchimp** : `MAILCHIMP_API_KEY`, `MAILCHIMP_KEY`, `MAILCHIMP_LIST_ID`
|
|
- **OVH SMS** : `OVH_APP_KEY`, `OVH_APP_SECRET`, `OVH_CONSUMER_KEY`, `OVH_SMS_SERVICE_NAME`, `SMS_FACTOR_TOKEN`, `SMS_PROVIDER`
|
|
- **IPFS Pinata** : `PINATA_API_KEY`, `PINATA_API_SECRET`, `PINATA_GATEWAY`, `PINATA_GATEWAY_TOKEN`
|
|
- **Stripe** : `STRIPE_PAYMENT_CANCEL_URL`, `STRIPE_PAYMENT_SUCCESS_URL`, `STRIPE_SECRET_KEY`, `STRIPE_STANDARD_ANNUAL_SUBSCRIPTION_PRICE_ID`, `STRIPE_STANDARD_SUBSCRIPTION_PRICE_ID`, `STRIPE_UNLIMITED_ANNUAL_SUBSCRIPTION_PRICE_ID`, `STRIPE_UNLIMITED_SUBSCRIPTION_PRICE_ID`
|
|
- **Secure API** : `SECURE_API_KEY`
|
|
|
|
**Sécurité** :
|
|
|
|
- `ACCESS_TOKEN_SECRET` : Secret pour les tokens d'accès
|
|
- `REFRESH_TOKEN_SECRET` : Secret pour les tokens de rafraîchissement
|
|
- `ACCESS_TOKEN_TTL_MINUTES` : Durée de vie des tokens d'accès en minutes (format numérique)
|
|
- `REFRESH_TOKEN_TTL_MINUTES` : Durée de vie des tokens de rafraîchissement en minutes (format numérique)
|
|
- `JWT_EXPIRES_IN` : Durée de vie JWT en format legacy (ex: "1h", "7d")
|
|
- `REFRESH_TOKEN_EXPIRES_IN` : Durée de vie des tokens de rafraîchissement en format legacy (ex: "7d")
|
|
|
|
#### Variables Migration V1 (Ne PAS utiliser en V2)
|
|
|
|
Les variables suivantes sont définies dans `.secrets/<env>/infos.json`, `.secrets/<env>/infos_v1.json` et, pour la BDD V1, dans `.secrets/<env>/.env.<env>.connectDB` (DATABASE_V1_*). Elles sont **uniquement utilisées par les scripts de migration V1 vers V2**. Elles ne doivent **JAMAIS** être utilisées par l'application V2 en production.
|
|
|
|
**Scaleway S3 (Migration uniquement)** :
|
|
|
|
- `ACCESS_KEY_ID` : Clé d'accès S3 Scaleway
|
|
- `ACCESS_KEY_SECRET` : Secret S3 Scaleway
|
|
- `BUCKET_NAME` : Nom du bucket S3 Scaleway
|
|
- `BUCKET_ENDPOINT` : Endpoint S3 Scaleway
|
|
- `container` : Informations conteneur Scaleway V1
|
|
|
|
**Scripts utilisant ces variables** :
|
|
|
|
- `migrate-files-from-s3.ts` : Migration des fichiers depuis S3 Scaleway vers IPFS
|
|
- `migrate-ribs-from-s3.ts` : Migration des RIBs depuis S3 Scaleway vers IPFS
|
|
- `recover-files-from-v1.ts` : Récupération des fichiers V1 depuis S3 Scaleway
|
|
- `inject-s3-vars-from-infos.ts` : Injection des variables S3 dans `system_configuration`
|
|
|
|
**Docapost (Non utilisé en V2)** :
|
|
|
|
- `DOCAPOST_APP_ID`, `DOCAPOST_APP_PASSWORD`, `DOCAPOST_BASE_URL`, `DOCAPOST_CONNECT_PROCESS_ID`, `DOCAPOST_DOCUMENT_PROCESS_ID`, `DOCAPOST_ROOT`, `DOCAPOST_VERSION`, `NEXT_PUBLIC_DOCAPOST_API_URL`, `NEXT_PUBLIC_DOCAPOST_APP_ID`, `NEXT_PUBLIC_DOCAPOST_CONNECT_PROCESS_ID`, `NEXT_PUBLIC_DOCAPOST_DOCUMENT_PROCESS_ID`
|
|
|
|
**FranceConnect (Non utilisé en V2)** :
|
|
|
|
- `FC_AUTHORIZE_ENDPOINT`, `FC_CLIENT_ID`, `NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT`, `NEXT_PUBLIC_FC_CLIENT_ID`
|
|
|
|
**Sentry (Non utilisé en V2)** :
|
|
|
|
- `SENTRY_AUTH_TOKEN`, `SENTRY_DSN`, `SENTRY_FRONT_DSN`
|
|
|
|
**Secure API Base URL (Non utilisé en V2)** :
|
|
|
|
- `SECURE_API_BASE_URL`
|
|
|
|
**Prisma Studio (Développement uniquement)** :
|
|
|
|
- `DEV_PRISMA_STUDIO_DB_URL`, `DEV_PRISMA_STUDIO_SHADOW_URL`
|
|
|
|
### Chargement de la Configuration V2
|
|
|
|
#### Flux de Chargement de Configuration
|
|
|
|
**Application V2 (Production)** :
|
|
|
|
L'application V2 **ne charge JAMAIS** directement les fichiers `infos.json` ou `infos_v1.json`.
|
|
|
|
**Flux de chargement** :
|
|
```text
|
|
1. env-full-<env>-for-bdd-injection.txt
|
|
↓
|
|
2. import-env-to-db.ts (script de déploiement)
|
|
↓
|
|
3. system_configuration (base de données)
|
|
↓
|
|
4. ConfigLoader (application V2)
|
|
↓
|
|
5. BackendVariables / FrontendVariablesOptimized
|
|
```
|
|
|
|
**Fichiers utilisés** :
|
|
|
|
- `.secrets/<env>/env-full-<env>-for-bdd-injection.txt` : Variables injectées dans `system_configuration`
|
|
- **PAS** `infos.json` ou `infos_v1.json` pour l'application V2
|
|
|
|
**Scripts de Migration V1** :
|
|
|
|
Les scripts de migration V1 chargent les credentials BDD V1 depuis `.secrets/<env>/.env.<env>.connectDB` (DATABASE_V1_*). Ils peuvent charger depuis `infos.json` **uniquement** pour :
|
|
|
|
- Variables Scaleway S3 (migration fichiers V1 → V2)
|
|
|
|
**Scripts concernés** :
|
|
|
|
- `inject-s3-vars-from-infos.ts` : Injection variables S3 Scaleway dans `system_configuration` (lit `infos-migration-scaleway.json` en priorité, puis `infos.json` en fallback)
|
|
- `import-v1-data-direct-into-v2.sh` : Import données V1 depuis base Scaleway
|
|
- `migrate-files-from-s3.ts` : Migration fichiers depuis S3 Scaleway
|
|
- `migrate-ribs-from-s3.ts` : Migration RIBs depuis S3 Scaleway
|
|
|
|
#### Isolation des Variables V1
|
|
|
|
**Variables V1 Supprimées** :
|
|
|
|
Les variables suivantes ont été **supprimées** du code et des fichiers de configuration :
|
|
|
|
- `SECURE_API_KEY` : SecureService non utilisé (ancrage via BitcoinSignetService)
|
|
- `SECURE_API_BASE_URL` : SecureService non utilisé
|
|
- `NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT` : FranceConnect non utilisé en V2
|
|
- `NEXT_PUBLIC_FC_CLIENT_ID` : FranceConnect non utilisé en V2
|
|
- `NEXT_PUBLIC_FRANCE_CONNECT_CLIENT_ID` : FranceConnect non utilisé en V2
|
|
- `DOCAPOST_*` : Docapost non utilisé en V2
|
|
- `SENTRY_*` : Sentry non utilisé en V2
|
|
|
|
**Variables Scaleway (Migration uniquement)** :
|
|
|
|
Les variables Scaleway sont **isolées** pour la migration uniquement :
|
|
|
|
- `SCW_BUCKET_NAME`, `BUCKET_NAME` : Migration fichiers V1 → V2
|
|
- `SCW_ACCESS_KEY_ID`, `ACCESS_KEY_ID` : Migration fichiers V1 → V2
|
|
- `SCW_SECRET_ACCESS_KEY`, `ACCESS_KEY_SECRET` : Migration fichiers V1 → V2
|
|
- `SCW_REGION` : Migration fichiers V1 → V2
|
|
|
|
**Isolation** :
|
|
|
|
- ✅ Lues depuis `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) uniquement par les scripts de migration
|
|
- ✅ Injectées dans `system_configuration` avec le préfixe `SCW_`
|
|
- ✅ L'application V2 ne les utilise pas (stockage via IPFS Pinata)
|
|
- ✅ **Retirées de `env-full-<env>-for-bdd-injection.txt`** pour éviter toute utilisation par l'application V2
|
|
|
|
#### Fichiers de Configuration
|
|
|
|
**Structure** :
|
|
```text
|
|
.secrets/<env>/
|
|
├── infos_v2.json # Variables utilisées par l'application V2 (PRODUCTION)
|
|
├── infos-migration-scaleway.json # Variables Scaleway S3 (migration uniquement, PRIORITÉ)
|
|
├── infos.json # Variables migration V1 (NE PAS UTILISER EN V2, fallback Scaleway)
|
|
├── infos_v1.json # Variables migration V1 (NE PAS UTILISER EN V2)
|
|
└── env-full-<env>-for-bdd-injection.txt # Variables injectées dans system_configuration (SANS Scaleway)
|
|
```
|
|
|
|
**`env-full-<env>-for-bdd-injection.txt`** :
|
|
|
|
**Usage** : Variables injectées dans `system_configuration` lors du déploiement.
|
|
|
|
**Contenu** :
|
|
|
|
- Variables définies dans `CONFIG_MAPPING` (utilisées par l'application V2)
|
|
- Variables système nécessaires (`DATABASE_*`, `ENV`, `NODE_ENV`)
|
|
|
|
**Ne contient PAS** :
|
|
|
|
- Variables Scaleway (isolées dans `infos-migration-scaleway.json`, injectées séparément via `inject-s3-vars-from-infos.ts`)
|
|
- Variables V1 supprimées (SECURE_API_*, NEXT_PUBLIC_FC_*, DOCAPOST_*, SENTRY_*)
|
|
|
|
**Génération** :
|
|
|
|
- Généré manuellement ou via script `generate-env-full-from-infos-v2.sh`
|
|
- Nettoyé automatiquement pour supprimer les variables V1 non utilisées
|
|
|
|
#### Scripts de Déploiement
|
|
|
|
**`set-settings.sh`** :
|
|
|
|
**Usage** : Injection des variables dans `system_configuration`.
|
|
|
|
**Flux** :
|
|
|
|
1. Lit `env-full-<env>-for-bdd-injection.txt`
|
|
2. Injecte dans `system_configuration` via `import-env-to-db.ts`
|
|
3. Injecte variables S3 Scaleway depuis `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) via `inject-s3-vars-from-infos.ts`
|
|
|
|
**Fichiers utilisés** :
|
|
|
|
- `env-full-<env>-for-bdd-injection.txt` : Variables application V2 (SANS Scaleway)
|
|
- `infos-migration-scaleway.json` : Variables S3 Scaleway (migration uniquement, priorité)
|
|
- `infos.json` : Variables S3 Scaleway (migration uniquement, fallback)
|
|
|
|
**`apply-connectdb.sh`** :
|
|
|
|
**Usage** : Configuration de la connexion à la base de données.
|
|
|
|
**Flux** :
|
|
|
|
1. Lit `.env.<env>.connectDB` pour les credentials base de données
|
|
2. `backend.env` n'existe plus sur les cibles (sauvegarde puis suppression à chaque sync). Source unique BDD : `.env.<env>.connectDB` (voir docs/OPERATIONS.md).
|
|
3. `env-full-<env>-for-bdd-injection.txt` est utilisé pour setSettings et build frontend.
|
|
|
|
**Fichiers utilisés** :
|
|
|
|
- `.env.<env>.connectDB` : Credentials base de données
|
|
- `env-full-<env>-for-bdd-injection.txt` : Variables supplémentaires
|
|
|
|
**Scripts hors déploiement** (dans `deploy/scripts_v2`) : tous les scripts qui se connectent à la base sans être des scripts de déploiement (ex. ensure-license, check-logs-and-db, ensure-role-permissions-matrix, promote-super-admins, seed-site-texts, reanchor-all, etc.) utilisent `.secrets/<env>/.env.<env>.connectDB` pour la connexion à la base. Les scripts de déploiement (deploy-app, migrate-resolve-database, prisma-baseline, set-settings, reset-database-from-schema-export, apply-connectdb, install-systemd-units) utilisent `.env.<env>.connectDB` (backend.env n'est plus utilisé ; voir OPERATIONS.md).
|
|
|
|
### Utilisation des Variables V2
|
|
|
|
#### Variables de Sécurité (Tokens JWT)
|
|
|
|
**`ACCESS_TOKEN_SECRET`** :
|
|
|
|
- **Usage** : Secret pour signer les tokens d'accès JWT
|
|
- **Fichiers** : `AuthService.ts` (génération/vérification), `VariablesBuilder.ts`, `Variables.ts`
|
|
- **Catégorie** : `SECURITY` (sensible, requis)
|
|
|
|
**`REFRESH_TOKEN_SECRET`** :
|
|
|
|
- **Usage** : Secret pour signer les tokens de rafraîchissement JWT
|
|
- **Fichiers** : `AuthService.ts` (génération/vérification/rafraîchissement), `VariablesBuilder.ts`, `Variables.ts`
|
|
- **Catégorie** : `SECURITY` (sensible, requis)
|
|
|
|
**`ACCESS_TOKEN_TTL_MINUTES`** :
|
|
|
|
- **Usage** : Durée de vie des tokens d'accès en minutes (format numérique)
|
|
- **Fichiers** : `TokenConfigHelper.ts` (résolution TTL)
|
|
- **Catégorie** : `SECURITY` (non sensible, optionnel)
|
|
- **Valeur par défaut** : 60 minutes (1h)
|
|
|
|
**`REFRESH_TOKEN_TTL_MINUTES`** :
|
|
|
|
- **Usage** : Durée de vie des tokens de rafraîchissement en minutes (format numérique)
|
|
- **Fichiers** : `TokenConfigHelper.ts` (résolution TTL)
|
|
- **Catégorie** : `SECURITY` (non sensible, optionnel)
|
|
- **Valeur par défaut** : 10080 minutes (7 jours)
|
|
|
|
**`JWT_EXPIRES_IN`** :
|
|
|
|
- **Usage** : Durée de vie JWT en format legacy (ex: "1h", "7d")
|
|
- **Fichiers** : `TokenConfigHelper.ts` (fallback si `ACCESS_TOKEN_TTL_MINUTES` non défini)
|
|
- **Catégorie** : `SECURITY` (non sensible, optionnel)
|
|
- **Valeur par défaut** : "1h" (60 minutes)
|
|
- **Note** : Variable legacy, `ACCESS_TOKEN_TTL_MINUTES` est préféré
|
|
|
|
**`REFRESH_TOKEN_EXPIRES_IN`** :
|
|
|
|
- **Usage** : Durée de vie des tokens de rafraîchissement en format legacy (ex: "7d")
|
|
- **Fichiers** : `TokenConfigHelper.ts` (fallback si `REFRESH_TOKEN_TTL_MINUTES` non défini)
|
|
- **Catégorie** : `SECURITY` (non sensible, optionnel)
|
|
- **Valeur par défaut** : "7d" (10080 minutes)
|
|
- **Note** : Variable legacy, `REFRESH_TOKEN_TTL_MINUTES` est préféré
|
|
|
|
#### Variables Scaleway (Migration V1 uniquement)
|
|
|
|
**`SCW_BUCKET_NAME`** :
|
|
|
|
- **Usage** : Nom du bucket S3 Scaleway pour la migration des fichiers V1 → V2
|
|
- **Fichiers** : `migrate-files-from-s3.ts`, `migrate-ribs-from-s3.ts`, `inject-s3-vars-from-infos.ts`
|
|
- **Catégorie** : `SYSTEM` (non sensible, optionnel - migration uniquement)
|
|
- **Source** : `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) - injecté dans `system_configuration` avec préfixe `SCW_`
|
|
- **Note** : Variable utilisée uniquement par les scripts de migration, pas par l'application V2 en production
|
|
|
|
**`SCW_ACCESS_KEY_ID`** :
|
|
|
|
- **Usage** : Clé d'accès S3 Scaleway pour la migration des fichiers V1 → V2
|
|
- **Fichiers** : `migrate-files-from-s3.ts`, `migrate-ribs-from-s3.ts`, `inject-s3-vars-from-infos.ts`
|
|
- **Catégorie** : `SYSTEM` (sensible, optionnel - migration uniquement)
|
|
- **Source** : `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) - injecté dans `system_configuration` avec préfixe `SCW_`
|
|
|
|
**`SCW_SECRET_ACCESS_KEY`** :
|
|
|
|
- **Usage** : Clé secrète S3 Scaleway pour la migration des fichiers V1 → V2
|
|
- **Fichiers** : `migrate-files-from-s3.ts`, `migrate-ribs-from-s3.ts`, `inject-s3-vars-from-infos.ts`
|
|
- **Catégorie** : `SYSTEM` (sensible, optionnel - migration uniquement)
|
|
- **Source** : `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) - injecté dans `system_configuration` avec préfixe `SCW_`
|
|
|
|
**`SCW_REGION`** :
|
|
|
|
- **Usage** : Région S3 Scaleway pour la migration des fichiers V1 → V2
|
|
- **Fichiers** : `migrate-files-from-s3.ts`, `migrate-ribs-from-s3.ts`, `inject-s3-vars-from-infos.ts`
|
|
- **Catégorie** : `SYSTEM` (non sensible, optionnel - migration uniquement)
|
|
- **Valeur par défaut** : "fr-par"
|
|
- **Source** : `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) - injecté dans `system_configuration` avec préfixe `SCW_`
|
|
|
|
#### Flux d'Utilisation
|
|
|
|
**Tokens JWT** :
|
|
```text
|
|
1. ACCESS_TOKEN_SECRET / REFRESH_TOKEN_SECRET
|
|
↓ (system_configuration)
|
|
2. BackendVariables
|
|
↓
|
|
3. AuthService.generateAccessToken() / generateRefreshToken()
|
|
↓
|
|
4. JWT signé avec secret + TTL
|
|
```
|
|
|
|
**TTL Tokens** :
|
|
```text
|
|
1. ACCESS_TOKEN_TTL_MINUTES (priorité) ou JWT_EXPIRES_IN (fallback)
|
|
↓ (system_configuration)
|
|
2. TokenConfigHelper.getAccessTokenTtlMinutes()
|
|
↓
|
|
3. AuthService.generateAccessToken() avec expiresIn
|
|
```
|
|
|
|
**Migration Scaleway** :
|
|
```text
|
|
1. SCW_* variables depuis infos-migration-scaleway.json (priorité) ou infos.json (fallback)
|
|
↓ (inject-s3-vars-from-infos.ts)
|
|
2. system_configuration (préfixe SCW_)
|
|
↓
|
|
3. migrate-files-from-s3.ts / migrate-ribs-from-s3.ts
|
|
↓
|
|
4. Client S3 Scaleway créé
|
|
```
|
|
|
|
### Nettoyage des Variables V1
|
|
|
|
#### Variables à garder (Migration uniquement)
|
|
|
|
Ces variables sont utilisées **uniquement** par les scripts de migration V1 → V2 et ne doivent **pas** être utilisées par l'application V2 en production.
|
|
|
|
- `SCW_BUCKET_NAME`, `BUCKET_NAME` : Migration fichiers V1 → V2
|
|
- `SCW_ACCESS_KEY_ID`, `ACCESS_KEY_ID` : Migration fichiers V1 → V2
|
|
- `SCW_SECRET_ACCESS_KEY`, `ACCESS_KEY_SECRET` : Migration fichiers V1 → V2
|
|
- `SCW_REGION` : Migration fichiers V1 → V2
|
|
|
|
**Isolation** : Ces variables sont lues depuis `infos.json` et injectées dans `system_configuration` avec le préfixe `SCW_` uniquement lors de l'exécution des scripts de migration.
|
|
|
|
#### Variables à nettoyer (Non utilisées)
|
|
|
|
Ces variables ne sont **pas utilisées** par l'application V2 et doivent être supprimées du code.
|
|
|
|
**FranceConnect** :
|
|
|
|
- `NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT` : Dépréciée, filtrée dans `deprecatedKeys`
|
|
- `NEXT_PUBLIC_FC_CLIENT_ID` : Dépréciée, filtrée dans `deprecatedKeys`
|
|
- `NEXT_PUBLIC_FRANCE_CONNECT_CLIENT_ID` : Dépréciée
|
|
|
|
**Statut** : Aucune utilisation dans le frontend. Variables déjà filtrées dans `ConfigController.ts` mais toujours définies dans `ImportEnvConfigMapping.ts`.
|
|
|
|
**Docapost** :
|
|
|
|
- `DOCAPOST_*` : Déjà commenté/retiré
|
|
|
|
**Statut** : Déjà retiré du code. Aucune action nécessaire.
|
|
|
|
**Sentry** :
|
|
|
|
- `SENTRY_DSN`, `SENTRY_AUTH_TOKEN`, `SENTRY_FRONT_DSN` : Non utilisé
|
|
|
|
**Statut** : Aucune référence trouvée dans le code. Aucune action nécessaire.
|
|
|
|
#### Actions de Nettoyage
|
|
|
|
**1. Supprimer les variables FranceConnect** :
|
|
|
|
**`ImportEnvConfigMapping.ts`** :
|
|
|
|
- Supprimer `NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT`
|
|
- Supprimer `NEXT_PUBLIC_FC_CLIENT_ID`
|
|
- Supprimer `NEXT_PUBLIC_FRANCE_CONNECT_CLIENT_ID`
|
|
|
|
**`ConfigController.ts`** :
|
|
|
|
- Supprimer les entrées du `deprecatedKeys` :
|
|
- `NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT`
|
|
- `NEXT_PUBLIC_FC_CLIENT_ID`
|
|
|
|
**Note** : Ces variables sont déjà filtrées mais peuvent être complètement supprimées maintenant.
|
|
|
|
#### Règles de Séparation
|
|
|
|
**✅ Variables V2 (Production)** :
|
|
|
|
- **Source** : `infos_v2.json`
|
|
- **Usage** : Application V2 en production
|
|
- **Stockage** : Base de données locale (`localhost`)
|
|
- **Stockage fichiers** : IPFS (Pinata)
|
|
- **Base de données** : PostgreSQL locale
|
|
|
|
**❌ Variables Migration V1** :
|
|
|
|
- **Source** : `infos.json` ou `infos_v1.json`
|
|
- **Usage** : Scripts de migration uniquement
|
|
- **Stockage** : Scaleway S3 (lecture seule)
|
|
- **Base de données** : Base V1 Scaleway (lecture seule)
|
|
|
|
**🔒 Isolation** :
|
|
|
|
**L'application V2 ne doit JAMAIS** :
|
|
|
|
- Se connecter à Scaleway S3
|
|
- Se connecter à la base de données V1 Scaleway
|
|
- Utiliser les variables Docapost, FranceConnect, Sentry
|
|
- Charger `infos.json` ou `infos_v1.json` en production
|
|
|
|
**Les scripts de migration peuvent** :
|
|
|
|
- Lire depuis `infos-migration-scaleway.json` (priorité) ou `infos.json` (fallback) pour accéder à Scaleway S3
|
|
- Se connecter à la base de données V1 Scaleway
|
|
- Injecter les variables Scaleway dans `system_configuration` avec le préfixe `SCW_`
|
|
|
|
---
|
|
|
|
## 2. Migration Pattern : Classes vers Factories
|
|
|
|
### Contexte
|
|
|
|
Migration complète des classes héritant de `BaseApiService` vers des factories pour résoudre le problème TDZ (Temporal Dead Zone) dans Webpack/Next.js.
|
|
|
|
### Pattern Général
|
|
|
|
#### Avant (Classe)
|
|
|
|
```typescript
|
|
import BaseSuperAdmin from "../BaseSuperAdmin";
|
|
|
|
export default class MyService extends BaseSuperAdmin {
|
|
private static instance: MyService;
|
|
|
|
private get baseUrl(): string {
|
|
return this.namespaceUrl.concat("/my-service");
|
|
}
|
|
|
|
private constructor() {
|
|
super();
|
|
}
|
|
|
|
public static getInstance() {
|
|
return (this.instance ??= new this());
|
|
}
|
|
|
|
public async myMethod(body?: ApiPayload) {
|
|
const url = new URL(this.baseUrl.concat("/endpoint"));
|
|
return this.postRequest<MyResponse>(url, body ?? {});
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Après (Factory)
|
|
|
|
**1. Créer `MyServiceFactory.ts` :**
|
|
|
|
```typescript
|
|
import { createBaseSuperAdmin, type IBaseSuperAdmin } from "../BaseSuperAdminFactory";
|
|
import type { ApiPayload } from "../../../types";
|
|
import type { MyResponse } from "./MyService";
|
|
|
|
interface MyServiceInstance {
|
|
myMethod(body?: ApiPayload): Promise<MyResponse>;
|
|
}
|
|
|
|
let instance: MyServiceInstance | null = null;
|
|
let baseSuperAdminInstance: IBaseSuperAdmin | null = null;
|
|
|
|
function createMyService(): MyServiceInstance {
|
|
if (!baseSuperAdminInstance) {
|
|
baseSuperAdminInstance = createBaseSuperAdmin();
|
|
}
|
|
|
|
const baseSuperAdmin = baseSuperAdminInstance;
|
|
const baseUrl = baseSuperAdmin.namespaceUrl.concat("/my-service");
|
|
|
|
return {
|
|
myMethod: async (body?: ApiPayload): Promise<MyResponse> => {
|
|
const url = new URL(baseUrl.concat("/endpoint"));
|
|
return baseSuperAdmin.postRequest<MyResponse>(url, body ?? {});
|
|
},
|
|
};
|
|
}
|
|
|
|
export function getMyServiceInstance(): MyServiceInstance {
|
|
if (!instance) {
|
|
instance = createMyService();
|
|
}
|
|
return instance;
|
|
}
|
|
```
|
|
|
|
**2. Modifier `MyService.ts` :**
|
|
|
|
```typescript
|
|
// Export types
|
|
export type MyResponse = {
|
|
// ...
|
|
};
|
|
|
|
// Export interface
|
|
export interface IMyService {
|
|
myMethod(body?: unknown): Promise<MyResponse>;
|
|
}
|
|
|
|
// Lazy import of factory
|
|
let factoryInstance: (() => IMyService) | null = null;
|
|
|
|
function getFactory(): () => IMyService {
|
|
if (!factoryInstance) {
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
factoryInstance = require("./MyServiceFactory").getMyServiceInstance;
|
|
}
|
|
return factoryInstance as () => IMyService;
|
|
}
|
|
|
|
// Export the instance getter
|
|
export const MyService = {
|
|
getInstance: (): IMyService => {
|
|
return getFactory()();
|
|
},
|
|
};
|
|
|
|
// Export default for backward compatibility
|
|
export default MyService;
|
|
```
|
|
|
|
### Remplacements Clés
|
|
|
|
| Avant (Classe) | Après (Factory) |
|
|
|----------------|-----------------|
|
|
| `this.namespaceUrl` | `baseSuperAdmin.namespaceUrl` |
|
|
| `this.baseUrl` | Variable locale `baseUrl` |
|
|
| `this.postRequest()` | `baseSuperAdmin.postRequest()` |
|
|
| `this.getRequest()` | `baseSuperAdmin.getRequest()` |
|
|
| `this.putRequest()` | `baseSuperAdmin.putRequest()` |
|
|
| `this.deleteRequest()` | `baseSuperAdmin.deleteRequest()` |
|
|
| `this.onError()` | `baseSuperAdmin.onError()` |
|
|
| `this.buildHeaders()` | `baseSuperAdmin.buildHeaders()` |
|
|
|
|
### Factories de Base Disponibles
|
|
|
|
- `createBaseApiService()` → `BaseApiServiceFactory`
|
|
- `createBaseNotary()` → `BaseNotaryFactory`
|
|
- `createBaseSuperAdmin()` → `BaseSuperAdminFactory`
|
|
- `createBaseAdmin()` → `BaseAdminFactory`
|
|
- `createBaseCustomer()` → `BaseCustomerFactory`
|
|
- `createBasePublic()` → `BasePublicFactory`
|
|
- `createBaseId360()` → `BaseId360Factory`
|
|
|
|
### Checklist de Migration
|
|
|
|
1. ✅ Créer `MyServiceFactory.ts` avec la logique métier
|
|
2. ✅ Déplacer les types dans `MyService.ts` (si nécessaire)
|
|
3. ✅ Modifier `MyService.ts` pour exporter la factory via lazy import
|
|
4. ✅ Vérifier que tous les imports existants fonctionnent encore
|
|
5. ✅ Tester TypeScript (`npm run typecheck`)
|
|
6. ✅ Tester le build (`npm run build`)
|
|
|
|
### Ordre de Migration Recommandé
|
|
|
|
1. Commencer par les classes les plus simples (peu de méthodes)
|
|
2. Migrer par namespace (SuperAdmin → Admin → Customer → Notary → Public)
|
|
3. Tester après chaque groupe de 5-10 fichiers
|
|
|
|
---
|
|
|
|
## 3. Checklist de Migration V1 → V2
|
|
|
|
### Phase 1 : Préparation
|
|
|
|
- [ ] **Dump V1 disponible** : `deploy/bdd.<env>` (format PGDMP)
|
|
- [ ] **Schéma V2 exporté** : `deploy/schema-v2.sql` (optionnel)
|
|
- [ ] **Clés V1 extraites** : `.secrets/v1-file-keys.json` (via `extract-v1-file-keys.sh`)
|
|
- [ ] **Clé maître V2 configurée** : `FILE_ENCRYPTION_MASTER_KEY` dans `system_configuration` ou `.env`
|
|
|
|
### Phase 2 : Reset + Import
|
|
|
|
- [ ] **Reset de la base** : Baseline V2 depuis schéma
|
|
```bash
|
|
bash deploy/scripts_v2/deploy.sh <env> \
|
|
--resetDatabaseFromSchemaExport \
|
|
--migrateResolveDatabase
|
|
```
|
|
|
|
- [ ] **Import des données V1** : Import complet (destructif)
|
|
```bash
|
|
bash deploy/scripts_v2/deploy.sh <env> \
|
|
--importV1Data \
|
|
--deploy
|
|
```
|
|
|
|
- [ ] **Vérification de l'import** :
|
|
- [ ] Nombre de fichiers importés cohérent
|
|
- [ ] Clés individuelles présentes dans `files.key` et `files_notary.key`
|
|
- [ ] RIB présents dans `rib_encrypted` (si migrés depuis S3)
|
|
|
|
### Phase 3 : Récupération des Fichiers V1
|
|
|
|
- [ ] **Extraction des clés V1** (si pas déjà fait)
|
|
```bash
|
|
./deploy/scripts_v2/backup/extract-v1-file-keys.sh .secrets/<env>/bdd.<env>
|
|
```
|
|
|
|
- [ ] **Récupération des fichiers** : Déchiffrement V1 → Re-chiffrement V2
|
|
```bash
|
|
cd lecoffre-back-main
|
|
npx ts-node src/scripts/recover-files-from-v1.ts \
|
|
--output-report logs/v1-files-recovery-report.json
|
|
```
|
|
|
|
- [ ] **Vérification de la récupération** :
|
|
- [ ] Rapport de récupération généré
|
|
- [ ] Fichiers récupérés avec succès
|
|
- [ ] Nouveaux hash IPFS en base
|
|
- [ ] Fichiers accessibles via les nouveaux chemins IPFS
|
|
|
|
### Phase 4 : Migration des RIB
|
|
|
|
- [ ] **Migration des RIB vers IPFS** (si nécessaire)
|
|
```bash
|
|
cd lecoffre-back-main
|
|
npx ts-node src/scripts/migrate-ribs-to-ipfs.ts \
|
|
--output-report logs/ribs-migration-to-ipfs-report.json
|
|
```
|
|
|
|
- [ ] **Vérification des RIB** :
|
|
- [ ] RIB migrés avec succès
|
|
- [ ] `rib_url` présent en base
|
|
- [ ] `rib_encrypted` supprimé après migration
|
|
- [ ] RIB accessibles via IPFS
|
|
|
|
### Phase 5 : Réancrage
|
|
|
|
- [ ] **Réancrage des documents** (si nécessaire)
|
|
```bash
|
|
cd lecoffre-back-main
|
|
npm run reanchor-all-complete
|
|
```
|
|
|
|
- [ ] **Vérification de l'ancrage** :
|
|
- [ ] Documents réancrés avec succès
|
|
- [ ] Nouveaux `tx_id` en base
|
|
- [ ] Certificats régénérés
|
|
|
|
---
|
|
|
|
## 4. Conformité des Scripts de Migration
|
|
|
|
### Scripts de Migration des Fichiers
|
|
|
|
#### `recover-files-from-v1.ts` - 100% Conforme
|
|
|
|
**Corrections appliquées** :
|
|
|
|
- ✅ Utilise AES-256-CTR (algorithme V1)
|
|
- ✅ Lit le format V1 : `[IV 16 bytes][encrypted data]`
|
|
- ✅ Utilise les clés individuelles UUID depuis `files.key` et `files_notary.key`
|
|
- ✅ Convertit UUID → clé de chiffrement via SHA-256 + base64 (32 premiers caractères)
|
|
- ✅ Re-chiffre avec clé maître V2 (AES-256-GCM)
|
|
- ✅ Upload sur IPFS V2
|
|
- ✅ Met à jour `file_path` avec nouveau hash IPFS
|
|
|
|
**⚠️ IMPORTANT** : Ce script traite uniquement les fichiers sur IPFS (`files` et `files_notary`). Il ne traite **pas** les RIB.
|
|
|
|
### Scripts de Migration des RIB
|
|
|
|
#### `migrate-ribs-to-ipfs.ts` - 100% Conforme
|
|
|
|
**Fonctionnalités** :
|
|
|
|
- ✅ Identifie les RIB en PostgreSQL (`rib_encrypted` sans `rib_url`)
|
|
- ✅ Déchiffre depuis PostgreSQL
|
|
- ✅ Migre vers IPFS via `uploadEncryptedRIB()` (filigrane → ancrage → chiffrement → IPFS)
|
|
- ✅ Supprime `rib_encrypted` après migration réussie
|
|
- ✅ Génère un rapport détaillé
|
|
|
|
**⚠️ IMPORTANT** : Ce script traite uniquement les RIB stockés en PostgreSQL. Les RIB déjà sur IPFS sont ignorés.
|
|
|
|
---
|
|
|
|
## 5. Erreurs et Corrections de Migration
|
|
|
|
### Erreur "Missing parameters" dans check-v1-migration-status.sh
|
|
|
|
**Date** : 2026-01-27
|
|
|
|
**Problème** : Les paramètres `ENV`, `DOMAIN`, `APP_ROOT` n'étaient pas correctement passés au script distant via SSH.
|
|
|
|
**Root Cause** : Le heredoc dans `ssh_run` attendait des paramètres positionnels (`$1`, `$2`, `$3`) mais ils n'étaient pas passés correctement. Les paramètres étaient passés après la fermeture du heredoc, ce qui ne fonctionne pas avec `bash -s`.
|
|
|
|
**Solution** : Passer les variables comme variables d'environnement avant le heredoc :
|
|
|
|
```bash
|
|
# Avant (incorrect)
|
|
ssh_run ... 'bash -s' <<'CHECK_MIGRATION'
|
|
ENV="${1:-}"
|
|
CHECK_MIGRATION
|
|
"$ENV" "$DOMAIN" "$APP_ROOT"
|
|
|
|
# Après (correct)
|
|
ssh_run ... env "ENV=$ENV" "DOMAIN=$DOMAIN" "APP_ROOT=$APP_ROOT" 'bash -s' <<'CHECK_MIGRATION'
|
|
if [[ -z "${ENV:-}" || -z "${DOMAIN:-}" || -z "${APP_ROOT:-}" ]]; then
|
|
echo "Missing parameters" >&2
|
|
exit 1
|
|
fi
|
|
CHECK_MIGRATION
|
|
```
|
|
|
|
**Fichier modifié** : `deploy/scripts_v2/check-v1-migration-status.sh`
|
|
|
|
### Erreur SQL "relation public.ribs does not exist"
|
|
|
|
**Date** : 2026-01-27
|
|
|
|
**Problème** : Le script `check-v1-migration-status.sh` interrogeait une table `public.ribs` qui n'existe pas.
|
|
|
|
**Root Cause** : Les données RIB sont stockées dans la table `public.offices` avec les colonnes `rib_url` et `rib_encrypted`.
|
|
|
|
**Solution** : Remplacer toutes les références à `public.ribs` par `public.offices` :
|
|
|
|
```sql
|
|
-- Avant
|
|
FROM public.ribs
|
|
WHERE rib_url IS NOT NULL
|
|
|
|
-- Après
|
|
FROM public.offices
|
|
WHERE rib_url IS NOT NULL
|
|
```
|
|
|
|
**Fichier modifié** : `deploy/scripts_v2/check-v1-migration-status.sh`
|
|
|
|
### Suppression de la limite de date 2025-05-01
|
|
|
|
**Date** : 2026-01-27
|
|
|
|
**Problème** : Les scripts de migration avaient une limite de date fixe qui empêchait le traitement des fichiers créés avant le 01/05/2025.
|
|
|
|
**Solution** : Suppression de toutes les limites de dates dans les scripts de migration :
|
|
|
|
- `recover-files-from-v1.ts` : Suppression de `maxAgeDate` et des filtres `created_at >= maxAgeDate`
|
|
- `migrate-files-from-s3.ts` : Suppression de `maxAgeDate` et des filtres `created_at >= maxAgeDate`
|
|
- `diagnose-failed-files-v1.ts` : Suppression de `maxAgeDate` et des filtres `created_at >= maxAgeDate`
|
|
- `reanchor-all-complete.ts` : Suppression de `maxAgeDate` et `maxAgeDateNotary`
|
|
- `import-v1-data-direct-into-v2.sh` : Suppression des filtres `WHERE "created_at" >= '2025-05-01'`
|
|
|
|
**Fichiers modifiés** :
|
|
|
|
- `lecoffre-back-main/src/scripts/recover-files-from-v1.ts`
|
|
- `lecoffre-back-main/src/scripts/migrate-files-from-s3.ts`
|
|
- `lecoffre-back-main/src/scripts/diagnose-failed-files-v1.ts`
|
|
- `lecoffre-back-main/src/scripts/reanchor-all-complete.ts`
|
|
- `deploy/scripts_v2/remote/import-v1-data-direct-into-v2.sh`
|
|
|
|
### Erreurs de syntaxe Bash dans import-v1-data-direct-into-v2.sh
|
|
|
|
**Date** : 2026-01-23
|
|
|
|
**Problèmes** :
|
|
|
|
1. Syntaxe incorrecte avec `ssh_run` et heredoc avec arguments
|
|
2. Substitution de commande avec heredoc entre guillemets
|
|
3. Variables échappées empêchant l'interpolation
|
|
4. Dollar quotes (`$$`) interprétés comme PID par bash
|
|
|
|
**Solutions** :
|
|
|
|
1. Passer les arguments séparément sans guillemets : `bash -s arg1 arg2` au lieu de `"bash -s \"arg1\" \"arg2\""`
|
|
2. Retirer les guillemets autour de `$(...)` avec heredoc
|
|
3. Utiliser les variables directement sans échappement dans heredoc
|
|
4. Utiliser Python avec variables d'environnement pour générer `$$` (fallback awk)
|
|
|
|
**Fichier modifié** : `deploy/scripts_v2/remote/import-v1-data-direct-into-v2.sh`
|
|
|
|
**Leçons apprises / À éviter :**
|
|
|
|
- Ne jamais générer `$$` directement dans bash (toujours interprété comme PID). Utiliser Python avec variables d'environnement et heredoc en guillemets simples, ou awk en fallback.
|
|
- Heredoc avec arguments : passer les arguments séparément, sans guillemets autour de `bash -s` (`bash -s arg1 arg2`).
|
|
- Substitution de commande avec heredoc : ne pas mettre de guillemets autour de `$(...)`.
|
|
- Variables dans heredoc : ne pas échapper avec `\"` ou `\$` ; utiliser `"${VAR}"` directement.
|
|
|
|
---
|
|
|
|
---
|
|
|
|
**Dernière mise à jour** : 2026-01-28 (consolidation MIGRATION.md, V1_TO_V2_MIGRATION_PROCESS.md, V2_ENVIRONMENT_VARIABLES.md, V2_CONFIGURATION_LOADING.md, V2_VARIABLES_USAGE.md, V1_VARIABLES_CLEANUP.md)
|