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:
54
app/services/seed.py
Normal file
54
app/services/seed.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Seed-Export/-Import: Startdaten (importierte Tenants) als Datei im Repo.
|
||||
|
||||
So startet eine frische Instanz (z. B. Coolify) mit den aktuellen Tenants, ohne Zugriff auf das alte Git-Repo.
|
||||
Secrets-WERTE sind nie enthalten – nur die Schlüsselnamen.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import gzip
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models import Blob, Secret, Setting, Tenant, Version
|
||||
|
||||
COLS = ["id", "name", "status", "source", "subdomain", "custom_domain", "domain_status", "cpu", "memory",
|
||||
"current_version", "deployed_version", "deployed_image", "preview_sample", "oidc", "notes"]
|
||||
|
||||
|
||||
def export(db: Session, path: Path) -> None:
|
||||
tenants = db.scalars(select(Tenant).order_by(Tenant.id)).all()
|
||||
shas = set()
|
||||
out = {"tenants": [], "versions": [], "secrets": [], "settings": {}}
|
||||
for t in tenants:
|
||||
out["tenants"].append({c: getattr(t, c) for c in COLS})
|
||||
for v in db.scalars(select(Version).where(Version.tenant_id == t.id).order_by(Version.number)):
|
||||
out["versions"].append({"tenant_id": t.id, "number": v.number, "message": v.message, "author": v.author,
|
||||
"config": v.config, "files": v.files})
|
||||
shas |= set(v.files.values())
|
||||
for s in db.scalars(select(Secret)):
|
||||
out["secrets"].append({"tenant_id": s.tenant_id, "key": s.key})
|
||||
out["settings"] = {s.key: s.value for s in db.scalars(select(Setting))}
|
||||
out["blobs"] = {sha: {"b64": base64.b64encode(db.get(Blob, sha).content).decode(), "text": db.get(Blob, sha).is_text} for sha in shas}
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(gzip.compress(json.dumps(out).encode(), 9))
|
||||
|
||||
|
||||
def load(db: Session, path: Path) -> int:
|
||||
data = json.loads(gzip.decompress(path.read_bytes()))
|
||||
for sha, b in data["blobs"].items():
|
||||
raw = base64.b64decode(b["b64"])
|
||||
db.add(Blob(sha=sha, content=raw, size=len(raw), is_text=b["text"]))
|
||||
for t in data["tenants"]:
|
||||
db.add(Tenant(**t))
|
||||
db.flush()
|
||||
for v in data["versions"]:
|
||||
db.add(Version(**v))
|
||||
for s in data["secrets"]:
|
||||
db.add(Secret(tenant_id=s["tenant_id"], key=s["key"], value_enc=None, source="sops-import"))
|
||||
for k, v in data["settings"].items():
|
||||
db.add(Setting(key=k, value=v))
|
||||
return len(data["tenants"])
|
||||
Reference in New Issue
Block a user