Dominion/src/db/postgres.ts

123 lines
3.2 KiB
TypeScript
Raw Normal View History

2026-08-07 09:38:37 +00:00
import pg from 'pg';
import type { DB } from '../../server';
const { Pool } = pg;
let pool: pg.Pool | null = null;
export function getPgPool(): pg.Pool | null {
if (pool) return pool;
const connectionString =
process.env.DATABASE_URL ||
process.env.POSTGRES_URL ||
process.env.DATABASE_PRIVATE_URL;
const host = process.env.POSTGRES_HOST || process.env.SQL_HOST;
const user = process.env.POSTGRES_USER || process.env.SQL_USER;
const password = process.env.POSTGRES_PASSWORD || process.env.SQL_PASSWORD;
const database = process.env.POSTGRES_DB || process.env.SQL_DB_NAME || 'balance_sheet_db';
const port = parseInt(process.env.POSTGRES_PORT || '5432', 10);
if (connectionString) {
console.log('[PostgreSQL] Initializing connection pool via DATABASE_URL');
pool = new Pool({
connectionString,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
} else if (host && user) {
console.log(`[PostgreSQL] Initializing connection pool via host ${host}:${port}, db: ${database}`);
pool = new Pool({
host,
port,
user,
password,
database,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
} else {
console.log('[PostgreSQL] No DATABASE_URL or POSTGRES_HOST provided. Operating in file-backed mode.');
return null;
}
pool.on('error', (err) => {
console.error('[PostgreSQL] Unexpected idle client error:', err);
});
return pool;
}
/**
* Initializes PostgreSQL schema table if needed
*/
export async function initPgDatabase(): Promise<boolean> {
const p = getPgPool();
if (!p) return false;
try {
const client = await p.connect();
try {
await client.query(`
CREATE TABLE IF NOT EXISTS app_portal_state (
id VARCHAR(50) PRIMARY KEY,
data JSONB NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
`);
console.log('[PostgreSQL] Database table app_portal_state ensured.');
return true;
} finally {
client.release();
}
} catch (err) {
console.error('[PostgreSQL] Initialization failed:', err);
return false;
}
}
/**
* Load state from PostgreSQL
*/
export async function loadStateFromPg(): Promise<DB | null> {
const p = getPgPool();
if (!p) return null;
try {
const res = await p.query(`SELECT data FROM app_portal_state WHERE id = $1`, ['main_state']);
if (res.rows.length > 0 && res.rows[0].data) {
console.log('[PostgreSQL] Successfully restored portal state from PostgreSQL database.');
return res.rows[0].data as DB;
}
return null;
} catch (err) {
console.error('[PostgreSQL] Error loading state:', err);
return null;
}
}
/**
* Save state to PostgreSQL asynchronously
*/
export async function saveStateToPg(state: DB): Promise<void> {
const p = getPgPool();
if (!p) return;
try {
await p.query(
`
INSERT INTO app_portal_state (id, data, updated_at)
VALUES ($1, $2, NOW())
ON CONFLICT (id) DO UPDATE
SET data = EXCLUDED.data, updated_at = NOW();
`,
['main_state', JSON.stringify(state)]
);
} catch (err) {
console.error('[PostgreSQL] Error saving state:', err);
}
}