import { Pool, PoolClient, PostgresError, } from "https://deno.land/x/postgres@v0.16.1/mod.ts"; import { type QueryArguments, type QueryArrayResult, type QueryObjectResult, } from "https://deno.land/x/postgres@v0.16.1/query/query.ts?s=QueryArguments"; import { config } from "@/config.ts"; import { type Identifiable, type Note, type Timestamped, type User, } from "@/types.ts"; export { PostgresError }; export { type QueryObjectResult }; const pool = new Pool(config.postgres.url, 3, true); async function dbOp(op: (connection: PoolClient) => Promise) { let result = null; let exception = null; try { const connection = await pool.connect(); try { result = await op(connection); } catch (err) { console.error("Error querying database:", { ...err }); exception = err; } finally { connection.release(); } } catch (err) { exception = err; console.error("Error connecting to database:", err); } if (exception != null) throw exception; return result; } export async function queryObject( sql: string, args?: QueryArguments, ): Promise | null> { return await dbOp(async (connection) => await connection.queryObject({ camelcase: true, text: sql.trim(), args, }) ); } export async function queryArray( sql: string, args?: QueryArguments, ): Promise | null> { return await dbOp(async (connection) => await connection.queryArray({ text: sql.trim(), args, }) ); } export async function listNotes() { return await queryObject( "select * from note order by created_at desc", ); } export async function getNote(id: string | { id: string }) { const idVal = typeof id == "object" ? id.id : id; console.debug("getNote id =", JSON.stringify(idVal)); return await queryObject( "select * from note where id = $1", [idVal], ); } type Ungenerated = Omit; export async function createNote({ content }: Ungenerated) { return await queryObject( "insert into note (content) values ($1) returning *", [content], ); } export async function createUser( { username, passwordDigest }: Ungenerated, ) { return await queryObject<{ user_id: string }>( ` with new_user as ( insert into "user" (username, password_digest, status) values ($username, $passwordDigest, 'unverified') returning id as user_id ), new_team as ( insert into "team" (display_name) values ($teamName) returning id as team_id ) insert into "team_user" (user_id, team_id, status) values ( (select user_id from new_user), (select team_id from new_team), 'owner' ) returning user_id `, { username, passwordDigest, teamName: `${username}'s First Team` }, ); } export async function getUser({ id, username }: Partial) { if (!id && !username) throw "getUser called without id or username"; const column = id ? "id" : "username"; return await queryObject( `select * from "user" where "${column}" = $1`, [id || username], ); }