Files
LogiFlow/scripts/bench/schema-check.ts
T
LogiFlowandClaude Opus 5.5 600627c632 feat(rust) : serveur réécrit en Rust, Node retiré ; Calendrier en tête du menu (#571)
- Serveur Rust (server-rs/, axum + sqlx) : les 148 adresses de l'API,
  l'interface, les migrations de démarrage et les tâches de fond ; même
  comportement que Node (banc : 2027 cas, 0 écart hors écarts déclarés
  USERS-02, RAPPRO-21, R4, R5).
- Node retiré : server/, dépendances serveur, image Docker sans Node
  (51 Mo), docker-compose à un seul service applicatif.
- R4 : écritures d'un autre site refusées (Sec-Fetch-Site / Origin).
- R5 : « aujourd'hui » et sauvegarde automatique à l'heure de Paris.
- R6 : mainteneur dans .github/CODEOWNERS.
- Menu latéral : Accueil puis Calendrier en tête.
- Plan et état : docs/PLAN-MIGRATION-RUST.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ke2qyS8rgacP3ap5iezDbM
2026-10-15 20:31:14 +02:00

48 lines
2.0 KiB
TypeScript

// Compare les colonnes déclarées dans shared/schema.ts à celles d'une base réelle.
// Usage : DATABASE_URL=postgres://… npx tsx scripts/bench/schema-check.ts
import pg from "pg";
import * as schema from "../../shared/schema";
import { getTableConfig, PgTable } from "drizzle-orm/pg-core";
async function main() {
const declared = new Map<string, Map<string, string>>();
for (const value of Object.values(schema)) {
if (!(value instanceof PgTable)) continue;
const config = getTableConfig(value);
declared.set(config.name, new Map(config.columns.map((c) => [c.name, `${c.getSQLType()}${c.notNull ? " not null" : ""}`])));
}
const client = new pg.Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
const { rows } = await client.query(
`SELECT table_name, column_name, data_type, is_nullable
FROM information_schema.columns WHERE table_schema = 'public'`,
);
await client.end();
const actual = new Map<string, Map<string, string>>();
for (const row of rows) {
if (!actual.has(row.table_name)) actual.set(row.table_name, new Map());
actual.get(row.table_name)!.set(row.column_name, `${row.data_type}${row.is_nullable === "NO" ? " not null" : ""}`);
}
let problems = 0;
for (const [table, columns] of declared) {
const real = actual.get(table);
if (!real) { console.log(`table absente : ${table}`); problems++; continue; }
for (const [column, type] of columns) {
if (!real.has(column)) { console.log(`colonne absente : ${table}.${column} (${type})`); problems++; }
}
for (const [column, type] of real) {
if (!columns.has(column)) console.log(`colonne en plus dans la base : ${table}.${column} (${type})`);
}
}
for (const table of actual.keys()) {
if (!declared.has(table)) console.log(`table en plus dans la base : ${table}`);
}
console.log(problems === 0 ? "Schéma conforme à shared/schema.ts" : `${problems} écart(s) bloquant(s)`);
process.exit(problems === 0 ? 0 : 1);
}
main().catch((error) => { console.error(error); process.exit(1); });