Onyscribe Admin: Initialer Stand

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>
This commit is contained in:
RubenRWU
2026-10-15 15:30:26 +02:00
commit 7d8c226da2
89 changed files with 13975 additions and 0 deletions

35
app/services/guard.py Normal file
View File

@@ -0,0 +1,35 @@
"""Schreib-Freigabe für externe Systeme (Mittwald, Login-API) – pro Tenant.
`DEPLOY_ALLOWED_TENANTS` (Env der Admin-Plattform) legt fest, welche Tenants live angefasst werden dürfen:
(leer/nicht gesetzt) keiner – auch mit DEPLOY_LIVE=true wird nichts geschrieben
demo-admin,demo2 nur diese
* alle
Greift bei Live-Deploy, Offboard, Rollout/Rollback und beim Schreiben im Login-System (OIDC-Abgleich/Löschen).
Dry-Runs und Lesezugriffe sind nie betroffen. Damit lässt sich die Plattform schrittweise und gefahrlos parallel zum Altbetrieb einführen.
"""
from __future__ import annotations
import os
def allowed_tenants() -> set[str] | None:
"""None = alle, sonst die erlaubten IDs (leere Menge = keiner)."""
raw = os.getenv("DEPLOY_ALLOWED_TENANTS", "").strip()
if raw == "*":
return None
return {x.strip() for x in raw.split(",") if x.strip()}
def live_allowed(tenant_id: str) -> bool:
a = allowed_tenants()
return a is None or tenant_id in a
def describe() -> str:
a = allowed_tenants()
return "alle Tenants" if a is None else (", ".join(sorted(a)) if a else "keiner")
def denial(tenant_id: str) -> str:
return (f"Tenant „{tenant_id}“ ist für Live-Schreibzugriffe nicht freigegeben (Env DEPLOY_ALLOWED_TENANTS = {describe()}). "
"Dry-Runs sind weiterhin möglich.")