feat(wiki): bake custom navigation + HTML rendering + UX CSS into the config job
Make the wiki UX reproducible: set_navigation writes the custom static sidebar (Home + Anwender + Betrieb, betrieb items visible only to admin groups 1/3); ensure_html_rendering turns on markdownCore.allowHTML (v-encoded, other renderer configs preserved) so the HTML card landing renders — note it needs a Wiki.js restart to take effect on a fresh instance. The injectCSS is rebuilt: content links get an underline + accent (the old broad primary--text coloring made non-links look like links) plus the landing card/button styles. Page content lives in git-storage.
This commit is contained in:
@@ -7,9 +7,11 @@ stdlib. Alle Verträge wurden am 2026-08-12 live gegen die laufende Instanz gepr
|
|||||||
|
|
||||||
Ablauf:
|
Ablauf:
|
||||||
warten -> (falls Setup) /finalize mit SOPS-Admin -> warten auf Normalmodus
|
warten -> (falls Setup) /finalize mit SOPS-Admin -> warten auf Normalmodus
|
||||||
-> login -> Gruppen + Seitenregeln (Abschottung) -> OIDC-Strategy setzen
|
-> login -> Gruppen + Seitenregeln (Abschottung) -> OIDC-Strategy -> hideLocal
|
||||||
-> local auf der Login-Seite ausblenden (hideLocal).
|
-> Theming/Branding -> Navigation (Custom-Sidebar) -> HTML-Rendering (allowHTML)
|
||||||
Idempotent: jeder Schritt prüft erst den Ist-Zustand.
|
-> Git-Storage.
|
||||||
|
Idempotent: jeder Schritt prüft erst den Ist-Zustand. Seiten-Inhalt (Startseite,
|
||||||
|
Anwender-/Betriebsseiten) liegt in git-storage, nicht in diesem Job.
|
||||||
|
|
||||||
Variante B ("kein manuelles Login"): local wird NICHT deaktiviert, sondern nur per
|
Variante B ("kein manuelles Login"): local wird NICHT deaktiviert, sondern nur per
|
||||||
Wiki.js-eigenem hideLocal aus der Login-Seite ausgeblendet. Grund: (a) local ist
|
Wiki.js-eigenem hideLocal aus der Login-Seite ausgeblendet. Grund: (a) local ist
|
||||||
@@ -47,15 +49,27 @@ ADMIN_PERMS = [
|
|||||||
]
|
]
|
||||||
READER_PERMS = ["read:pages", "read:assets", "read:comments"]
|
READER_PERMS = ["read:pages", "read:assets", "read:comments"]
|
||||||
|
|
||||||
# Akzentfarbe fürs App-/Inhalts-UI (#0050): blau wie das alte Docusaurus.
|
# App-/Inhalts-UI-CSS (#0050 + UX-Fixes). Gilt NICHT für die Login-Seite (die rendert
|
||||||
# Gilt NICHT für die Login-Seite — die rendert Wiki.js ohne Custom-CSS.
|
# Wiki.js ohne Custom-CSS).
|
||||||
|
# - Content-Links: Akzent + Unterstreichung, damit klar als Link erkennbar. Die frühere
|
||||||
|
# breite .primary--text-Einfärbung ließ Nicht-Links wie Links aussehen -> raus.
|
||||||
|
# - Startseiten-Kacheln + Schnellzugriff-Buttons (die Startseite nutzt HTML, s. allowHTML).
|
||||||
ACCENT_CSS = (
|
ACCENT_CSS = (
|
||||||
".v-application a{color:#2b6cb0}"
|
".v-application .contents a{color:#2b6cb0;text-decoration:underline}"
|
||||||
".v-application .primary--text{color:#2b6cb0!important;caret-color:#2b6cb0!important}"
|
".theme--dark.v-application .contents a{color:#63b3ed}"
|
||||||
".v-application .primary{background-color:#2b6cb0!important;border-color:#2b6cb0!important}"
|
".v-application .contents a.toc-anchor{color:inherit!important;text-decoration:none!important}"
|
||||||
".theme--dark.v-application a{color:#63b3ed}"
|
".wiki-hero{background:rgba(255,255,255,.05);border-radius:10px;padding:26px 24px;margin:8px 0 20px}"
|
||||||
".theme--dark.v-application .primary--text{color:#63b3ed!important;caret-color:#63b3ed!important}"
|
".wiki-hero h1{border:none!important;margin:0 0 6px}.wiki-hero p{margin:0;color:#9ca3af}"
|
||||||
".theme--dark.v-application .primary{background-color:#63b3ed!important;border-color:#63b3ed!important}"
|
".wiki-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;margin:10px 0 26px}"
|
||||||
|
".wiki-card{display:flex!important;flex-direction:column;gap:6px;padding:20px;border-radius:10px;"
|
||||||
|
"background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.10);text-decoration:none!important}"
|
||||||
|
".wiki-card:hover{background:rgba(99,179,237,.12);border-color:#63b3ed}"
|
||||||
|
".wiki-card .wc-t{font-size:1.15rem;font-weight:600;color:#e5e7eb}"
|
||||||
|
".wiki-card .wc-d{color:#9ca3af;font-size:.9rem}"
|
||||||
|
".wiki-btns{display:flex;flex-wrap:wrap;gap:10px;margin:6px 0 22px}"
|
||||||
|
".wiki-btn{display:inline-block;padding:9px 16px;border-radius:6px;background:#2b6cb0;color:#fff!important;"
|
||||||
|
"text-decoration:none!important;font-size:.92rem;font-weight:500}"
|
||||||
|
".wiki-btn:hover{background:#63b3ed}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -248,6 +262,82 @@ def ensure_git_storage(jwt: str):
|
|||||||
log(f"Git-Storage aktiviert (Gitea, sync): {repo}")
|
log(f"Git-Storage aktiviert (Gitea, sync): {repo}")
|
||||||
|
|
||||||
|
|
||||||
|
def set_navigation(jwt: str):
|
||||||
|
# Eigenes Sidebar-Menü (STATIC) statt des Browse-Baums: Home + Anwender + Betrieb.
|
||||||
|
# Betrieb-Einträge nur für Admin-Gruppen sichtbar (1 Administrators, 3 authentik
|
||||||
|
# Admins); die Seiten sind ohnehin per checkAccess für Anwender gesperrt.
|
||||||
|
admin = [1, 3]
|
||||||
|
def lnk(i, label, icon, path, g=None):
|
||||||
|
return {"id": i, "kind": "link", "label": label, "icon": icon,
|
||||||
|
"targetType": "page", "target": path,
|
||||||
|
"visibilityMode": "restricted" if g else "all", "visibilityGroups": g or []}
|
||||||
|
def hdr(i, label, g=None):
|
||||||
|
return {"id": i, "kind": "header", "label": label, "icon": "",
|
||||||
|
"targetType": "external", "target": "",
|
||||||
|
"visibilityMode": "restricted" if g else "all", "visibilityGroups": g or []}
|
||||||
|
items = [
|
||||||
|
{"id": "home", "kind": "link", "label": "Startseite", "icon": "mdi-home",
|
||||||
|
"targetType": "home", "target": "", "visibilityMode": "all", "visibilityGroups": []},
|
||||||
|
hdr("hanw", "Anwenderdokumentation"),
|
||||||
|
lnk("a1", "Registrierung", "mdi-account-plus", "anwender/registrierung"),
|
||||||
|
lnk("a2", "Anmelden & Konto", "mdi-login", "anwender/anmelden-und-konto"),
|
||||||
|
lnk("a3", "Erste Schritte", "mdi-rocket-launch-outline", "anwender/erste-schritte"),
|
||||||
|
lnk("a4", "Nachrichten", "mdi-message-text", "anwender/nachrichten"),
|
||||||
|
lnk("a5", "Anrufe & Screensharing", "mdi-phone", "anwender/anrufe"),
|
||||||
|
lnk("a6", "Verschlüsselung", "mdi-lock", "anwender/verschluesselung"),
|
||||||
|
lnk("a7", "Benachrichtigungen", "mdi-bell", "anwender/benachrichtigungen"),
|
||||||
|
lnk("a8", "ThreadNet Desktop", "mdi-desktop-classic", "anwender/threadnet-desktop-setup"),
|
||||||
|
hdr("hbet", "Betriebsdokumentation", admin),
|
||||||
|
lnk("b0", "Einführung & Architektur", "mdi-sitemap", "betrieb/einfuehrung", admin),
|
||||||
|
lnk("b1", "Installation", "mdi-download", "betrieb/installation", admin),
|
||||||
|
lnk("b2", "ConfigMap-Sync", "mdi-sync", "betrieb/configmap-sync", admin),
|
||||||
|
lnk("b3", "Authentik OIDC", "mdi-shield-account", "betrieb/authentik-oidc", admin),
|
||||||
|
lnk("b4", "Element / ThreadNet", "mdi-palette", "betrieb/element-customization", admin),
|
||||||
|
lnk("b5", "Element Call & MatrixRTC", "mdi-video", "betrieb/element-call", admin),
|
||||||
|
lnk("b6", "TURN Server", "mdi-server-network", "betrieb/turn-server", admin),
|
||||||
|
lnk("b7", "Moderation & Scanning", "mdi-shield-check", "betrieb/moderation-content-scanning", admin),
|
||||||
|
lnk("b8", "Monitoring", "mdi-chart-line", "betrieb/monitoring", admin),
|
||||||
|
lnk("b9", "Backups", "mdi-backup-restore", "betrieb/backups", admin),
|
||||||
|
lnk("b10", "Host-Wartung", "mdi-wrench", "betrieb/host-maintenance-notifications", admin),
|
||||||
|
lnk("b11", "Room Policies", "mdi-gavel", "betrieb/room-policies", admin),
|
||||||
|
lnk("b12", "Upgrades", "mdi-update", "betrieb/upgrades", admin),
|
||||||
|
]
|
||||||
|
gql('mutation($m:NavigationMode!){navigation{updateConfig(mode:$m){responseResult{succeeded}}}}',
|
||||||
|
jwt, {"m": "STATIC"})
|
||||||
|
gql('mutation($t:[NavigationTreeInput]!){navigation{updateTree(tree:$t)'
|
||||||
|
'{responseResult{succeeded message}}}}', jwt, {"t": [{"locale": "en", "items": items}]})
|
||||||
|
log("Navigation gesetzt (Custom-Sidebar; Betrieb nur für Admins)")
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_html_rendering(jwt: str):
|
||||||
|
# Die Startseite nutzt HTML-Kacheln -> markdownCore.allowHTML muss an sein. Wiki.js'
|
||||||
|
# updateRenderers-Resolver liest jeden Wert via _.get(JSON.parse(value),'v',null),
|
||||||
|
# also {"v":…}-kodiert; die übrigen Renderer-Werte müssen mitgesendet werden, sonst
|
||||||
|
# werden sie genullt.
|
||||||
|
# ⚠️ Wiki.js lädt die Renderer-Config beim START. Auf einer FRISCHEN Instanz greift
|
||||||
|
# allowHTML erst nach einem Wiki.js-Neustart; die Startseite rendert dann beim
|
||||||
|
# nächsten git-Sync korrekt (sonst erscheint sie kurz als HTML-Text). Idempotent.
|
||||||
|
rs = gql('{rendering{renderers{isEnabled key config{key value}}}}', jwt)["rendering"]["renderers"]
|
||||||
|
def actual(v):
|
||||||
|
try:
|
||||||
|
p = json.loads(v)
|
||||||
|
return p["value"] if isinstance(p, dict) and "value" in p else p
|
||||||
|
except Exception:
|
||||||
|
return v
|
||||||
|
out = []
|
||||||
|
for r in rs:
|
||||||
|
cfg = []
|
||||||
|
for c in (r["config"] or []):
|
||||||
|
a = actual(c["value"])
|
||||||
|
if r["key"] == "markdownCore" and c["key"] == "allowHTML":
|
||||||
|
a = True
|
||||||
|
cfg.append({"key": c["key"], "value": json.dumps({"v": a})})
|
||||||
|
out.append({"isEnabled": r["isEnabled"], "key": r["key"], "config": cfg})
|
||||||
|
gql('mutation($r:[RendererInput]){rendering{updateRenderers(renderers:$r)'
|
||||||
|
'{responseResult{succeeded message}}}}', jwt, {"r": out})
|
||||||
|
log("HTML-Rendering aktiviert (markdownCore.allowHTML) — Neustart nötig auf frischer Instanz")
|
||||||
|
|
||||||
|
|
||||||
def group_id(jwt: str, name: str):
|
def group_id(jwt: str, name: str):
|
||||||
for g in gql("{groups{list{id name}}}", jwt)["groups"]["list"]:
|
for g in gql("{groups{list{id name}}}", jwt)["groups"]["list"]:
|
||||||
if g["name"] == name:
|
if g["name"] == name:
|
||||||
@@ -299,6 +389,8 @@ def main():
|
|||||||
ensure_oidc(jwt)
|
ensure_oidc(jwt)
|
||||||
set_hide_local(jwt)
|
set_hide_local(jwt)
|
||||||
ensure_theming(jwt)
|
ensure_theming(jwt)
|
||||||
|
set_navigation(jwt)
|
||||||
|
ensure_html_rendering(jwt)
|
||||||
ensure_git_storage(jwt)
|
ensure_git_storage(jwt)
|
||||||
log("fertig — Wiki.js konfiguriert")
|
log("fertig — Wiki.js konfiguriert")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user