import { execFile } from 'child_process'; import { existsSync } from 'fs'; import { join } from 'path'; import { promisify } from 'util'; import { Logger } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; import { runMigrations, runOnce } from './migrate'; const execFileAsync = promisify(execFile); function resolveSeedScript(): string | undefined { const candidates = [ process.env.SEED_SCRIPT, join(process.cwd(), 'seed.js'), join(__dirname, '..', '..', 'seed.js'), ].filter((p): p is string => Boolean(p)); return candidates.find((file) => existsSync(file)); } /** * Runs pending SQL migrations before Nest boots, then seeds once. * * Seeding is gated on a marker row written only after the seed succeeds, so an * interrupted seed is retried on the next boot (the seed itself upserts). * Set RUN_MIGRATIONS=false or RUN_SEED=false to skip. */ export async function prepareDatabase(): Promise { if (process.env.RUN_MIGRATIONS === 'false') return; const logger = new Logger('DatabaseBootstrap'); const prisma = new PrismaClient(); try { const { applied } = await runMigrations(prisma); if (applied.length > 0) { logger.log(`Applied migrations: ${applied.join(', ')}`); } else { logger.log('No pending migrations'); } if (process.env.RUN_SEED === 'false') return; const seedScript = resolveSeedScript(); if (!seedScript) { logger.warn('No seed script found — skipping seed'); return; } const seeded = await runOnce(prisma, 'seed:initial', async () => { const { stdout } = await execFileAsync(process.execPath, [seedScript]); if (stdout.trim()) logger.log(stdout.trim()); }); if (seeded) logger.log('Seed data inserted'); } finally { await prisma.$disconnect(); } }