From 5cdaa7d4a17ea98f6ad17c5a7a8d32862c69cb34 Mon Sep 17 00:00:00 2001 From: z3n Date: Sat, 27 Jun 2026 07:05:30 +0000 Subject: [PATCH] back(db): infra migrations TypeORM + migration initiale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - data-source.ts (CLI) + scripts migration:generate/run/revert/show - migration InitialSchema : schéma complet (categories, api_entries), vérifiée en l'appliquant sur une base vierge (crée aussi l'ext uuid-ossp) - app.module: charge les migrations et migrationsRun en prod uniquement. Corrige un trou : la prod avait synchronize:false SANS migrations (schéma vide au déploiement). Dev inchangé (synchronize:true). Bascule de dev vers migrations-only (synchronize:false + baseline de la base dev existante) = étape ultérieure à faire en supervisé. Co-Authored-By: Claude Opus 4.8 --- back/package.json | 6 +++++- back/src/app.module.ts | 4 ++++ back/src/data-source.ts | 20 +++++++++++++++++++ .../migrations/1782543826810-InitialSchema.ts | 16 +++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 back/src/data-source.ts create mode 100644 back/src/migrations/1782543826810-InitialSchema.ts diff --git a/back/package.json b/back/package.json index e0c6bb0..c25487f 100644 --- a/back/package.json +++ b/back/package.json @@ -10,7 +10,11 @@ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", "test": "vitest run", "test:watch": "vitest", - "test:cov": "vitest run --coverage" + "test:cov": "vitest run --coverage", + "migration:generate": "typeorm-ts-node-commonjs migration:generate -d src/data-source.ts", + "migration:run": "typeorm-ts-node-commonjs migration:run -d src/data-source.ts", + "migration:revert": "typeorm-ts-node-commonjs migration:revert -d src/data-source.ts", + "migration:show": "typeorm-ts-node-commonjs migration:show -d src/data-source.ts" }, "dependencies": { "@asyncapi/bundler": "^1.0.1", diff --git a/back/src/app.module.ts b/back/src/app.module.ts index cf459a6..f9f83a7 100644 --- a/back/src/app.module.ts +++ b/back/src/app.module.ts @@ -22,7 +22,11 @@ import { validateEnv } from './config/env.validation'; password: config.getOrThrow('DATABASE_PASSWORD'), database: config.getOrThrow('DATABASE_NAME'), entities: [__dirname + '/**/*.entity{.ts,.js}'], + migrations: [__dirname + '/migrations/*{.ts,.js}'], + // Dev : synchronize auto (itération rapide). Prod : pas de synchronize, + // le schéma est appliqué par les migrations exécutées au démarrage. synchronize: config.get('NODE_ENV') !== 'production', + migrationsRun: config.get('NODE_ENV') === 'production', logging: config.get('NODE_ENV') === 'development', }), }), diff --git a/back/src/data-source.ts b/back/src/data-source.ts new file mode 100644 index 0000000..271ab74 --- /dev/null +++ b/back/src/data-source.ts @@ -0,0 +1,20 @@ +import 'reflect-metadata'; +import { DataSource } from 'typeorm'; + +/** + * DataSource dédié à la CLI TypeORM (migrations). Lit l'environnement + * directement (les mêmes DATABASE_* que l'app). Utilisé par les scripts + * migration:generate / migration:run / migration:revert. + * + * NB : la config runtime de l'app vit dans app.module.ts (TypeOrmModule). + */ +export default new DataSource({ + type: 'postgres', + host: process.env.DATABASE_HOST ?? 'localhost', + port: Number(process.env.DATABASE_PORT ?? 5432), + username: process.env.DATABASE_USER ?? 'datacat', + password: process.env.DATABASE_PASSWORD ?? 'datacat', + database: process.env.DATABASE_NAME ?? 'datacat', + entities: ['src/**/*.entity.ts'], + migrations: ['src/migrations/*.ts'], +}); diff --git a/back/src/migrations/1782543826810-InitialSchema.ts b/back/src/migrations/1782543826810-InitialSchema.ts new file mode 100644 index 0000000..33355fc --- /dev/null +++ b/back/src/migrations/1782543826810-InitialSchema.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class InitialSchema1782543826810 implements MigrationInterface { + name = 'InitialSchema1782543826810' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "categories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "description" text, "parent_id" character varying(36), "order_index" integer NOT NULL DEFAULT '0', "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_24dbc6126a28ff948da33e97d3b" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "api_entries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "convention" character varying(30) NOT NULL DEFAULT 'CONSULTER', "name" character varying(255) NOT NULL DEFAULT '', "description" text, "type" character varying(10) NOT NULL, "provider" character varying(255) NOT NULL DEFAULT '', "version_major" integer NOT NULL DEFAULT '0', "version_minor" integer NOT NULL DEFAULT '0', "version_patch" integer NOT NULL DEFAULT '0', "yaml_content" text NOT NULL, "html_path" character varying(500), "status" character varying(10) NOT NULL DEFAULT 'PENDING', "error_message" text, "category_id" character varying(36), "is_current" boolean NOT NULL DEFAULT true, "contact_functional_name" character varying(255) NOT NULL DEFAULT '', "contact_functional_entity" character varying(255) NOT NULL DEFAULT '', "contact_functional_email" character varying(255) NOT NULL DEFAULT '', "contact_technical_name" character varying(255) NOT NULL DEFAULT '', "contact_technical_entity" character varying(255) NOT NULL DEFAULT '', "contact_technical_email" character varying(255) NOT NULL DEFAULT '', "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_d7b668541d2539a0c212b198d32" PRIMARY KEY ("id"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "api_entries"`); + await queryRunner.query(`DROP TABLE "categories"`); + } + +}