Admin-Plattform für die Tenants von Onyscribe: Editor mit Live-Vorschau (echter Core in Sandbox), Versionen, KI-Agent (Cortecs), OIDC-Abgleich über die Login-Developer-API, Deploy-Engine (Mittwald, gesperrt ohne DEPLOY_LIVE, Freigabe pro Tenant), Core-Vertrag (Schema/Manifest), Core-Releases mit Kompatibilitätsprüfung, Canary-Rollout und Rollback, Zustandsabgleich. Runbook: docs/RUNBOOK_PARALLELTEST.md Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
43 lines
1.2 KiB
Python
43 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from app.db import init_db, session
|
|
from app import auth
|
|
from app.routers import api, pages
|
|
from sqlalchemy import func, select
|
|
|
|
from app.models import Tenant
|
|
from app.services import seed
|
|
from app.services.importer import seed_settings
|
|
from app.settings import ROOT
|
|
|
|
SEED_FILE = ROOT / "seed" / "tenants.seed.json.gz"
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(_: FastAPI):
|
|
init_db()
|
|
with session() as db:
|
|
# Frische Instanz → mit den importierten Tenants starten (ohne Secret-Werte)
|
|
if SEED_FILE.exists() and not db.scalar(select(func.count()).select_from(Tenant)):
|
|
print(f"[seed] {seed.load(db, SEED_FILE)} Tenants aus {SEED_FILE.name} geladen")
|
|
seed_settings(db)
|
|
yield
|
|
|
|
|
|
app = FastAPI(title="Onyscribe Admin", lifespan=lifespan)
|
|
app.middleware("http")(auth.middleware)
|
|
app.include_router(auth.router)
|
|
app.mount("/static", StaticFiles(directory=str(ROOT / "app" / "static")), name="static")
|
|
app.include_router(api.router)
|
|
app.include_router(pages.router)
|
|
|
|
|
|
@app.get("/healthz")
|
|
def healthz():
|
|
return {"ok": True}
|