123 lines
3.2 KiB
TypeScript
123 lines
3.2 KiB
TypeScript
|
|
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);
|
||
|
|
}
|
||
|
|
}
|