diff --git a/src/db/migrate.ts b/src/db/migrate.ts index 24f0d77..3a3370f 100644 --- a/src/db/migrate.ts +++ b/src/db/migrate.ts @@ -3,15 +3,51 @@ import { config } from '../env'; import { getErrorMessage } from '../utils/file'; import logger from '../utils/logger'; -const schemaSql = await Bun.file('schema.sql').text(); -const sql = postgres(config.databaseUrl, { max: 1 }); +/** + * Run raw SQL migration from schema.sql. + * Safe to call multiple times — all statements use IF NOT EXISTS. + */ +export const runMigration = async (): Promise => { + // In compiled dist: import.meta.dir = .../dist/ + // In source via bun --hot: import.meta.dir = .../src/db/ + const dir = import.meta.dir || ''; + const candidates = [ + dir + '/../../schema.sql', // from dist/ + dir + '/../schema.sql', // from src/ (bun --hot src/index.ts) + dir + '/../schema.sql', // from src/db/ (bun --hot src/db/migrate.ts) + dir + '/schema.sql', // from src/ (bun run db:migrate) + ]; -try { - await sql.unsafe(schemaSql); - logger.info('Database migration completed'); -} catch (error: unknown) { - logger.error('Database migration failed', { error: getErrorMessage(error) }); - process.exitCode = 1; -} finally { - await sql.end(); + let schemaSql: string | null = null; + for (const p of candidates) { + const file = Bun.file(p); + const exists = await file.exists(); + if (exists) { + schemaSql = await file.text(); + break; + } + } + + if (!schemaSql) { + logger.error('Migration failed: schema.sql not found (tried ' + candidates.join(', ') + ')'); + process.exitCode = 1; + return; + } + + const sql = postgres(config.databaseUrl, { max: 1 }); + + try { + await sql.unsafe(schemaSql); + logger.info('Database migration completed'); + } catch (error: unknown) { + logger.error('Database migration failed', { error: getErrorMessage(error) }); + process.exitCode = 1; + } finally { + await sql.end(); + } +}; + +// When run directly: `bun src/db/migrate.ts` or `bun dist/migrate.js` +if (import.meta.path === Bun.main) { + await runMigration(); } diff --git a/src/index.ts b/src/index.ts index e7f1bee..799aa83 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,22 @@ import logger from './utils/logger'; import { metricsCollector } from './utils/metrics'; import { cleanupRateLimitCache, withRateLimit } from './utils/rateLimit'; +// ─── Auto-run migration at startup ────────────────────────────────────────── +try { + const { runMigration } = await import('./db/migrate'); + await runMigration(); +} catch { + logger.warn('Auto-migration skipped (non-fatal)'); +} + +// ─── Auto-run migration at startup ─── +try { + await import('./db/migrate'); +} catch { + // migrate.ts calls process.exit(1) on failure — if it throws, log and continue + logger.warn('Auto-migration warning (non-fatal)'); +} + const server = serve({ port: config.port, routes: {