Add AI DEV Node deployment foundation

This commit is contained in:
unknown
2026-07-13 10:33:21 +09:00
parent 6abcee3727
commit b90bfe35ca
23 changed files with 3978 additions and 0 deletions

56
server/db/migrate.ts Normal file
View File

@@ -0,0 +1,56 @@
import { readdir, readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { closePool, query } from './pool.js';
const migrationsDir = path.resolve(process.cwd(), 'migrations');
async function ensureMigrationTable(): Promise<void> {
await query(`
CREATE TABLE IF NOT EXISTS schema_migrations (
filename VARCHAR(255) PRIMARY KEY,
applied_at TIMESTAMP NOT NULL DEFAULT now()
)
`);
}
async function appliedMigrations(): Promise<Set<string>> {
const result = await query<{ filename: string }>('SELECT filename FROM schema_migrations');
return new Set(result.rows.map((row) => row.filename));
}
export async function runMigrations(): Promise<void> {
await ensureMigrationTable();
const applied = await appliedMigrations();
const files = (await readdir(migrationsDir))
.filter((file) => file.endsWith('.sql'))
.sort();
for (const file of files) {
if (applied.has(file)) continue;
const sql = await readFile(path.join(migrationsDir, file), 'utf8');
await query('BEGIN');
try {
await query(sql);
await query('INSERT INTO schema_migrations (filename) VALUES ($1)', [file]);
await query('COMMIT');
console.log(`Applied migration ${file}`);
} catch (error) {
await query('ROLLBACK');
throw error;
}
}
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
runMigrations()
.then(async () => {
await closePool();
})
.catch(async (error: unknown) => {
console.error(error);
await closePool();
process.exitCode = 1;
});
}