from __future__ import annotations from contextlib import contextmanager from typing import Any, Iterator import psycopg from psycopg.rows import dict_row from . import settings def configured() -> bool: return bool(settings.PORTAL_DATABASE_URL) @contextmanager def connect() -> Iterator[psycopg.Connection[Any]]: if not settings.PORTAL_DATABASE_URL: raise RuntimeError("portal database not configured") with psycopg.connect(settings.PORTAL_DATABASE_URL, row_factory=dict_row) as conn: yield conn def ensure_schema() -> None: if not settings.PORTAL_DATABASE_URL: return with connect() as conn: conn.execute( """ CREATE TABLE IF NOT EXISTS access_requests ( request_code TEXT PRIMARY KEY, username TEXT NOT NULL, contact_email TEXT, note TEXT, status TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), decided_at TIMESTAMPTZ, decided_by TEXT ) """ ) conn.execute( """ CREATE INDEX IF NOT EXISTS access_requests_status_created_at ON access_requests (status, created_at) """ ) conn.execute( """ CREATE UNIQUE INDEX IF NOT EXISTS access_requests_username_pending ON access_requests (username) WHERE status = 'pending' """ )