Spaces:
Sleeping
Sleeping
| """ | |
| db.py β Database abstraction layer. | |
| - Production (HF Spaces): set DATABASE_URL env var β uses Neon PostgreSQL via psycopg2 | |
| - Local dev: no DATABASE_URL β falls back to SQLite | |
| Usage in other modules: | |
| from db import get_conn, PH, IntegrityError | |
| conn = get_conn() | |
| cursor = conn.cursor() | |
| cursor.execute(f"SELECT * FROM users WHERE username = {PH}", (username,)) | |
| row = cursor.fetchone() | |
| conn.close() | |
| """ | |
| import os | |
| import sqlite3 | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| # Connection string from environment (set as HF Space Secret) | |
| DATABASE_URL = os.environ.get('DATABASE_URL', '') | |
| # Are we using PostgreSQL? | |
| USE_POSTGRES = bool(DATABASE_URL) | |
| if USE_POSTGRES: | |
| import psycopg2 | |
| import psycopg2.extras | |
| import psycopg2.errors | |
| # Placeholder style for PostgreSQL | |
| PH = '%s' | |
| # Exception alias | |
| IntegrityError = psycopg2.errors.UniqueViolation | |
| logger.info("DB: Using Neon PostgreSQL") | |
| else: | |
| # Placeholder style for SQLite | |
| PH = '?' | |
| # Exception alias | |
| IntegrityError = sqlite3.IntegrityError | |
| logger.info("DB: Using local SQLite (dev mode)") | |
| # SQLite path (only used in local dev) | |
| _SQLITE_PATH = os.path.join(os.path.dirname(__file__), 'data', 'users.db') | |
| def get_conn(): | |
| """ | |
| Returns an open database connection. | |
| Caller is responsible for calling conn.close() (use finally blocks). | |
| """ | |
| if USE_POSTGRES: | |
| if 'sslmode=' not in DATABASE_URL: | |
| conn = psycopg2.connect(DATABASE_URL, sslmode='require') | |
| else: | |
| conn = psycopg2.connect(DATABASE_URL) | |
| conn.autocommit = False | |
| return conn | |
| else: | |
| os.makedirs(os.path.dirname(_SQLITE_PATH), exist_ok=True) | |
| conn = sqlite3.connect(_SQLITE_PATH, timeout=10) | |
| conn.execute('PRAGMA busy_timeout=5000') | |
| return conn | |