Checkpoint: Correctif de première installation : après les migrations Drizzle, le serveur crée le compte administrateur Itinova requis avec mot de passe haché, sans doublon. Validation complète réussie : 62 tests, TypeScript et build.
All checks were successful
Validation applicative / TypeScript, tests et build (push) Successful in 1h3m35s

This commit is contained in:
Manus
2026-08-30 09:54:50 +00:00
parent b2c8580382
commit f759032793
3 changed files with 47 additions and 1 deletions

View File

@@ -1,6 +1,35 @@
import { migrate } from "drizzle-orm/mysql2/migrator";
import { drizzle } from "drizzle-orm/mysql2";
import { eq } from "drizzle-orm";
import { createPool } from "mysql2/promise";
import bcrypt from "bcryptjs";
import { users } from "../drizzle/schema";
const INITIAL_ADMIN_LOGIN = "adminItinova";
const INITIAL_ADMIN_PASSWORD = process.env.INITIAL_ADMIN_PASSWORD ?? "Itinova69!";
async function ensureInitialAdmin(pool: ReturnType<typeof createPool>) {
const database = drizzle(pool);
const existingAdmin = await database
.select({ id: users.id })
.from(users)
.where(eq(users.login, INITIAL_ADMIN_LOGIN))
.limit(1);
if (existingAdmin.length > 0) return;
const passwordHash = await bcrypt.hash(INITIAL_ADMIN_PASSWORD, 12);
await database.insert(users).values({
login: INITIAL_ADMIN_LOGIN,
email: "adminItinova@santinova-soft.org",
firstName: "Admin",
lastName: "Itinova",
passwordHash,
role: "admin",
isActive: true,
});
console.log("[Database] Compte administrateur initial créé.");
}
/**
* Synchronise la base avec les migrations versionnées avant d'exposer l'API.
@@ -16,7 +45,9 @@ export async function runDatabaseMigrations() {
const pool = createPool(databaseUrl);
try {
await migrate(drizzle(pool), { migrationsFolder: "./drizzle" });
const database = drizzle(pool);
await migrate(database, { migrationsFolder: "./drizzle" });
await ensureInitialAdmin(pool);
console.log("[Database] Migrations Drizzle synchronisées.");
} finally {
await pool.end();

View File

@@ -0,0 +1,14 @@
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
describe("bootstrap du compte administrateur", () => {
it("crée le compte administrateur requis après migration sans stocker de mot de passe en clair", async () => {
const bootstrap = await readFile(resolve(process.cwd(), "server/databaseMigrations.ts"), "utf8");
expect(bootstrap).toContain('const INITIAL_ADMIN_LOGIN = "adminItinova"');
expect(bootstrap).toContain("await ensureInitialAdmin(pool)");
expect(bootstrap).toContain("await bcrypt.hash(INITIAL_ADMIN_PASSWORD");
expect(bootstrap).not.toContain("passwordHash: INITIAL_ADMIN_PASSWORD,");
});
});

View File

@@ -216,3 +216,4 @@
- [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
- [ ] Créer le compte administrateur initial requis lorsque la base Budget SI est vide