Multi-page public site (Next.js), Node/Express backend with Postgres, and Vite admin/account app, restructured from a single-page layout into dedicated service, promotions, cases, about, FAQ, and blog pages for SEO. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013otXCiTZsxdZ4SJKZ9wUky
38 lines
1.1 KiB
JavaScript
38 lines
1.1 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const pool = require('./db');
|
|
|
|
async function migrate() {
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
filename TEXT PRIMARY KEY,
|
|
applied_at TIMESTAMPTZ DEFAULT now()
|
|
)
|
|
`);
|
|
|
|
const dir = path.join(__dirname, '..', 'migrations');
|
|
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.sql')).sort();
|
|
|
|
for (const file of files) {
|
|
const { rows } = await pool.query('SELECT 1 FROM schema_migrations WHERE filename = $1', [file]);
|
|
if (rows.length) continue;
|
|
|
|
const sql = fs.readFileSync(path.join(dir, file), 'utf8');
|
|
console.log(`[migrate] applying ${file}`);
|
|
const client = await pool.connect();
|
|
try {
|
|
await client.query('BEGIN');
|
|
await client.query(sql);
|
|
await client.query('INSERT INTO schema_migrations (filename) VALUES ($1)', [file]);
|
|
await client.query('COMMIT');
|
|
} catch (err) {
|
|
await client.query('ROLLBACK');
|
|
throw err;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = migrate;
|