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:
65
app/services/preview_worker.py
Normal file
65
app/services/preview_worker.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Läuft in einem isolierten Verzeichnis mit Kopie des Cores + materialisiertem Tenant.
|
||||
|
||||
Aufruf: python preview_worker.py <index|pdf|validate> <ausgabedatei> (JSON-Eingabe auf stdin)
|
||||
Führt den ECHTEN Core aus (auch overrides.py) – deshalb nur als Subprozess ohne Secrets/Netz-Keys.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
kind, out = sys.argv[1], sys.argv[2]
|
||||
args = json.loads(sys.stdin.read() or "{}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
from core import app as appmod
|
||||
from core.config import CFG
|
||||
|
||||
if kind == "index":
|
||||
page = appmod._render_index()
|
||||
open(out, "w", encoding="utf-8").write(page)
|
||||
elif kind == "pdf":
|
||||
pdf = appmod.build_pdf(args.get("title") or "Vorschau", args["summary"], "SPRECHER_0: Beispiel.")
|
||||
open(out, "wb").write(pdf)
|
||||
elif kind == "prompt":
|
||||
base = appmod._summary_prompt()
|
||||
ctx = {"title": args.get("title", ""), "insurance_type": args.get("terminart", ""),
|
||||
"customer_2": args.get("projekt", ""), "customer_name": args.get("kunde", ""), "customer_address": ""}
|
||||
final = appmod.HOOKS.build_prompt(base, ctx)
|
||||
json.dump({"prompt": final, "chars": len(final)}, open(out, "w"))
|
||||
elif kind == "validate":
|
||||
errors, warnings = [], []
|
||||
page = appmod._render_index()
|
||||
if "@@" in page:
|
||||
errors.append("Web-UI: nicht ersetzte @@PLATZHALTER@@ im Template")
|
||||
if not CFG.logo_path:
|
||||
warnings.append("Kein Logo (logo.svg/logo.png) hinterlegt")
|
||||
if not CFG.favicon_path:
|
||||
warnings.append("Kein favicon.svg – es wird ein generisches Favicon verwendet")
|
||||
try:
|
||||
pdf = appmod.build_pdf("Validierung", args["summary"], "SPRECHER_0: Beispiel.")
|
||||
if pdf[:5] != b"%PDF-":
|
||||
errors.append("PDF-Rendering lieferte kein PDF")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
errors.append(f"PDF-Rendering fehlgeschlagen: {exc}")
|
||||
prompt = CFG.prompt_path.read_text(encoding="utf-8")
|
||||
if len(prompt.strip()) < 200:
|
||||
warnings.append("Prompt ist sehr kurz")
|
||||
json.dump({"ok": not errors, "errors": errors, "warnings": warnings,
|
||||
"tenant": CFG.id, "prompt_chars": len(prompt)}, open(out, "w"))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except BaseException: # noqa: BLE001
|
||||
tb = traceback.format_exc()
|
||||
if kind == "validate":
|
||||
json.dump({"ok": False, "errors": ["Tenant lässt sich nicht laden:\n" + tb[-1500:]], "warnings": []}, open(out, "w"))
|
||||
else:
|
||||
sys.stderr.write(tb)
|
||||
sys.exit(2)
|
||||
Reference in New Issue
Block a user