import { Pool, PoolConfig, QueryResult, QueryResultRow } from "pg"; import { config } from "./config"; import type { RaceRow } from "./mappers/race"; const poolConfig: PoolConfig = { host: config.db.host, port: config.db.port, database: config.db.database, user: config.db.user, password: config.db.password, max: 10, idleTimeoutMillis: 30_000, connectionTimeoutMillis: 5_000, }; function mockRowFromInsert(sql: string, params: unknown[]): RaceRow { const match = sql.match(/INSERT INTO races\s*\(([^)]+)\)\s*VALUES/i); const now = new Date().toISOString(); if (!match) { return { id: String(params[0] ?? ""), race_date: "", title: "", distance_km: "0", status: null, official_url: null, start_time: null, cluster_schedule: null, bib_pickup: null, bib_number: null, finish_time: null, finish_place: null, notes: null, created_at: now, updated_at: null, }; } const cols = match[1].split(",").map((c) => c.trim()); const row: Record = {}; cols.forEach((col, i) => { row[col] = params[i]; }); return { id: String(row.id ?? ""), race_date: String(row.race_date ?? ""), title: String(row.title ?? ""), distance_km: String(row.distance_km ?? "0"), status: row.status != null ? String(row.status) : null, official_url: row.official_url != null ? String(row.official_url) : null, start_time: row.start_time != null ? String(row.start_time) : null, cluster_schedule: row.cluster_schedule != null ? String(row.cluster_schedule) : null, bib_pickup: row.bib_pickup != null ? String(row.bib_pickup) : null, bib_number: row.bib_number != null ? String(row.bib_number) : null, finish_time: row.finish_time != null ? String(row.finish_time) : null, finish_place: row.finish_place != null ? String(row.finish_place) : null, notes: row.notes != null ? String(row.notes) : null, created_at: now, updated_at: null, }; } function createMockPool(): Pool { const emptyResult = (): QueryResult => ({ rows: [], rowCount: 0, command: "", oid: 0, fields: [], }) as QueryResult; const store = new Map(); const mockQuery = async ( text: string, params?: unknown[], ): Promise> => { const sql = text.replace(/\s+/g, " ").trim(); const p = params ?? []; if (sql.includes("INSERT INTO races") && sql.includes("RETURNING")) { const row = mockRowFromInsert(text, p); store.set(row.id, row); return { rows: [row as unknown as T], rowCount: 1, command: "INSERT", oid: 0, fields: [], } as QueryResult; } if (sql.includes("DELETE FROM races")) { const id = String(p[0] ?? ""); const existed = store.delete(id); return { rows: [], rowCount: existed ? 1 : 0, command: "DELETE", oid: 0, fields: [], } as QueryResult; } if (sql.includes("UPDATE races") && sql.includes("RETURNING")) { const id = String(p[p.length - 1] ?? ""); const existing = store.get(id); if (!existing) { return emptyResult(); } const updated = { ...existing, updated_at: new Date().toISOString() }; store.set(id, updated); return { rows: [updated as unknown as T], rowCount: 1, command: "UPDATE", oid: 0, fields: [], } as QueryResult; } if (sql.includes("SELECT * FROM races WHERE id =")) { const id = String(p[0] ?? ""); const row = store.get(id); return row ? { rows: [row as unknown as T], rowCount: 1, command: "SELECT", oid: 0, fields: [] } as QueryResult : emptyResult(); } if (sql.includes("SELECT * FROM races")) { const rows = Array.from(store.values()); return { rows: rows as unknown as T[], rowCount: rows.length, command: "SELECT", oid: 0, fields: [] } as QueryResult; } return emptyResult(); }; const mockPool = { query: mockQuery, connect: async () => { throw new Error( "CALENDAR_RUN_MOCK_DB is enabled: migrate/seed require a real database; unset CALENDAR_RUN_MOCK_DB and configure DB_*.", ); }, end: async () => {}, on() { return mockPool; }, }; return mockPool as unknown as Pool; } export const pool = config.useMockDb ? createMockPool() : new Pool(poolConfig); if (!config.useMockDb) { pool.on("error", (err) => { console.error("[db] Unexpected pool error:", err.message); }); } else { console.warn("[db] Mock database enabled (CALENDAR_RUN_MOCK_DB); no PostgreSQL connection is used."); } export async function checkDbConnection(): Promise { if (config.useMockDb) { return true; } try { const client = await pool.connect(); client.release(); return true; } catch { return false; } }