초기 커밋: Docker/PostgreSQL 이관, 구글 OAuth 제거, Gitea 푸시 스크립트 추가
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
import logging
|
||||
import os
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_env_file(path: str = ".env"):
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
|
||||
with open(path, "r", encoding="utf-8") as env_file:
|
||||
for raw_line in env_file:
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"').strip("'")
|
||||
if key and key not in os.environ:
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
load_env_file()
|
||||
|
||||
|
||||
def _build_pg_url(db_name: str) -> str:
|
||||
user = os.getenv("DB_USER", "")
|
||||
password = os.getenv("DB_PASSWORD", "")
|
||||
host = os.getenv("DB_HOST", "localhost")
|
||||
port = os.getenv("DB_PORT", "5432")
|
||||
return (
|
||||
f"postgresql+psycopg2://{quote_plus(user)}:{quote_plus(password)}"
|
||||
f"@{host}:{port}/{db_name}"
|
||||
)
|
||||
|
||||
|
||||
def _resolve_database_url() -> str:
|
||||
explicit_url = os.getenv("DATABASE_URL")
|
||||
if explicit_url:
|
||||
return explicit_url
|
||||
|
||||
order_db = os.getenv("ORDER_DB_NAME")
|
||||
if order_db and os.getenv("DB_HOST"):
|
||||
return _build_pg_url(order_db)
|
||||
|
||||
return "sqlite:///./app.db"
|
||||
|
||||
|
||||
DATABASE_URL = _resolve_database_url()
|
||||
|
||||
connect_args = {}
|
||||
engine_kwargs = {}
|
||||
if DATABASE_URL.startswith("sqlite"):
|
||||
connect_args = {"check_same_thread": False, "timeout": 30}
|
||||
else:
|
||||
engine_kwargs.update(pool_pre_ping=True, pool_recycle=1800)
|
||||
|
||||
engine = create_engine(DATABASE_URL, connect_args=connect_args, **engine_kwargs)
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
# --- Secondary engines for the other PostgreSQL databases ----------------
|
||||
# These are exposed for code that needs to query item / return / checklist
|
||||
# databases. They are created lazily and only when PostgreSQL is configured.
|
||||
_secondary_engines: dict[str, object] = {}
|
||||
|
||||
|
||||
def get_secondary_engine(env_var: str):
|
||||
"""Get (or create) an engine for ITEM_DB_NAME / RETURN_DB_NAME / CHECKLIST_DB_NAME."""
|
||||
db_name = os.getenv(env_var)
|
||||
if not db_name or not os.getenv("DB_HOST"):
|
||||
return None
|
||||
if env_var in _secondary_engines:
|
||||
return _secondary_engines[env_var]
|
||||
eng = create_engine(_build_pg_url(db_name), pool_pre_ping=True, pool_recycle=1800)
|
||||
_secondary_engines[env_var] = eng
|
||||
return eng
|
||||
|
||||
|
||||
# Dependency for FastAPI to get DB session
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def check_db_connection() -> tuple[bool, str]:
|
||||
"""Ping the primary database. Returns (ok, message)."""
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
return True, "ok"
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return False, str(exc)
|
||||
|
||||
|
||||
def log_db_startup_status() -> None:
|
||||
safe_url = engine.url.render_as_string(hide_password=True)
|
||||
ok, msg = check_db_connection()
|
||||
if ok:
|
||||
logger.warning("[DB] Connected: %s", safe_url)
|
||||
else:
|
||||
logger.error("[DB] Connection FAILED: %s -> %s", safe_url, msg)
|
||||
Reference in New Issue
Block a user