77 lines
2.6 KiB
Python
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()
|