Scaffolder admin-ui/ (Vite + React + TypeScript, almindelig CSS) med en minimal login-side (POST /auth/login + GET /auth/me) der beviser hele kæden virker. Vites build skriver direkte til app/admin_dist/. Admin-UI'et serveres af samme FastAPI-app som API'et og den offentlige side, men på sit eget dedikerede domæne (ADMIN_DOMAIN, default admin.localhost) via en ny AdminDomainDispatch-middleware (app/admin_site.py) der genbruger Host-header-teknikken fra 8b: den offentlige side ejer allerede roden "/" domæneopløst, så admin-UI'et kan ikke dele det domæne uden at kollidere. Samme trick undgår CORS helt, da admin-UI'ets fetch-kald går til samme origin. API-kald (/auth, /participants, ...) passerer uændret gennem til det almindelige API uanset domæne; alt andet på admin-domænet serveres fra den byggede SPA med index.html-fallback for client-side routing. Et sikkerhedsnet deaktiverer dispatch'en stille hvis admin_dist/ mangler (frisk clone uden frontend-build), i stedet for at crashe hele appen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
40 lines
1.1 KiB
Python
40 lines
1.1 KiB
Python
from fastapi import FastAPI
|
|
from fastapi.staticfiles import StaticFiles
|
|
from sqlalchemy import text
|
|
|
|
from app.admin_site import AdminDomainDispatch
|
|
from app.db import SessionDep
|
|
from app.routers import (
|
|
auth,
|
|
mail_logs,
|
|
mail_templates,
|
|
orders,
|
|
participants,
|
|
public_orders,
|
|
public_site,
|
|
purchase_rounds,
|
|
webhooks,
|
|
wine_categories,
|
|
wine_offerings,
|
|
)
|
|
|
|
app = FastAPI(title="Vinindkøb Admin API")
|
|
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
|
app.include_router(auth.router)
|
|
app.include_router(participants.router)
|
|
app.include_router(purchase_rounds.router)
|
|
app.include_router(wine_offerings.router)
|
|
app.include_router(wine_categories.router)
|
|
app.include_router(mail_templates.router)
|
|
app.include_router(mail_logs.router)
|
|
app.include_router(orders.router)
|
|
app.include_router(webhooks.router)
|
|
app.include_router(public_orders.router)
|
|
app.include_router(public_site.router)
|
|
app.add_middleware(AdminDomainDispatch)
|
|
|
|
|
|
@app.get("/health")
|
|
def health(session: SessionDep) -> dict:
|
|
session.exec(text("SELECT 1"))
|
|
return {"status": "ok", "database": "ok"}
|