Files
p42.f-w/backend/app/main.py
T
guyg1474andClaude Opus 5.5 dd58b3d1f8 P42 Dashboard: erster Stand
FastAPI-Backend und React-Frontend mit Konten, Sendungsverfolgung,
Projekt-Artikeln, Vorrat & Einkauf mit Rezeptabgleich, Reise-Weltkarte,
Kanban-Projekten mit Priorität, Server-Monitoring und den Designs
Matrix und Blackwall. Betrieb per Docker hinter nginx (siehe DEPLOY.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:08:11 +02:00

73 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""P42 Dashboard – FastAPI-Backend.
Liefert die API unter /api, die hochgeladenen Bilder unter /uploads
und (falls gebaut) das React-Frontend aus frontend/dist.
"""
import re
from fastapi import Depends, FastAPI, HTTPException, Request
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from . import auth, haushalt, kanban, links, monitoring, projects, reisen, rezept
from .auth import current_user
from .config import FRONTEND_DIST
from .tracking import track
app = FastAPI(title="P42 Dashboard", docs_url="/api/docs", openapi_url="/api/openapi.json")
# Komprimiert u. a. die JS-Bundles und die Weltkarte (~740 KB → ~200 KB)
app.add_middleware(GZipMiddleware, minimum_size=1000)
# ---------- Fehler einheitlich als {"error": "..."} ----------
@app.exception_handler(HTTPException)
async def http_error(_: Request, exc: HTTPException):
return JSONResponse({"error": exc.detail}, status_code=exc.status_code)
@app.exception_handler(projects.ValidationError)
@app.exception_handler(links.ValidationError)
async def validation_error(_: Request, exc: ValueError):
return JSONResponse({"error": str(exc)}, status_code=400)
# ---------- API ----------
app.include_router(auth.router)
app.include_router(links.router)
app.include_router(projects.router)
app.include_router(monitoring.router)
app.include_router(haushalt.router)
app.include_router(rezept.router)
app.include_router(reisen.router)
app.include_router(kanban.router)
@app.get("/api/track")
async def get_tracking(number: str = "", _: dict = Depends(current_user)):
number = re.sub(r"\s+", "", number).upper()
if not re.fullmatch(r"[A-Z0-9-]{6,40}", number):
raise HTTPException(400, "Ungültige Sendungsnummer.")
return await track(number)
@app.api_route("/api/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], include_in_schema=False)
def api_not_found(path: str):
raise HTTPException(404, "Nicht gefunden.")
# ---------- React-Frontend (nach `npm run build`) ----------
if (FRONTEND_DIST / "index.html").exists():
app.mount("/assets", StaticFiles(directory=FRONTEND_DIST / "assets"), name="assets")
@app.get("/{path:path}", include_in_schema=False)
def spa(path: str):
file = (FRONTEND_DIST / path).resolve()
if path and file.is_file() and file.is_relative_to(FRONTEND_DIST.resolve()):
return FileResponse(file)
# Alle anderen Pfade übernimmt der React-Router
return FileResponse(FRONTEND_DIST / "index.html", headers={"Cache-Control": "no-cache"})