refactor: auto-run DB migration at startup, robust schema.sql path resolution

This commit is contained in:
asepharyana
2026-07-06 22:32:06 +07:00
parent 0ad783b794
commit 2d3d7891a0
2 changed files with 62 additions and 10 deletions
+46 -10
View File
@@ -3,15 +3,51 @@ import { config } from '../env';
import { getErrorMessage } from '../utils/file'; import { getErrorMessage } from '../utils/file';
import logger from '../utils/logger'; 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<void> => {
// 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 { let schemaSql: string | null = null;
await sql.unsafe(schemaSql); for (const p of candidates) {
logger.info('Database migration completed'); const file = Bun.file(p);
} catch (error: unknown) { const exists = await file.exists();
logger.error('Database migration failed', { error: getErrorMessage(error) }); if (exists) {
process.exitCode = 1; schemaSql = await file.text();
} finally { break;
await sql.end(); }
}
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();
} }
+16
View File
@@ -14,6 +14,22 @@ import logger from './utils/logger';
import { metricsCollector } from './utils/metrics'; import { metricsCollector } from './utils/metrics';
import { cleanupRateLimitCache, withRateLimit } from './utils/rateLimit'; 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({ const server = serve({
port: config.port, port: config.port,
routes: { routes: {