Sprint 0: Backend, Docker, KI-Layer mit Free/Premium-Trennung

This commit is contained in:
rene 2026-04-12 16:39:34 +02:00
parent 84f49fafcf
commit 00be2bbcd5
17 changed files with 1107 additions and 0 deletions

114
backend/main.py Normal file
View file

@ -0,0 +1,114 @@
"""
BAN YARO FastAPI Hauptanwendung
"""
import os
import logging
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from contextlib import asynccontextmanager
from database import init_db
import ki
logging.basicConfig(
level = logging.INFO,
format = "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)
# ------------------------------------------------------------------
# Startup / Shutdown
# ------------------------------------------------------------------
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("Ban Yaro startet...")
init_db()
logger.info(f"KI-Modus: {ki.KI_MODE}")
yield
logger.info("Ban Yaro beendet.")
# ------------------------------------------------------------------
# App
# ------------------------------------------------------------------
app = FastAPI(
title = "Ban Yaro API",
version = "0.1.0",
lifespan = lifespan,
docs_url = "/api/docs" if os.getenv("ENV") != "production" else None,
redoc_url = None,
)
# ------------------------------------------------------------------
# API-Router registrieren (werden nach und nach hinzugefügt)
# ------------------------------------------------------------------
from routes.auth import router as auth_router
from routes.dogs import router as dogs_router
from routes.diary import router as diary_router
from routes.health import router as health_router
from routes.poison import router as poison_router
from routes.push import router as push_router
from routes.ki import router as ki_router
app.include_router(auth_router, prefix="/api/auth", tags=["Auth"])
app.include_router(dogs_router, prefix="/api/dogs", tags=["Hunde"])
app.include_router(diary_router, prefix="/api/dogs", tags=["Tagebuch"])
app.include_router(health_router, prefix="/api/dogs", tags=["Gesundheit"])
app.include_router(poison_router, prefix="/api/poison", tags=["Giftköder"])
app.include_router(push_router, prefix="/api/push", tags=["Push"])
app.include_router(ki_router, prefix="/api/ki", tags=["KI"])
# ------------------------------------------------------------------
# Fehlerbehandlung — einheitliches JSON-Format
# ------------------------------------------------------------------
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.error(f"Unbehandelter Fehler: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={"detail": "Interner Serverfehler."}
)
# ------------------------------------------------------------------
# Statische Dateien + SPA-Fallback
# ------------------------------------------------------------------
STATIC_DIR = os.path.join(os.path.dirname(__file__), "static")
app.mount("/css", StaticFiles(directory=f"{STATIC_DIR}/css"), name="css")
app.mount("/js", StaticFiles(directory=f"{STATIC_DIR}/js"), name="js")
app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons")
@app.get("/manifest.json")
async def manifest():
return FileResponse(f"{STATIC_DIR}/manifest.json")
@app.get("/sw.js")
async def service_worker():
return FileResponse(
f"{STATIC_DIR}/sw.js",
headers={"Cache-Control": "no-cache, no-store, must-revalidate"}
)
# Web Share Target
@app.post("/share")
async def share_target(request: Request):
# Empfängt geteilte Inhalte vom Handy (Fotos, Links, Text)
# Weiterleitung zur App mit den Daten
return FileResponse(
f"{STATIC_DIR}/index.html",
headers={"Cache-Control": "no-cache"}
)
# SPA Fallback — ALLE nicht-API-Routen gehen zur index.html
@app.get("/{full_path:path}")
async def spa_fallback(full_path: str):
return FileResponse(
f"{STATIC_DIR}/index.html",
headers={"Cache-Control": "no-cache"}
)