Files

27 lines
1.7 KiB
TypeScript

import {Pool,type PoolClient} from 'pg';
export type Connection=Pool|PoolClient;
const runtime=globalThis as typeof globalThis & {coursesPool?:Pool;coursesReady?:Promise<void>};
async function inTransaction<T>(pool:Pool,work:(client:PoolClient)=>Promise<T>):Promise<T>{
const client=await pool.connect();
try{await client.query('BEGIN');const result=await work(client);await client.query('COMMIT');return result;}
catch(error){await client.query('ROLLBACK').catch(()=>{});throw error;}
finally{client.release();}
}
export async function database():Promise<Pool>{
if(!runtime.coursesPool){
if(!process.env.DATABASE_URL)throw new Error('DATABASE_URL is required');
runtime.coursesPool=new Pool({connectionString:process.env.DATABASE_URL,max:4,connectionTimeoutMillis:5000,idleTimeoutMillis:30000,statement_timeout:15000});
runtime.coursesPool.on('error',()=>console.error('PostgreSQL connection unavailable'));
}
const pool=runtime.coursesPool;
runtime.coursesReady??=inTransaction(pool,async client=>{
await client.query('SELECT pg_advisory_xact_lock(72847321)');
await client.query('CREATE TABLE IF NOT EXISTS records(key TEXT PRIMARY KEY NOT NULL,value TEXT NOT NULL)');
await client.query("CREATE TABLE IF NOT EXISTS scan(id INTEGER PRIMARY KEY,data TEXT NOT NULL,lease BIGINT NOT NULL DEFAULT 0,token TEXT NOT NULL DEFAULT '')");
});
try{await runtime.coursesReady;}catch(error){runtime.coursesReady=undefined;throw error;}
return pool;
}
export async function transaction<T>(work:(client:PoolClient)=>Promise<T>):Promise<T>{return inTransaction(await database(),work);}
export async function closeDatabase(){const pool=runtime.coursesPool;runtime.coursesPool=undefined;runtime.coursesReady=undefined;if(pool)await pool.end();}