Files
GuidoandClaude Opus 4.8 2c795a57c8 feat: Phase 0 – Gerüst (FastAPI+SQLite, Multi-User-Login, React-App-Shell, Deploy)
Grundgerüst für Certain Finance:
- Backend: FastAPI + SQLAlchemy + SQLite, Alembic-Migrationen (Batch-Mode),
  argon2-Passwort-Hashing, server-seitige Sitzungen im Cookie, Erst-Admin,
  Benutzerverwaltung mit Temporär-Passwort, Einstellungen je Benutzer.
- Ein Prozess, ein Port: uvicorn liefert API (/api) und gebautes SPA aus,
  inkl. SPA-Deeplink-Fallback.
- Frontend: React 18 + Vite + JSX, Design-Tokens (Light/Dark), i18n DE/EN,
  Inline-SVG-Icons (keine Emojis), Login, Passwort-ändern, Dashboard-Platzhalter,
  Benutzer- und Einstellungsseiten.
- Deploy: nativer LXC-Installer + Update-Skript (systemd), env-Vorlage, Doku.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:27:42 +02:00

63 lines
1.6 KiB
Python

"""Datenbank-Anbindung (SQLite).
SQLite braucht ein paar PRAGMAs, damit es sich unter FastAPIs Thread-Pool
vernünftig verhält: WAL für gleichzeitige Leser, ``foreign_keys=ON`` (sonst
greifen die ON-DELETE-Regeln nicht), und ein Busy-Timeout, damit kurze
Schreibsperren nicht sofort krachen.
"""
import os
from collections.abc import Generator
from sqlalchemy import create_engine, event
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
from .config import get_settings
settings = get_settings()
_url = settings.effective_database_url
# Für die SQLite-Datei das Datenverzeichnis sicherstellen.
if _url.startswith("sqlite:///"):
_path = _url.replace("sqlite:///", "", 1)
_dir = os.path.dirname(_path)
if _dir:
os.makedirs(_dir, exist_ok=True)
connect_args = {"check_same_thread": False} if _url.startswith("sqlite") else {}
engine = create_engine(
_url,
connect_args=connect_args,
pool_pre_ping=True,
future=True,
)
@event.listens_for(engine, "connect")
def _sqlite_pragmas(dbapi_connection, _record):
"""Bei jeder neuen SQLite-Verbindung sinnvolle Voreinstellungen setzen."""
if not _url.startswith("sqlite"):
return
cur = dbapi_connection.cursor()
cur.execute("PRAGMA journal_mode=WAL")
cur.execute("PRAGMA foreign_keys=ON")
cur.execute("PRAGMA busy_timeout=5000")
cur.close()
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True)
class Base(DeclarativeBase):
pass
def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
try:
yield db
finally:
db.close()