import { readFile, readdir } from "node:fs/promises"; import path from "node:path"; import mysql from "mysql2/promise"; function databaseUrl() { if (process.env.DATABASE_URL) return process.env.DATABASE_URL; const host = process.env.MYSQL_HOST ?? "127.0.0.1"; const port = process.env.MYSQL_PORT ?? "3306"; const user = encodeURIComponent(process.env.MYSQL_USER ?? "koc"); const password = encodeURIComponent(process.env.MYSQL_PASSWORD ?? ""); const database = process.env.MYSQL_DATABASE ?? "koc_loop"; return `mysql://${user}:${password}@${host}:${port}/${database}`; } const pool = mysql.createPool({ uri: databaseUrl(), connectionLimit: 2, charset: "utf8mb4", timezone: "Z", }); const connection = await pool.getConnection(); try { const [lockRows] = await connection.query( "SELECT GET_LOCK('koc-loop-schema-migration', 60) AS acquired", ); if (Number(lockRows[0]?.acquired ?? 0) !== 1) { throw new Error("无法获取数据库迁移锁"); } await connection.query(`CREATE TABLE IF NOT EXISTS schema_migrations ( version VARCHAR(255) PRIMARY KEY, applied_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`); const migrationDir = path.join(process.cwd(), "mysql"); const files = (await readdir(migrationDir)) .filter((file) => file.endsWith(".sql")) .sort(); for (const file of files) { const [rows] = await connection.query( "SELECT version FROM schema_migrations WHERE version = ?", [file], ); if (rows.length > 0) continue; const source = await readFile(path.join(migrationDir, file), "utf8"); const statements = source .split(/^-- statement-breakpoint\s*$/m) .map((statement) => statement.trim()) .filter(Boolean); for (const statement of statements) { await connection.query(statement); } await connection.query( "INSERT INTO schema_migrations (version) VALUES (?)", [file], ); console.info(`applied ${file}`); } await connection.query("SELECT RELEASE_LOCK('koc-loop-schema-migration')"); } finally { connection.release(); await pool.end(); }