39246-vm/database.py
abbashkyt-creator 7d8ce0e322 V0.1
2026-03-14 04:02:22 +03:00

77 lines
2.6 KiB
Python

"""
Ghost Node — Database Engine
Supports SQLite (default, zero-config) and PostgreSQL (production scale).
To switch to PostgreSQL, set the DATABASE_URL environment variable:
Linux/Mac: export DATABASE_URL="postgresql://user:password@localhost:5432/ghostnode"
Windows: set DATABASE_URL=postgresql://user:password@localhost:5432/ghostnode
Heroku/Railway/Render auto-set DATABASE_URL — Ghost Node detects it automatically.
SQLite remains the default for local/single-machine use.
"""
import os
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker, declarative_base
# ── Resolve DATABASE_URL ───────────────────────────────────────────────────────
_raw_url = os.environ.get("DATABASE_URL", "").strip()
if not _raw_url:
DATABASE_URL = "sqlite:///./sniper.db"
elif _raw_url.startswith("postgres://"):
# Heroku uses "postgres://" — SQLAlchemy 1.4+ requires "postgresql://"
DATABASE_URL = _raw_url.replace("postgres://", "postgresql://", 1)
else:
DATABASE_URL = _raw_url
_is_sqlite = DATABASE_URL.startswith("sqlite")
_is_postgresql = DATABASE_URL.startswith("postgresql")
# ── Engine ─────────────────────────────────────────────────────────────────────
if _is_sqlite:
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False, "timeout": 30},
pool_pre_ping=True,
echo=False,
)
@event.listens_for(engine, "connect")
def _set_sqlite_pragmas(dbapi_conn, _record):
cur = dbapi_conn.cursor()
cur.execute("PRAGMA journal_mode=WAL")
cur.execute("PRAGMA synchronous=NORMAL")
cur.execute("PRAGMA cache_size=10000")
cur.execute("PRAGMA temp_store=MEMORY")
cur.close()
else:
# PostgreSQL — connection pooling for multi-thread FastAPI
engine = create_engine(
DATABASE_URL,
pool_size=10,
max_overflow=20,
pool_pre_ping=True,
pool_recycle=1800,
echo=False,
)
print(
f"[DB] {'SQLite' if _is_sqlite else 'PostgreSQL'} -> "
f"{DATABASE_URL[:60]}{'...' if len(DATABASE_URL) > 60 else ''}"
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
"""FastAPI dependency — yields a DB session, always closes after use."""
db = SessionLocal()
try:
yield db
finally:
db.close()