import 'dotenv/config' import { createHash } from 'node:crypto' import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import pg from 'pg' const { Client } = pg const databaseUrl = process.env.DATABASE_URL if (!databaseUrl) { console.error('DATABASE_URL is required') process.exit(1) } const migrationsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'migrations') const client = new Client({ connectionString: databaseUrl }) await client.connect() try { await client.query('CREATE SCHEMA IF NOT EXISTS osint') await client.query(`CREATE TABLE IF NOT EXISTS osint.schema_migrations ( name TEXT PRIMARY KEY, checksum TEXT NOT NULL, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW() )`) const files = (await fs.readdir(migrationsDir)).filter(name => /^\d+.*\.sql$/.test(name)).sort() for (const name of files) { const sql = await fs.readFile(path.join(migrationsDir, name), 'utf8') const checksum = createHash('sha256').update(sql).digest('hex') const existing = await client.query<{ checksum: string }>('SELECT checksum FROM osint.schema_migrations WHERE name = $1', [name]) if (existing.rows[0]) { if (existing.rows[0].checksum !== checksum) throw new Error(`Applied migration was modified: ${name}`) console.log(`skip ${name}`) continue } await client.query('BEGIN') try { await client.query(sql) await client.query('INSERT INTO osint.schema_migrations (name, checksum) VALUES ($1, $2)', [name, checksum]) await client.query('COMMIT') console.log(`apply ${name}`) } catch (error) { await client.query('ROLLBACK') throw error } } console.log('OSINT migrations complete.') } finally { await client.end() }