# 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 ` **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//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//infos.json`, `.secrets//infos_v1.json` et, pour la BDD V1, dans `.secrets//.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--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-full--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..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--for-bdd-injection.txt`** pour éviter toute utilisation par l'application V2 #### Fichiers de Configuration **Structure** : ```text .secrets// ├── 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--for-bdd-injection.txt # Variables injectées dans system_configuration (SANS Scaleway) ``` **`env-full--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--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--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..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..connectDB` (voir docs/OPERATIONS.md). 3. `env-full--for-bdd-injection.txt` est utilisé pour setSettings et build frontend. **Fichiers utilisés** : - `.env..connectDB` : Credentials base de données - `env-full--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..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..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(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; } 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 => { const url = new URL(baseUrl.concat("/endpoint")); return baseSuperAdmin.postRequest(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; } // 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.` (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 \ --resetDatabaseFromSchemaExport \ --migrateResolveDatabase ``` - [ ] **Import des données V1** : Import complet (destructif) ```bash bash deploy/scripts_v2/deploy.sh \ --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//bdd. ``` - [ ] **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)