From b2c85803827eb7babaca677ae76cee3bae139d23 Mon Sep 17 00:00:00 2001 From: Manus Date: Sun, 30 Aug 2026 09:40:46 +0000 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Correctif=20de=20production=20:?= =?UTF-8?q?=20le=20serveur=20ex=C3=A9cute=20d=C3=A9sormais=20les=20migrati?= =?UTF-8?q?ons=20Drizzle=20versionn=C3=A9es=20avant=20l'ouverture=20HTTP,?= =?UTF-8?q?=20ce=20qui=20initialise=20une=20base=20vide=20lors=20du=20prem?= =?UTF-8?q?ier=20d=C3=A9ploiement.=20Test=20de=20r=C3=A9gression=20ajout?= =?UTF-8?q?=C3=A9.=20Validation=20compl=C3=A8te=20r=C3=A9ussie=20:=2061=20?= =?UTF-8?q?tests,=20TypeScript=20et=20build.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/salaires-securite.md | 8 ++++++++ server/_core/index.ts | 3 +++ server/databaseMigrations.ts | 24 ++++++++++++++++++++++++ server/dockerMigrationBootstrap.test.ts | 14 ++++++++++++++ todo.md | 14 +++++++++----- 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 server/databaseMigrations.ts create mode 100644 server/dockerMigrationBootstrap.test.ts diff --git a/docs/salaires-securite.md b/docs/salaires-securite.md index d490552..65600c2 100644 --- a/docs/salaires-securite.md +++ b/docs/salaires-securite.md @@ -43,3 +43,11 @@ La CI Gitea `validate.yml` associée au correctif de résolution MySQL a termin Après le déploiement du correctif, la connexion avec le compte administrateur de recette a de nouveau abouti et l'interface générale s'affiche sans erreur de base de données. La validation finale en HTTPS a confirmé le menu **Santinova > Salaires**, l'absence de fausse alerte de chiffrement, l'affichage de la liasse archivée et l'ouverture de cette liasse exclusivement via le bouton de consultation protégé. Aucun lien de stockage n'est exposé dans l'interface. + +Le portail de recette et le dashboard ont été synchronisés à partir du manifeste applicatif. La carte **Gestion budget informatique** est présente dans l'onglet Santinova du portail, utilise son visuel dédié et ouvre l'URL HTTPS de recette. + +La promotion vers la production utilise le domaine HTTPS `budget-si.santinova-soft.org`, un secret de session et une clé de chiffrement distincts de la recette, ainsi qu'un volume persistant de paie dédié. La page de connexion de production a répondu après le déploiement initial. + +Le dashboard de production a découvert Budget SI et le signale en ligne. Le portail de production présente désormais la carte **Gestion budget informatique** dans l'onglet Santinova, avec son visuel dédié et le lien HTTPS de production. La promotion a été réalisée depuis le dépôt Gitea production par le flux Santinova de déploiement sécurisé ; la CI Gitea reste validée sur la recette avant promotion. + +La validation de connexion administrateur de production a mis en évidence une initialisation de schéma incomplète : la table des utilisateurs n'est pas encore disponible. Cette correction est requise avant de déclarer la production utilisable. diff --git a/server/_core/index.ts b/server/_core/index.ts index 1069313..7908909 100644 --- a/server/_core/index.ts +++ b/server/_core/index.ts @@ -7,6 +7,7 @@ import { registerOAuthRoutes } from "./oauth"; import { registerStorageProxy } from "./storageProxy"; import { appRouter } from "../routers"; import { registerSalairesHttpRoutes } from "../salairesHttp"; +import { runDatabaseMigrations } from "../databaseMigrations"; import { createContext } from "./context"; import { serveStatic, setupVite } from "./vite"; @@ -30,6 +31,8 @@ async function findAvailablePort(startPort: number = 3000): Promise { } async function startServer() { + // Une instance ne devient disponible qu'après la synchronisation du schéma. + await runDatabaseMigrations(); const app = express(); const server = createServer(app); // Configure body parser with larger size limit for file uploads diff --git a/server/databaseMigrations.ts b/server/databaseMigrations.ts new file mode 100644 index 0000000..29d6522 --- /dev/null +++ b/server/databaseMigrations.ts @@ -0,0 +1,24 @@ +import { migrate } from "drizzle-orm/mysql2/migrator"; +import { drizzle } from "drizzle-orm/mysql2"; +import { createPool } from "mysql2/promise"; + +/** + * Synchronise la base avec les migrations versionnées avant d'exposer l'API. + * Une base sans DATABASE_URL reste utilisable en développement statique, mais + * un déploiement connecté échoue explicitement si la migration ne peut aboutir. + */ +export async function runDatabaseMigrations() { + const databaseUrl = process.env.DATABASE_URL; + if (!databaseUrl) { + console.warn("[Database] DATABASE_URL absent : migrations ignorées."); + return; + } + + const pool = createPool(databaseUrl); + try { + await migrate(drizzle(pool), { migrationsFolder: "./drizzle" }); + console.log("[Database] Migrations Drizzle synchronisées."); + } finally { + await pool.end(); + } +} diff --git a/server/dockerMigrationBootstrap.test.ts b/server/dockerMigrationBootstrap.test.ts new file mode 100644 index 0000000..3cc4809 --- /dev/null +++ b/server/dockerMigrationBootstrap.test.ts @@ -0,0 +1,14 @@ +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +describe("bootstrap de migrations Docker", () => { + it("exécute les migrations Drizzle avant de lancer le serveur", async () => { + const server = await readFile(resolve(process.cwd(), "server/_core/index.ts"), "utf8"); + const migrationBootstrap = await readFile(resolve(process.cwd(), "server/databaseMigrations.ts"), "utf8"); + + expect(server).toContain("await runDatabaseMigrations()"); + expect(migrationBootstrap).toContain("migrate("); + expect(migrationBootstrap).toContain("migrationsFolder: \"./drizzle\""); + }); +}); diff --git a/todo.md b/todo.md index a0095e3..f82e4f8 100644 --- a/todo.md +++ b/todo.md @@ -206,9 +206,13 @@ - [x] Corriger l'accès à la base de recette empêchant la connexion administrateur après le dernier redéploiement ## Promotion Salaires — recette et production -- [ ] Vérifier la version Salaires active et le statut Gitea de recette avant promotion -- [ ] Mettre à jour le dashboard et le portail de recette pour la fenêtre Salaires -- [ ] Promouvoir la version Salaires validée vers la production via Gitea CI/CD -- [ ] Mettre à jour le dashboard et le portail de production pour la fenêtre Salaires +- [x] Vérifier la version Salaires active et le statut Gitea de recette avant promotion +- [x] Mettre à jour le dashboard et le portail de recette pour la fenêtre Salaires +- [x] Promouvoir la version Salaires validée vers la production depuis le dépôt Gitea via le flux Santinova de déploiement sécurisé +- [x] Mettre à jour le dashboard et le portail de production pour la fenêtre Salaires - [ ] Vérifier les accès HTTPS administrateur et le statut des déploiements dans les deux environnements -- [ ] Paramétrer le domaine Traefik Budget SI par environnement pour que la production réponde sur son URL dédiée +- [x] Paramétrer le domaine Traefik Budget SI par environnement pour que la production réponde sur son URL dédiée +- [x] Corriger le visuel Budget SI non chargé sur la carte du portail de recette +- [x] Créer et synchroniser le dépôt Gitea production Budget SI avant le déploiement du serveur de production +- [ ] Initialiser et vérifier le schéma de la base Budget SI de production avant la validation administrateur +- [ ] Exécuter les migrations Drizzle au démarrage du conteneur pour les futurs déploiements de production