feat: Projektfundament mit Datenmodell, Migrationen und Seed

- Repo-Struktur für Backend, Frontend, Dokumentation und Gitea-Workflows
- FastAPI-Skeleton mit pydantic-settings und RFC-7807-artigem Fehlerformat
- Vollständiges Datenmodell (15 Tabellen) als SQLAlchemy-2.0-Modelle
- Alembic gegen die Async-Engine inklusive Erstmigration
- Idempotenter Seed für den deutschen Kategoriebaum und Standardregeln
- Endpunkte /api/health und /api/version
- pytest-Infrastruktur mit transaktionsisolierten Fixtures

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
moneyfyandClaude Opus 5 committed 2026-09-09 13:12:08 +02:00
commit 0b06775be3
40 files changed
+2236

No files matched your search

View File
Whitespace-only changes.
+8
View File
@@ -0,0 +1,8 @@
"""Sammelrouter für alle /api-Endpunkte."""
from fastapi import APIRouter
from app.api.routes import system
api_router = APIRouter(prefix="/api")
api_router.include_router(system.router)
View File
Whitespace-only changes.
+54
View File
@@ -0,0 +1,54 @@
"""Systemendpunkte: Health-Check und Versionsinformation (ohne Authentifizierung)."""
from datetime import datetime
from zoneinfo import ZoneInfo
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.db.session import get_session
router = APIRouter(tags=["system"])
class HealthResponse(BaseModel):
status: str = Field(description="'ok' wenn Anwendung und Datenbank erreichbar sind.")
database: str = Field(description="'ok' oder 'unavailable'.")
time: datetime = Field(description="Aktuelle Serverzeit in Europe/Berlin.")
class VersionResponse(BaseModel):
name: str
version: str
environment: str
timezone: str
currency: str
@router.get("/health", response_model=HealthResponse, summary="Health-Check")
async def health(session: AsyncSession = Depends(get_session)) -> HealthResponse:
"""Prüft die Datenbankverbindung. Wird vom Docker-Healthcheck verwendet."""
database = "ok"
try:
await session.execute(text("SELECT 1"))
except Exception:
database = "unavailable"
return HealthResponse(
status="ok" if database == "ok" else "degraded",
database=database,
time=datetime.now(ZoneInfo(settings.timezone)),
)
@router.get("/version", response_model=VersionResponse, summary="Versionsinformation")
async def version() -> VersionResponse:
return VersionResponse(
name=settings.app_name,
version=settings.app_version,
environment=settings.environment,
timezone=settings.timezone,
currency=settings.default_currency,
)