55
66_pool : asyncpg .Pool | None = None
77_pool_lock : asyncio .Lock | None = None
8+ _users_table_ensured : bool = False
89_DEFAULT_POOL_MIN_SIZE = 2
910_DEFAULT_POOL_MAX_SIZE = 10
1011_DEFAULT_POOL_RETRIES = 5
@@ -31,8 +32,28 @@ def _float_env(name: str, default: float, minimum: float = 0.0) -> float:
3132 return default
3233
3334
35+ async def ensure_users_table (pool : asyncpg .Pool ) -> None :
36+ """Create the users table if it does not already exist (idempotent)."""
37+ async with pool .acquire () as conn :
38+ await conn .execute (
39+ """
40+ CREATE TABLE IF NOT EXISTS users (
41+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
42+ email TEXT UNIQUE NOT NULL,
43+ name TEXT,
44+ image TEXT,
45+ approved BOOLEAN NOT NULL DEFAULT FALSE,
46+ domain TEXT NOT NULL,
47+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
48+ last_login_at TIMESTAMPTZ NOT NULL DEFAULT now()
49+ );
50+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
51+ """
52+ )
53+
54+
3455async def get_pool () -> asyncpg .Pool :
35- global _pool , _pool_lock
56+ global _pool , _pool_lock , _users_table_ensured
3657 if _pool_lock is None :
3758 _pool_lock = asyncio .Lock ()
3859 if _pool is not None :
@@ -65,6 +86,16 @@ async def get_pool() -> asyncpg.Pool:
6586 await asyncio .sleep (retry_delay * (attempt + 1 ))
6687 if _pool is None and last_error is not None :
6788 raise last_error
89+ if _pool is not None and not _users_table_ensured :
90+ try :
91+ await ensure_users_table (_pool )
92+ _users_table_ensured = True
93+ except Exception :
94+ import logging
95+
96+ logging .getLogger (__name__ ).warning (
97+ "Could not create users table — DB may not be ready yet"
98+ )
6899 return _pool
69100
70101
0 commit comments