Audit + Umbau: Faktenfehler behoben, Server-/Admin-Lücken geschlossen, 49 Screenshots, Schema-Matrix generiert

- Audit: plans/doku-audit.md (Befunde, Phasen P1-P9, Ausführungsprotokoll) + plans/audit/ (8 Detailreports) + plans/offene-issues.md
- Werkzeuge: scripts/audit-docs.mjs (Links/Anker/Bilder/Tabellen), scripts/gen-schema-matrix.mjs (npm run gen:schemas)
- Neue Seiten: projekt/feld-rechte, reference/rest-system, reference/schema-properties, projekt/tests, 5 Anwender-Seiten
- Fehler behoben: EMBEDDING_BGE_M3_*-Env, allowSnapshots, MCP-Scopes (.read/.create/.update), meta.views, loadAll-Signatur, OpenAPI-Endpunkt, MongoDB 8, Befehlspalette
- Screenshots: 9 Admin + 10 Anwender aus Mock-Projekt corporate (t1 entfernt), 49 Bilder gesamt
- AGENTS.md: Sync-Behauptung richtiggestellt, Belegpflicht/UI-Namen/Screenshot-Regeln, Werkzeug-Abschnitt
- Build: 71 Seiten grün, 0 tote Links, 0 kaputte Tabellen
This commit is contained in:
apairon committed 2026-10-17 01:49:35 +00:00
1 parent 1897bb50d9
commit 1928922e07
104 files changed
+4756 -1044

No files matched your search

+27 -12
View File
@@ -12,20 +12,25 @@ Regeln für alle Agenten, die an dieser Doku, am Tibi-Server / tibi-admin oder a
- **Single Source:** Code-Nähe schlägt Doku-Kopie. Details stehen in `tibi-server/docs/` bzw. `tibi-admin-nova/docs/` (Doku-Begriff: **tibi-admin**), diese Seite rendert nur. **Code schlägt Doku:** Bestandsdoku gilt als Behauptung, Quelle ist der Code (`internal/*`, `tibi-types/schemas`, `frontend/src/lib`, Tests, Test-Pläne). - **Single Source:** Code-Nähe schlägt Doku-Kopie. Details stehen in `tibi-server/docs/` bzw. `tibi-admin-nova/docs/` (Doku-Begriff: **tibi-admin**), diese Seite rendert nur. **Code schlägt Doku:** Bestandsdoku gilt als Behauptung, Quelle ist der Code (`internal/*`, `tibi-types/schemas`, `frontend/src/lib`, Tests, Test-Pläne).
- **Definition of Done:** Jede PR an `tibi-server` oder `tibi-admin-nova`, die API, Hooks, Collections, Permissions, Jobs, Realtime, LLM, Deploy oder UX ändert, aktualisiert auch die Docs — sonst ist die PR nicht fertig. - **Definition of Done:** Jede PR an `tibi-server` oder `tibi-admin-nova`, die API, Hooks, Collections, Permissions, Jobs, Realtime, LLM, Deploy oder UX ändert, aktualisiert auch die Docs — sonst ist die PR nicht fertig.
- **Belegpflicht:** Jede prüfbare Aussage trägt einen Stempel am Seitenende (ausgenommen: `index.mdx`, `adr/0000-vorlage.md`, ADRs ohne prüfbaren Inhalt und generierte Seiten wie `reference/schema-properties.md`):
`Verifiziert am <7-stelliger-hash> <JJJJ-MM-TT> (<repo>)` — mehrere Quellen als Liste. Kein Hash ohne Prüfung, kein Datum ohne Prüftag.
- **UI-Namen wörtlich:** Jeder **fett** oder „zitierte" Bedienname muss wörtlich in
`../tibi-admin-nova/frontend/src/lib/i18n/locales/de.json` stehen. Findet man keinen String, beschreibt
man die Wirkung („der Knopf oben rechts") statt einen Namen zu erfinden.
- **Sprache:** Deutsch, saubere Umlaute (ä ö ü ß), Du-Form, kurze Sätze. Ton: **per Du, aber professionell** — sachlich wie ein gutes Handbuch, niemals Babysprache. Keine belehrenden Binsen („wenn du nichts findest, verändere die Suche"), keine Verniedlichung („keine Angst"), keine erfundenen Floskeln. UI-Namen immer wörtlich aus `de.json` bzw. Screenshot. Anweisungen nennen den exakten Klickpfad; „Was-tun-wenn" nur für echte, am Code belegte Fälle. - **Sprache:** Deutsch, saubere Umlaute (ä ö ü ß), Du-Form, kurze Sätze. Ton: **per Du, aber professionell** — sachlich wie ein gutes Handbuch, niemals Babysprache. Keine belehrenden Binsen („wenn du nichts findest, verändere die Suche"), keine Verniedlichung („keine Angst"), keine erfundenen Floskeln. UI-Namen immer wörtlich aus `de.json` bzw. Screenshot. Anweisungen nennen den exakten Klickpfad; „Was-tun-wenn" nur für echte, am Code belegte Fälle.
## 2. Wo was hingehört — Stand: Gerüst steht, Hüllen mit Arbeitsaufträgen ## 2. Wo was hingehört — Stand: ausgebaut (keine Hüllen mehr)
Jede Hüllen-Seite enthält Abschnitt „Für spätere Agenten“ mit Quelle-der-Wahrheit (Code zuerst), Prüfschritten und Ziel-Struktur. Hüllen abarbeiten heißt: Code prüfen, Seite befüllen, `Verifiziert am <Commit> <Datum>` ergänzen. Jede Seite nennt ihre Quelle-der-Wahrheit (Code zuerst), hat einen Stempel am Ende und mindestens einen Querverweis auf verwandte Seiten. Neue Fähigkeiten bekommen genau eine Dokustelle (Tabelle unter §3 „Vollständigkeit").
| Ordner | Inhalt | Beispiel | | Ordner | Inhalt | Beispiel |
|---|---|---| |---|---|---|
| `einstieg/` | Tutorials, Erste Schritte (Projekt-Blick) | Schnellstart, Neues Projekt, Begriffe | | `einstieg/` | Tutorials, Erste Schritte (Projekt-Blick) | Schnellstart, Neues Projekt, Begriffe |
| `projekt/` | Projekt-Entwickler baut eigenständige Website (Sammlungen → Deploy) | Hooks, SSR, Suche | | `projekt/` | Projekt-Entwickler baut eigenständige Website (Sammlungen → Deploy) | Hooks, SSR, Suche |
| `server/` | **SYNC — nicht von Hand editieren**, Quelle `../tibi-server/docs/`; dazu Handseiten Betrieb | Konfiguration, Sicherheit | | `server/` | **Handseiten** (deutsch): Server-Sicht für Projekt-Entwickler und Betreiber. Die englische Core-Referenz liegt in `../tibi-server/docs/` und wird **nicht** synchronisiert | Konfiguration, Sicherheit |
| `admin/` | Teils SYNC aus `../tibi-admin-nova/docs/`, plus Handseiten | Konzept, Editor, Exporte | | `admin/` | Handseiten (deutsch) zur Bedienoberfläche tibi-admin, teils mit Bezug auf `../tibi-admin-nova/docs/` | Konzept, Editor, Exporte |
| `anwender/` | Redakteurs-Anleitungen im Verwaltungs-Backend (Aufgaben, keine Konzepte) | Anmelden, Medien | | `anwender/` | Redakteurs-Anleitungen im Verwaltungs-Backend (Aufgaben, keine Konzepte) | Anmelden, Medien |
| `reference/` | Teils **generiert — nie von Hand** (openapi), teils Code-Stand-Tabellen | Hook-Kontexte, Token-Köpfe | | `reference/` | Teils **generiert — nie von Hand** (openapi, schema-properties), teils Code-Stand-Tabellen | Hook-Kontexte, Token-Köpfe |
| `adr/` | Architektur-Entscheide, Format siehe `adr/0000-vorlage.md` | Code-schlägt-Doku | | `adr/` | Architektur-Entscheide, Format siehe `adr/0000-vorlage.md` | Code-schlägt-Doku |
## 3. Neue Seite anlegen ## 3. Neue Seite anlegen
@@ -33,19 +38,29 @@ Jede Hüllen-Seite enthält Abschnitt „Für spätere Agenten“ mit Quelle-der
- Datei `src/content/docs/<bereich>/<slug>.md` mit Frontmatter `title` + `description`. - Datei `src/content/docs/<bereich>/<slug>.md` mit Frontmatter `title` + `description`.
- Sidebar: jede Gruppe explizit in `astro.config.mjs` eintragen (beim Neuaufbau pro Bereich ergänzen; `autogenerate` erst nutzen, sobald der Ordner gepflegt befüllt ist). - Sidebar: jede Gruppe explizit in `astro.config.mjs` eintragen (beim Neuaufbau pro Bereich ergänzen; `autogenerate` erst nutzen, sobald der Ordner gepflegt befüllt ist).
- Bilder nach `src/assets/`, max. 200 KB, deutsche Alt-Texte. - Bilder nach `src/assets/`, max. 200 KB, deutsche Alt-Texte.
- Screenshots für Anwender-Seiten: nur Mock-Projekt `corporate` (nie `t1`), Ablauf unter `plans/t-shots-arbeitsauftrag.md`. - Screenshots: nur Mock-Projekt `corporate` (nie `t1`), Ablauf unter `plans/t-shots-arbeitsauftrag.md`.
Anwender-Bilder nach `src/assets/anwender/`, Admin-Bilder nach `src/assets/admin/`, je ≤ 200 KB (WebP, 1440×900), deutscher Alt-Text im Markdown. Offene Motive stehen als `<!-- Screenshot (T-SHOTS): … -->` in der Seite.
- Vollständigkeit: neue Fähigkeit braucht genau eine Dokustelle — Tabelle in `plans/doku-ausbau.md` („Vollständigkeit") miterweitern. - Vollständigkeit: neue Fähigkeit braucht genau eine Dokustelle — Tabelle in `plans/doku-ausbau.md` („Vollständigkeit") miterweitern.
## 4. Sync-Workflow ## 4. Werkzeuge und Prüfschritte
```sh ```sh
# nach jeder Änderung in tibi-server/docs oder tibi-admin-nova/docs: node scripts/audit-docs.mjs # Pflicht vor jedem Push: Frontmatter, Codeblöcke, Links, Anker, Bilder, Tabellen, Stil-Marker
npm run sync npm run coverage # Coverage-Inventar (Routen, Hook-Kontexte, MCP-Tools, Schemas, Komponenten)
npm run build # muss fehlerfrei durchlaufen npm run gen:schemas # erzeugt reference/schema-properties.md aus ../tibi-types/schemas/config
npm run sync:openapi # holt die OpenAPI-Spec (braucht TIBI_NAMESPACE und laufenden tibi-server)
npm run build # muss grün durchlaufen (prüft interne Links NICHT)
``` ```
- `server/uebersicht.md` und `admin/konzept.md` sind Handarbeit und werden vom Sync nie überschrieben (`keep`) — Sync-Ziel für Admin ist `src/content/docs/admin/` (Repo bleibt `tibi-admin-nova`). - `scripts/audit-docs.mjs` meldet tote Links/Anker, kaputte Tabellen, fehlende Alt-Texte und offene
- Sync-Artefakte werden mitcommittet (statischer Build braucht sie). Platzhalter-Marker. Ergebnis **muss** „0 tote Links, 0 kaputte Tabellen" sein; verbleibende Marker
sind zu begründen.
- `scripts/gen-schema-matrix.mjs` und `reference/schema-properties.md` sind ein Paar: Seite nie von Hand
editieren, Schemas nie in der Doku nachpflegen — nur `npm run gen:schemas` laufen lassen.
- `scripts/sync-server-docs.mjs` ist **stillgelegt** (der Server-Sync erzeugte verwaiste Seiten ohne
Sidebar-Eintrag). Nicht mehr aufrufen; die Datei wird entfernt, sobald niemand sie vermisst.
- `server/uebersicht.md` und `admin/konzept.md` sind Handarbeit.
- Screenshots werden mitgeliefert (statischer Build braucht sie).
## 5. Verbote ## 5. Verbote
+10
View File
@@ -35,6 +35,7 @@ export default defineConfig({
baseUrl: 'https://gitbase.de/cms/tibi-docs/_edit/master/', baseUrl: 'https://gitbase.de/cms/tibi-docs/_edit/master/',
}, },
tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 }, tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 },
lastUpdated: true,
sidebar: [ sidebar: [
{ {
label: 'Einstieg', label: 'Einstieg',
@@ -62,6 +63,7 @@ export default defineConfig({
{ label: 'Felder und Widgets', slug: 'projekt/felder-widgets' }, { label: 'Felder und Widgets', slug: 'projekt/felder-widgets' },
{ label: 'Lesen und suchen', slug: 'projekt/abfragen-lesen' }, { label: 'Lesen und suchen', slug: 'projekt/abfragen-lesen' },
{ label: 'Rechte und Tokens', slug: 'projekt/rechte-tokens' }, { label: 'Rechte und Tokens', slug: 'projekt/rechte-tokens' },
{ label: 'Feld-Rechte', slug: 'projekt/feld-rechte' },
{ label: 'Hooks', slug: 'projekt/hooks-schreiben' }, { label: 'Hooks', slug: 'projekt/hooks-schreiben' },
{ label: 'Dateien und Bilder', slug: 'projekt/dateien-bilder' }, { label: 'Dateien und Bilder', slug: 'projekt/dateien-bilder' },
{ label: 'Navigation und Sprachen', slug: 'projekt/navigation-i18n' }, { label: 'Navigation und Sprachen', slug: 'projekt/navigation-i18n' },
@@ -73,6 +75,7 @@ export default defineConfig({
{ label: 'MCP nutzen', slug: 'projekt/mcp-nutzen' }, { label: 'MCP nutzen', slug: 'projekt/mcp-nutzen' },
{ label: 'Sicherung', slug: 'projekt/audit-backup' }, { label: 'Sicherung', slug: 'projekt/audit-backup' },
{ label: 'Veröffentlichen', slug: 'projekt/deploy' }, { label: 'Veröffentlichen', slug: 'projekt/deploy' },
{ label: 'Tests und Mock', slug: 'projekt/tests' },
], ],
}, },
{ {
@@ -99,9 +102,14 @@ export default defineConfig({
{ label: 'Einträge bearbeiten', slug: 'anwender/eintrag-bearbeiten' }, { label: 'Einträge bearbeiten', slug: 'anwender/eintrag-bearbeiten' },
{ label: 'Bilder und Dateien', slug: 'anwender/medien' }, { label: 'Bilder und Dateien', slug: 'anwender/medien' },
{ label: 'Seiten bauen', slug: 'anwender/seiten-bauen' }, { label: 'Seiten bauen', slug: 'anwender/seiten-bauen' },
{ label: 'Verlauf und Wiederherstellen', slug: 'anwender/verlauf-wiederherstellen' },
{ label: 'Mehrere Einträge ändern', slug: 'anwender/massenbearbeitung' },
{ label: 'Aktionen ausführen', slug: 'anwender/aktionen' },
{ label: 'Tastenkürzel', slug: 'anwender/tastenkombinationen' },
{ label: 'Benutzer und Teams', slug: 'anwender/benutzer-teams' }, { label: 'Benutzer und Teams', slug: 'anwender/benutzer-teams' },
{ label: 'Exportieren', slug: 'anwender/exportieren' }, { label: 'Exportieren', slug: 'anwender/exportieren' },
{ label: 'Sichern', slug: 'anwender/sichern' }, { label: 'Sichern', slug: 'anwender/sichern' },
{ label: 'Backup einspielen', slug: 'anwender/backup-einspielen' },
{ label: 'Hilfe', slug: 'anwender/hilfe' }, { label: 'Hilfe', slug: 'anwender/hilfe' },
{ label: 'KI nutzen', slug: 'anwender/ki-nutzen' }, { label: 'KI nutzen', slug: 'anwender/ki-nutzen' },
], ],
@@ -111,6 +119,8 @@ export default defineConfig({
items: [ items: [
{ label: 'OpenAPI', slug: 'reference/openapi' }, { label: 'OpenAPI', slug: 'reference/openapi' },
{ label: 'Schemas', slug: 'reference/schemas' }, { label: 'Schemas', slug: 'reference/schemas' },
{ label: 'Schema-Properties', slug: 'reference/schema-properties' },
{ label: 'System-Routen', slug: 'reference/rest-system' },
{ label: 'Hook-Kontexte', slug: 'reference/hook-kontexte' }, { label: 'Hook-Kontexte', slug: 'reference/hook-kontexte' },
{ label: 'Hook-Referenz: Daten', slug: 'reference/hooks-daten' }, { label: 'Hook-Referenz: Daten', slug: 'reference/hooks-daten' },
{ label: 'Hook-Referenz: Netze und Nachrichten', slug: 'reference/hooks-netze' }, { label: 'Hook-Referenz: Netze und Nachrichten', slug: 'reference/hooks-netze' },
+1 -1
View File
@@ -9,8 +9,8 @@
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"astro": "astro", "astro": "astro",
"sync": "node scripts/sync-server-docs.mjs",
"sync:openapi": "node scripts/sync-openapi.mjs", "sync:openapi": "node scripts/sync-openapi.mjs",
"gen:schemas": "node scripts/gen-schema-matrix.mjs",
"coverage": "node scripts/coverage-inventory.mjs" "coverage": "node scripts/coverage-inventory.mjs"
}, },
"dependencies": { "dependencies": {
+151
View File
@@ -0,0 +1,151 @@
{
"generatedAt": "2026-10-17T01:48:54.973Z",
"stats": {
"files": 70,
"byDir": {
"admin": 10,
"adr": 4,
"anwender": 16,
"einstieg": 4,
"index.mdx": 1,
"projekt": 17,
"reference": 13,
"server": 5
},
"components": {
"Aside": 111,
"Badge": 30,
"Steps": 92,
"TabItem": 64,
"Tabs": 28,
"Card": 11,
"CardGrid": 7,
"FileTree": 3,
"LinkCard": 19
},
"lacksDescription": [],
"emptyFenceLang": 0,
"proseWalls": [],
"asciiUmlauts": [
"src/content/docs/admin/aktionen.md",
"src/content/docs/anwender/aktionen.md",
"src/content/docs/projekt/felder-widgets.md",
"src/content/docs/projekt/mcp-nutzen.md",
"src/content/docs/projekt/navigation-i18n.md",
"src/content/docs/projekt/ssr-website.md",
"src/content/docs/reference/schema-properties.md"
],
"hollowMarkers": [
"src/content/docs/admin/exporte.md:37 Platzhalter",
"src/content/docs/anwender/seiten-bauen.md:45 Platzhalter",
"src/content/docs/einstieg/neues-projekt.md:26 Platzhalter",
"src/content/docs/einstieg/schnellstart.md:48 noch nicht",
"src/content/docs/einstieg/schnellstart.md:21 Platzhalter",
"src/content/docs/einstieg/was-ist-tibi.md:11 Platzhalter",
"src/content/docs/projekt/deploy.md:26 Platzhalter",
"src/content/docs/reference/openapi.md:6 Platzhalter"
],
"t1Refs": [
"src/content/docs/admin/konzept.md",
"src/content/docs/admin/setup.md",
"src/content/docs/adr/0003-selbstlaeufer.md"
],
"tibiAdminRefs": [
"src/content/docs/admin/aktionen.md (2×)",
"src/content/docs/admin/ansichten-listen.md (2×)",
"src/content/docs/admin/benutzer-rechte.md (2×)",
"src/content/docs/admin/customizing.md (2×)",
"src/content/docs/admin/eintrags-editor.md (2×)",
"src/content/docs/admin/exporte.md (2×)",
"src/content/docs/admin/ki-funktionen.md (2×)",
"src/content/docs/admin/konzept.md (3×)",
"src/content/docs/admin/medien.md (2×)",
"src/content/docs/admin/setup.md (4×)",
"src/content/docs/adr/0001-starlight.md (1×)",
"src/content/docs/adr/0002-code-schlaegt-doku.md (2×)",
"src/content/docs/adr/0003-selbstlaeufer.md (1×)",
"src/content/docs/anwender/anmelden.md (5×)",
"src/content/docs/anwender/benutzer-teams.md (1×)",
"src/content/docs/anwender/exportieren.md (3×)",
"src/content/docs/anwender/ki-nutzen.md (1×)",
"src/content/docs/anwender/medien.md (1×)",
"src/content/docs/anwender/orientierung.md (2×)",
"src/content/docs/anwender/seiten-bauen.md (1×)",
"src/content/docs/anwender/sichern.md (1×)",
"src/content/docs/einstieg/begriffe.md (4×)",
"src/content/docs/einstieg/neues-projekt.md (1×)",
"src/content/docs/einstieg/schnellstart.md (1×)",
"src/content/docs/einstieg/was-ist-tibi.md (3×)",
"src/content/docs/projekt/audit-backup.md (2×)",
"src/content/docs/projekt/jobs-actions.md (1×)",
"src/content/docs/projekt/navigation-i18n.md (1×)",
"src/content/docs/projekt/pagebuilder-dev.md (1×)",
"src/content/docs/projekt/sammlungen-anlegen.md (1×)",
"src/content/docs/projekt/suche.md (2×)",
"src/content/docs/reference/tastatur-kurzbefehle.md (2×)"
],
"fm_hero": 1,
"fm_template": 1
},
"sidebar": {
"count": 69,
"notInSidebar": [],
"sidebarMissing": []
},
"findings": [
{
"file": "src/content/docs/admin/exporte.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": Stehen im Export-Dialog eigene Reiter **vor** dem Standard-Tab, hat das Projekt fertige Berichte in `collectio",
"line": 37
},
{
"file": "src/content/docs/anwender/seiten-bauen.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": Die kleine Vorschau benötigt nach der Bildauswahl einen Moment. Ein Platzhalterbild ist in dieser Phase normal",
"line": 45
},
{
"file": "src/content/docs/einstieg/neues-projekt.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": ## 2. Platzhalter und Starter-Reste",
"line": 26
},
{
"file": "src/content/docs/einstieg/schnellstart.md",
"kind": "hollow",
"msg": "Marker \"noch nicht\": - `project not found` → Projektdateien liegen da, aber das Projekt ist im laufenden tibi-server noch nicht reg",
"line": 48
},
{
"file": "src/content/docs/einstieg/schnellstart.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": ## 2. Platzhalter ersetzen",
"line": 21
},
{
"file": "src/content/docs/einstieg/was-ist-tibi.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": - **Projekt-Website (aus `tibi-svelte-starter` geklont):** eigenständige Website auf Tibi-Basis — normale klei",
"line": 11
},
{
"file": "src/content/docs/projekt/deploy.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": Im Starter stehen Platzhalter (`__PROJECT_NAME__`, `_CUSTOMER_ID_`, `LIVE_URL=https://www`).",
"line": 26
},
{
"file": "src/content/docs/reference/openapi.md",
"kind": "hollow",
"msg": "Marker \"Platzhalter\": > Stand: Platzhalter · Quelle: `GET /api/v1/_openapi/:project` — nicht von Hand editieren. Erzeugen mit `npm r",
"line": 6
}
],
"summary": {
"total": 8,
"byKind": {
"hollow": 8
}
}
}
+200
View File
@@ -0,0 +1,200 @@
# Audit: tibi-server (Code) vs. tibi-docs (Doku)
Stand: 2026-10-16 · Code-Commit lt. Doku `68b42785` (nicht per git verifiziert — kein git-Check in diesem Lauf).
Methode: `internal/`-Inventar + `internal/controller/setup.go` als Routen-Quelle, Hook-`RegisterPackageFunctions`-Blöcke,
MCP-`tools_*.go`, `internal/config/server.go` + `embedding_group.go`; Doku: `src/content/docs/{server,projekt,reference}/*.md`.
Jeder Code-Beleg mit `datei:zeile`. „nicht verifiziert" = im Code nicht prüfbar gefunden.
---
## 1) Feature-Matrix
Legende: **ok** = abgedeckt · **thin** = erwähnt, aber unvollständig/oberflächlich · **gap** = fehlt.
### 1.1 Module `internal/*`
| Modul | Code-Beleg | Dokuseite(n) | Verdikt | Was fehlt |
|---|---|---|---|---|
| `cache` (projektweiter Hook-Cache, LRU, Default `MaxEntries 10000`, `DefaultTTL 0`) | `internal/cache/cache.go:23-33` | `reference/hooks-daten.md` (cache.*), `server/konfiguration.md` (`hook.cache.*`) | thin | Defaults (10000/0) und LRU/Eviction nirgends genannt |
| `config` (Datei + Env, `Server`-Struct) | `internal/config/server.go:229-242` | `server/konfiguration.md` | thin | siehe Config-Tabelle 1.5 |
| `dbapi` (CRUD-Handler, Login, Meta) | `internal/dbapi/register.go:62-135` | (keine eigene Seite) | gap | System-CRUD-Routen (`user/project/org/team/token`), `_meta`-Routen nirgends als Referenz (nur Neben­erwähnung in `projekt/mcp-nutzen.md:408`) |
| `dbcrud` (Index-Typen inkl. TTL) | `internal/dbcrud/index.go:5-37` | — | gap | nur indirekt (Audit-TTL). Kein Dokuseiten-Eintrag nötig, aber `expireAfterSeconds`-Semantik fehlt bei der „`"0"` = ewig"-Behauptung (s. 3.5) |
| `embedding` (Registry, Gruppen, Provider-Typen) | `internal/embedding/registry.go` (Existenz), `internal/config/embedding_group.go:150+` | `server/konfiguration.md:98-107`, `projekt/suche.md:37-42`, `server/betrieb.md:57` | thin | Policies (`prefer-in-order/fastest-wins/round-robin/lowest-latency/weighted`, `embedding_group.go:44-60`), Distributions, Health-/Race-/Retry-/Bulk-Tuning (`embedding_group.go:102-190`+ Env `server.go:489-625`) fehlen komplett |
| `filecleanup` ( orphan-Dateien, `SafeRemoveAll`) | `internal/filecleanup/cleanup.go:21-149` | — | gap | keine Dokuseite; nur Starter-Upload-Seite (`projekt/dateien-bilder.md`) |
| `hook` (23 Pakete + `request`-Fn + Legacy-Aliase) | `internal/hook/context_*.go`, `internal/hook/compatibility.go:8-35` | `reference/hook-kontexte.md`, `reference/hooks-{daten,netze,sicherheit,medien,meta}.md` | ok | ein Fehler: `crypto` „(plus weitere)" (s. 3.9); Legacy-Aliase (`mail`, `template`, `file`, `fetch`, `dump`, `header`, `auth`, …) bewusst undokumentiert — ok |
| `image` (FilterParams: width/height/fit/quality/…) | `internal/hook/context_image.go:75-235` | `reference/hooks-medien.md:28-46`, `projekt/dateien-bilder.md:80` | ok | — |
| `llm` (nur Chat + Budgets; **kein** Summarizer/STT/TTS) | `internal/llm/{provider,registry,quota,openai,models}.go`, `internal/controller/llm_chat.go` | `projekt/llm-nutzen.md` | ok | — (Doku erfindet nichts; Preise explizit als „steht nirgends" markiert) |
| `mcp` (36 Tools, Guards, Dispatch) | `internal/mcp/tools_{crud,discovery,system}.go`, `internal/mcp/guards.go:22-51`, `internal/controller/mcp_handler.go:43-71` | `projekt/mcp-nutzen.md`, `server/schnittstellen.md:72-88` | ok | Zählung stimmt (10+12+14=36, s. 1.3) |
| `mock`, `testsuite`, `mgocrud.old` | Verzeichnisse vorhanden | — | ok | intern/Test-Helfer bzw. Altlast — keine Doku nötig; `mgocrud.old` ist toter Code |
| `models` (Project/Collection/Hook/Job/Action/Token/Org/Team/Audit/…) | diverse, s. unten | verteilt | thin | Feld-Level-Permissions (s. 2.3), Scope-Katalog (s. 2.4), Snapshot-Detailfelder (s. 2.5) |
| `mongocrud` | Verzeichnis vorhanden | — | ok | Treiber-Detail, keine Doku nötig |
| `rankfusion` (RRF + `score_rrf/score_sum/rank_score/hybrid`, `minmax/zscore`) | `internal/rankfusion/*.go`, `internal/controller/collection_combined_search.go:142-230` | `projekt/suche.md:65-75` | thin | Formel ok; Strategien/Normierung/`scoreWeight`/`sourceConfig` nur als „existiert" erwähnt, nicht erklärt |
| `ratelimit` (Login-Backoff + Hook-Windows) | `internal/ratelimit/*`, `internal/controller/ratelimit_middleware.go:18-57`, `internal/hook/context_ratelimit.go` | `server/sicherheit.md:46-57`, `reference/hooks-netze.md:101-190` | ok | — |
| `realtime` (In-Memory-Hub, `Subscribe/Send`, Ringpuffer) | `internal/realtime/hub.go:94-179`, `internal/models/api.go:51-523` (Hub je Projekt-API) | `server/schnittstellen.md:64-70` | thin | Kapazität „Standard 256", `LastN/MaxAge/TTL`, Drop-Policy („Alt oder Neu") im Code nicht am Hub verifiziert — nur via Hook-Kontext (`context_channel.go`) plausibel; kein Abo-Endpunkt — korrekt dokumentiert |
| `suexec` (`RunInFilesUID`, kein Schalter) | `internal/suexec/thread.go:1-50`, Nutzung `internal/models/api.go:88-490`, `internal/filecleanup/cleanup.go:53` | `server/sicherheit.md:59-67`, `projekt/jobs-actions.md:49` | ok | — |
| `utils` (`ParseSize`, `ErrorObject` …) | diverse | — | ok | intern, keine Doku nötig |
| `vectorindex` (In-Memory-Vektorindex) | `internal/vectorindex/index.go` | `projekt/suche.md` (nur als „Vektor") | thin | kein eigener Doku-Eintrag (akzeptabel, da über `search`-Blöcke bedient) |
| `version` (`GET /version`) | `internal/controller/setup.go:703-707`, `internal/version/*` | `server/schnittstellen.md:26`, `server/betrieb.md:35` | ok | — |
### 1.2 HTTP-Routen (Quelle: `internal/controller/setup.go`)
Gesamt unter `/api/v1`: **48** (`27 GET + 14 POST + 2 PUT + 4 DELETE + 1 OPTIONS`) + `/metrics` (Root) = **49**.
Dazu dbapi-Systemrouten: 5 Modelle (`user, project, org, team, token` je GET-Liste/GET-:id/POST/PUT-:id/DELETE-:id = 25, `register.go:90-94`),
`_meta` + `_meta/<collection>` (`register.go:100-114`), Login ×2 (in den 14 POST enthalten).
| Methode + Pfad | Handler (setup.go) | Dokuseite(n) | Verdikt |
|---|---|---|---|
| `GET /metrics` (Root, kein `/api/v1`) | `setup.go:159` | `server/betrieb.md:32-42` | ok |
| `GET /version` | `setup.go:703` | `server/schnittstellen.md:26`, `server/betrieb.md:35` | ok |
| `POST /login`, `POST /login/impersonate` | `setup.go:728-740` (+ `models/impersonate.go:29-101`) | `server/schnittstellen.md:28`, `reference/token-koepfe.md:45` | thin | Logout-Flow (`?logout=1`), Impersonation-Chains (`impersonate.go:86-101`), Org-Scope-Regel (`impersonate.go:214-233`) fehlen |
| `GET /token/_scopes` | `setup.go:303-311` | `server/schnittstellen.md:28` | thin | Katalog-Inhalt (`token_scope_catalog.go:47-90`: System- vs. Namespace-Scopes, UND-Regel) nicht erklärt |
| `POST /llm/chat`, `GET /llm/providers` | `setup.go:321-340` | `projekt/llm-nutzen.md:12-57` | ok |
| `GET /audit` | `setup.go:743-751` | `server/schnittstellen.md:31`, `projekt/audit-backup.md:18-46` | ok |
| `GET+POST /mcp` | `setup.go:754-755`, `mcp_handler.go:43-71` | `server/schnittstellen.md:72-88`, `projekt/mcp-nutzen.md` | ok |
| `GET /admin/embedding/status`, `POST /admin/shutdown` | `setup.go:769-798` | `server/schnittstellen.md:31` | ok |
| `GET /_/:project/:collection`, `POST /_/:project/:collection` (+ `X-HTTP-Method-Override: GET`) | `setup.go:506-636` | `projekt/abfragen-lesen.md`, `server/schnittstellen.md:38-46` | ok |
| `GET /_/:project/:collection/_count`, `/_facet`, `/_queue` | `setup.go:514-535` | `projekt/abfragen-lesen.md:95-110` | ok |
| `GET+PUT+DELETE /_/:project/:collection/:id` | `setup.go:536-665` | dto. | ok |
| `GET /_/:project/:collection/:id/:field`, `…/:field/*path` | `setup.go:545-568` | `server/schnittstellen.md:42`, `projekt/dateien-bilder.md` | thin | Feld-Route ok; Datei-Route nur via MCP-`file_read`-Hinweis |
| `PUT+DELETE /_/:project/:collection` (Bulk) | `setup.go:645-673` | `server/schnittstellen.md:41` | thin | Bulk-Semantik (`Allow`+`Bulk`-Flag, `permission.go:145-182`) nicht erklärt; `collection_bulk_post.go` existiert, hat aber **keine** eigene Route (Aufrufweg nicht verifiziert) |
| `GET+POST /_/:project/_actions/:action` | `setup.go:676-690` | `projekt/jobs-actions.md:54-119` | ok |
| `GET /_openapi/:project` | `setup.go:692-701` | `server/schnittstellen.md:90-92` | thin | `reference/openapi.md` ist Platzhalter mit falscher Quelle (s. 3.3) |
| `GET /_/:project/_/assets/:name/*path` | `setup.go:497-504` | `server/schnittstellen.md:44`, `projekt/mcp-nutzen.md:81` | ok |
| `POST …/_/admin/reload` | `setup.go:372-376` | `server/betrieb.md:54`, `projekt/deploy.md:84-93` | ok |
| `POST …/_/admin/regenerate-search`, `GET …/search-worker/status`, `GET …/embedding/status` | `setup.go:378-394` | `server/betrieb.md:54-57`, `projekt/suche.md:87-90` | ok |
| Backup: `GET analyze-download`, `GET` Download, `GET download/:file`, `POST` Analyse, `POST :restoreToken`, `DELETE :restoreToken` | `setup.go:398-433`, `admin_backup*.go` | `projekt/audit-backup.md:110-133`, `server/betrieb.md:66` | thin | Restore-Token-Flow (Analyse → Token → Restore/Cleanup) nur angerissen; `checkProjectAdminAuth`-Reihenfolge (Server-Token → Projekt-Token → JWT-Admin, `admin_auth.go:20-74`) fehlt |
| Snapshots: `GET configs`, `POST configs/:name/trigger`, `GET` Liste, `GET download/:file`, `GET :id`, `DELETE :id`, `POST :id/restore`, `GET :id/download`, `GET stats`, `POST prune` (10 Routen) | `setup.go:436-495` | `projekt/audit-backup.md:60-108` (`…/snapshots…`-Sammelzeile in schnittstellen.md:57) | thin | Einzelne Routen (stats/prune/trigger/download) nicht benannt; `collections: []` = alle ok (`snapshot_config.go:144`); `mode: include/exclude`, `collectionDedup`, `permissions`-Feld (`snapshot_config.go:34-53`), `retention.maxAge/maxSizeBytes` (`snapshot_config.go:89-93`) fehlen |
| `OPTIONS /*path` (404 ohne echte Route) | `setup.go:226-230` | `server/sicherheit.md:44`, `server/schnittstellen.md` (implizit) | ok |
| System-CRUD `user/project/org/team/token` (je 5 Routen) | `dbapi/register.go:90-94` via `RegisterModel` (`setup.go:274-312`) | `reference/token-koepfe.md:31-42` (Projekt-Anlage), MCP-`user_*`-Zeilen | gap | keine REST-Referenz für User-/Org-/Team-/Token-CRUD; `token`-Collection-Felder (`scopes`, `expiresAt`, `token.go:39-76`) undokumentiert |
| `GET /_meta`, `GET /_meta/<collection>` | `dbapi/register.go:100-114` | nur Neben­erwähnung `projekt/mcp-nutzen.md:408` | gap |
**Routen-Bilanz (gruppiert): ca. 44/49 dokumentiert, 2 gaps (System-CRUD, `_meta`), Rest thin bei Detail­feldern.**
### 1.3 Hook-Kontexte (alle `context_*.go`)
Pakete (23 + `request`-Funktion): `base64, bcrypt, cache, channel, charset, config, cookie, crypto, db, debug, exec, fs, http, image, json, jwt, pdf, ratelimit, request(fn: `context_request.go:12`), response, smtp, tpl, user, xml` — **alle 23 in `reference/hook-kontexte.md:36-61` + Detail­kapiteln vorhanden.**
Funktionen je Paket — Abgleich `RegisterPackageFunctions`-Blöcke gegen Referenz:
| Paket | Code-Funktionen | Referenz | Fehlend |
|---|---|---|---|
| db (7) | find/count/create/update/delete/deleteMany/updateMany (`context_db.go:16`) | hooks-daten.md:8-112 | — |
| cache (6) | set/get/has/delete/clear/count (`context_cache.go:11`) | hooks-daten.md:114-194 | — |
| request (1) | `request()` (`context_request.go:12`) | hooks-daten.md:196-217 | — |
| response (5) | header/writeHeader/write/flush/writeSSE (`context_response.go:13`) | hooks-daten.md:219-297 | — |
| fs (8) | configDir/readFile/writeFile/stat/readDir/mkDir/remove/copy (`context_fs.go:17`) | hooks-daten.md:299-409 | — |
| http (2) | fetch/fetchStream (`context_http.go`) | hooks-netze.md:8-44 | — |
| smtp (1) | sendMail (`context_smtp.go:18`) | hooks-netze.md:46-68 | — |
| channel (2) | subscribe/send (`context_channel.go:13`) | hooks-netze.md:69-100 | — |
| ratelimit (6) | backoffCheck/backoffRecord/backoffReset/windowCheck/windowReset/windowCount (`context_ratelimit.go`) | hooks-netze.md:101-190 | — |
| jwt (2) | create/parse | hooks-sicherheit.md:8-37 | — |
| user (1) | auth → nur `{id, username, role, permissions}` (`context_user.go:26-31`) | hooks-sicherheit.md:39-55 | thin (Rückgabe­felder; keine Orgs/Teams) |
| bcrypt (2) | hash/check | hooks-sicherheit.md:56-86 | — |
| crypto (2) | **nur** sha256/md5 (`context_crypto.go:13-16`) | hooks-sicherheit.md:87-116 („plus weitere") | **1 Fehler** (s. 3.9) |
| cookie (2) | get/set | hooks-sicherheit.md:117-146 | — |
| exec (1) | command | hooks-sicherheit.md:147-163 | — |
| config (3) | namespace/project/server | hooks-sicherheit.md:164-209 | — |
| tpl (1) | execute | hooks-medien.md:8-27 | — |
| image (1) | filter | hooks-medien.md:28-46 | — |
| pdf (2) | fromHTML/cpu | hooks-medien.md:47-84 | — |
| base64 (2) | encode/decode | hooks-meta.md:8-37 | — |
| json (2) | parse/stringify | hooks-meta.md:38-67 | — |
| xml (2) | create/parse | hooks-meta.md:68-97 | — |
| charset (2) | decode/iso8859ToUtf8 | hooks-meta.md:98-127 | — |
| debug (3) | dump/sentryTraceId/sleep | hooks-meta.md:128-173 | — |
| Legacy-Aliase (26, `compatibility.go:8-35`) | readCollection, mail, template, file, fetch, dump, header, auth, … | — (bewusst) | ok |
**Hook-Bilanz: alle 23 Pakete + alle 70 Funktionen dokumentiert; 1 Fehler (`crypto`), 1 thin (`user.auth`-Felder).**
### 1.4 MCP-Tools (36 = 10 + 12 + 14)
- crud (`tools_crud.go:8-104`): documents_list, document_get, documents_count, documents_facet, queue_status, document_create, document_update, document_delete, file_read, action_call — alle 10 in `projekt/mcp-nutzen.md:63-72` ✓
- discovery (`tools_discovery.go:8-103`): projects_list, project_get, collections_list, collection_describe, actions_list, action_describe, jobs_list, openapi_get, system_meta_get, agent_guide_get, assets_list, asset_read — alle 12 in `mcp-nutzen.md:73-81` ✓
- system (`tools_system.go:8-34`): users_list, user_get, user_create, user_update, user_delete, orgs_list, org_get, teams_list, team_get, audit_query, project_reload, regenerate_search, backup_create, snapshots_list — alle 14 in `mcp-nutzen.md:82-90` ✓ (REST-only-Badges entsprechen den `REST-only`-Descriptions im Code)
- Guards korrekt dokumentiert: PAT-only (`guards.go:22-51`), Admin-Token-Reject (`mcp_handler.go:51-56`), disabled → 404 (`mcp_handler.go:45-49`), GET-Probe (`mcp_handler.go:63-71`), Fehler­codes -32601/-32602/-32003/-32004 (`mcp_handler.go:107-127`) ✓
### 1.5 Config (`config.yml` + Env, Quelle `internal/config/server.go`)
| Bereich | Code (yaml-Tag) | Doku | Verdikt |
|---|---|---|---|
| `db` (dial/prefix/logging) | `server.go:22-24` + Env `DB_DIAL(/MGO_DIAL)`, `DB_PREFIX`, `DB_LOGGING` (`server.go:276-286`) | konfiguration.md:28 | ok |
| `api` (port, jwtSecret, secureCookies, Lifetimes, upload, adminTokens) | `server.go:45-56` + Env `server.go:290-318` | konfiguration.md:29,79-83,109-124 | thin | `upload.allowedMimeTypes` (`server.go:46`) fehlt überall (gap); `secureCookies`-Default true korrekt (`server.go:61-64` ✓) |
| `mail` (host/username/password) | `server.go:97-99` + nur `MAIL_HOST` (`server.go:320`) | konfiguration.md:30 | thin | `MAIL_USERNAME/PASSWORD` existieren **nicht** — Doku nennt sie implizit als Schlüssel ohne Env; ok, aber unklar |
| `security` (allowAbsolutePaths/allowUpperPaths) | `server.go:103-104` + Env (`server.go:324-328`) | konfiguration.md:31, sicherheit.md:61-67 | ok |
| `response.error` (stack/message) | `server.go:125-130` + Env (`server.go:338-341`) | konfiguration.md:33 | ok |
| `audit` (enabled/defaultTTL/defaultLimit/maxLimit) | `server.go:171-174` + Env (`server.go:345-354`) | konfiguration.md:33, betrieb.md:44-46 | ok (TTL-Default 720h ✓ `setup.go:344`) |
| `ratelimit` (enabled/loginInitialDelay/loginMaxDelay/loginResetAfter) | `server.go:201-208` + Env (`server.go:627-639`) | konfiguration.md:34, sicherheit.md:46-57 | ok (Defaults 1s/60s/15m ✓ `server.go:203-207`; 429+Retry-After ✓) |
| `hook.cache/ratelimit` | `server.go:179-188` + Env `HOOK_*` (`server.go:642-665`) | konfiguration.md:35 | thin | Feld­namen ok; Defaults/SEMANTIK (LRU, Fenster) fehlen |
| `mcp` (enabled/route/maxResultItems/allowAdminActions) | `server.go:221-224` + Env (`server.go:668-686`) | konfiguration.md:36, schnittstellen.md:72-88 | ok | Route-Aussage korrekt: Routing hart in `setup.go:754-755`, `mcp.Route()` ungenutzt (s. 3.7) |
| `sentry` (dsn/environment/serverName/enableTracing/debug) | `server.go:108-112` (kein Env) | konfiguration.md:37, betrieb.md:36 | thin | nur `dsn` erwähnt; Rest fehlt |
| `cors` | `server.go:116-121` | konfiguration.md:38, sicherheit.md:34-44 | thin | Server-`cors` als „Basis"+feldweises Merge: globaler CORS-Block in `setup.go:141-149` ist **auskommentiert** — Merge-Logik (`CORSMap`, `api.go`-Umfeld) nicht verifiziert |
| `llm`/`embedding` Provider + Gruppen | `server.go:135-167` + Env-Muster (`server.go:359-625`) | konfiguration.md:39,85-107 | **1 Fehler**: `EMBEDDING_BGE-M3_*` (s. 3.1); Gruppen-Details fehlen (s. 1.1 embedding) |
| Go-Version-Hinweis | `go.mod` (`go 1.25.0`, Toolchain `go1.25.7` — nicht verifiziert, go.mod nicht gelesen) | konfiguration.md:41-43 | nicht verifiziert |
| Datei-Fehlstart/Panic-Regeln | `server.go:260-296` (Default 8080/tibi/Stack-an; Port `<= 0` → Panic) | konfiguration.md:19-20 | ok |
---
## 2) Nicht dokumentierte Fähigkeiten (keine Dokustelle in tibi-docs)
1. **System-CRUD per REST** (`user/project/org/team/token`, je 5 Routen, `dbapi/register.go:90-94`): nur JWT-Projekt­anlage in `reference/token-koepfe.md:31-42` angerissen; Org-/Team-/Token-CRUD, `token`-Felder (`scopes`, `expiresAt`, `token.go:39-76`) fehlen.
2. **`_meta`-Routen** (`dbapi/register.go:100-114`): nur Neben­erwähnung (`projekt/mcp-nutzen.md:408`).
3. **Feld-Level-Permissions** (`models/field_permissions.go`; Status­codes `http_status.go:53-58`): **null Treffer** in tibi-docs. Server-Doc `docs/17-field-level-permissions.md` existiert, wurde nicht übernommen.
4. **Scope-Katalog** (`models/token_scope_catalog.go:47-90`: `systemScopesFor`, UND-Regel Token×Besitzer): nur Beispiel in `server/sicherheit.md:23-28`; Katalog­logik fehlt.
5. **`upload.allowedMimeTypes`** (`config/server.go:46`): null Treffer.
6. **Snapshot-Detailfelder**: `mode: include/exclude`, `collectionDedup` (`snapshot_config.go:34-42`), `permissions` (`snapshot_config.go:53`, „Phase 2"), `retention.maxAge/maxSizeBytes` (`snapshot_config.go:89-93`), Einzel­routen (stats/prune/trigger/:id/download).
7. **Embedding-Gruppen-Tuning**: Policies/Distributions/Health/Race/Retry/Bulk (`config/embedding_group.go`, Env `server.go:489-625`); nur MEMBERS/POLICY/TIMEOUT_TOTAL erwähnt.
8. **Such­modi jenseits Starter**: `text/regex/eval/filter` (`models/collection.go:314`), `sourceConfig/scoreWeight`/Strategien im Detail, `autoRegenerate`, Search-Worker, Vector-Loader; `aggregate` fehlt in `projekt/abfragen-lesen.md` (nur MCP/SSR/Fehler­codes nennen es); Such-Debug (`controller/search_debug.go`, `search_enrichment.go`) fehlt.
9. **Ephemeral REST Tokens**: `docs/24-ephemeral-rest-tokens.md` beschreibt sie — **im Code kein Treffer** (`phemeral` nur in `docs/24*`, `tmp/diff.patch`). Entweder unverified/unge­landet oder entfernt: Doku beschreibt nicht-existente Fähigkeit.
10. **SSR als Server-Fähigkeit**: kein `ssr`-Code in `internal/` (nur Starter-Hooks + `program_async_test.go:1024` erwähnen `ssrRequestKirby`). Korrekt als Starter-Doku abgelegt, aber `server/schnittstellen.md:68` („kein fertiger Abo-Endpunkt … nur über Hooks") ist die einzige Server-Aussage — ein expliziter Satz „SSR-Bundle/Cache sind Starter-Konvention, kein Server-Feature" fehlt.
11. **Gzip-Ausnahmen** (`setup.go:151-154`: kein Gzip für `.pdf/.mp4/.jpg/.jpeg/.png` + `/metrics`): undokumentiert.
12. **NaN-Sanitizing / `_search`-Stripping** (`setup.go:814-825`): undokumentiert (intern, niedrige Priorität).
13. **Backup-Restore-Token-Lebenszyklus** (Analyse → `:restoreToken` → Restore/Cleanup, `admin_backup*.go`) und Snapshot-RAM-Begrenzung: nur angerissen.
14. **Sentry-Felder** außer `dsn`, **Hook-Cache-Defaults**, **Filecleanup**, **`bulkCreate`-Aufrufweg** (`controller/collection_bulk_post.go` ohne eigene Route — Aufruf­weg nicht verifiziert).
15. **`mgocrud.old/`**: toter Code im Repo, keine Doku nötig — aber Aufräum­kandidat.
## 3) Falsche Doku-Aussagen (12 geprüfte Behauptungen; falsche zuerst)
| # | Doku-Zitat (Datei:Zeile) | Code (Datei:Zeile) | Urteil |
|---|---|---|---|
| 1 | `EMBEDDING_BGE-M3_*` — „Bindestrich → Unterstrich + Großschrift: `bge-m3` → `EMBEDDING_BGE-M3_*`" (`server/konfiguration.md:21`, Beispiel `:98-105`) | `normalizeEnvKeyName`: `-` → `_`, dann UPPER (`config/server.go:246-250`): `bge-m3` → `BGE_M3`, also `EMBEDDING_BGE_M3_*` | **FALSCH** (Bindestrich bleibt im Doku-Beispiel stehen) |
| 2 | „braucht dein Token `AllowSnapshot: true`" (`projekt/audit-backup.md:107`; Tabelle `:127-128`) | Projekt-Admin-Token heißt `allowSnapshots` (plural, klein): `models/project.go:104`, Prüfung `t.AllowSnapshots` (`controller/admin_snapshot_list.go:23-40`) | **FALSCH** (Singular statt Plural; korrekt: `allowSnapshots`) |
| 3 | `reference/openapi.md` komplett: Quelle `TIBI_API_URL/openapi.json`, „Erzeugen mit `npm run sync:openapi`" | Route ist `GET /api/v1/_openapi/:project` (`controller/setup.go:692-701`, `openapi_project_get.go:12`); kein `openapi.json`, kein `sync:openapi` im Server-Code gefunden | **FALSCH** (Platzhalter, passt nicht zum Server-Generator; `server/schnittstellen.md:90-92` ist dagegen korrekt) |
| 4 | `crypto.sha256`, `crypto.md5` „(plus weitere)" (`reference/hook-kontexte.md:52`) | Nur 2 Funktionen registriert (`hook/context_crypto.go:13-16`) | **FALSCH** („weitere" existieren nicht) |
| 5 | `audit.defaultTTL: 720h` „(30 Tage, `"0"` = ewig)" (`projekt/audit-backup.md:29`) | Default 720h ✓ (`controller/setup.go:344`); **kein** `"0"`-Sonderfall gefunden (`models/auditlog.go:498-508`, `dbcrud/index.go:12-14` geben `ttl` direkt an den Index) | **UNKLAR, eher falsch** — „ewig" nicht belegt; `expireAfterSeconds: 0` liefe auf sofortigen Verfall hinaus. Nicht verifiziert, ob `mongocrud` 0 abfängt |
| 6 | `POST /api/ssr?clear=1` / `POST /api/_/admin/reload` (ohne `/v1`, ohne Namespace) (`projekt/deploy.md:90-91`) | Server-Routen sind `/api/v1/_/:project/_/admin/reload` (`setup.go:372`) und haben **keine** `ssr`-Route (kein `ssr`-Code in `internal/`) | **UNKLAR** — nur als Starter-Proxy-Pfade sinnvoll; als Server-Aussage falsch. Doku markiert sie als Proxy („Live/Staging-URL"), aber der Kontrast zur Server-Tabelle fehlt |
| 7 | „ein gesetztes `mcp.route`/`MCP_ROUTE` wird ignoriert (Routing hart in `setup.go`)" (`server/schnittstellen.md:77`, `projekt/mcp-nutzen.md:10`) | Routing fix `apiGroup.GET/POST("/mcp")` (`setup.go:754-755`); `mcp.Route()`-Helper existiert (`mcp/server.go:27-32`), wird aber im Routing nirgends verwendet (nur `MaxResultItems()` in `mcp_tools_impl.go:701-707` genutzt) | **KORREKT** |
| 8 | „`Content-Range:` **mit Doppelpunkt** — Tippfehler im Code" (`projekt/abfragen-lesen.md:34`) | `c.Header("Content-Range:", …)` (`controller/collection_get.go:32`) | **KORREKT** |
| 9 | „429 plus `Retry-After`-Header (Sekunden) … nur für Login" (`server/sicherheit.md:52-57`) | `Retry-After` + 429 (`controller/ratelimit_middleware.go:56-57`); Login-Geltung via `loginMiddleware` nur an `/login` (`setup.go:712-732`) | **KORREKT** |
| 10 | „`model` ist optional … Allowlist leer = keine Prüfung" + 403-vor-400-Reihenfolge (`projekt/llm-nutzen.md:31-57,71-86`) | Auth+Scope vor Validierung (`controller/llm_chat.go:28-57`); Allowlist-Gate, Default-Redirect, Secrets-Ausschluss (`llm_chat.go`, `setup.go:322-340`) | **KORREKT** |
| 11 | Legacy-Default: Konflikt 500/409, Validierung 400/422, Schalter `legacy.httpCodes` (`reference/fehlercodes.md:20-21,43-45`) | `http_status.go:13-58` (201/200, 204/200, 401/403, 422/400, 409/500, 403/400); Default `true` (`models/project.go:80-87`); Key `httpCodes` (`project.go:77`) | **KORREKT** |
| 12 | „CORS-Kaskade `/_actions/<name>` → `/<collection>` → `/` → `""`" + „Projekt mergt feldweise (`merge: true/false`)" (`server/sicherheit.md:36-44`) | Kaskaden-Reihenfolge exakt so (`setup.go:193-223`); `CORSConfig.Merge` existiert (`models/project.go:107-114`); globaler Server-CORS ist auskommentiert (`setup.go:141-149`) | **KORREKT** (Merge-Semantik im Detail nicht verifiziert) |
Weitere verifizierte Korrekt-Aussagen (Stichprobe): Port-Panic `<= 0` ✓ (`server.go:295-296`), Not-Defaults 8080/tibi/Stack ✓ (`server.go:265-268`), `secureCookies`-Default true ✓ (`server.go:61-64`), Lifetimes 60m/10h ✓ (`server.go:69-90`), Audit-TTL-Default 720h ✓, MCP-404-bei-disabled ✓ (`mcp_handler.go:45-49`), `X-Auth-Token`-PAT-Prefix `tibi_pat_` ✓ (`mcp/guards.go:51`), Bulk-`Allow`+`Bulk` ✓ (`models/permission.go:145-182`), `snapshotPath`-Default „snapshots" ✓ (`models/project.go:65-67`), `collections: []` = alle ✓ (`snapshot_config.go:144`), Admin-Auth-Reihenfolge ✓ (`admin_auth.go:20-74`), 100-MB-Body-Default ✓ (`middleware_bodysize.go:14-29`), Kaskade Server→Projekt→Collection mit Server-Cap ✓ (`middleware_bodysize.go:34-69`).
## 4) Veraltete Doku in `tibi-server/docs/*.md` (je Datei eine Zeile)
| Datei | Inhalt (Kurz) | Abdeckung in tibi-docs |
|---|---|---|
| 01-architecture.md | Architektur-Überblick | teilweise (keine Architektur-Seite; verstreut in `server/uebersicht.md`) |
| 02-configuration.md | Vollständige Config-Referenz | teilweise (`server/konfiguration.md`; Gruppen-Tuning/Env-Details fehlen) |
| 03-api-endpoints.md (2406 Zeilen) | Alle Endpunkte | teilweise (`server/schnittstellen.md` + Projekt­seiten; System-CRUD/`_meta` fehlen) |
| 04-collections.md | Collection-Schema/Operatoren | teilweise (`projekt/sammlungen-anlegen.md`, `abfragen-lesen.md`; aggregate/eval fehlen) |
| 05-authentication.md | Login/JWT/Impersonate | teilweise (Login/Impersonate nur Tabellen­zeilen; Logout-Flow fehlt) |
| 06-hooks.md (1717 Zeilen) | Hook-Lifecycle + Kontexte | vollständig (Lifecycle via tibi-types-Schema nicht verifiziert; Kontexte alle da) |
| 07-realtime.md | Realtime-Hub | teilweise (`server/schnittstellen.md:64-70` nur Überblick) |
| 08-file-upload-images.md | Upload/Bilder | teilweise (Starter-Sicht in `projekt/dateien-bilder.md`; `allowedMimeTypes`/Filecleanup fehlen) |
| 09-llm-integration.md | LLM/Budgets | vollständig (`projekt/llm-nutzen.md`, inkl. Verifizierungs­zeile) |
| 10-audit.md | Audit-Log | teilweise (zweistufig + TTL ok; `"0"`-Claim fraglich, s. 3.5) |
| 11-jobs.md | Jobs/Cron | vollständig (`projekt/jobs-actions.md`) |
| 12-deployment.md | Deployment/Profile | teilweise (`server/betrieb.md`, `projekt/deploy.md`; Compose-Profile Starter-seitig) |
| 13-openapi-metrics.md | OpenAPI + Metriken | teilweise (OpenAPI-Generator ok; Metrik-Labels/Referenz-`openapi.md` defekt, s. 3.3) |
| 14-security.md | Tokens/CORS/Ratelimit/Pfade | vollständig (`server/sicherheit.md` + `konfiguration.md`) |
| 15-troubleshooting.md | Fehlerbilder | teilweise (verteilt auf Fehler­tabellen; keine eigene Seite) |
| 16-cookbook.md | Rezepte | teilweise (Rezepte in Projekt­seiten verstreut) |
| 17-field-level-permissions.md | Feld-Rechte | **nicht abgedeckt** (null Treffer, s. 2.3) |
| 18-orgs-teams.md | Orgs/Teams | teilweise (nur Überblick in `projekt/rechte-tokens.md:125-142`) |
| 19-actions.md | Actions | vollständig (`projekt/jobs-actions.md:54-119`) |
| 20-referenced-read-safety.md | Lookup-Read-Safety | teilweise (`projekt/abfragen-lesen.md:89-93`, `rechte-tokens.md`) |
| 21-backup-restore.md | Backup/Restore | teilweise (Flows ok; Token-Lebens­zyklus fehlt) |
| 22-personal-access-tokens.md | PATs | teilweise (Köpfe/Scopes ok; `token`-CRUD-Felder fehlen) |
| 23-mcp.md | MCP | vollständig (`projekt/mcp-nutzen.md`, 36/36 Tools) |
| 24-ephemeral-rest-tokens.md | Ephemere REST-Tokens | **nicht abgedeckt — und im Code nicht existent** (s. 2.9); keine Übernahme, sondern Klärung nötig (implementieren oder Doku löschen) |
+128
View File
@@ -0,0 +1,128 @@
# Audit 02 — Entwickler- und Anwender-Schnittstellen von tibi-admin-nova
> Audit-Agent, keine Doku-Änderung. Stand: 2026-10-16.
> Repo: `/WM_Dev/src/gitbase.de/cms/tibi-admin-nova` (= `NOVA:` unten).
> Doku: `/WM_Dev/src/gitbase.de/cms/tibi-docs` (= `DOKU:` unten).
> Verwendete Schemata: `NOVA:tibi-types/schemas/config/*.json`, `NOVA:types/admin.d.ts`,
> `NOVA:schemas/config/*.schema.json`. Frontend: `NOVA:frontend/src/`.
## 1) Entwickler-Schnittstellen
Legende Verdikt: `ok` = dokumentiert und korrekt · `thin` = erwähnt, aber unvollständig/falsch verlinkt ·
`gap` = fehlt in tibi-docs **und** in `NOVA:docs/`.
| Schnittstelle | Code-Beleg | Beschreibung | Dokuseite | Verdikt |
|---|---|---|---|---|
| Collection-Schema (`name`, `permissions` Pflicht; `fields`, `indexes`, `hooks`, `projections`, `audit`, `bulk`, `cors`, `queryLimits`, `readonlyFields`, `hiddenFields`, `strictFields`, `search`) | `NOVA:tibi-types/schemas/config/collection.schema.json:1-40` (props; `required: [name, permissions]`) | Top-Level-Sammlungsdefinition | `DOKU:projekt/sammlungen-anlegen.md` (Minimal, Meta, Indizes/Suche); Referenz `DOKU:reference/schemas.md` | ok (thin für `queryLimits`, `bulk`, `cors`, `strictFields` — nur in `NOVA:docs/collection-config.md` belegt) |
| `meta.label/group/muiIcon/hide/imageUrl/designGuide` | `NOVA:tibi-types/schemas/config/collection-meta.schema.json` (props); `NOVA:types/admin.d.ts:734ff` | Anzeige-/Gruppierungs-Meta | `DOKU:projekt/sammlungen-anlegen.md:79ff` (Tabelle Meta) | ok |
| `meta.preview` (label/secondary/tertiary/image, table-Spalten, Slots, Eval, FK-Auflösung) | `NOVA:tibi-types/schemas/config/defs.schema.json` (`previewConfig`, `previewSlots`); `NOVA:frontend/src/lib/components/entry/PreviewElement.svelte:1ff`; `NOVA:frontend/src/lib/components/ui/FieldValuePreview.svelte:1ff` | Vorschau-Auflösung inkl. FK-Badges, `_lookup`, Dot-Pfade | `DOKU:admin/customizing.md:35ff` (Tabelle, Verweis `preview-resolver`); `DOKU:projekt/sammlungen-anlegen.md:79ff` | thin (Resolver-Regeln — Auto-Detection vs. strikt, Nested-Pfade `profile.name`, FK-Badge-Auflösung — nur `NOVA:docs/test-plans/39-preview-resolver.md:1ff`) |
| `meta.viewHint` (`table`/`cards`/`{media}`/`{navigation}`) | `NOVA:types/admin.d.ts:460-470`; `NOVA:frontend/src/lib/components/CollectionView.svelte:575-607` (Default-Anwendung, URL gewinnt) | Startansicht der Liste | `DOKU:admin/ansichten-listen.md:10ff`; `DOKU:projekt/sammlungen-anlegen.md:79ff` | ok |
| `meta.views` (custom views, `AdminCollectionView`) | `NOVA:types/admin.d.ts:452-458` („custom views not yet supported“); Schema `collection-meta.schema.json` enthält **kein** `views` (`additionalProperties: false`) | Eigene Listenansichten — **nicht implementiert / schema-invalid** | `DOKU:admin/customizing.md:14` behauptet fälschlich „`meta.views` (Tabelle, Karten, Kalender …)“ | gap (**Doku-Fehler**: Behauptung ohne Code-Deckung) |
| `meta.subNavigation` (filter, defaultSort, setDefault) | `NOVA:tibi-types/schemas/config/collection-meta.schema.json` (`subNavigation`); `NOVA:types/admin.d.ts:364ff` | Reiter-Filter über der Liste | `DOKU:admin/ansichten-listen.md:29ff` | ok |
| `meta.defaultSort` (field, order inkl. `MANUALLY`) | Schema `collection-meta.schema.json` (`defaultSort`); `NOVA:types/admin.d.ts:426ff` | Standard-Sortierung, inkl. manuell per Drag | `DOKU:admin/ansichten-listen.md` (Sortierung); Drag in `DOKU:admin/customizing.md:10ff` | thin (`MANUALLY`/Drag-Freigabe nicht als Config dokumentiert) |
| `meta.singleton` (enabled, key) | Schema `collection-meta.schema.json` (`singleton`) | Ein-Eintrag-Sammlung | — (grep `singleton` in `DOKU:src/content/docs` ohne Treffer) | gap |
| `meta.sections` / `meta.sidebar` (Formular-Gruppen) | Schema (`sections`, `sidebar`); Feld-Seite `meta.position: main\|sidebar:<gruppe>` | Formular-Layout | `DOKU:projekt/sammlungen-anlegen.md:79ff` (`sidebar`-Zeile, `position`-Beispiel); `sections` ohne Treffer | thin (`sections`-Array und `collapsible/collapsed` fehlen) |
| `meta.i18n` + `i18n.entry` (languages, languageField/groupField/clearFields/copyFields) | Schema (`i18n`); `NOVA:frontend/src/lib/stores/form-i18n-support.svelte.ts:1ff`; `I18nLanguageTabs`, `TranslationBar` | Übersetzungs-Kopplung | `DOKU:admin/eintrags-editor.md:58ff`; `DOKU:projekt/sammlungen-anlegen.md:79ff` | thin (`clearFields`/`copyFields`-Semantik nur Code) |
| `meta.pagebuilder` (defaultViewport, blockTypeField, blockRegistry.file, screenshot) | Schema (`pagebuilder`); `NOVA:frontend/src/lib/components/entry/widgets/pagebuilder/load-registry.ts:14-38`; `NOVA:types/admin.d.ts:618-669` | Pagebuilder-Anbindung pro Sammlung/Feld | `DOKU:projekt/pagebuilder-dev.md:17ff` (Registry-Vertrag) | ok |
| `cssTransform`-Präzedenz (Block > Feld > Collection, Default `true`) | `NOVA:frontend/src/lib/components/entry/widgets/pagebuilder/load-registry.ts:41-60` | CSS-Umschreibung steuern | `DOKU:projekt/pagebuilder-dev.md:59ff` | ok |
| Block-Vertrag (`render/update/destroy`, `BlockRenderContext`, `css`, `previewStyles`, `label/icon/color`) | `NOVA:frontend/src/lib/components/entry/widgets/pagebuilder/BlockMount.ts:20-60` | Eigener Block (framework-agnostisch, Shadow-DOM) | `DOKU:projekt/pagebuilder-dev.md:154ff` | ok |
| Asset-Specifier (`_/` projekt-relativ, `/_/`-Alias, `/`-absolut, `https/data/blob` untouched, `{eval}`) | `NOVA:frontend/src/lib/utils/project-asset-specifier.ts:17-50`; genutzt von `load-registry.ts:30-38` und Custom-Exports | Pfadauflösung für Registry- + Export-Skripte | `DOKU:admin/exporte.md:35ff`; `DOKU:projekt/pagebuilder-dev.md:17ff` | thin (zentrale Regel nur im Code; `{eval}`-Variante nicht dokumentiert) |
| `meta.exports` (name+file Pflicht, filename-`{date}`) | Schema (`exports`); `NOVA:types/admin.d.ts:720ff` (file-Specifier, filename) | Skript-Exporte anmelden | `DOKU:admin/exporte.md:35ff` | ok |
| Export-Skript-API (`db.find/count/loadAll`, `xlsx`, `helpers`, `progress`, `signal`, `view`, kein `$auth`, genau 1 Payload + `contentType`) | `NOVA:frontend/src/lib/utils/custom-export.types.ts:17-100`; `NOVA:docs/custom-export-script.md:12-60`; `NOVA:frontend/src/lib/components/ui/ExportDialog.svelte` | Eigene Berichte schreiben | `DOKU:admin/exporte.md:48-72` (verkürzt; verweist für Details auf Repo-Doku) + `DOKU:admin/customizing.md:51ff` | thin (vollständiges `ctx`: `db.loadAll`-Batching 500er, `selector`-vs-`select`, nur `sort[0]`, `csvCell`-Injection-Guard, Dateinamen-Sanitizing nur in `NOVA:docs/custom-export-script.md`) — Hinweis: Beispiel `DOKU:admin/exporte.md:60` ruft `loadAll({filter})` ohne Collection auf (abweichend vom Typ `loadAll(collection, opts)`) |
| `meta.query` (typisierte Zusatz-Querykeys für Hooks, MCP-validiert) | Schema (`query`, description „hooks may read ctx.request().query“) | Projekt-eigene Query-Parameter deklarieren | nur Erwähnung in `DOKU:projekt/mcp-nutzen.md` | gap (für Projektentwickler) |
| `meta.agent` (purpose/instructions/fieldHints/workflows) | Schema (`agent`); ausgespielt via MCP/REST `_meta` | Editor-/Agenten-Doku pro Sammlung | nur Erwähnung in `DOKU:projekt/mcp-nutzen.md`, `DOKU:projekt/pagebuilder-dev.md` | gap (für Projektentwickler) |
| `meta.openapi` / `collection.openapi` | Schema (`openapi` → `openapi.schema.json`) | OpenAPI-Overrides | `DOKU:reference/openapi.md`, `DOKU:server/schnittstellen.md` | ok (Server-Seite; Admin-Nutzung nicht verifiziert) |
| `permissions` (public/user + Token-Rollen, methods, filter, validProjections, readonly-/hiddenFields) | `NOVA:tibi-types/schemas/config/permissions.schema.json:1-60` | Zugriffsrechte | `DOKU:projekt/rechte-tokens.md`; `DOKU:admin/benutzer-rechte.md:47ff` | ok |
| `project.admin.tokens` (`allowReload`, `allowRegenerateSearch`, `allowBackup`, `allowSnapshots`) | `NOVA:tibi-types/schemas/config/project.schema.json` (`admin`) | Token-Fähigkeiten für Backup/Snapshots/Search-Rebuild | — (grep `allowSnapshots`/`allowBackup` in Doku ohne Treffer) | gap |
| `hooks` (get/post/put/delete/audit, `{type:javascript,file,timeout}`) | `NOVA:tibi-types/schemas/config/hooks.schema.json:1-60`; Action-`hooks` ein String oder Hooks-Objekt (`action.schema.json`) | Server-Hooks | `DOKU:projekt/hooks-schreiben.md` | ok (Server-Seite) |
| `hooksTrigger` / `exportScript` (Task-Begriffe) | — (grep im gesamten `NOVA:`-Repo ohne Treffer) | nicht verifiziert — Begriffe existieren im Code nicht | — | nicht verifiziert |
| Feld-Schema (Typen, `index`, `subFields`, `validator`, `readonly/hidden` als bool-or-eval, `strictFields`) | `NOVA:tibi-types/schemas/config/field.schema.json:1-120`; `NOVA:tibi-types/schemas/config/field-meta.schema.json` (label/widget/i18n/helperText/dependsOn/choices/foreign/downscale/imageEditor/drillDown/pageSize/preview/addElementLabel/filter/pagebuilder/schemaRef/openapi) | Felddefinition + Admin-Meta | `DOKU:projekt/felder-widgets.md`; `DOKU:projekt/sammlungen-anlegen.md`; Widget-Tabelle `DOKU:admin/customizing.md:19ff` | ok (thin für `schemaRef`, `drillDown`, `pageSize`, `containerProps`, `protected`) |
| Actions (`query`/`data`-Felder, `execution.kind/cancellable`, `output.mapping.summary/details`, `output.mapping.progress` mit index-/count-/status-/messageField + fields) | `NOVA:tibi-types/schemas/config/action.schema.json` (`actionMethodMeta`, `actionOutputProgressMapping`, `actionExecutionMeta`); `NOVA:frontend/src/lib/components/ActionView.svelte:260-271,349,378-379` (SSE → Balken) | Action definieren + Fortschritt mappen | `DOKU:admin/aktionen.md:10-77`; `DOKU:projekt/jobs-actions.md:54ff` | ok (thin: `execution.kind`/`cancellable`-Semantik nur Code) |
| Jobs (`cron`, `{type:javascript,file,timeout}`) | `NOVA:tibi-types/schemas/config/job.schema.json:1-40` | Cron-Jobs | `DOKU:projekt/jobs-actions.md:22ff` | ok |
| Assets (`name`, `path`) | `NOVA:tibi-types/schemas/config/asset.schema.json` | Projekt-Assets | nicht verifiziert | nicht verifiziert |
| KI-Proxy-Profile (`chat` + `summarizer/translation/vision`, `stt`, `tts`, Pricing) | `NOVA:config/ai-proxy.config.json:1-50`; `NOVA:schemas/config/ai-proxy-config.schema.json` (chat/stt/tts Pflichtfelder) | LLM-Provider/Modelle/Preise (Server) | `DOKU:projekt/llm-nutzen.md:59ff`; `DOKU:admin/ki-funktionen.md:107ff` (Grenzen/Kosten) | thin (Profil-Vererbung summarizer/translation/vision, `chatTemplateKwargs`, Pricing-Felder nur Code) |
| Runtime-System-Config (`frontend/assets/system.config.json`, Endpunkte, Engine-Modi `auto/api/browser/none`, TTS-Stimmen) | `NOVA:frontend/src/lib/runtime-system-config.ts:205` (`loadRuntimeSystemConfig`); `NOVA:schemas/config/system-config.schema.json` | KI-Laufzeitconfig des Admins | `DOKU:admin/setup.md:63ff` (ein Absatz); `DOKU:admin/ki-funktionen.md` | thin (Engine-Modi, Endpoint-Tabelle, Fallback-Verhalten fehlen) |
| API-Anbindung (`apiBaseURL=/api/`, `apiBaseOverride`-Store, `withApiPath/buildApiUrl` mit `_/`-Prefix) | `NOVA:frontend/src/config.ts:4`; `NOVA:frontend/src/lib/store.ts:99`; `NOVA:frontend/src/lib/api/url.ts:1-30` | Wie der Admin den Server erreicht | `DOKU:admin/setup.md:24ff,63ff` (`API_BASE`, Mock-Tabelle) | thin (`apiBaseOverride`-Laufzeit-Umschaltung, `namespace → _/`-Routing nur Code) |
| Mock-Modus (`__MOCK_MODE_DEFAULT__`, `isMockMode()`, Mock-Interceptor, Demo-Projekt `t1`) | `NOVA:esbuild.config.js:83`; `NOVA:frontend/src/lib/mock/config.ts:15-34`; `NOVA:frontend/src/index.ts:54ff` (Boot mit Mock-Interceptor) | Offline-/Demo-Betrieb | `DOKU:admin/setup.md:63ff` | thin (Umschalt-Regeln, Caching, Mock-Grenzen nur Code) |
| Boot-Sequenz (`__TIBI_BOOT_MAP__`, Stages, `tibi:boot-progress`, `__TIBI_APP_READY__`) | `NOVA:frontend/src/index.ts:14-80` | Lade-/Diagnose-Haken | — | gap (nur für Admin-Entwickler relevant; nicht verifiziert ob gewollt öffentlich) |
| Theme/i18n (`loadTheme`, `themeStore`, `setupI18n`, `locales/`) | `NOVA:frontend/src/lib/store.ts:283-300`; `NOVA:frontend/src/index.ts:2`; `NOVA:frontend/src/lib/i18n/` | Eigenes Theme/Sprachen — **lädt der Admin das?** | `DOKU:admin/setup.md`, `DOKU:admin/customizing.md` ohne Theme/i18n-Customizing | gap (kein Customizing-Pfad belegt: kein Theme-Slot, keine i18n-Override-Datei im Code gefunden) |
| Navigation/Gruppen (`group`, `muiIcon`, AdminHub, Breadcrumb, `jumpTargets`) | `NOVA:frontend/src/lib/components/AdminLayout.svelte`; `NOVA:frontend/src/lib/jumpTargets.svelte.ts`; `NOVA:frontend/src/lib/components/ui/Breadcrumb.svelte` | Admin-Navigation | `DOKU:admin/konzept.md`; `DOKU:anwender/orientierung.md` | thin (eigene Navigations-Einträge/Gruppen-Reihenfolge nur via `meta.group`, dokumentiert in `sammlungen-anlegen.md`) |
| Eigene Views/Meta-Felder/Skripte laden (Registrierung, Reihenfolge) | Registry-Loader (`load-registry.ts:14-38`, lazy + Cache pro URL); Export-Loader analog (`resolveProjectAssetSpecifier`) | Lade-Reihenfolge | `DOKU:projekt/pagebuilder-dev.md:17ff`; `DOKU:admin/exporte.md:48ff` | thin (Lazy/Cache-Verhalten, Fehler-Fallback Blueprint nur Code) |
## 2) Fehlende Interface-Doku (Top-Liste für Projektentwickler)
Steht weder in `DOKU:` noch in `NOVA:docs/` — jeweils mit Code-Beleg:
1. **`meta.singleton`** — Ein-Eintrag-Sammlungen (`enabled`, `key`). Beleg: Schema `collection-meta.schema.json` (`singleton`). Doku: kein Treffer.
2. **`project.admin.tokens`-Fähigkeiten** (`allowReload`, `allowRegenerateSearch`, `allowBackup`, `allowSnapshots`) — wer Backup/Snapshot/Search-Rebuild per Token darf. Beleg: `NOVA:tibi-types/schemas/config/project.schema.json` (`admin`). Doku: kein Treffer.
3. **`meta.query`** — deklarierte Zusatz-Querykeys für Hooks (`ctx.request().query`), MCP-validiert. Beleg: Schema `collection-meta.schema.json` (`query`). Doku: nur Neben­erwähnung `DOKU:projekt/mcp-nutzen.md`.
4. **`meta.agent`** — Zweck/Workflows/Gotchas pro Sammlung für Agents (via MCP/REST `_meta`). Beleg: Schema (`agent`). Doku: nur Neben­erwähnung `mcp-nutzen.md`/`pagebuilder-dev.md`.
5. **`meta.sections`** (`group/label/collapsible/collapsed`) — zweite Formular-Gruppierung neben `sidebar`. Beleg: Schema (`sections`). Doku: kein Treffer.
6. **`meta.defaultSort.order: MANUALLY`** + Drag-Freigabe — manuelle Reihenfolge als Config. Beleg: Schema (`defaultSort` enum `ASC/DESC/MANUALLY`); `NOVA:types/admin.d.ts:426ff`. Doku: Drag nur Bedienung (`customizing.md`), nicht Config.
7. **`meta.viewHint.navigation`-Details** (`nodesField`-Default `nodes`, `declaredTrees` mit `singleton/maxLevel`) — nur angerissen (`ansichten-listen.md:21`, `sammlungen-anlegen.md`). Beleg: Schema (`viewHint.navigation`); `NOVA:types/admin.d.ts:734ff`; `NOVA:frontend/src/lib/components/entry/widgets/NavigationTreeField.svelte`.
8. **Export-`ctx` vollständig** (`db.loadAll`-Batching/Retry/`signal`, nur `sort[0]`, `selector`-vs-`select`, `csvCell`-Guard, `csvWithBom`, Dateinamen-Sanitizing, `{date}`) — nur `NOVA:docs/custom-export-script.md:12-117` + Typdatei, in `DOKU:admin/exporte.md` verkürzt **und** Beispiel mit falscher Signatur (`loadAll({filter})` ohne Collection). Beleg: `NOVA:frontend/src/lib/utils/custom-export.types.ts:17-100`.
9. **Action-`execution` (`kind`, `cancellable`) + Upload-Fortschritt** (`createByteProgressToast`, `hasInlineUploadPayload`) — Fortschritts-Balken ja, Abbruch-/Upload-Pfad nein. Beleg: Schema (`actionExecutionMeta`); `NOVA:frontend/src/lib/components/ActionView.svelte:349-379`; `upload-progress`-Util. Doku: `DOKU:admin/aktionen.md:47ff` ohne `cancellable`/`kind`.
10. **`views`-Widerspruch klären** — `DOKU:admin/customizing.md:14` verspricht `meta.views` (u. a. Kalender), aber Schema verbietet `views` (`additionalProperties: false`, `NOVA:tibi-types/schemas/config/collection-meta.schema.json`) und `NOVA:types/admin.d.ts:452-458` markiert Custom-Views als „not yet supported“. Entweder implementieren oder Doku-Zeile streichen.
Nicht verifiziert (Begriffe existieren im Code nicht / unklar): `hooksTrigger`, `exportScript` (grep `NOVA:` ohne Treffer); `assets`-Admin-Nutzung (`asset.schema.json`: nur `name`/`path`).
## 3) Anwender-Oberflächen-Inventar
Routen: `NOVA:frontend/src/App.svelte:77-141`. Komponenten: `NOVA:frontend/src/lib/components/` (u. a. `admin/`, `ai/`, `entry/`, `entry/widgets/`, `ui/`). Shortcuts: `NOVA:frontend/src/lib/shortcuts.ts:22ff` (`SHORTCUTS`), Anzeige `ui/CommandPalette.svelte`, `ui/KeyboardShortcuts.svelte`.
| UI-Bereich | Code-Beleg | Doku-Seite | Screenshot? (`DOKU:src/assets/anwender/`) |
|---|---|---|---|
| Login / Abmelden / Session-Timeout | `NOVA:frontend/src/lib/components/LoginPage.svelte`; `ui/SessionTimeoutWarning.svelte`; `lib/api/auth.ts` | `DOKU:anwender/anmelden.md`; `DOKU:anwender/hilfe.md:10ff` | ja (`anmelden-login.webp`, `anmelden-abmelden.webp`) / Timeout-Dialog **fehlt** |
| Projektübersicht / Projektwechsel | `NOVA:frontend/src/lib/components/ProjectList.svelte` (implizit via Route); `store.ts:207ff` (`navigate`) | `DOKU:anwender/orientierung.md:10ff,26ff` | ja (`orientierung-start.webp`, `orientierung-projektwechsel.webp`) |
| Globale Schnellsuche vs. Command-Palette (`Ctrl/Cmd-K`?) | `NOVA:frontend/src/lib/components/ui/CommandPalette.svelte`; `shortcuts.ts` | `DOKU:anwender/orientierung.md:58ff` (nur Schnellsuche, keine Palette) | Palette-Overlay **fehlt** (`orientierung-schnellsuche.webp` zeigt Listen-Suche) |
| Tastaturkürzel-Hilfe (`?`, `g→p`, `s`, `v`) | `NOVA:frontend/src/lib/components/ui/KeyboardShortcuts.svelte`; `shortcuts.ts:22ff` | `DOKU:reference/tastatur-kurzbefehle.md`; `DOKU:anwender/lesen-suchen-filtern.md` (?) | **fehlt** |
| Listen (Tabelle/Karten/Medien/Navigation), Suche/Filter/Sortierung/Blättern, Mehrfachauswahl, URL-State | `NOVA:frontend/src/lib/components/CollectionView.svelte`; `CollectionTable/Cards.svelte`; `ui/FilterPopover.svelte`; `ui/SortPopover.svelte`; `ui/SearchInput.svelte` | `DOKU:anwender/lesen-suchen-filtern.md`; `DOKU:admin/ansichten-listen.md:10-94` | ja (`lesen-liste/karten/filter/suche.webp`) |
| Suchindex-Hinweis (Queue/Polling) | `NOVA:frontend/src/lib/components/ui/SearchIndexBuildNotice.svelte`; `NOVA:docs/test-plans/41-search-index-status.md:1-24` | nur Server-Seite (`server/betrieb.md`, `projekt/suche.md`) | **fehlt** (Anwender sieht Banner ohne Doku) |
| Stapel-Bearbeitung (Bulk) | `NOVA:frontend/src/lib/components/BulkEditPanel.svelte` | `DOKU:admin/customizing.md` (1 Zeile); Anwender: keine | **fehlt** |
| Eintrag anlegen/bearbeiten (Widgets, Pflicht/Formate, `dependsOn`) | `NOVA:frontend/src/lib/components/EntryView.svelte`; `entry/WidgetDispatcher.svelte`; `entry/widgets/*.svelte` | `DOKU:anwender/eintrag-bearbeiten.md`; `DOKU:admin/eintrags-editor.md:10-77` | ja (`eintrag-formular/neu/gespeichert.webp`) |
| Feldsuche (`Ctrl+F`), Sprungziele (`j`), Drill-Down, Verlauf Undo/Redo, Diff-Dialog, JSON-Overlay | `NOVA:entry/FieldSearch.svelte`; `entry/DrillDownBreadcrumb.svelte`; `entry/DiffDialog.svelte`; `entry/EntryJsonOverlay.svelte`; `lib/stores/drilldown-store.ts`, `entry-history` | `DOKU:admin/eintrags-editor.md:49ff,77ff` (teilweise) | **fehlt** (kein Screenshot für Suche/Diff/JSON/Drill-Down) |
| Fremdschlüssel (Drawer, Chips, Inline-Anlage, Rückverweise) | `NOVA:entry/ForeignKeyDrawer.svelte`; `entry/widgets/ForeignKeyWidget.svelte`; `entry/InlineEntryForm.svelte`; `entry/ReferencedBy.svelte` | `DOKU:admin/eintrags-editor.md:49ff`; `DOKU:admin/customizing.md:35ff` | **fehlt** |
| Übersetzung (Sprachtabs, Übersetzungsleiste) | `NOVA:entry/I18nLanguageTabs.svelte`; `entry/TranslationBar.svelte` | `DOKU:admin/eintrags-editor.md:58ff` | **fehlt** |
| Medien (Raster, Upload+Queue, Bildeditor, Lightbox, Einbetten) | `NOVA:frontend/src/lib/components/MediaGrid.svelte`; `ui/MediaLightbox.svelte`; `entry/widgets/ImageEditor.svelte`; `FileUploadQueue.svelte` | `DOKU:anwender/medien.md`; `DOKU:admin/medien.md:10-72` | ja Raster/Upload/Editor (`medien-*.webp`); Lightbox + Queue **fehlen** |
| Dateien/Fremddateien (`foreignFile`, Fremd-Medien) | `NOVA:entry/widgets/ForeignFileWidget.svelte`; `NOVA:docs/test-plans/38-foreign-file.md:1-49` | `DOKU:admin/medien.md:50ff` (Einbetten) | **fehlt** (eigenständig) |
| Export-Dialog (generisch + Skript-Tabs, Feldauswahl, Count-Warnung) | `NOVA:frontend/src/lib/components/ui/ExportDialog.svelte`; `NOVA:docs/collection-export.md:1-60` | `DOKU:anwender/exportieren.md`; `DOKU:admin/exporte.md:10ff` | ja (`export-dialog/fertig.webp`) |
| Aktionen (Formular, SSE-Balken, Ergebnis-Mapping) | `NOVA:frontend/src/lib/components/ActionView.svelte`; `ActionProgressMapping.svelte` | `DOKU:admin/aktionen.md`; Anwender: keine eigene Seite | **fehlt** (Anwender-Seite + Screenshot) |
| KI (Chat, Zusammenfassen, Stimme/TTS, Vision, Werkzeuge, Kosten) | `NOVA:frontend/src/lib/components/ai/AiChat.svelte`; `ai/*.ts` (`aiController`, `aiTools`, `aiUIRegistry`, `aiVoiceInput`, `aiTTS`); `ui/NovaAiPill.svelte` | `DOKU:anwender/ki-nutzen.md`; `DOKU:admin/ki-funktionen.md:10-107` | ja (`ki-chat/stimme/zusammenfassen.webp`); Tool-Inspektor/Verlauf/Vision-Overlay **fehlen** |
| Seiten bauen (Pagebuilder-Editor, Overlays, Viewports, Screenshot-Pipeline) | `NOVA:entry/widgets/pagebuilder/PagebuilderWidget.svelte`; `PagebuilderOverlay.svelte`; `PagebuilderOverviewOverlay.svelte`; `PagebuilderScreenshotHost.svelte` | `DOKU:anwender/seiten-bauen.md`; `DOKU:projekt/pagebuilder-dev.md:128ff` | ja (`seiten-*.webp`, 6 Dateien) |
| Verlauf/Audit (Liste, Diff, Wiederherstellen) | `NOVA:frontend/src/lib/components/AuditView.svelte`; `AuditHistoryPanel.svelte`; `AuditSidebarHistory.svelte`; `AuditDiff.svelte`; `AuditRestoreDialog.svelte` | Anwender: keine Seite; Admin: nur implizit | **fehlt** (Seite + alle Screenshots) |
| Snapshots/Backup (Übersicht, Auslösen/SSE, Download, Restore, Import-Analyse) | `NOVA:frontend/src/lib/components/SnapshotView.svelte`; `BackupView.svelte`; `lib/api/snapshot-api.ts`; `backup-api.ts`; `NOVA:docs/test-plans/43-backup-restore.md:1-89`, `44-snapshots.md:1-89` | `DOKU:anwender/sichern.md:10-49` | ja (`sichern-uebersicht/details/statistik.webp`); Backup-Import-Dialog + Restore-Bestätigung **fehlen** |
| Benutzer/Teams/Orgs/Rollen/Tokens (AdminHub, Drawers, Chips) | `NOVA:frontend/src/lib/components/admin/AdminHub.svelte`; `UserTab/TeamTab/OrgTab.svelte`; `*DetailDrawer.svelte`; `RoleSection.svelte`; `PermissionChipsInput.svelte`; `ProjectAdminView.svelte` | `DOKU:anwender/benutzer-teams.md`; `DOKU:admin/benutzer-rechte.md:27-80` | teilweise (`benutzer-teams/profil.webp`); Drawer-Details, Token-Seite, Projekt-Admin **fehlen** |
| Stellvertretung (Button + Amber-Banner + Stopp) | `NOVA:frontend/src/lib/api/auth.ts:270-300` (`impersonate`); `NOVA:docs/test-plans/42-impersonation.md:1-47`; Banner in `AdminLayout.svelte` | `DOKU:admin/benutzer-rechte.md:81ff` | **fehlt** |
| Profilseite | `NOVA:frontend/src/lib/components/ProfilePage.svelte` (Route `/profile`, `App.svelte:114ff`) | `DOKU:anwender/benutzer-teams.md:25` | ja (`benutzer-profil.webp`) |
| Avatar/Theme-Umschalter, Toasts, Voice-Eingabe in Filtern | `NOVA:ui/ThemeToggle.svelte`; `ui/Toast.svelte`; `ui/VoiceFilterTextInput.svelte`; `ui/VoiceRecorderButton.svelte` | verstreut/keine | **fehlen** |
UI-Bereiche **ohne** Screenshot (Namen): Tastaturkürzel-Hilfe, Command-Palette-Overlay, Suchindex-Banner, Bulk-Edit-Panel, Feldsuche/Sprungziele/Drill-Down, Diff-Dialog, JSON-Overlay, FK-Drawer/Inline-Anlage/Rückverweise, Übersetzungsleiste/Sprachtabs, Medien-Lightbox, Upload-Warteschlange, Fremddatei-Widget, Action-Ansicht (Anwender), KI-Werkzeug-Inspektor/Chat-Verlauf/Vision-Overlay, Audit-/Verlaufs-UI (komplett), Backup-Import-Dialog, Restore-Bestätigung, Admin-Drawers (Benutzer/Team/Org-Details), Token-Seite, Projekt-Admin-Ansicht, Stellvertretungs-Banner, Session-Timeout-Dialog, Theme-Umschalter, Voice-Filter-Eingabe.
## 4) Löschkandidaten `NOVA:docs/` (je Datei eine Zeile)
| Datei | Inhalt (kurz) | Abdeckung in tibi-docs | Urteil |
|---|---|---|---|
| `docs/collection-config.md` (2293 Zeilen) | Vollreferenz Projekt-/Collection-/Feld-/Meta-Schema inkl. Server-Optionen | teilweise (`projekt/sammlungen-anlegen.md`, `felder-widgets.md`, `reference/schemas.md`; Server-Details in `server/*`) | **muss bleiben** (einzige Vollreferenz; Auszug nach `reference/` wäre Follow-up) |
| `docs/collection-export.md` (327) | Generischer Listen-Export: Flow, Dialog, Batch-Laden, Warnschwellen | weitgehend (`admin/exporte.md:10ff`, `anwender/exportieren.md`) | teilweise → **vorerst bleiben** (Schwellenwerte/Batch-Details nur hier) |
| `docs/custom-export-script.md` (117) | `ctx`-Vertrag für Skript-Exporte (einzige Quelle für `db.loadAll`-Semantik, Payload-Regeln, Styling) | thin (`admin/exporte.md:48ff` verweist hierher) | **muss bleiben** (bis `ctx` voll in tibi-docs steht; siehe Punkt 2.8) |
| `docs/test-plans/02-navigation-routing.md` (31) | SPA-Routing, History, Breadcrumbs, Navigation-Guard | teilweise (`anwender/orientierung.md`, `admin/ansichten-listen.md:76ff`) | teilweise → bleiben (Guard-/History-Details nur hier) |
| `docs/test-plans/05-collection-view.md` | Listen-Rendering (4 View-Modi), Suche/Sort/Filter/Pagination, URL-State | weitgehend (`admin/ansichten-listen.md`, `anwender/lesen-suchen-filtern.md`) | teilweise → bleiben (Mock-Fälle, Debounce-Details) |
| `docs/test-plans/07-entry-editor-advanced.md` | Undo/Redo, Diff, Drill-Down, Feldsuche, Jump-Targets | teilweise (`admin/eintrags-editor.md`) | teilweise → bleiben |
| `docs/test-plans/11-foreign-keys.md` | FK-Widgets, Chip-Arrays, Drawer-Flow, Inline-Create, Nested-Drawers | teilweise (`admin/eintrags-editor.md:49ff`, `customizing.md:35ff`) | teilweise → bleiben |
| `docs/test-plans/16-ai-integration.md` (41) | KI-Integration (Chat/Modelle/Werkzeuge) | weitgehend (`admin/ki-funktionen.md`, `projekt/llm-nutzen.md`) | teilweise → bleiben (Mock-/Test-Spezifika) |
| `docs/test-plans/17-ai-tts.md` (24) | TTS/Stimme | weitgehend (`admin/ki-funktionen.md:52ff`) | teilweise → bleiben |
| `docs/test-plans/25-file-upload.md` (43) | Datei-Upload-Flows | teilweise (`admin/medien.md:19ff`) | teilweise → bleiben |
| `docs/test-plans/27-media-grid.md` (40) | Medien-Raster, DnD-Upload, Inline-Edit | teilweise (`admin/medien.md:10ff`) | teilweise → bleiben |
| `docs/test-plans/34-i18n-fields.md` (34) | Mehrsprachige Felder | teilweise (`admin/eintrags-editor.md:58ff`) | teilweise → bleiben |
| `docs/test-plans/35-entry-i18n.md` (54) | Eintrags-Übersetzung (Kopplung, Leiste) | teilweise (s. o.) | teilweise → bleiben |
| `docs/test-plans/36-voice-input-fields.md` (31) | Spracheingabe in Feldern | teilweise (`admin/ki-funktionen.md:52ff`) | teilweise → bleiben |
| `docs/test-plans/37-pagebuilder.md` | Pagebuilder-Vorschau, Lookup-Hydratisierung, URL-Normalisierung | weitgehend (`anwender/seiten-bauen.md`, `projekt/pagebuilder-dev.md`) | teilweise → bleiben (Mock-/URL-Edge-Cases nur hier) |
| `docs/test-plans/38-foreign-file.md` (49) | Fremddateien/-medien | teilweise (`admin/medien.md:50ff`) | teilweise → bleiben |
| `docs/test-plans/39-preview-resolver.md` | Vorschau-Regeln (Auto-Detect vs. strikt, Slot-Pfade, FK-Badges) | thin (`admin/customizing.md:35ff`) | **muss bleiben** (Regeln sonst nirgends) |
| `docs/test-plans/40-actions.md` | Action-Routen, Methoden-Tabs, SSE-Fortschritt, Disabled-States | weitgehend (`admin/aktionen.md`, `projekt/jobs-actions.md`) | teilweise → bleiben (Routing-/State-Details) |
| `docs/test-plans/41-search-index-status.md` (24) | Suchindex-Banner (Projekt/Collection, Polling) | nicht abgedeckt (nur Server-Seite `server/betrieb.md`, `projekt/suche.md`) | **muss bleiben** (einzige UI-Quelle) |
| `docs/test-plans/42-impersonation.md` (47) | Stellvertretung (Endpoint, Drawer-Button, Banner, Rollback) | teilweise (`admin/benutzer-rechte.md:81ff`) | teilweise → bleiben (Mock-Endpoint, Rollback-Details) |
| `docs/test-plans/43-backup-restore.md` (89) | Backup-Export/Import (SSE, 6 Endpunkte, Rechte) | teilweise (`anwender/sichern.md`, `projekt/audit-backup.md`) | teilweise → bleiben (API-/SSE-Details) |
| `docs/test-plans/44-snapshots.md` (89) | Snapshot-CRUD (SSE, 10 Endpunkte, ZIP, Prune) | teilweise (s. o.) | teilweise → bleiben |
Löschkandidaten-Zählung: **0 von 21 sofort löschbar** — 3× „muss bleiben“, 18× „teilweise → vorerst bleiben“, 0× „vollständig abgedeckt“.
+125
View File
@@ -0,0 +1,125 @@
# Audit 03: tibi-types + tibi-svelte-starter vs. tibi-docs
Stand: 2026-10-16. Codebasen: `tibi-types` (schemas + `index.d.ts`), `tibi-svelte-starter` (README, AGENTS, package.json, frontend/api/Docker/Env). Doku: `tibi-docs/src/content/docs` (Seiten `einstieg/*`, `projekt/*`, `reference/schemas.md`, `reference/hooks-*.md`).
Regel: Jede Aussage mit `datei:zeile` belegt; ohne Beleg steht „nicht verifiziert".
## 1) Schema-Matrix tibi-types (16 Schemas in `schemas/config/`)
Quelle Top-Level-Properties: per Python aus den Schema-Dateien extrahiert. Dokustand: `reference/schemas.md:10-32` listet alle 16 Schemas nur tabellarisch (Name → Datei → Pflichtfelder), beschreibt aber keine Properties im Detail; Details liegen ggf. auf Projektseiten.
| Schema (`schemas/config/*.json`) | Top-Level-Properties | in `reference/schemas.md` beschrieben? | undokumentierte Properties (vs. schemas.md + Projektseite) |
|---|---|---|---|
| `project.schema.json` | `namespace, meta, upload, collections, jobs, actions, assets, cors, admin, hook, audit, strictFields, snapshotPath, snapshots, legacy` (`project.schema.json:99-333` für Sub-Blöcke: `agent` :102, `assets` :174, `cors` :187, `admin` :227, `hook` :262, `audit` :303, `strictFields` :312, `snapshotPath` :315, `snapshots` :319, `legacy` :333) | nur Zeile in Tabelle (`schemas.md:18`); `hook/cache/ratelimit`-Details nur in `server/konfiguration.md`, `snapshots` nur in `projekt/audit-backup.md`, `admin` nur Server-Seiten | `snapshotPath` (String, Ablagepfad Snapshots), `snapshots` (Array, Zeitplan/Retention — nur Ablauf in `audit-backup.md`, kein Feld- mapping), `legacy` (nur in `reference/fehlercodes.md` erwähnt, sonst nicht verifiziert), `strictFields` auf Projektebene (nur `fehlercodes.md`, nicht verifiziert), `cors` auf Projektebene (nur Server-Seiten `server/sicherheit.md`, `server/konfiguration.md`), `admin`-Subkeys (nur Server-Seiten) |
| `collection.schema.json` | `name, defaultLanguage, uploadPath, upload, permissions, projections, hooks, meta, imageFilter, fields, indexes, audit, bulk, cors, queryLimits, readonlyFields, hiddenFields, strictFields, search` (`collection.schema.json:17` `uploadPath`, `:63` `imageFilter`, `:103` `audit`, `:126` `bulk`, `:147` `cors`, `:187` `queryLimits`, `:201` `readonlyFields`, `:207` `hiddenFields`, `:213` `strictFields`, `:216` `search`) | nur Tabellenzeile (`schemas.md:19`); Teilabdeckung: `indexes`+`search` in `projekt/sammlungen-anlegen.md:104-150`, `imageFilter` in `projekt/dateien-bilder.md`, `audit` in `projekt/audit-backup.md`, `cors` in `projekt/jobs-actions.md` | `uploadPath` (String, veralteter Upload-Pfad neben `upload`; nicht verifiziert in Doku), `queryLimits` (`defaultLimit`/`maxLimit`; kein Treffer in Doku — nicht verifiziert), `readonlyFields`/`hiddenFields` auf Collection-Ebene (kein Treffer in Doku — nicht verifiziert), `strictFields` (nur `fehlercodes.md`, kein Konzepttext), `bulk` (`optimize.create/update/delete`; in `projekt/*` nur `bulk`-Wort in `mcp-nutzen.md`/`hooks-schreiben.md` ohne Feld-Doku), `upload.allowedMimeTypes`/`maxBodySize` (nur Server-Seite, nicht pro Collection), Index-Subkeys `dropDups/sparse/background/defaultLanguage/languageOverride` (Schema-`$defs` `indexConfig`; Doku `sammlungen-anlegen.md:106-135` zeigt nur Text/Unique-Beispiel) |
| `field.schema.json` | via `$defs.field`: `name, type, index, subFields, validator, readonly, hidden, strictFields, meta` (Typ-Enum `string…any`, `index`-Enum `single/unique/text/sparse/background`) | nur Tabellenzeile (`schemas.md:20`) | `readonly`/`hidden` als Bool-oder-eval (`BoolOrEval`, `index.d.ts:13`); Doku `felder-widgets.md`/`sammlungen-anlegen.md` enthalten kein `readonly`/`hidden` (grep ohne Treffer) — nicht verifiziert; `strictFields` auf Feldebene (nur `fehlercodes.md`); `index`-Enum-Details (`sparse/background`) ohne Doku |
| `field-list.schema.json` | Array-Items `string \| field.schema` (Datei 1:20) | nur Tabellenzeile (`schemas.md:21`) | keine Properties (Wrapper); Verwendung (String-Kurzform vs. Objekt) in `felder-widgets.md:12-32` teilweise gezeigt — abgedeckt |
| `field-meta.schema.json` | `label, widget, i18n, helperText, hideLabel, hide, protected, position, section, containerProps, defaultValue, dependsOn, inputProps, choices, foreign, downscale, imageEditor, drillDown, pageSize, preview, addElementLabel, filter, pagebuilder, schemaRef, openapi` (`field-meta.schema.json:14` `widget`, `:50` `hideLabel`, `:66` `containerProps`, `:131` `dependsOn`, `:183` `downscale`, `:198` `imageEditor`, `:229` `drillDown`, `:232` `pageSize`, `:239` `addElementLabel`, `:242` `filter`, `:303` `schemaRef`) | nur Tabellenzeile (`schemas.md:22`); Teilabdeckung in `projekt/felder-widgets.md` (Aufbau :12, Typen :33, Validatoren :60, Choices/FK :80, dependsOn :131, i18n :151) | `hideLabel` (Bool; kein Treffer — nicht verifiziert), `containerProps` (Objekt; kein Treffer), `position`/`section` (Layout; `section` nur `collection-meta`, Feld-`section` nicht verifiziert), `pageSize` (Zahl; kein Treffer), `addElementLabel` (String; kein Treffer), `imageEditor` (Konfig; kein Treffer), `schemaRef` (String-Ref; kein Treffer), `filter: true/false` als Admin-Filter-Opt-in (`field-meta.schema.json:242-246`; Doku zeigt nur `filter_public.js`-Hook, nicht das Flag), `inputProps`/`defaultValue`-Details nur angerissen |
| `collection-meta.schema.json` | `label, muiIcon, group, exports, designGuide, imageUrl, hide, singleton, subNavigation, preview, i18n, defaultSort, viewHint, sidebar, sections, openapi, query, agent` (`collection-meta.schema.json:69` `singleton`, `:84` `subNavigation`, `:211` `defaultSort`, `:231` `viewHint`, `:423` `sidebar`, `:441` `sections`, `:465` `openapi`, `:468` `query`, `:503` `agent`) | nur Tabellenzeile (`schemas.md:23`); Teilabdeckung: `sammlungen-anlegen.md:79-103` (Label/Gruppe/Icon/Vorschau), `admin/ansichten-listen.md` (`subNavigation`, `defaultSort`, `viewHint`) | `singleton` (Bool, Ein-Dokument-Collection; kein Treffer in Doku — nicht verifiziert), `sections` (Array, Editor-Sektionen; kein Treffer), `hide` auf Collection-Ebene, `exports/designGuide/imageUrl` (nicht verifiziert), `query`-Block (Meta-Abfragen; nicht verifiziert), `agent`-Block (nicht verifiziert), `openapi`-Meta (nur Tabellenzeile) |
| `permissions.schema.json` | `public, user` + `$defs.permissionSet`: `methods, filter, validProjections, readonlyFields, hiddenFields` | nur Tabellenzeile (`schemas.md:24`) | `validProjections` (String-Array; kein Treffer — nicht verifiziert), `filter` (Mongo-Filter je Permission-Set; nur `rechte-tokens.md` grob, kein Feld-Mapping — teilweise), `readonlyFields`/`hiddenFields` je Permission-Set (kein Treffer) |
| `hooks.schema.json` | `get, post, put, delete, audit` + `$defs`: `hook(type/file/timeout)`, `getHooks(handle/read/return/file)`, `postHooks(bind/validate/handle/create/return/bulkCreate/bulkReturn)`, `putHooks(bind/validate/update/return/bulkUpdate/bulkReturn)`, `deleteHooks(delete/return/bulkDelete/bulkReturn)`, `auditHooks(return)` | nur Tabellenzeile (`schemas.md:25`); Hook-Protokoll in `projekt/hooks-schreiben.md` | `bulkCreate/bulkReturn`, `bulkUpdate/bulkReturn`, `bulkDelete/bulkReturn` (Stufen im Schema, in `hooks-schreiben.md` nur `bulk`-Wort ohne Stufen-Mapping — teilweise), `bind` vs. `validate` vs. `handle`-Reihenfolge (Schema-`$defs`, Doku-Erwähnung prüfen — teilweise), `audit.return`-Hook (Schema vorhanden; Doku nur `audit-backup.md`, kein Hook-Stufen-Text) |
| `job.schema.json` | `cron, type, file, timeout, meta` | nur Tabellenzeile (`schemas.md:26`); Ablauf in `projekt/jobs-actions.md` | `timeout` (Sekunden; in `jobs-actions.md` erwähnt — abgedeckt/teilweise), `meta` (frei; nicht verifiziert) |
| `action.schema.json` | `name, meta, permissions, hooks, audit, cors` + `$defs`: `actionField`, `actionMappedField(source/name/...)`, `actionFieldSection`, `actionExecutionMeta(kind/cancellable)`, `actionOutputProgressMapping(indexField/countField/statusField/messageField/fields)`, `actionOutputMapping(summary/details/progress)`, `actionOutputMeta(title/mapping)`, `actionMethodMeta(query/data/execution/output)`, `actionMeta(label/muiIcon/description/group/hide/methods)` | nur Tabellenzeile (`schemas.md:27`); Actions in `projekt/jobs-actions.md`, Admin-Ansicht in `admin/aktionen.md` | `actionOutputMapping`/`actionOutputProgressMapping`-Felder (`indexField/countField/statusField/messageField/fields`; nur `admin/aktionen.md`-Treffer für „mapping", kein Feld-Mapping — teilweise/nicht verifiziert), `actionExecutionMeta.kind/cancellable` (nicht verifiziert), `actionMappedField.source` vs. `name` (Dot-Path vs. Legacy; nicht verifiziert), `cors`/`audit` je Action (nicht verifiziert) |
| `asset.schema.json` | `name, path` | nur Tabellenzeile (`schemas.md:28`) | keine weiteren Properties; Verwendung (`config.yml`-Assets) in `server/schnittstellen.md`/`neues-projekt.md` — abgedeckt |
| `image-filter.schema.json` | Array von Filter-Objekten (kein Top-Level-Props-Objekt) | nur Tabellenzeile (`schemas.md:29`); Anwendung in `projekt/dateien-bilder.md` | Filter-Parameter (`width/height/fit/fill/resampling/anchor/…/quality/outputType` aus `ImageFilterParams`, `index.d.ts:314-358`) sind in `dateien-bilder.md` nur als `filter`-Query-Param gezeigt, kein Parameter-Mapping — teilweise |
| `projections.schema.json` | Pattern-Key `{name: {select: {feld: 0\|1}}}` (Wrapper-Datei 1:30) | nur Tabellenzeile (`schemas.md:30`); Nutzung in `projekt/abfragen-lesen.md` | `select: null`-Variante (Schema erlaubt `object|null`; Doku nur 0/1 — teilweise) |
| `snapshot.schema.json` | `name, description, collections, includeMedia, collectionDedup, mode, retention, schedule, permissions, enabled` | nur Tabellenzeile (`schemas.md:31`); Ablauf in `projekt/audit-backup.md`, Endpunkte in `server/schnittstellen.md` | `includeMedia` (Bool), `collectionDedup` (Bool/Strategie), `mode`, `retention`, `schedule`, `permissions`, `enabled` — Feld-für-Feld-Mapping in `audit-backup.md` prüfen, per Stichprobe nur Ablauf erzählt — teilweise (genaue Abdeckung nicht verifiziert) |
| `openapi.schema.json` | `disabled, get, getOne, post, put, delete` + `$defs`: `codeSample(lang/label/source)`, `responseObject`, `operationObject(disabled/tags/summary/description/x-codeSamples/parameters/responses/security)`, `fieldOpenapi(disabled/description/example/format/enum/deprecated)` | nur Tabellenzeile (`schemas.md:32`); generierte Referenz in `reference/openapi.md` | `x-codeSamples`, `getOne`-Trennung (`get` vs. `getOne`), `fieldOpenapi.example/format/enum/deprecated` — Detail-Mapping nicht verifiziert (teilweise) |
| `defs.schema.json` | nur `$ref`-Bausteine, kein Ziel (`schemas.md:33` sagt das explizit) | abgedeckt (`schemas.md:33`) | — |
Wichtigste undokumentierte Schema-Properties (Top-10, mit Typ/Wirkung aus Schema):
1. `collection.queryLimits` (`collection.schema.json:187`) — Objekt `{defaultLimit, maxLimit}` (Zahlen): GET-Limits je Collection. Kein Treffer in Doku — nicht verifiziert.
2. `collection.readonlyFields` / `hiddenFields` (`collection.schema.json:201-207`) — String-Arrays: Collection-weite Schreib-/Sichtschutz-Felder. Kein Treffer in Doku — nicht verifiziert.
3. `collection.uploadPath` (`collection.schema.json:17`) — String: Legacy-Upload-Pfad neben `upload:{maxBodySize, allowedMimeTypes}`. Nicht verifiziert in Doku.
4. `collection.strictFields` + Feld-`strictFields` (`collection.schema.json:213`; Feld-`$defs`) — Bool: unbekannte Felder ablehnen. Nur `fehlercodes.md`-Treffer, kein Konzept — faktisch undokumentiert.
5. `collection-meta.singleton` (`collection-meta.schema.json:69`) — Bool: Collection auf ein Dokument begrenzen. Kein Treffer — nicht verifiziert.
6. `collection-meta.sections` (`collection-meta.schema.json:441`) — Array: Editor-Sektionen. Kein Treffer — nicht verifiziert.
7. `field-meta.hideLabel` (`field-meta.schema.json:50`) — Bool: Label im Editor verstecken. Kein Treffer — nicht verifiziert.
8. `field-meta.imageEditor` (`field-meta.schema.json:198`) — Objekt: Bildeditor-Konfig je Feld. Kein Treffer — nicht verifiziert.
9. `permissionSet.validProjections` (Schema-`$defs.permissionSet`) — String-Array: erlaubte Projections je Rolle. Kein Treffer — nicht verifiziert.
10. `action.actionOutputMapping`/`actionOutputProgressMapping` (`action.schema.json`-`$defs`) — `{summary/details/progress{indexField/countField/statusField/messageField/fields}}`: Fortschritts-/Ergebnis-Mapping von Actions. Nur „mapping"-Wort in `admin/aktionen.md`, kein Feld-Mapping — faktisch undokumentiert.
## 2) Hook-Typen (`tibi-types/index.d.ts`, 1716 Zeilen)
Die `reference/hooks-*.md` dokumentieren Pakete/Funktionen (`db.find` …), nicht Typen. `reference/schemas.md` listet nur Schema-Dateien. `reference/hook-kontexte.md:8-28` listet Pakete aus `internal/hook` (Server-Code), nicht `index.d.ts`-Typen. Ergebnis: fast alle globalen Typen sind als Typen undokumentiert (Verwendung teils in Beispielen, aber kein Typ-Mapping).
| Typ (`index.d.ts`) | Zeilen | in hooks-*/schemas.md dokumentiert? |
|---|---|---|
| `CollectionDocument` | `index.d.ts:2-7` | nicht verifiziert (nirgends als Typ; implizit in Beispielen) |
| `BoolOrEval` | `index.d.ts:13` | nein — `readonly`/`hidden`-Felder selbst fehlen (s. §1) |
| `FieldImageValidator` (`minWidth/maxWidth/minHeight/maxHeight/allowNonImages/allowUnknownDimensions`) | `index.d.ts:16-29` + Schema `field.schema.json:130-142` | nein — `felder-widgets.md:60-78` kennt nur `maxFileSize/accept`, keine Bildmaß-Validatoren |
| `FieldValidator` (alle Schlüssel) | `index.d.ts:31-68` | teilweise — `felder-widgets.md:60-78` listet `required/pattern/in/minLength/maxLength/min/max/minDate/maxDate/minItems/maxItems/format/maxFileSize/minFileSize/accept/eval/allowZero`, aber **nicht** `image: FieldImageValidator` als Unterschlüssel |
| `SearchConfig` + `RrfConfig`/`SourceFusionConfig`/`VectorConfig`/`NgramConfig`/`NormalizeConfig`/`RegexConfig`/`ScoreConfig` | `index.d.ts:70-184` | teilweise — `projekt/suche.md:33-38` zeigt `autoRegenerate`, `:74` nennt `score_rrf/score_sum/rank_score/hybrid`, `minmax/zscore`, `scoreWeight`, `sourceConfig` als „existiert im Code", ohne Feld-Mapping |
| `SearchExplainSource/SearchExplanation/SearchMeta` (`qExplain`) | `index.d.ts:120-147` | nicht verifiziert (kein Treffer `qExplain`/`SearchExplanation` in Doku-Stichprobe) |
| `QueryLimitsConfig/BulkOptimizeConfig/BulkConfig` | `index.d.ts:187-209` | nein (s. §1 `queryLimits`/`bulk`) |
| `CollectionIndex` (`name/key/unique/dropDups/background/sparse/defaultLanguage/languageOverride`) | `index.d.ts:211-228` | teilweise — `sammlungen-anlegen.md:106-135` zeigt Text-/Unique-Beispiel, keine Subkey-Tabelle |
| `AuditCollectionConfig` | `index.d.ts:231-243` | teilweise — `audit-backup.md` erzählt Ablauf, kein Feld-Mapping (`enabled/actions`-Liste) |
| `UploadConfig` (`maxBodySize/allowedMimeTypes`) | `index.d.ts:245-248` | teilweise — Server-Seite ja, Collection-Ebene nein |
| `LocalizedText/ChoiceValue/ChoiceStyle/ChoiceItem` | `index.d.ts:250-272` | teilweise — `felder-widgets.md:82-110` zeigt Choices, aber kein `badgeVariant/chipVariant/badgeStyle/chipStyle`-Mapping |
| `PreviewTableColumn/PreviewSlots/PreviewConfig/EvalStringConfig/EvalHtmlConfig` | `index.d.ts:274-311` | teilweise — `sammlungen-anlegen.md:79-103` + `admin/ansichten-listen.md` zeigen Vorschau, kein Slot-Mapping (`label/secondary/tertiary/badge/image/table/select`) |
| `ImageFilterParams` | `index.d.ts:314-358` | teilweise (s. §1 `image-filter`) |
| `CollectionInfo` (inkl. `imageFilter/indexes/audit/bulk/queryLimits/readonlyFields/hiddenFields/search/strictFields`) | `index.d.ts:361-388` | nein als Typ; Felder s. §1 |
| `ActionInfo/ActionMeta/ActionMethodMeta/ActionExecutionMeta/ActionMappedField/ActionOutputProgressMapping/ActionOutputMapping/ActionOutputMeta` | `index.d.ts:391-465` | nein als Typen; Ablauf in `jobs-actions.md`/`admin/aktionen.md` |
| `MethodPermission/SimpleMethodPermission/AllowedMethodPermission/CollectionYourPermissions/ActionYourPermissions/ProjectYourPermissions/PermissionSet/ProjectionConfig` | `index.d.ts:467-521` | teilweise — `rechte-tokens.md` + `abfragen-lesen.md` (Projections) behandeln Konzepte, kein Typ-Mapping (`bulk`-Flag, `filter`, `validProjections` fehlen) |
| `CollectionFieldInfo` (`readonly/hidden/strictFields`) | `index.d.ts:523-536` | teilweise (s. Feld-§1) |
| `ApiInfo/ProjectInfo` | `index.d.ts:539-562` | nicht verifiziert als Typen |
| `AuditLogEntry` | `index.d.ts:565-611` | teilweise — `audit-backup.md` (Eintrags-Felder `userId/action/snapshot/changes` erzählt, kein Typ-Mapping) |
| `DbReadOptions` (`filter/selector/projection/offset/limit/sort/pipelineMod`) | `index.d.ts:613-627` | teilweise — `hooks-daten.md:8-113` dokumentiert `find/count/…`-Signaturen, `pipelineMod` als Option nicht verifiziert |
| `GetHookData/GetHookGetOnlyData/PostHookData/JobConfig/JobData` | `index.d.ts:629-737` | teilweise — `hooks-schreiben.md` (Stufen), `jobs-actions.md` (Jobs); `bulkReturn`-Felder (`created/ids/modified/deleted`) ohne Typ-Mapping |
| `ConfigPackage/DbPackage/SmtpPackage/FsPackage/TplPackage/HttpPackage` | `index.d.ts:738-1062` | ja (Funktionen) — `hooks-daten.md`, `hooks-netze.md`, `hooks-medien.md`, `hooks-sicherheit.md` |
| `DebugPackage/ResponsePackage/UserPackage/BcryptPackage/JwtPackage/CharsetPackage/ImagePackage/XmlPackage/CookiePackage/PdfPackage/CryptoPackage/JsonPackage/ExecPackage/Base64Package` | `index.d.ts:1064-1474` | ja (Funktionen) — `hooks-meta.md`, `hooks-medien.md`, `hooks-sicherheit.md` |
| `CachePackage/RatelimitPackage/ChannelSubscribeOptions/ChannelSubscription/ChannelPackage` (+ Rest ab `:1616`) | `index.d.ts:1481-1640` | ja (Funktionen) — `hooks-daten.md:114-195` (cache), `hooks-netze.md:69-176` (channel, ratelimit) |
Undokumentierte Typen (als Typ-Mapping): `CollectionDocument`, `BoolOrEval`, `FieldImageValidator`, `SearchExplainSource/SearchExplanation/SearchMeta`, `QueryLimitsConfig`, `BulkOptimizeConfig/BulkConfig`, `ChoiceItem` (Varianten/Styles), `PreviewSlots/PreviewConfig`, `CollectionInfo`, `ActionInfo`-Familie (8 Interfaces), `*YourPermissions`-Familie, `ApiInfo/ProjectInfo`, `AuditLogEntry` (als Typ), `DbReadOptions.pipelineMod`.
## 3) Felder-Widgets / Validator-Optionen vs. `projekt/felder-widgets.md`
Widget-Enum (`field-meta.schema.json:14-25`): `text, textarea, number, checkbox, switch, select, selectArray, chipArray, date, datetime, file, image, json, richtext, html, foreignKey, foreignKeyChipArray, foreignFile, foreignMedia, pagebuilder, pageBuilder, templateProps, containerLessObject, containerLessObjectArray, color` — alle in `felder-widgets.md` + `admin/eintrags-editor.md` per grep nachweisbar (Stichprobe §-Befund: alle 20+ Treffer). Lücken: `pagebuilder` vs. `pageBuilder` (beide im Enum, Doku unterscheidet nicht — nicht verifiziert), `containerLessObject` vs. `containerLessObjectArray` (beide erwähnt, Unterschied nicht verifiziert), `image` vs. `file` vs. `foreignFile` vs. `foreignMedia` (alle erwähnt, Abgrenzung nur in `dateien-bilder.md` angerissen — teilweise).
Validatoren (`felder-widgets.md:60-78`): `required, pattern, in, minLength/maxLength, min/max, minDate/maxDate, minItems/maxItems, format(email/url/uuid/slug), maxFileSize/minFileSize, accept, eval, allowZero` — alle dokumentiert. Fehlt: `image`-Unterschlüssel (`minWidth/maxWidth/minHeight/maxHeight/allowNonImages/allowUnknownDimensions`, `field.schema.json:130-142`, `index.d.ts:16-29`) — kein Treffer in Doku außer `minWidth`-Messung in `dateien-bilder.md:93` (Medialib-Anzeige, kein Validator). Meta-Lücken (s. §1): `hideLabel, containerProps, position, pageSize, addElementLabel, imageEditor, schemaRef, filter-Flag (field-meta.schema.json:242-246), drillDown (nur pagebuilder-dev.md-Treffer), downscale (nur dateien-bilder.md-Treffer)`.
## 4) Starter-Abgleich (`tibi-svelte-starter` → Doku)
| Thema | Beleg (Starter) | Dokuseite | Verdikt | fehlt |
|---|---|---|---|---|
| Klonen + Platzhalter (`__PROJECT_NAME__`, `__TIBI_NAMESPACE__`, `.env`, `api/config.yml`, `.htaccess`) | `README.md:7-22`, `.env` (`PROJECT_NAME=__PROJECT_NAME__` etc.) | `einstieg/schnellstart.md:16-52`, `einstieg/neues-projekt.md:12-43` | abgedeckt | nichts |
| Token-Köpfe (`Token` vs. `X-Admin-Token` vs. `X-Auth-Token`, `ADMIN_TOKEN` in `config.yml.env`) | `AGENTS.md: Token header distinction`, `README-Checkliste` | `einstieg/schnellstart.md:85-94`, `projekt/deploy.md:95-110`, `projekt/rechte-tokens.md` | abgedeckt | nichts |
| `make docker-up`, Docker-Profile, Traefik-Routen | `Makefile`, `docker-compose-local.yml`, `AGENTS.md: Quick reference` | `schnellstart.md:53-74`, `neues-projekt.md:44-54` | teilweise | Traefik-/Router-Details, Profile (`tibi` vs. `tibi-dev`), `docker-restart-frontend`/`docker-logs` nur Starter-lokal |
| `yarn validate` / `yarn build` / `yarn build:server` (`app.server.js`, `buildInfo`) | `package.json: scripts` (`validate`, `build`, `build:server`, `dev/start/start:mock/start:ssr`), `esbuild.config*.js`, `scripts/esbuild-wrapper.js` | `schnellstart.md:75-84`, `ssr-website.md:105-121`, `deploy.md` | abgedeckt (Basis) | `start:mock`/`MOCK=1`-Modus, `esbuild-wrapper`-Flags, `buildInfo.ts`-Mechanik nur angerissen |
| SSR-Hook (`api/hooks/ssr/get_read.js`, `<html lang>`, `<!--HEAD-->`, Cache-Collection `ssr`) | `README.md:24-40`, `api/hooks/ssr/`, `frontend/src/ssr.ts` | `projekt/ssr-website.md` (Routen :23, Lookups :36, Blöcke :70, Bundle :105, Cache :122, Abnahme :146) | abgedeckt | SSR-Cache-Invalidierungsregeln (`PUT/DELETE` vs. `POST`, `?clear=1`) stehen nur als Code-Kommentar in `ssr-website.md:153-164` — ok, aber `ssr.yml`-Felder nicht gemappt |
| Mock-Modus (`MOCK=1`, `__MOCK__`, `frontend/mocking/*.json`, `lib/mock.ts`) | `frontend/src/lib/mock.ts:4-8`, `frontend/src/lib/api.ts:88-89`, `package.json: start:mock` | `einstieg/begriffe.md`, `einstieg/was-ist-tibi.md` (nur Wort-Treffer) | fehlt | Mock-Aktivierung, `mocking/`-Verzeichnis-Format, `__MOCK__`-Flag — kein eigenes Doku-Kapitel (nur Wort in `begriffe.md`) |
| Playwright-Tests (`tests/e2e`, `tests/api`, `test:e2e/test:api/test:visual/tour`, `global-setup.ts`) | `package.json: scripts (test/test:e2e/test:api/test:visual/tour)`, `tests/`, `playwright.config.ts` | keine (kein Treffer `playwright` in `einstieg/*`, `ssr-website.md`, `deploy.md`) | fehlt | gesamtes Test-Setup (Spec-Ordner, `global-setup`, CI ohne Playwright It. `AGENTS.md`) in Projekt-Doku nicht verziehen |
| Frontend-Struktur (`App.svelte`, `routes/`, `blocks/`, `widgets/`, `lib/api.ts`, `store.ts`, `i18n`, Tailwind, Sentry, `svelte:head`) | `frontend/src/` (`App.svelte`, `blocks/*`, `widgets/`, `lib/*`, `config.ts`, `ssr.ts`, `sentry.ts`) | `ssr-website.md` (Routen/Blöcke/i18n/Tailwind angerissen) | teilweise | `widgets/`-Katalog, `lib/`-Module (`api.ts`, `store.ts`, `navigation.ts`, `versionCheck.ts`), Sentry-Verdrahtung, `svelte:head`-Pflicht nur Checklisten-Zeile |
| API-Verzeichnis (`api/collections/*.yml`, `api/hooks/*`, `api/templates/`, `config.yml.env`, `ssr.yml`, `medialib.yml`) | `api/` (`collections/`, `hooks/`, `templates/`, `config.yml`, `config.yml.env`) | `neues-projekt.md:55-104` (Dateien vs. registriert), `sammlungen-anlegen.md`, `ssr-website.md` | teilweise | `templates/`-Verzeichnis, `config.yml.env`-Mechanik (`ADMIN_ASSET_VERSION`), `medialib.yml`-Felder nur Beispiel |
| Env/Deploy (`CODING_URL`, `LIVE_URL`/`STAGING_URL`, `ADMIN_ASSET_VERSION`, `.basic-auth-*`, CI-Skripte `scripts/ci-*.sh`, `validate-schemas.mjs` in tibi-types) | `.env`, `scripts/ci-*.sh`, `AGENTS.md: Required secrets` | `neues-projekt.md`, `deploy.md:12-110` (URL-Bild, Ablauf, Reload-Endpunkte, Token-Trennung) | teilweise | `.basic-auth-*`-Dateien, `ADMIN_ASSET_VERSION`-Mechanik, CI-Skript-Details (`ci-modify-config.sh`, `ci-upload-sourcemaps.sh`), BrowserSync-Proxy — Starter-lokal, Doku schweigt |
| `.agents/`-Wissen (BUILD_CHECKLIST, Skills, `tibi-project-setup`) | `.agents/BUILD_CHECKLIST.md`, `.agents/skills/`, `AGENTS.md` | keine Entsprechung | fehlt (bewusst?) | Build-Checklisten-Phasen und Skill-Pflege sind Starter-intern; Doku muss entscheiden, ob das Projekt-Delivery-Wissen je in `neues-projekt.md` gehört |
Top-5 Starter-Lücken (für Projekt-Doku `schnellstart`/`neues-projekt`/`ssr-website`/`deploy`):
1. Playwright-Test-Setup (`tests/e2e|api|visual`, `test:e2e/test:api/tour`-Skripte) — kein Doku-Kapitel.
2. Mock-Modus (`MOCK=1`/`start:mock`, `mocking/*.json`, `lib/mock.ts`) — nur Wort-Treffer.
3. `api/templates/` + `ssr.yml`-Felder + Cache-Invalidierung als Konzept (nur Code-Kommentar in `ssr-website.md:153-164`).
4. CI/Env-Mechanik (`.basic-auth-*`, `ADMIN_ASSET_VERSION`, `scripts/ci-*.sh`, `esbuild-wrapper`-Flags).
5. Frontend-`lib/`-Module + `widgets/`-Katalog + Sentry-Verdrahtung (nur angerissen).
## 5) Löschkandidaten (Datei → Inhalt → Doku-Status)
tibi-types:
- `index.d.ts` (1716 Zeilen: alle Hook-Typen) → in tibi-docs **teilweise** (Funktionen ja, Typen nein) → **muss bleiben** (einzige Typquelle; Starter referenziert via `types/global.d.ts` + `tibi-types/`-Submodul lt. `AGENTS.md`).
- `schemas/config/*.json` (16 Dateien) → in tibi-docs **teilweise** (`schemas.md:10-33` listet, Details fehlen) → **muss bleiben** (einzige Form-Quelle; Doku verlinkt per `$schema`-Zeile).
- `scripts/validate-schemas.mjs` → Inhalt nicht verifiziert (nicht gelesen) → **nicht verifiziert** (vermutlich Build-Hilfe → muss bleiben).
- `demo-api/` (`config.yml`, `collections/mycol.yml`, `collections/fields|global/`, `actions/`, `hooks/`, `testfiles/`) → Doku-Beispiele nutzen Starter-`api/`, nicht `demo-api` → **teilweise/nicht abgedeckt**; vor Löschen prüfen, ob Tests/Validierung sie nutzen (nicht verifiziert) → **nicht löschen ohne Prüfung**.
- `package.json`/`tsconfig.json`/`README` (falls vorhanden) → Paket-Metadaten → **muss bleiben**.
tibi-svelte-starter:
- `README.md` (Setup-Checkliste :7-22, Architektur/SSR :24-40) → in tibi-docs **abgedeckt** (`schnellstart.md`, `neues-projekt.md`, `ssr-website.md`) → **muss bleiben** (Einstiegspunkt im Repo; Doku dupliziert mit Absicht für Website-Kontext).
- `AGENTS.md` (Token-Köpfe, Medialib-URL-Pattern, Constraints, Secrets, Skills-Pflege) → in tibi-docs **teilweise** (Token-Köpfe abgedeckt, Medialib-Pattern in `dateien-bilder.md`, Skill-/Constraint-Wissen nicht) → **muss bleiben** (Agenten-Arbeitsanweisung im Repo).
- `api/` (`collections/*.yml`, `hooks/ssr/*`, `templates/`, `config.yml`, `config.yml.env`) → in tibi-docs **teilweise** (Konzepte ja, Feld-Mapping nein) → **muss bleiben** (lauffähiges Beispiel).
- `frontend/src/` (App/blocks/widgets/lib/routes) → **teilweise** → **muss bleiben**.
- `.agents/` (`BUILD_CHECKLIST.md`, `STARTER_ALIGNMENT_*.md`, `skills/`) → in tibi-docs **nicht abgedeckt** → **muss bleiben** (Starter-interne Liefer-/Pflege-Doku, kein Doku-Duplikat).
- `docker-compose-*.yml`, `Dockerfile`, `Makefile`, `esbuild.config*.js`, `scripts/esbuild-wrapper.js`, `.env` → **teilweise** (Befehle ja, Details nein) → **muss bleiben**.
- `tests/` + `playwright.config.ts` + `video-tours/` → in tibi-docs **nicht abgedeckt** → **muss bleiben** (kein Ersatz in Doku).
- `tibi-types/`-Submodul + `types/global.d.ts` → Typ-Brückenkopf → **muss bleiben**.
Zählung: 0 Dateien mit Verdikt „löschen". 2× „muss bleiben" (Typ-/Schema-Quellen), Rest „teilweise/nicht abgedeckt, muss bleiben" (lauffähige Beispiele, Tests, Ops-Dateien). Einziger Klärungsfall: `tibi-types/demo-api/` (Nutzung durch Tests/CI vor Lösch-Entscheidung prüfen — nicht verifiziert).
+106
View File
@@ -0,0 +1,106 @@
# Audit 04 — Alt-Doku-Inventar vs. tibi-docs (zentrale Doku)
> Audit-Agent, nichts geändert/gelöscht. Stand: 2026-10-16.
> Kernbefund: `tibi-docs` wurde per Commit `06c8363` („Neuaufbau: Inhalte bis auf Einstieg entfernt") neu aufgebaut —
> die 24 englischen Server-Docs + 3 Nova-Docs liegen **nicht** als Sync-Artefakte in `tibi-docs` (git-ls-files zeigt nur je 5/10 Handseiten).
> `scripts/sync-server-docs.mjs` + `AGENTS.md` („server/ = SYNC") beschreiben einen Sync, dessen Ergebnis nicht (mehr) im Repo steht —
> ein Lauf würde 24+3 Dateien als verwaiste Seiten (kein Sidebar-Eintrag in `astro.config.mjs`) einspielen.
## 1) Datei-Inventar
### A. `tibi-server/docs/` — alle 24 Dateien einzeln
| Pfad | Inhalt (1 Zeile) | Ziel in tibi-docs | Bewertung | Begründung |
|---|---|---|---|---|
| `tibi-server/docs/01-architecture.md` (257 Z.) | Architektur-Überblick (Go, Module) | `server/uebersicht.md` | behalten-intern | Core-Referenz für Server-Entwicklung; tibi-docs nur Nutzer-Sicht |
| `tibi-server/docs/02-configuration.md` (705 Z.) | Server-Konfiguration (Env, YAML) | `server/konfiguration.md` | behalten-intern | Vollreferenz (705 Z.) vs. Handseite; Quelle-der-Wahrheit für Core |
| `tibi-server/docs/03-api-endpoints.md` (2406 Z.) | REST-Routen-Referenz, englisch | `server/schnittstellen.md` + `projekt/abfragen-lesen.md` | behalten-intern | Größte Datei; tibi-docs fasst nutzerorientiert zusammen, ersetzt keine Referenz |
| `tibi-server/docs/04-collections.md` (1745 Z.) | Collection-System im Detail | `projekt/sammlungen-anlegen.md`, `projekt/felder-widgets.md` | behalten-intern | Detailtiefe (1745 Z.) bleibt Core-Referenz |
| `tibi-server/docs/05-authentication.md` (602 Z.) | Auth (Login, JWT, Tokens, Impersonation) | `server/sicherheit.md` + `projekt/rechte-tokens.md` | behalten-intern | stimmen überein (`POST /api/v1/login`, Impersonation); Detail bleibt hier |
| `tibi-server/docs/06-hooks.md` (1717 Z.) | JS-Hook-Laufzeit, alle Stufen | `projekt/hooks-schreiben.md` + `reference/hook-kontexte.md` | behalten-intern | 1717 Z. Vollreferenz; tibi-docs deckt 24 Kontexte + Signaturen ab, aber kein Ersatz |
| `tibi-server/docs/07-realtime.md` (186 Z.) | SSE Pub/Sub-Channels | `server/schnittstellen.md` (Realtime-Abschnitt) | behalten-intern | kurze Referenz, intern weiter nötig |
| `tibi-server/docs/08-file-upload-images.md` (288 Z.) | Uploads + Bildfilter | `projekt/dateien-bilder.md` | behalten-intern | Core-Referenz |
| `tibi-server/docs/09-llm-integration.md` (256 Z.) | LLM-Chat-API, Budgets | `projekt/llm-nutzen.md` | behalten-intern | tibi-docs-Seite war lt. `plans/doku-ausbau.md` „Hülle" — prüfen vor Wegfall-Urteil |
| `tibi-server/docs/10-audit.md` (273 Z.) | Audit-Log (TTL, Lesen) | `projekt/audit-backup.md` | behalten-intern | Core-Referenz |
| `tibi-server/docs/11-jobs.md` (150 Z.) | Cron-Jobs | `projekt/jobs-actions.md` | behalten-intern | kurz, kein Löschgrund (Single Source) |
| `tibi-server/docs/12-deployment.md` (148 Z.) | Deployment | `projekt/deploy.md` + `server/betrieb.md` | behalten-intern | kurz, Core-Sicht |
| `tibi-server/docs/13-openapi-metrics.md` (147 Z.) | OpenAPI + Prometheus | `reference/openapi.md` + `server/betrieb.md` | behalten-intern | kurz, Core-Sicht |
| `tibi-server/docs/14-security.md` (632 Z.) | Security-Audit (englisch) | `server/sicherheit.md` | behalten-intern | Audit-Charakter (632 Z.), nicht nutzerrelevant 1:1 |
| `tibi-server/docs/15-troubleshooting.md` (228 Z.) | Troubleshooting/FAQ | `reference/fehlercodes.md` + `anwender/hilfe.md` | behalten-intern | Core-Fehlersuche vs. Anwender-Hilfe |
| `tibi-server/docs/16-cookbook.md` (682 Z.) | Collection-Config-Rezepte | `projekt/sammlungen-anlegen.md` | behalten-intern | Rezeptsammlung für Core-/Power-User |
| `tibi-server/docs/17-field-level-permissions.md` (546 Z.) | Feld-Rechte im Detail | `projekt/rechte-tokens.md` | behalten-intern | Detailreferenz |
| `tibi-server/docs/18-orgs-teams.md` (716 Z.) | Orgs & Teams | `admin/benutzer-rechte.md` + `anwender/benutzer-teams.md` | behalten-intern | 716 Z. Modell-Referenz vs. Bedienungs-Seiten |
| `tibi-server/docs/19-actions.md` (311 Z.) | Actions | `projekt/jobs-actions.md` + `admin/aktionen.md` | behalten-intern | Core-Referenz |
| `tibi-server/docs/20-referenced-read-safety.md` (70 Z.) | GET-Sichtbarkeits-Sicherheitsregel | `server/sicherheit.md` (unklar ob abgedeckt) | behalten-intern | Sicherheitsregel für Core-Entwicklung, 70 Z. |
| `tibi-server/docs/21-backup-restore.md` (530 Z.) | Backup/Restore | `projekt/audit-backup.md` | behalten-intern | inkl. Snapshots-Plan-Kontext; Core-Referenz |
| `tibi-server/docs/22-personal-access-tokens.md` (291 Z.) | Personal Access Tokens, Scopes | `projekt/rechte-tokens.md` + `reference/token-koepfe.md` | behalten-intern | Scope-Modell-Referenz |
| `tibi-server/docs/23-mcp.md` (152 Z.) | MCP-Server (Routen, Env) | `projekt/mcp-nutzen.md` (+ `server/schnittstellen.md`) | behalten-intern | **Widerspruch** zu tibi-docs, siehe §4 |
| `tibi-server/docs/24-ephemeral-rest-tokens.md` (71 Z.) | Ephemere REST-Tokens, Status PLAN | `reference/token-koepfe.md` (unklar ob abgedeckt) | behalten-intern | Plan-Status, nicht implementiert — nicht löschen, als Plan kennzeichnen |
Fazit A: **0 Wegfall-Kandidaten** in `tibi-server/docs/`. Alle 24 = englische Core-Referenz (Single Source); tibi-docs ist deutsche Nutzer-Doku, kein Ersatz.
### B. `tibi-admin-nova/docs/` — alle Dateien einzeln
| Pfad | Inhalt (1 Zeile) | Ziel in tibi-docs | Bewertung | Begründung |
|---|---|---|---|---|
| `tibi-admin-nova/docs/collection-config.md` (2293 Z.) | Collection-Config-Referenz (größte Nova-Datei) | `admin/*` + `projekt/sammlungen-anlegen.md` | behalten-intern | Vollreferenz; von tibi-docs-T-ADMIN-SO als Quelle genutzt, nicht ersetzt |
| `tibi-admin-nova/docs/collection-export.md` (327 Z.) | CSV/XLSX/JSON-Export aus Listen | `admin/exporte.md` + `anwender/exportieren.md` | behalten-intern | Detail (u. a. `qm`-Param); tibi-docs fasst zusammen |
| `tibi-admin-nova/docs/custom-export-script.md` (117 Z.) | Custom-Export-Skripte (`collection.meta.exports`) | `admin/exporte.md` | behalten-intern | API-Detail (`db.loadAll`/`ctx.xlsx`); T-ADMIN-A-SO belegt Nutzung als Quelle |
| `tibi-admin-nova/docs/test-plans/` (19 Dateien, Nrn. 02,05,07,11,16,17,25,27,34–44) | Manuelle UI-Testpläne (Navigation, Editor, Upload, i18n, Pagebuilder, Backup …) | `admin/*` als Quelle, kein 1:1-Ziel | behalten-intern | Test-Artefakte, keine Nutzer-Doku; T-ADMIN-B-SO verlinkt Test-Pläne 40/42 |
### C. Zusammengefasst je Verzeichnis
| Verzeichnis | Inhalt | Ziel in tibi-docs | Bewertung | Begründung |
|---|---|---|---|---|
| `tibi-server/README.md` | Feature-Matrix + Tech Stack (englisch) | `server/uebersicht.md` | behalten-öffentlich | Repo-Einstieg; kein tibi-docs-Ersatz |
| `tibi-server/design/` (2 Dateien: `snapshot-plan.md`, `snapshot-review-2026-06-28.md`) | Snapshot-Architekturplan + Review | `projekt/audit-backup.md` (Ergebnis, nicht Plan) | behalten-intern | Architektur-Referenz für Core-Entwicklung |
| `tibi-server/examples/` (`mcp/curl.sh`, `testproject/*`, `testproject2/collections/*.yml`) | Lauffähige Beispiel-Configs/Skripte | keines (Code, keine Doku) | behalten-intern | Test-/Beispiel-Artefakte |
| `tibi-server/plans/` (22 Dateien: ISSUES, Backup-, MCP-, Impersonation-, Org/Team-Pläne …) | Arbeitspläne Core-Entwicklung | keines | behalten-intern | aktive/historische Arbeitspläne |
| `tibi-admin-nova/README.md` | Setup-Anleitung Nova (git-lfs, yarn) | `admin/setup.md` | behalten-intern | Entwickler-Setup des Admin-Repos |
| `tibi-admin-nova/frontend/docs/*.pdf` (4 Dateien, je 1 Seite, <1,5 KB) | `brand-guidelines`, `design-specs`, `media-kit-2026`, `validation-report` | keines bekannt | unklar | Winzig-Platzhalter-PDFs — Inhalt ungeprüft; Ersteller fragen |
| `tibi-admin-nova/plans/{open,in-progress,closed}/` (26 Dateien) | Feature-Arbeitspläne (Actions, Pagebuilder, Import/Export, Voice …) | keines | behalten-intern | Arbeitspläne, keine Doku |
| `tibi-admin-nova/plans/test-plans/{open,closed,…}` (~40 Dateien) | Manuelle Testpläne aller UI-Bereiche | als Quelle für `admin/*`, `anwender/*` | behalten-intern | Test-Artefakte; T-ADMIN-B-SO verlinkt 40/42 |
| `tibi-svelte-starter/README.md` | Starter-Setup-Checkliste + SSR-Architektur | `einstieg/neues-projekt.md`, `projekt/ssr-website.md` | behalten-öffentlich | Repo-Einstieg des Templates; als Eigen-Datei nötig |
| `tibi-svelte-starter/AGENTS.md` | Token-Header-Regel, Medialib-URL-Pattern, Constraints | `reference/token-koepfe.md` (Header), `projekt/*` (teils) | behalten-intern | Arbeitsanweisung für Agenten im Starter-Repo |
| `tibi-docs-legacy/docs/` (`begriff.md`, `probleme.md`, `servergrundlagen`, `restapi`, `tibiAI`, `server-javascript-kontext` …) | Alte deutsche TibiCMS-Doku (Docpress-Ära) | `einstieg/begriffe.md` u. a. (unklar, Volltext-Abgleich fehlt) | unklar | historisch; vor Löschung Volltext-Abgleich gegen tibi-docs nötig |
| `tibi-starter/docs/` (docpress.json, md/, …) | offenbar weitere Legacy-Doku-Kopie | keines geprüft | unklar | nur oberflächlich geprüft — Inventar fehlt |
## 2) Wegfall-Kandidaten (konkret)
**Keine Datei erfüllt „kann weg, weil in tibi-docs abgedeckt".** Zählung: **0 Wegfall-Kandidaten, 27 Muss-bleiben-Dateien** (24 + 3).
Stattdessen wegfall-/reparaturbedürftig auf Meta-Ebene:
1. `tibi-docs/scripts/sync-server-docs.mjs` — Sync-Ziel (`server/01–24*.md`, `admin/collection-*.md`) steht nicht im Repo und nicht in der Sidebar (`astro.config.mjs` listet nur 5/10 Handseiten). Entweder reaktivieren (mit Sidebar-`autogenerate` + Linkpflege) oder Skript + AGENTS.md-Sync-Abschnitt als veraltet kennzeichnen. Ungenutzter Code mit falscher Doku-Behauptung.
2. `tibi-docs/AGENTS.md` §2-Tabelle („server/ = SYNC — nicht von Hand editieren") + §4-Sync-Workflow — widersprechen dem tatsächlichen Neubau-Stand (nur Handseiten, `git log 06c8363`). Korrektur fällig, kein Datei-Wegfall.
3. `tibi-docs-legacy/`, `tibi-starter/docs/`, `tibi-docs-kaputt/` — nach Volltext-Abgleich (siehe §5) Löschkandidaten auf **Repo-Ebene**, nicht Datei-Ebene.
Sync-Abhängigkeit: nichts in `tibi-server/docs/` oder `tibi-admin-nova/docs/` darf gelöscht werden, **weil** das Sync-Skript genau diese Pfade als Quelle liest (`serverSrc`, `novaSrc` in `sync-server-docs.mjs:14–15`); Löschen dort bräche einen evtl. reaktivierten Sync.
## 3) Muss bleiben (Begründung)
- `tibi-server/docs/01–24` (alle): einzige vollständige englische Core-Referenz + Single Source für Reaktivierung des Syncs.
- `tibi-admin-nova/docs/*.md` (3): Config-/Export-Referenz, von tibi-docs-Autoren als Quelle benutzt (Second Opinions in `plans/doku-ausbau.md`).
- `tibi-admin-nova/docs/test-plans/` + `plans/test-plans/`: manuelle Testpläne, keine Nutzer-Doku, weiter als Test-Artefakte nötig.
- `tibi-admin-nova/plans/` + `tibi-server/plans/` + `tibi-server/design/`: Arbeits-/Architekturpläne der Core-Entwicklung.
- `tibi-server/examples/`, READMEs, `tibi-svelte-starter/{README,AGENTS}.md`: Repo-Einstiege + Agenten-Arbeitsanweisungen.
## 4) Dubletten/Widersprüche
1. **MCP-Route konfigurierbar vs. fest.** `tibi-server/docs/23-mcp.md:42,47` (`route: "/api/v1/mcp"`, Env `MCP_ROUTE`) vs. `tibi-docs/src/content/docs/server/schnittstellen.md:77` („fest `POST` + `GET /api/v1/mcp` — ein gesetztes `mcp.route`/`MCP_ROUTE` wird ignoriert"). Eine Seite irrt — am Code (`internal/controller/setup.go`, `internal/config/server.go`) klären.
2. **Sync-Behauptung vs. Neubau-Realität.** `tibi-docs/AGENTS.md:25–26,39–48` („server/ = SYNC", Sync-Workflow) vs. `git log 06c8363` + `git ls-files` (nur Handseiten, keine Sync-Artefakte). AGENTS.md ist veraltet.
3. **Login-Pfad-Darstellung.** `tibi-server/docs/05-authentication.md:12` (`POST /api/v1/login`) vs. `tibi-docs/.../server/schnittstellen.md:12,27` (verkürzt `/login`, Tabelle mit Präfix `/api/v1`). Inhaltlich konsistent, aber Kürzung riskiert Copy-Paste-Fehler — unklar, kein harter Widerspruch.
4. **Token-Header `Token` vs. reservierte Namen.** `tibi-svelte-starter/AGENTS.md` (Collection-API: Header-Name aus Permission-Key, typisch `Token`) vs. `tibi-docs/.../reference/token-koepfe.md:52` (reservierte Namen `x-auth-token`/`x-admin-token`/`authorization` fail-closed). Ergänzend, aber Starter-Regel „typisch `Token`" ohne Warnung vor reservierten Namen — Angleichung empfohlen.
5. **LLM-/MCP-Seiten Hüllen-Status.** `plans/doku-ausbau.md` listet `projekt/llm-nutzen` + `projekt/mcp-nutzen` als „Hülle" — falls weiterhin Hüllen, sind `09-llm-integration.md`/`23-mcp.md` die einzigen vollständigen Quellen; kein Widerspruch, aber Abhängigkeit.
## 5) Repo-Hygiene-Funde
| Verzeichnis | Befund | Empfehlung |
|---|---|---|
| `/WM_Dev/src/gitbase.de/cms/tibi-admin.bak/`, `tibi-admin.bak2/` (je eigenes `.git`-Repo, Woocommerce-Ära-Layout) | tote Vollkopien | archivieren/löschen nach Backup-Abgleich |
| `/WM_Dev/src/gitbase.de/cms/tibi-admin.kaputt/` (eigenes `.git`) | defekte Kopie per Namensgebung | löschen nach Backup-Abgleich |
| `/WM_Dev/src/gitbase.de/cms/tibi-server.bak/` | Server-Backup-Kopie | löschen nach Abgleich |
| `/WM_Dev/src/gitbase.de/cms/tibi-docs-kaputt/` (eigenes `.git`) | defekte Doku-Kopie | löschen nach Abgleich |
| `/WM_Dev/src/gitbase.de/cms/tibi-admin/`, `tibi-admin-dev/`, `tibi-admin-master/` (READMEs sagen alle „tibi-svelte-starter", alte Svelte-Geschichte) | alte Starter-/Admin-Varianten, unklar ob referenziert | klären, dann archivieren/löschen |
| `/WM_Dev/src/gitbase.de/cms/tibi-admin-server/` (Python `run.py`, `uploads/`) | artfremder Uploader-Dienst | prüfen, ob noch deployed; sonst archivieren |
| `/WM_Dev/src/gitbase.de/cms/tibi-starter/` vs. `tibi-svelte-starter/` | zwei Starter-Varianten parallel | eine als kanonisch erklären, andere archivieren |
| `tibi-docs-legacy/` (Docpress + `node_modules/` eingecheckt) | Legacy-Doku inkl. `node_modules/` | nach Volltext-Abgleich archivieren/löschen |
+55
View File
@@ -0,0 +1,55 @@
# Audit: Admin- und Referenz-Doku
- Umfang: `src/content/docs/admin/*.md` (10 Dateien, 920 Zeilen) und `src/content/docs/reference/*.md` (11 Dateien, 1434 Zeilen), jeweils vollständig gelesen.
- Datum: 2026-10-16. Code-Stand: tibi-admin-nova `2b091d89`, tibi-server `68b42785`, tibi-types `4c55f82` (aus den `Verifiziert`-Zeilen).
- Belege: `frontend/src/lib/i18n/locales/de.json` (1456 Schlüssel), `frontend/assets/system.config.json`, `config/ai-proxy.config.json`, `frontend/src/lib/**`, `tibi-server/internal/**`, `tibi-types/schemas/config/` (16 Dateien, Angabe in `schemas.md` stimmt).
- Alle internen Links (`/admin/*`, `/projekt/*`, `/reference/*`, `/anwender/anmelden/`) lösen auf existierende Dateien auf. Alle Codeblöcke sind balanciert. Keine Datei überschreitet 500 Zeilen (Maximum: `hooks-daten.md` mit 413).
## Befunde
| # | Datei:Zeile | Kategorie | Befund (kurz) | Vorschlag |
|---|---|---|---|---|
| 1 | `reference/openapi.md:1-5` | PLATZHALTER | Datei hat nur 5 Zeilen, kein `description`-Frontmatter, Inhalt nur „Stand: Platzhalter“. | `npm run sync:openapi` ausführen oder Seite bis dahin aus der Sidebar nehmen. |
| 2 | `reference/hook-kontexte.md:63-69` | PLATZHALTER | Abschnitt „Fehlende oder veraltete Einträge melden“ mit „Die Hülle nannte nur …“ ist Aufbau-Rückstand, kein Nutzer-Inhalt. | Abschnitt streichen; Rest als normale Einleitung („24 Pakete, Stand …“) formulieren. |
| 3 | `admin/ki-funktionen.md:91` | UI-TEXT | `open_collection_record` wird mit Anzeige „(Datensatz öffnen)“ zitiert, aber weder `de.json` (`admin.ai.tools.*`, 56 Schlüssel geprüft) noch `frontend/src/lib` kennen „Datensatz öffnen“. | Label im Code ergänzen oder Doku auf Tool-Namen zurückstufen wie bei `toggle_sidebar`/`drill_navigate`. |
| 4 | `admin/aktionen.md:54` + `admin/ki-funktionen.md:23` | UI-TEXT | „Kurzfassung“ als UI-Name zitiert; `de.json` kennt für `outputSummary` aber „Zusammenfassung“, „Kurzfassung“ kommt in `de.json` und `frontend/src/lib` nicht vor. | Auf „Zusammenfassung“ korrigieren oder Code-Beleg nachliefern. |
| 5 | `admin/aktionen.md:56` | UI-TEXT | Fallback-Text „keine Ausgabefelder“ in Anführungszeichen, aber weder in `de.json` noch in `frontend/src/lib` gefunden. | Exakten Code-String übernehmen oder als Beschreibung (ohne Anführungszeichen) formulieren. |
| 6 | `admin/benutzer-rechte.md:16` | UI-TEXT | „60-Sekunden-Countdown“ fett als UI-Name; Dauer 60 s stimmt (`SessionTimeoutWarning.svelte:18` `COUNTDOWN_SECONDS = 60`), aber die Zeichenkette steht nicht in `de.json`. | Fettung entfernen, beschreibend formulieren („… warnt dich 60 Sekunden vorher …“). |
| 7 | `reference/tastatur-kurzbefehle.md:39` + `admin/ki-funktionen.md:99-101` | MD-FEHLER | Gemischte Anführungszeichen: „Eintrag speichern", „Theme wechseln", „Sprache wechseln", „UI erklären" u. a. schließen mit `"` statt `"`. | Durchgehend deutsche Zeichen („…") verwenden. |
| 8 | 9 Dateien (s. Liste unten) | MD-FEHLER | Ungenutzte Starlight-Importe (nie als Komponente verwendet). | Importzeilen bereinigen. |
| 9 | `admin/benutzer-rechte.md:57` | STIL | „Platzhalter-Beispiel: `pages, media`“ — inhaltsleeres Beispiel in einer Handseite. | Reales Beispiel aus der Projekt-Meta oder streichen. |
| 10 | `reference/tastatur-kurzbefehle.md:8` | STIL | „Du musst die Maus nicht anfassen“ — flapsig, passt nicht zum sachlichen Ton aus AGENTS §1. | Streichen oder sachlich („Alle Funktionen sind per Tastatur erreichbar“). |
| 11 | `admin/ki-funktionen.md:87-101` | STRUKTUR | Eine Tabelle mit 62 Werkzeugen ist als Nachschlagewerk kaum lesbar. | Pro Gruppe eine eigene kleine Tabelle unter Zwischenüberschriften; Einleitungssatz „Labels wörtlich aus `de.json`“ einschränken (6 Tools haben dort keinen Eintrag: `open_collection_record`, `toggle_sidebar`, `open_field_search`, `drill_navigate`, `get_collection_records`, `get_collection_schema`). |
| 12 | `admin/customizing.md:23-30` ↔ `admin/eintrags-editor.md:29-43` ↔ `projekt/felder-widgets.md` | STRUKTUR | Widget-Liste steht an drei Stellen (zwei Widget-Tabellen im Admin plus Feldtypen im Projekt). | Eine kanonische Tabelle (Projekt-Seite), Admin-Seiten verlinken nur. |
| 13 | `admin/ansichten-listen.md:69-73` + `admin/eintrags-editor.md:77-92` ↔ `reference/tastatur-kurzbefehle.md` | STRUKTUR | Tastaturkürzel doppelt gepflegt (Listen-/Editor-Seiten und Referenz). | Auf den Referenz-Seiten nur die seitenrelevanten Kürzel behalten, Rest verlinken. |
| 14 | `admin/*` → `anwender/*` | STRUKTUR | Keine Admin-Seite verlinkt die Anwender-Anleitungen (nur `konzept.md` erwähnt den Bereich). | Je Seite einen Satz mit Link auf das passende Anwender-Pendant (z. B. Editor → `anwender/eintrag-bearbeiten/`). |
| 15 | `admin/ki-funktionen.md:30` | STARLIGHT | Dichte Kennzahlen-Prosa („grün < 40 %, gelb < 70 % …“) als Fließtext. | Als Tabelle (Schwelle → Farbe) oder `Badge`-Legende darstellen. |
| 16 | `admin/konzept.md:1-36` | STRUKTUR | Nur 36 Zeilen, reiner Hub ohne eigenen Inhalt — für die Zielgruppe (Admin einrichten/anpassen) ohne Mehrwert über die Links hinaus. | Entweder ausbauen (Betriebsmodell: SPA, Mock, Reverse Proxy in 2–3 Sätzen) oder mit `setup.md` zusammenführen. |
| 17 | `admin/aktionen.md:45` | PRUEFEN | Route `POST /api/v1/<namespace>/_actions/<name>` nicht gegen `action_handler.go` geprüft. | Pfad und `?method=`-Parameter am Code verifizieren. |
| 18 | `admin/benutzer-rechte.md:25` | PRUEFEN | „Passwort (min. 6 Zeichen, doppelt bestätigt)“ nicht gegen `UserTab`/`UserDetailDrawer` geprüft. | Mindestlänge und Doppelbestätigung am Code belegen. |
| 19 | `reference/token-koepfe.md:29` | PRUEFEN | „Groß-/Kleinschreibung egal“ beim `Token`-Header nicht gegen `permission.go`/Header-Auswertung geprüft. | Am Code belegen oder streichen. |
| 20 | `admin/ki-funktionen.md:60-63` | PRUEFEN | STT-Details (Proxy-Modell `large-v3-turbo`, 3 s Stille, Schnipsel < ~1 KB) nur gegen `system.config.json` (Endpunkt `/api/ai/transcribe` stimmt) geprüft, nicht gegen `aiVoiceInput.ts` und `ai-proxy.config.json`. | Modellnamen und Schwellen am Code belegen. |
| 21 | `admin/setup.md:39-43` | PRUEFEN | Ports (3000/8080) und Profile (`tibi`/`tibi-dev`) nur gegen `Makefile` (Targets existieren) geprüft, nicht gegen `docker-compose-local.yml`/`.env`. | Ports und Profilnamen an Compose-Datei belegen. |
Ungenutzte Importe zu Nr. 8: `aktionen.md` (`Card`, `CardGrid`, `FileTree`), `ansichten-listen.md` (`Steps`, `Card`, `CardGrid`), `benutzer-rechte.md` (`Card`, `CardGrid`, `FileTree`), `customizing.md` (`Steps`, `Card`, `CardGrid`, `Badge`, `FileTree`), `eintrags-editor.md` (`Card`, `CardGrid`, `Badge`), `exporte.md` (`Tabs`, `TabItem`, `Card`, `CardGrid`, `Badge`), `ki-funktionen.md` (`Card`, `CardGrid`, `Badge`, `FileTree`), `medien.md` (`Aside`, `Card`, `CardGrid`, `Badge`), `setup.md` (`Card`, `CardGrid`, `Badge`, `FileTree`).
## Fehlende Screenshots je Admin-Seite
Alle 9 Handseiten außer `konzept.md` enthalten einen `Screenshot-Platzhalter (T-SHOTS)`; keine Seite hat bisher ein Bild unter `src/assets/`:
- `aktionen.md:29` — Action-Ansicht mit Methoden-Reitern, Eingabeformular und Ausführen-Knopf.
- `ansichten-listen.md:103` — Listenansicht mit Suchfeld, Filter-Popover und Paginierung.
- `benutzer-rechte.md:103` — Hub mit Benutzerliste und Detail-Drawer inkl. Rollen-Badge und Chips.
- `customizing.md:87` — Angepasste Listenansicht mit Spaltenwahl und eigenem Export-Reiter.
- `eintrags-editor.md:103` — Editor mit Formular, Seitenleiste und Übersetzungsleiste.
- `exporte.md:83` — Export-Dialog mit Formatwahl und Spaltenliste.
- `ki-funktionen.md:132` — Geöffneter KI-Chat mit Werkzeugstatus und Mikro-Knopf.
- `medien.md:81` — Medienraster mit Warteschlange und Bildeditor-Dialog.
- `setup.md:83` — Frisch gestarteter Admin mit Projektliste nach Login.
## Unsichere Punkte
- Nr. 17–21 oben (mit `PRUEFEN` markiert): jeweils Doku-Zitat gegen noch nicht gelesene Code-Stelle prüfen.
- Tastatur-Seite Zeile 57–61: Die Behauptung, die Overlay-Zeile `u` habe „keinen passenden Text in der Sprachdatei (Schlüssel `toggleUsers` fehlt) und keinen eigenen Handler“, ist plausibel, wurde aber nicht gegen `shortcuts.ts`/`KeyboardShortcuts.svelte` verifiziert.
- `fehlercodes.md` und `hooks-*.md` zitieren Fehler-Strings mit Datei- und Zeilenangaben aus `tibi-server`; stichprobenhaft bestätigt (`reponse.header`-Tippfehler in `context_response.go:29`, Legacy-Default in `http_status.go`, 16 Schema-Dateien), aber nicht jede einzelne Zeilennummer nachgeprüft — Zeilenangaben driften bei Code-Änderungen schnell.
- Positiv: keine `man`-Form gefunden, Umlaute durchgehend korrekt, alle Frontmatter (außer `openapi.md`) mit Titel und Beschreibung, keine toten internen Links.
+58
View File
@@ -0,0 +1,58 @@
# Review: Anwender-Doku, Index und ADRs
- Umfang: `src/content/docs/anwender/*.md` (11 Dateien), `src/content/docs/adr/*.md` (4 Dateien), `src/content/docs/index.mdx`.
- Datum: 2026-10-16. Gegenprüft mit `tibi-admin-nova` Commit `2b091d89` (existiert) und `frontend/src/lib/i18n/locales/de.json`.
- Positiv vorweg: alle 23 internen Links der Anwender-Seiten und alle 6 Karten-Links in `index.mdx` lösen auf existierende Seiten auf. Alle 30 Bilder sind je genau einmal referenziert, alle ≤ 200 KB, alle mit Alt-Text. Keine Babysprache („keine Angst" u. Ä.) gefunden; Du-Form durchgehend eingehalten (eine Ausnahme, siehe #14).
## Befund-Tabelle
| # | Datei:Zeile | Kategorie | Befund (kurz) | Vorschlag |
|---|-------------|-----------|---------------|-----------|
| 1 | orientierung.md:20 | MD-FEHLER | Alt-Text enthält Tippfehler „Brotkrumen"; UI-Begriff lautet „Navigationspfad" (`drawer.breadcrumb`, de.json:96). | Alt-Text: „Projektübersicht der Corporate Website mit Kopf, Navigationspfad und Sammlungen". |
| 2 | ki-nutzen.md:105 | MD-FEHLER | Redaktionelle Produktionsnotiz („KI-Antworten … simuliert … lief ohne sichtbares Banner durch") steht als Fließtext im Lesebereich. | Absatz entfernen oder in HTML-Kommentar wandern lassen; simulierte Stellen ersatzweise als Aside „Beispiel" kennzeichnen. |
| 3 | ki-nutzen.md:45-49 vs. :105 | FEHLER-INHALT | Schritte beschreiben Banner-Ablauf („Kontext wird zusammengefasst…" → „Kontext zusammengefasst"), Fußnote gibt zu, dass der Ablauf „ohne sichtbares Banner durchlief" und „Statusanzeigen simuliert" sind — das widerspricht dem „Geprüft an"-Vermerk. Strings selbst belegt (de.json:1028-1029). | Echte Screenshots aus Mock-Projekt aufnehmen; bis dahin Ablauf als „in der Regel" formulieren. |
| 4 | orientierung.md:58-71, hilfe.md:31 | UI-TEXT | Bereich heißt durchgehend „Schnellsuche"; UI-Name ist „Befehlspalette" (de.json:1207), Platzhalter „Befehl eingeben…" (de.json:1154). „Schnellsuche" kommt im Frontend-Code nirgends vor. | Überschrift und alle Erwähnungen in „Befehlspalette" umbenennen; „öffnet mit /" (CommandPalette.svelte:886) behalten. |
| 5 | medien.md:77 | UI-TEXT | „Bild auf Breite×Höhe verkleinert" setzt Wort-Platzhalter; Locale: „Bild auf {width}×{height} verkleinert" (de.json:79) bzw. „{count} Bilder auf {maxWidth}×{maxHeight} verkleinert" (de.json:534). | Konkretes Beispiel nennen, z. B. „Bild auf 1600×1200 verkleinert". |
| 6 | seiten-bauen.md:27 | UI-TEXT | „verfügst du über Lese-Recht" paraphrasiert; UI-Begriff ist „Nur-Lese-Modus" (de.json:1368) mit Hinweis „Du hast keine Berechtigung, diesen Eintrag zu bearbeiten." (de.json:1369). | „Nur-Lese-Modus" zitieren. |
| 7 | index.mdx:6 | TON | Tagline „wie man damit baut" nutzt man-Form (AGENTS §1 verlangt Du-Form). | „hier steht, wie du damit baust". |
| 8 | exportieren.md:24-34 | STARLIGHT | Formatwahl (CSV/XLSX/JSON) plus Array-Behandlung als Steps-Liste führt zu Fehlklicks ohne Vergleich. | `<Tabs>` mit je einem Tab pro Format (inkl. Aside-Hinweis aus :37-43 im JSON-Tab). |
| 9 | ki-nutzen.md:58-75 | STARLIGHT | Zwei aufeinanderfolgende `<Steps>`-Blöcke (Spracheingabe vs. Vorlesen) zum selben Thema. | `<Tabs>` „Eingabe" / „Ausgabe" statt zweier Steps-Blöcke. |
| 10 | seiten-bauen.md:89-109 | STARLIGHT | Zwei Viewport-Bilder plus Steps ohne Zuordnung Preset ↔ Bild. | `<Tabs>` Desktop (1280) / Tablet (768) / Mobil (375) mit je Bild; Werte belegt (PagebuilderViewportControl.svelte:22-29). |
| 11 | hilfe.md:10-73 | STRUKTUR | Doppelungen: Anmeldung (vgl. anmelden.md:27-41), Speichern blockiert (vgl. eintrag-bearbeiten.md:68-84), Bild fehlt (vgl. medien.md). | hilfe.md auf Verweisliste kürzen („Wenn X, siehe Y Schritt Z") statt Voll-Anleitungen zu wiederholen. |
| 12 | div. (anmelden:71, eintrag:86, lesen:101, orientierung:86, medien:93, ki:101 vs. benutzer-teams:66) | STRUKTUR | Sechs Seiten schließen mit identischem Satz und Linklabel [Mehr Details], benutzer-teams.md:66 nennt dasselbe Ziel [Admin-Konzept]. | Ein Label für `/admin/konzept/` festlegen (Vorschlag: „Admin-Konzept"). |
| 13 | hilfe.md (komplett), sichern.md:49-61, medien.md:77-79, eintrag-bearbeiten.md:40-43, lesen-suchen-filtern.md:75-84, ki-nutzen.md:27-39 | STRUKTUR | Beschriebene Schritte ohne Screenshot: hilfe.md (0 Bilder bei 5 Fällen), Wiederherstellungs-Dialog, Upload-Warteschlange/KI-Beschreibung, Durchsuchen/Auswählen-Fenster, Blättern-Fußzeile, Nova-Werkzeugliste. | Je Stelle einen Shot aus Mock-Projekt `corporate` aufnehmen (AGENTS §3). |
| 14 | (alle Anwender-Seiten) | LUECKE | Verlauf/Audit-Restore im Eintrag fehlt völlig. Beleg: EntryView.svelte:41-43,212-223 (AuditHistoryPanel, AuditRestoreDialog, AuditSidebarHistory); Locale `audit.history.button` „Verlauf" (de.json:1600), `audit.restore.*` (de.json:1615-1628). | Neue Seite „Verlauf ansehen und ältere Stände übernehmen". |
| 15 | (alle Anwender-Seiten) | LUECKE | Massenbearbeitung fehlt. Beleg: CollectionView.svelte:43,258-280 (BulkEditPanel); Locale `bulk.*` (de.json:1464-1540); Befehl „Massenbearbeitung" (de.json:1181). | Neue Seite „Mehrere Einträge gleichzeitig ändern oder löschen". |
| 16 | (alle Anwender-Seiten) | LUECKE | Aktionen fehlen. Beleg: ActionView.svelte; AdminLayout.svelte:114; Locale `actions.*` mit „Ausführen" (de.json:170-213). | Neue Seite „Aktionen starten und Ergebnisse prüfen". |
| 17 | orientierung.md:58-74 | LUECKE | Befehlspalette nur als Sprungziel beschrieben; Befehle (Filter zurücksetzen, Ansicht umschalten, Sprache, siehe de.json:1153-1224) und Tastenkürzel fehlen. | Neue Seite „Befehlspalette und Tastenkürzel" (öffnet mit `/`, CommandPalette.svelte:886). |
| 18 | anmelden.md:55-65 | LUECKE | Nur harter Auto-Logout beschrieben; vorgeschalteter Warn-Dialog „Sitzung läuft ab" mit „Angemeldet bleiben"/„Jetzt abmelden" fehlt. Beleg: SessionTimeoutWarning.svelte:100-151; de.json:1439-1443. | Schritt „Warnung → Angemeldet bleiben klicken" in „Automatisches Sitzungsende" ergänzen. |
| 19 | sichern.md:10-47 | LUECKE | Nur Ansehen beschrieben; Erstellen fehlt, Backup fehlt ganz. Beleg: SnapshotView.svelte:756 (`btnTrigger` „Snapshot erstellen"); App.svelte:96 (backup-view), BackupView.svelte; Locale `backup.*` (de.json:217-285). | Seite erweitern oder neue Seite „Snapshot erstellen und Backup herunterladen". |
| 20 | exportieren.md:24-43 | LUECKE | Eigene (vordefinierte) Exporte unerwähnt. Beleg: ExportDialog.svelte:103-106 (customExports aus `collection.meta.exports`); Locale `export.custom*` (de.json:1564-1569). | Abschnitt „Vordefinierte Exporte nutzen" ergänzen. |
| 21 | benutzer-teams.md:62-64 | LUECKE | Stellvertretung erwähnt Rückkehr nicht. Beleg: `impersonationBanner.stop` „Zurück zur Administration" (de.json:898). | Satz ergänzen: „Der Support kehrt über ‚Zurück zur Administration' zurück." |
| 22 | adr/0000-vorlage.md:6-11 | ADR | Vorlage kennt kein Status-Feld; ADR 0001-0003 haben entsprechend keinen Status („angenommen"/„umgesetzt"). | Vorlage um `- **Status:**` ergänzen, bestehende ADRs nachtragen. |
| 23 | anwender/*.md (11× Fußzeile) | ADR | „Geprüft an 2b091d89 (tibi-admin-nova)" weicht von ADR-Norm „Verifiziert am \<Commit> \<Datum>" (0002:8, 0003:8) und allen `server/*.md` (z. B. konfiguration.md:140) ab — Datum fehlt. | Fußzeile angleichen: „Verifiziert am 2b091d89 2026-10-16 (tibi-admin-nova)". |
| 24 | (keine Datei) | ADR | Fehlende ADRs: (a) Bereichsarchitektur der Doku (7 Bereiche aus AGENTS §2 nirgends begründet); (b) Sync-Regel (AGENTS §4: `npm run sync`, Artefakte committen — kein ADR). Screenshot-Regel steht in 0003:8. | Zwei kurze ADRs 0004 (Bereiche) und 0005 (Sync) anlegen. |
| 25 | adr/0003-selbstlaeufer.md:8 | ADR | „57 Seiten, Stand 2026-10-16" ist überholt: aktuell 61 md/mdx-Dateien unter `src/content/docs`. | Zahl aktualisieren oder Zählung streichen. |
## Bild-Abgleich
- Referenziert (je genau einmal, alle mit Alt-Text, alle ≤ 78 KB): anmelden-abmelden, anmelden-login, benutzer-profil, benutzer-teams, eintrag-formular, eintrag-gespeichert, eintrag-neu, export-dialog, export-fertig, ki-chat, ki-stimme, ki-zusammenfassen, lesen-filter, lesen-karten, lesen-liste, lesen-suche, medien-editor, medien-raster, medien-upload, orientierung-projektwechsel, orientierung-schnellsuche, orientierung-start, seiten-bloecke, seiten-overlay, seiten-pagebuilder, seiten-viewport-mobil, seiten-viewport-tablet, sichern-details, sichern-statistik, sichern-uebersicht (alle `.webp` unter `src/assets/anwender/`).
- Verwaist: keine.
- Doppelt referenziert: keine.
- Keine Referenz von außerhalb `anwender/` (geprüft per grep über alle docs).
## Fehlende Anwender-Seiten
1. „Verlauf ansehen und ältere Stände übernehmen" — Beleg: EntryView.svelte:41-43,212-223; de.json:1597-1628.
2. „Mehrere Einträge gleichzeitig ändern oder löschen" — Beleg: CollectionView.svelte:43,258-280 (BulkEditPanel); de.json:1464-1540.
3. „Aktionen starten und Ergebnisse prüfen" — Beleg: ActionView.svelte; AdminLayout.svelte:114; de.json:170-213.
4. „Befehlspalette und Tastenkürzel" — Beleg: CommandPalette.svelte:882-894; de.json:1153-1224,1250-1270.
5. „Snapshot erstellen und Backup herunterladen" (oder sichern.md erweitern) — Beleg: SnapshotView.svelte:756; App.svelte:96; BackupView.svelte; de.json:217-386.
## Unsichere Punkte
- eintrag-bearbeiten.md:77 „Pflichtfelder … mit Stern markiert": Stern-Markierung im Frontend-Code nicht gefunden (Validierungs-Strings „Pflichtfeld"/„Dieses Feld ist erforderlich" belegt, de.json:1323,1409). Bitte am UI verifizieren.
- anmelden.md:40 „höchstens drei Versuche": kein Limit in LoginPage.svelte gefunden; Backend nicht geprüft. Entweder Backend-Beleg nachliefern oder als unverbindliche Empfehlung umformulieren.
- sichern.md:51-64: Ob Anwender ohne Admin-Rolle den Button „Wiederherstellung starten" überhaupt sehen (Berechtigungs-Gate), ist ungeprüft. Falls nein, ist die Seite für Anwender irreführend.
- ki-nutzen.md:49 „Token-Status unter Kontext-Auslastung": beide Strings belegt (AiChat.svelte:2329,2352), Schachtelung am UI nicht verifiziert.
- Bindestrich in „Sitzung abgelaufen – bitte neu anmelden" (anmelden.md:57) nur visuell mit Locale (de.json:1438) verglichen, nicht byte-geprüft.
+68
View File
@@ -0,0 +1,68 @@
# Review: Einstieg + Server
- Umfang: `src/content/docs/einstieg/*.md` (4 Dateien), `src/content/docs/server/*.md` (5 Dateien). Alle vollständig gelesen.
- Datum: 2026-10-16.
- Geprüft gegen: `AGENTS.md` §1, `tibi-server/internal/**` (Code), `tibi-svelte-starter` (Compose, Makefile, `api/`, `scripts/`, `.env`), `tibi-admin-nova/.../de.json` (nur Stichworte), Server-HEAD `68b42785`, Starter-HEAD `e3f92ebf`.
- Regel: Nur belegte Fehler stehen als `FEHLER-INHALT`; alles andere als `PRUEFEN` am Ende.
## Befundtabelle
| # | Datei:Zeile | Kategorie | Befund (kurz) | Vorschlag |
|---|---|---|---|---|
| 1 | konfiguration.md:21 | FEHLER-INHALT | „`bge-m3` → `EMBEDDING_BGE-M3_*`" ist falsch. `normalizeEnvKeyName` ersetzt `-` durch `_` und uppercaset, Beleg `tibi-server/internal/config/server.go:245-251,437-439`. Korrekt wäre `EMBEDDING_BGE_M3_*`. | Beispiel korrigieren zu `EMBEDDING_BGE_M3_*`; Regel umformulieren („Bindestrich wird zu Unterstrich“). |
| 2 | konfiguration.md:99-105 | FEHLER-INHALT | Gleicher Fehler im Beispielblock: 7× `EMBEDDING_BGE-M3_*` (`TYPE`, `MODELPATH`, `DIMENSIONS`, `DOCUMENTPREFIX`, `QUERYPREFIX`). Keine dieser Variablen greift. Beleg wie #1. | Alle Prefixe zu `EMBEDDING_BGE_M3_*` ändern. |
| 3 | betrieb.md:21 | FEHLER-INHALT | „`mongo` (MongoDB 7)“ — der Starter pinnt `image: gitbase.de/server/mongo:8` (`docker-compose-local.yml`, Service `mongo`). | Auf „MongoDB 8“ ändern. |
| 4 | schnittstellen.md:62 | FEHLER-INHALT | „Alle Antworten sind gzip-komprimiert“ — falsch absolut. Ausgenommen sind `.pdf/.mp4/.jpg/.jpeg/.png` und `/metrics`. Beleg `internal/controller/setup.go:151-154`. | „Alle JSON-Antworten … (ausgenommen Mediendateien und `/metrics`)“. |
| 5 | neues-projekt.md:68,77 | MD-FEHLER | `$CODING_TIBISERVER_URL` ist nirgends definiert: nicht in Starter-`.env`, nicht in der URL-Liste in schnellstart.md:51, in keinem Script. Nur `-tibiserver`-Host, inline gebaut in `scripts/reload-local-tibi.sh:10`. Befehle so nicht kopierbar. | Variable definieren (`.env` + schnellstart.md:51) oder Befehle auf dokumentierten Host umschreiben. |
| 6 | uebersicht.md:36 | MD-FEHLER | „Verifiziert am 68b42785“ ohne Backticks; alle 4 anderen Server-Seiten nutzen `` `68b42785` ``. | Backticks ergänzen. |
| 7 | server/betrieb.md:6, konfiguration.md:6, schnittstellen.md:6, sicherheit.md:6 | MD-FEHLER | Identische 8er-Importzeile (`Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree`), davon pro Seite 3–6 ungenutzt (z. B. betrieb: `Tabs/TabItem/Card/CardGrid/FileTree` ohne Verwendung; schnittstellen: `Aside/Steps/Card/CardGrid/Badge/FileTree` ohne Verwendung). Copy-Paste-Rest. | Je Seite nur importieren, was verwendet wird. |
| 8 | server/*.md:1-4 vs einstieg/*.md:1-4 | MD-FEHLER | Frontmatter inkonsistent: Server-Seiten mit einfachen Quotes (`title: '...'`), Einstieg ohne. | Eine Form wählen (ohne Quotes, wie einstieg). |
| 9 | uebersicht.md:8-14 | MD-FEHLER | Zwei Hinweis-Stile auf einer Seite: Blockquote (`> **Handseite.** …`) plus `:::note`. | Einen Stil wählen (Repo-Konvention: `<Aside>`). |
| 10 | konfiguration.md:41-43 | STIL | „Das README-Beispiel ist unvollständig und nennt …“ + „README lügt an zwei Stellen“ + rohes `<del>/<ins>`-HTML. „Lügt“ ist unsachlich, HTML-Tags sind kein Doku-Stilmittel. | Sachlich: „Das README-Beispiel ist veraltet (noch Go 1.23 statt `go 1.25.0`/`go1.25.7` laut `go.mod`).“ Version als `Badge` oder Code, kein `<del>/<ins>`. |
| 11 | neues-projekt.md:10 | STIL | Agenten-Meta im Nutzertext: „Stand: am Code geprüft, nicht aus Alt-Doku.“ Für Leser wertlos. | Streichen; Prüfnachweis gehört in Commit/Plan, nicht auf die Seite. |
| 12 | neues-projekt.md:53 | STIL | Belehrung an den Leser: „Keine Pfad-B-Schritte im lokalen Starter-Stack erfinden.“ | Streichen oder in Ergebnis-Satz umwandeln („Im lokalen Stack entfällt Pfad B.“). |
| 13 | uebersicht.md:8 | STIL | Maintainer-Meta („Wird vom Sync nie überschrieben (`keep`) …“) steht als erster Satz im Nutzertext. | In HTML-Kommentar oder ans Seitenende verschieben. |
| 14 | uebersicht.md:10, begriffe.md:8-18 | STIL | Benennung uneinheitlich: „**Tibi-server**“, „**Tibi-Server**“, „**Tibi-Admin**“, „**Tibi-Projekt**“ — AGENTS.md §0 verlangt `tibi-server` / `tibi-admin` (klein). | Durchgehend Kleinschreibung wie in was-ist-tibi.md:10-11. |
| 15 | einstieg/*.md vs server/*.md | STIL | Codezaun-Sprache uneinheitlich: Einstieg `sh`, Server `bash` (bei identischem Inhalt Shell). | Einheitlich `bash` (oder `sh`) wählen. |
| 16 | schnittstellen.md:53-57 | STIL | Recht-Spalte uneinheitlich: `project.reload`, `project.regenerate-search`, dann bloß „Projekt-Admin“ (Embedding-Status), dann `project.backup` / `project.snapshot`. | Überall Scope schreiben (Embedding-Status: welches Recht genau, Beleg `setup.go:390` prüfen). |
| 17 | schnellstart.md:91 | STIL | „nur wenn Server-Token vorhanden“ — vage Bedingung ohne Handlung. | Konkret: „… oder `X-Admin-Token` mit Scope `project.create`“ (falls belegbar, sonst streichen). |
| 18 | konfiguration.md:123 | STIL | Tippfehler „`namespace….)`“ (vier Punkte + Klammer). | „`namespace.…`“ bzw. Beispiel wie sicherheit.md:21. |
| 19 | uebersicht.md:20 | STIL | Card verspricht „inkl. Go-Version und Legacy-Default“ — `Legacy-Default` kommt in konfiguration.md nirgends vor (gemeint sind wohl die 201/204-Legacy-Codes aus schnittstellen.md:62). | Begriff klären oder streichen. |
| 20 | was-ist-tibi.md:30-35 | STARLIGHT | `Card` mit Markdown-Link im Body („[Loslegen](…)“) ist kein klickbares Karten-Link. | `LinkCard` mit `href` nutzen, Text ohne Markdown-Link. |
| 21 | uebersicht.md:19-33 | STARLIGHT | 5× `Card` + `[Lesen](…)`-Link statt Karten-Link; doppelte Information (Titel + Link-Text identisch). | Auf `LinkCard` (`link.href`, `link.description`) umstellen. |
| 22 | neues-projekt.md:12-23 | STARLIGHT | Setup-Checkliste als statischer `text`-Codeblock mit `[ ]` — nicht abhakbar, kein Fortschritt. | `Steps` + echte Task-List (`- [ ]` im Fließtext) oder `Steps`-Komponente pro Meilenstein. |
| 23 | neues-projekt.md:81-83 | STARLIGHT | Wichtiger Token-Hinweis als `:::caution` (Syntax im Repo sonst nirgends, siehe PRUEFEN P1). | `<Aside type="caution" title="…">` wie sicherheit.md:30. |
| 24 | schnellstart.md:8-10 | STARLIGHT | `:::danger` für die Kern-Regel (Repo-Einzelfall). | `<Aside type="danger" title="Kern-Regel aus dem Starter">`. |
| 25 | konfiguration.md:46-77, schnittstellen.md:21-60 | STARLIGHT | `Tabs` ohne `syncKey` — gewählte Tabs (z. B. „Minimal-Beispiel“/„Lokaler Dial“) gehen beim Seitenwechsel verloren. | `syncKey` ergänzen (z. B. `syncKey="serverblick"`). |
| 26 | begriffe.md:6-19 | STARLIGHT | Reine Tabelle ohne einzige Heading: kein `tableOfContents`, keine Sprungmarken, kein Aside, keine Weiterführung. | Je Oberbegriff (`Namespace`, `Collection`, …) als `##`-Abschnitt oder Glossar-`CardGrid`; Tabelle als Kurzindex oben behalten. |
| 27 | schnittstellen.md:101 | STRUKTUR | „Wie frage ich Daten ab?“ verlinkt auf `/einstieg/begriffe/` statt auf die Abfrage-Seite. | Auf `/projekt/abfragen-lesen/` zeigen; begriffe-Zeile streichen oder umbenennen. |
| 28 | was-ist-tibi.md:27-36 | STRUKTUR | „Wo geht es weiter?“ nennt nur Schnellstart + Begriffe; „Neues Projekt“ (4. Sidebar-Eintrag) fehlt, obwohl schnellstart.md:94 darauf verweist. | Dritte Karte „Neues Projekt“ ergänzen. |
| 29 | schnellstart.md:85-92, neues-projekt.md:81-83 | STRUKTUR | Token-Kopf-Wissen steht doppelt (Tabelle vs. Caution) plus drittes Mal in `reference/token-koepfe.md`. Drei Wahrheiten zu einer Tatsache. | Kurzfassung + ein Verweis auf `reference/token-koepfe.md` als Single Source. |
| 30 | schnellstart.md:73, neues-projekt.md:105-115 | STRUKTUR | Nach „Abnahme“ endet der Pfad im Leeren: kein Verweis in Admin-/Anwender-Doku (erste Anmeldung, erste Inhalte). | „Nächster Schritt“-Box mit Links auf `admin/`- und `anwender/`-Seiten. |
| 31 | betrieb.md:62-70 | LUECKE | Backup/Snapshot nur als „Fortschritts-Stream“ erwähnt; kein Wort zu Wiederherstellung, Retention, `prune`, obwohl Endpunkte existieren (`setup.go:416-491`). | Abschnitt „Sichern und Wiederherstellen“ mit `backup`-, `snapshots`- und `prune`-Befehlen. |
| 32 | betrieb.md | LUECKE | `/api/v1/admin/shutdown` (`server.shutdown`) kommt nur als Tabellenzelle in schnittstellen.md:32 vor; Betreiber-Seite ohne Stop-/Neustart-Prozedur. | Störungs-/Wartungsabschnitt um Shutdown + Audit-Spur (`setup.go:772-798`) ergänzen. |
| 33 | konfiguration.md:24-43 | LUECKE | Beispielblöcke nur für DB/API/Mail + LLM/Embedding; `audit`, `ratelimit`, `hook`, `mcp` nur als Tabellenzeilen ohne YAML-Form. | Je ein Minimal-YAML pro Bereich (Defaults aus `server.go` sind belegt). |
| 34 | begriffe.md | LUECKE | Kein Eintrag zu Token-Arten (PAT/JWT/Admin-Token) und Scope-Syntax, obwohl Einstieg + Server ständig darauf verweisen. | 2 Zeilen + Verweis auf `reference/token-koepfe.md` und `server/sicherheit/`. |
| 35 | einstieg/*.md | LUECKE | Mock-Modus wird nur in begriffe.md:19 definiert; schnellstart.md erwähnt `MOCK` nirgends, obwohl Starter `start:mock` (`MOCK=1`) kennt. | Schnellstart-Schritt „Starten“ um Mock-Hinweis ergänzen oder bewusst ausklammern. |
## Starlight-Potenzial (pro Seite)
- was-ist-tibi.md: `Card` → `LinkCard` (#20); ASCII-Diagramm (Z. 15-23) als `Steps`- oder nummerierte Liste mit je einem Satz Wirkung pro Pfeil.
- schnellstart.md: `:::danger` → `<Aside type="danger">` (#24); Schritt 1-6 in `<Steps>` statt nummerierter Headings (konsistent zu betrieb.md:38).
- neues-projekt.md: Checkliste → `Steps`/Task-List (#22); `:::caution` → `<Aside>` (#23); `FileTree` um Stamm (`./`, `docker-compose-local.yml`, `.env`) erweitern.
- begriffe.md: Glossar-Struktur mit Headings + `CardGrid`-Kurzindex (#26); `tableOfContents` greift dann automatisch.
- uebersicht.md: `Card` → `LinkCard` (#21); Blockquote + `:::note` → ein `<Aside type="note">` (#9).
- konfiguration.md: `Tabs` mit `syncKey` (#25); `<del>/<ins>` → `Badge` für `go 1.25.0` (#10); Beispielblöcke (#33).
- betrieb.md: ungenutzte Imports entfernen (#7); Störungstabelle um Shutdown-Zeile erweitern (#32); `Badge`-Profilzeile (Z. 14) ans Seitenende in „Voraussetzungen“ wiederholen ist unnötig — streichen.
- sicherheit.md: ungenutzte Imports entfernen (#7); Token-Tabelle um PAT-Beispiel (`tibi_pat_…`) als `bash`-Block ergänzen; Checkliste (`Steps`) ist bereits Musterbeispiel — behalten.
- schnittstellen.md: `Tabs` mit `syncKey` (#25); API-Baum (Z. 10-17) als `FileTree` statt `text`-Block prüfen; Linkfix #27.
## Unsichere Punkte
- P1. `:::caution title="…"`, `:::danger title="…"`, `:::note title="…"` (neues-projekt.md:81, schnellstart.md:8, uebersicht.md:12): Titel-Attribut ohne Klammern ist keine Standard-Starlight-Syntax (`[Titel]` wäre sie); Repo-Konvention ist `<Aside>`. Build-Rendering nicht geprüft — bitte Rendern verifizieren oder auf `<Aside>` umstellen.
- P2. schnellstart.md:73 („Anmeldung im Admin lokal: `admin` / `admin`“): im geprüften Server-Code keine Seed-Stelle gefunden (nur massenhaft `GetLoginToken("admin","admin")` in Tests). Vermutlich Starter-/Admin-Seed — bitte belegen oder als Starter-Fakt kennzeichnen.
- P3. Doku-Domäne `*.code.testversion.online` vs. Compose-Label `online.testversion.code.subdomain=…`: Abbildungsregel steht nicht im geprüften Repo-Ausschnitt (nur ein auskommentierter `Host(...code.testversion.online)` in `docker-compose-local.yml`). Bitte Traefik-Regel belegen.
- P4. betrieb.md:72-74 („Jobs laufen unter der Projekt-UID“, „Quotas und Nutzung landen in der LLM-Nutzungsablage“): Cron-Registrierung belegt (`models/api.go:268`, `models/job.go:4`, `JOB_CONTEXT` in `models/function.go:276`), suexec-Pfad für Jobs und „Nutzungsablage“ nicht explizit nachgewiesen.
- P5. sicherheit.md:42 („Globales CORS ist auskommentiert“): keine Default-`config.yml` im geprüften Stand gefunden; unbelegt, ob das für alle Deployments gilt.
- P6. neues-projekt.md:57/71 (`project not found`, `GET /api/v1/project`): String existiert in `internal/filecleanup/cleanup.go:70`, System-Projekt-CRUD via `Project.Middleware` (`models/project.go:206`) ist belegt; ob genau dieser String bei fehlender Registrierung erscheint, nicht end-to-end nachgewiesen.
- P7. neues-projekt.md:61-64 (Login via `$CODING_TIBIADMIN_URL/api/login`): Admin-Frontend-Proxy-Pfad, nicht gegen `tibi-admin-nova`-Code geprüft (Server-Route ist `POST /api/v1/login`, `setup.go:728`).
+88
View File
@@ -0,0 +1,88 @@
# Review 05: Projekt-Seiten
- Umfang: `src/content/docs/projekt/*.md`, 15 Dateien, 2999 Zeilen (Stand 2026-10-16).
- Geprüft gegen: `AGENTS.md` §1, `tibi-svelte-starter` (`api/collections/*.yml`, `api/config.yml`, `api/hooks/*`, `frontend/src/App.svelte`), `tibi-server` (`internal/controller/collection_get.go`, `collection_count.go`, `setup.go:514-536`, `internal/mcp/*`, `internal/models/scope_enforce.go`, `internal/config/admin_token.go:115`), `tibi-types/schemas/config/*.schema.json`.
- Methode: jede Datei vollständig gelesen; Code-Behauptungen per grep am Quellcode verifiziert. Zählung: MD-FEHLER 6, FEHLER-INHALT 5 (+ 7 PRUEFEN), STIL 9, STARLIGHT 15 (1–2 pro Seite), STRUKTUR 7, LUECKE 8, KOPFZEILE 15 (eine je Seite).
## Befund-Tabelle
| # | Datei:Zeile | Kategorie | Befund (kurz) | Vorschlag |
|---|---|---|---|---|
| 1 | mcp-nutzen.md:61-90 | FEHLER-INHALT | Scope-Tabelle nennt `.get` (`namespace.<ns>.<col>.get`), Detail-Abschnitte (Z102ff.) nennen `.read`. Code: `MethodToAction("GET")` → `"read"` (`admin_token.go:115`). | Tabelle auf `.read`/`.create`/`.update`/`.delete` umstellen (fünf Zeilen 63-67, 71). |
| 2 | abfragen-lesen.md:99 | FEHLER-INHALT | „`…/comment/count` landet auf der Einzel-Route (`:id = "count"`)“. Code: `_count` (setup.go:514) ist statisch vor `:id` (:536) registriert, statisches Segment gewinnt in Gin. | Satz streichen oder umkehren: `/comment/count` zählt; nur `?facet=` an der Liste wird ignoriert (in `collection_get.go` keine Facet-Behandlung, verifiziert). |
| 3 | hooks-schreiben.md:85 | FEHLER-INHALT | „Ohne `return` läuft der Hook ins Leere — eingeloggte Nutzer sehen dann alles.“ Hook filtert nur Anonyme (`filter_public.js`); ohne `return` sehen **anonyme** alles. | „eingeloggte“ → „anonyme“; Folge (Entwürfe öffentlich) nennen. |
| 4 | sammlungen-anlegen.md:87 | FEHLER-INHALT | Gruppen-Zeile: „`content`, `content`, `community`“. Starter: `content` (content, navigation, medialib, tag), `community` (comment), `system` (ssr). Doppelung + `system` fehlt. | „`content`, `community`, `system` (`ssr.yml`)“; Beispiel-Spalte bereinigen. |
| 5 | abfragen-lesen.md:34-35 | STIL | Aside mit „Achtung, Tippfehler im Code“ ist inhaltlich korrekt (`collection_get.go:32` schreibt wörtlich `Content-Range:`), Ton („Tippfehler“) grenzt an Binsen. | Sachlich lassen, „Tippfehler im Code“ → „Abweichung im Code (mit Doppelpunkt)“; kein Handlungsbedarf sonst. |
| 6 | dateien-bilder.md:10, hooks-schreiben.md:10, navigation-i18n.md:9, suche.md:9 | MD-FEHLER | „Suche-Begriffe“ (4× mit Bindestrich) vs. „Suchbegriffe“ (audit-backup:10, deploy:10, jobs-actions:10); 8 Seiten (abfragen, felder, rechte, sammlungen, ssr, pagebuilder, llm, mcp) haben gar keine Suchbegriff-Zeile. | Einheitlich „Suchbegriffe:“ in allen 15 Seiten oder Zeile überall streichen (Entscheid dokumentieren). |
| 7 | navigation-i18n.md:75-76 vs. felder-widgets.md:19 | STIL | Pfad-Muster doppelt, notationell verschieden: `^/[a-z0-9-/]*$` vs. `^\/[a-z0-9\-\/]*$` (äquivalent; Starter nutzt in `content.yml` die escaped Form, in `meta.agent.instructions` die kurze). | Eine Schreibweise wählen (kurze), an beiden Stellen + ssr-website:32 angleichen. |
| 8 | audit-backup.md:50-58 | STRUKTUR | 4 Admin-UI-Absätze (Verlaufspanel, Diff-Ansicht, Wiederherstellen-Dialog, Seitenleiste) sind Prosa-Wände über **Admin**-Verhalten in einer Projekt-Seite; gehört nach `admin/` (Konzept/Editor). | Auf 1 Absatz + Verweis kürzen; Details nach `admin/eintrags-editor.md` oder `admin/konzept.md` verschieben. |
| 9 | mcp-nutzen.md:1-754 | STRUKTUR | 754 Zeilen (> 600er-Richtwert), 36 Werkzeug-Referenzen à Beispiel + Fehler. Seite ist de facto Referenz, kein Projekt-Leitfaden. | Werkzeug-Referenz (Z100-683) nach `reference/mcp-werkzeuge.md` auslagern; hier Übersicht + `agent_guide_get`-Pflege + REST-only-Tabelle behalten. |
| 10 | abfragen-lesen.md:124-129 vs. suche.md:44-49 | STRUKTUR | `qName`-Tabelle steht wortgleich in zwei Seiten (combined/fuzzy/semantic/default). | Tabelle nur in `suche.md` pflegen, in `abfragen-lesen.md` auf 2 Zeilen + Verweis kürzen. |
| 11 | rechte-tokens.md:90-123, dateien-bilder.md:46-50, navigation-i18n.md:43-47+82-88, ssr-website.md:64-68, abfragen-lesen.md:89-93 | STRUKTUR | `active:true`-Falle an 5 Stellen erklärt (teils wortgleich: „Vorfall 2026-10-05“ in dateien + navigation). Querverweise existieren, aber Wortlaut driftet (einmal „count 0“, einmal „Header/Footer leer“). | Kanonischen 3-Zeilen-Kasten in `rechte-tokens.md` definieren; andere Seiten nur Aside + Verweis. |
| 12 | pagebuilder-dev.md:183-247 | STRUKTUR | Editor-Overlay, Viewport-Steuerung, Übersichts-Overlay, BlueprintCard (65 Zeilen reines **Admin**-UI-Verhalten mit `de.json`-Schlüsseln) in einer Dev-Seite. | UI-Beschreibung nach `admin/eintrags-editor.md` verschieben oder verweisen; hier nur Registry-Vertrag + CSS-Transform + Lookup + Screenshot-Pipeline behalten. |
| 13 | jobs-actions.md:34, jobs-actions.md:20 | PRUEFEN | „Ohne `cron` läuft der Job nie“ + „Schema-Pflicht: nur `type` + `file`“ — gegen `job.schema.json` nicht verifiziert (Schema-Datei in diesem Lauf nicht gelesen). | Am Schema + Runner (`internal/models/api.go:270`) belegen oder als ungesichert kennzeichnen. |
| 14 | deploy.md:91 | PRUEFEN | „`POST /api/ssr?clear=1`, keiner nötig (`public.post: true`)“ — `ssr.yml` erlaubt `public.post: true` (Z17-22 verifiziert), aber ob `clear_cache`/`post_bind`-Pfad ohne Auth den Komplett-Wipe auslöst, ist nur aus Doku-Zitat übernommen. | Gegen `hooks/ssr/post_bind.js` + Server-Log belegen. |
| 15 | suche.md:21 | PRUEFEN | „`navigation.yml` und `medialib.yml` nutzen nur einen `default`-Index im Modus `regex`“ — `search`-Blöcke verifiziert (beide `default`/`regex`), aber „kein Textindex nötig“ ist eine Wertung ohne Beleg. | „kein Textindex nötig“ streichen oder begründen (regex braucht keinen `$text`-Index). |
| 16 | hooks-schreiben.md:111 | PRUEFEN | „Das ist die einzige Log-Hilfe im Starter.“ Absolut-Aussage, nur aus `lib/utils.js` abgeleitet. | Abschwächen („die Standard-Log-Hilfe“) oder Volltextsuche nach `console.log` im Starter belegen. |
| 17 | abfragen-lesen.md:105 | STIL | „Schnelle Schätzung statt exakter Zahl (nur **ohne** `filter`)“ — korrekt (`collection_count.go:145`), aber Klammer-Format inkonsistent zu anderen Tabellen. | Einheitlich: Bedingung in eigene Spalte oder Fußnote. |
| 18 | rechte-tokens.md:42 | STIL | „aber keinen Lese-Hook“ — `tag.yml` verifiziert (nur post/put/delete-`return`), Formulierung ok, aber Vorwärtsverweis auf `clear_cache.js` ohne Link. | Link auf `projekt/hooks-schreiben/` ergänzen. |
| 19 | llm-nutzen.md:89 | STIL | „Preise stehen nirgends im Code — erfinde keine.“ Imperativ an den Leser, Ton grenzwertig belehrend. | „Modellnamen aus `GET /llm/providers` übernehmen, keine erfinden.“ |
| 20 | sammlungen-anlegen.md:74 | STIL | „`uploadPath` setzen, auch wenn die Sammlung keine Dateien enthält“ — unbegründete Pflicht ohne Code-Beleg. | Begründung ergänzen (Server-Default/Fehlerbild Z159) oder zu Empfehlung abschwächen. |
| 21 | deploy.md:15, deploy.md:79 | STIL | Platzhalter `LIVE_URL=https://www` + `__PROJECT_NAME__` werden erklärt, aber das Beispiel ist keine gültige URL (Leser kopiert ggf.). | Kommentar `# TODO: echte Domain eintragen` direkt in den Block schreiben. |
| 22 | ssr-website.md:45-47 | STIL | `sort: "sort"` im Beispiel ohne Erklärung (Feld `sort` aus Starter vorausgesetzt, in `App.svelte:171` verifiziert). | Ein Satz: „`sort` ist ein Starter-Feld für die Reihenfolge.“ |
| 23 | hooks-schreiben.md:127 | STIL | „Weitere im Starter genutzte Zugriffe“ — Aufzählung mit 8 Gliedern in einer Zeile, schwer scannbar. | Als Tabelle oder Bullet-Liste mit je einem Halbsatz umbrechen. |
| 24 | felder-widgets.md:56-58 | STIL | 25 Enum-Werte als Fließtext-Code-Span; `pagebuilder`/`pageBuilder`-Doppelung nur in Klammern erklärt. | Als Tabelle (Widget → Wofür) oder `FileTree`-ähnliche Liste; Doppelung als `Aside note` mit Schema-Beleg. |
| 25 | audit-backup.md:96 | STIL | „Übernimm die Liste nicht blind“ — belehrender Ton (Binsen-Nähe), dazu Klammer-Schachtelung. | „Gleiche jeden Namen gegen `api/collections/*.yml` ab, sonst …“ |
| 26 | dateien-bilder.md:105 | STIL | „Passiert nicht von selbst“ als Lösungs-Spalte — flapsig, keine Handlungsanweisung. | „Kein Handlungsbedarf; Waisen per Dateisuche im Upload-Pfad prüfen.“ |
| 27 | navigation-i18n.md:66 | STIL | „Kollidiert auf `null` — kein ‚geht schon gut‘“ — umgangssprachlich, Anführungs-Floskel. | „Zweiter Datensatz ohne Schlüssel verletzt den Unique-Index (`null`/`lang`).“ |
| 28 | abfragen-lesen.md:143 | KOPFZEILE | „Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 68b42785 2026-10-15 (tibi-server, …).“ Format ok, Hash 7-stellig. | — |
| 29 | audit-backup.md:145 | KOPFZEILE | Vier Belege, Hashes 7-stellig, Daten 2026-10-13 bis 15. Doppel-Beleg gleiche Commit `2b091d89` zweimal. | Doppel-Beleg zusammenfassen. |
| 30 | dateien-bilder.md:109 | KOPFZEILE | Hash 8-stellig (`e3f92ebf`) — uneinheitlich zu 7-stelligen anderswo; nur Starter-Beleg trotz Server-Aussagen (filecleanup, Lookup-Regel). | Hash-Länge vereinheitlichen (7 oder 8 überall); Server-Beleg ergänzen. |
| 31 | deploy.md:131 | KOPFZEILE | Nur Starter-Beleg (`e3f92eb`), obwohl Reload-Endpunkte + Token-Tabelle Server-Verhalten beschreiben. | Server-Beleg (setup.go/reload-Docs) ergänzen. |
| 32 | felder-widgets.md:199 | KOPFZEILE | Drei Belege (Starter, tibi-types, Server), Hashes 7-stellig. Vorbildlich. | — |
| 33 | hooks-schreiben.md:234 | KOPFZEILE | Hashes 8-stellig (`e3f92ebf`, `4c55f824`) — uneinheitlich zu Z28/32. | Hash-Länge vereinheitlichen. |
| 34 | jobs-actions.md:144 | KOPFZEILE | Drei Belege, 7-stellig, mit Dateinamen (job-/action-schema, Test-Plan 40). Vorbildlich. | — |
| 35 | llm-nutzen.md:172 | KOPFZEILE | Ein Beleg, sehr lange Klammer (11 Dateien), Datum 2026-10-16 (jüngste Prüfung). Lesbarkeit leidet. | Dateiliste kürzen (nur Handler + Registry + Scope). |
| 36 | mcp-nutzen.md:754 | KOPFZEILE | Zwei Belege, Files namentlich — ok, aber längste Seite mit knappstem Beleg (keine Test-Pläne, keine Guards-Zeilen). | Guards-/Methoden-Beleg ergänzen. |
| 37 | navigation-i18n.md:119 | KOPFZEILE | Starter + Admin-Test-Pläne, 8-/7-stellig gemischt (`e3f92ebf`, `2b091d89`). | Hash-Länge vereinheitlichen. |
| 38 | pagebuilder-dev.md:248 | KOPFZEILE | Admin-Commit + Starter-Commit mit Komponentenliste. Vorbildlich, längste Klammer. | Evtl. kürzen, sonst ok. |
| 39 | rechte-tokens.md:154 | KOPFZEILE | Starter + Server (Matrix-Test). Vorbildlich. | — |
| 40 | sammlungen-anlegen.md:166 | KOPFZEILE | Starter + tibi-types, aber kein Server-Beleg trotz Aussagen zu `409` vs. `500` (Legacy-Default, Z133). | Server-Beleg (Fehler-Mapping) ergänzen. |
| 41 | ssr-website.md:178 | KOPFZEILE | Nur Starter-Beleg, obwohl Seite Server-Verhalten beschreibt (Cache-Regeln, `X-SSR-Cache`, `duplicate key`, 500-Dummy). | Server-Beleg (`get_read.js` läuft im Server, SSR-Docs) ergänzen. |
| 42 | suche.md:103 | KOPFZEILE | Drei Belege inkl. rankfusion + Test-Plan 41. Vorbildlich; Hash-Länge gemischt. | Hash-Länge vereinheitlichen. |
| 43 | abfragen-lesen.md:143, suche.md:74-75, sammlungen-anlegen.md:133 | LUECKE | Bulk-Operationen (`bulkCreate`/`bulkUpdate`/`bulkDelete`, `items[]` bei MCP-`document_create`) werden nirgends aus REST-Sicht gezeigt. | Abschnitt „Mehrere Einträge auf einmal“ in `abfragen-lesen.md` (Bulk-Query, Bulk-Freigabe, Fehlerbild). |
| 44 | ssr-website.md:45-51 | LUECKE | `aggregate` (commentCount) nur hier gezeigt, in `abfragen-lesen.md` fehlt jede Erwähnung (Parameter-Tabelle Z39-45 kennt nur filter/sort/limit/offset/count). | `aggregate`-Zeile + Verweis in `abfragen-lesen.md` ergänzen. |
| 45 | felder-widgets.md:76 | LUECKE | `accept` (MIME-Typen) ohne Beispiel („—“); Starter-`medialib.yml` setzt nur `maxFileSize`, kein `accept` — unklar, ob MIME-Filter überhaupt möglich. | Am Schema (`collection.schema.json`) klären und Beispiel oder „nicht zutreffend“ schreiben. |
| 46 | felder-widgets.md:194-197 | LUECKE | `meta.protected` in 2 Zeilen abgehandelt; Feld-Level-Read-Rechte/Projektionen (`invalid projection`, `lookup permission denied`) nur als Fehlerbilder. | Abschnitt „Wer darf welches Feld sehen/ändern“ oder Verweis auf Server-Permissions-Docs. |
| 47 | jobs-actions.md:121-123, mcp-nutzen.md:702-740 | LUECKE | `GET /_meta` / `system_meta_get` und `meta.query`/`extra`-Parameter nur als Nebensatz; keine Seite erklärt `_meta` aus Projektsicht. | Kurzabschnitt in `sammlungen-anlegen.md` oder `mcp-nutzen.md` (was `_meta` liefert, wofür `extra`). |
| 48 | alle Seiten | LUECKE | System-CRUD (Projekte/Collections per API anlegen, `X-Admin-Token`, Namensraum-Registrierung) fehlt komplett; `deploy.md` und `rechte-tokens.md` erwähnen nur Köpfe. | Verweis auf Server-Docs oder Kurzabschnitt (gehört ggf. nach `server/`, dann Link setzen). |
| 49 | suche.md:58, abfragen-lesen.md:131 | LUECKE | `qExplain=1`/`?qExplain=1` als Debug-Hinweis, aber Ausgabe-Format nirgends erklärt. | 1 Beispiel-Output oder Satz zur Interpretation. |
| 50 | audit-backup.md:62-86 | LUECKE | Snapshot-Restore per API (Fehlerfälle, Teilmengen, `collections: []`-Semantik beim Restore) fehlt; nur Admin-UI-Klickpfad. | Restore-Fehlerbilder (Platz, laufende Jobs, Cache danach) ergänzen. |
## Starlight-Potenzial (pro Seite)
- `abfragen-lesen.md`: `Steps` für „Erste Liste → filtern → zählen“-Rezept statt Fließtext (Z8-10); `Badge` für `_count`/`_facet`-Routen-Warnung (Z97-100); Fehlertabelle (Z135-141) ist bereits gut, zusätzlich `Aside tip` mit Copy-paste-fähiger Basis-URL.
- `audit-backup.md`: `CardGrid` bereits genutzt — zusätzlich `Tabs` (UI-Pfad vs. API-Pfad) in „Audit einschalten“ (Z18-46); `FileTree` für `snapshotPath`-Layout; `Badge` („Betreiber-Sache“) an Server-Schaltern (Z29).
- `dateien-bilder.md`: `Steps` bereits genutzt — zusätzlich `Tabs` um Upload-Antwort vs. Fehlerantwort (Z22-31); Filter-Tabelle (Z84-91) um `Badge`-Spalte (Standard vs. Spezial) erweitern; Code-Titel enthalten bereits Pfade, gut.
- `deploy.md`: `Tabs` bereits genutzt — zusätzlich `Aside tip` mit 1-Befehl-Verify (`curl` Reload + `clear=1`) nach Z93; Checkliste (Z113-121) als `Steps` ist gut, zusätzlich `Badge` („Staging“/„Live“) an Bühnen-Tabelle.
- `felder-widgets.md`: `Tabs` bereits genutzt — zusätzlich `Badge` für Pflicht-/Optional-Schlüssel in Validator-Tabelle (Z65-79); 25 Widgets (Z58) als einspaltige Liste mit `Badge` pro Kategorie (Auswahl, Datei, Datum) statt Fließtext.
- `hooks-schreiben.md`: `Steps` + `Tabs` bereits genutzt — zusätzlich `FileTree` für `api/hooks/`-Layout (Z50-59 statt Tabelle); `Aside` statt Klammer-Zusatz bei `context_response.header` (Z138).
- `jobs-actions.md`: `CardGrid` + `Tabs` bereits genutzt — zusätzlich `Badge` („Cron“/„HTTP“) an Baustein-Tabelle (Z95-101); `Steps` um Verify-Schritt mit erwartetem Log-Auszug erweitern.
- `llm-nutzen.md`: `Tabs` bereits genutzt — zusätzlich `Aside caution` für 429-Budget-Stopp statt Tabellen-Zeile (Z166); `Badge` für Normal-/Streaming-Modi an Z17/37; lange Budget-Regeln (Z105-113) als nummerierte `Steps` lesbarer.
- `mcp-nutzen.md`: `Badge` bereits genutzt — nach Auslagerung (Nr. 9) `LinkCard`s zu `reference/mcp-werkzeuge.md` + `projekt/hooks-schreiben/`; pro Werkzeug einklappbar wäre ideal (Starlight ohne Details-Plugin: stattdessen Anker-Links + Inhaltsverzeichnis oben).
- `navigation-i18n.md`: `Steps` + `Tabs` bereits genutzt — zusätzlich `FileTree` für 4 Singleton-Bäume (statt Z13-Satz); Checkliste (Z82-88) als `Badge`-Tabelle („Schema hat Feld: ja/nein“) schärfer als Fließtext.
- `pagebuilder-dev.md`: `Steps` + `Tabs` bereits genutzt — zusätzlich `Badge` („Admin-UI“ vs. „Projekt-Code“) an Abschnitten ab Z183 zur Trennung nach Nr. 12; Registry-Vertrag (Z47-54) als `Code`-Titel mit Dateipfad ist gut.
- `rechte-tokens.md`: `Tabs` bereits genutzt — zusätzlich `Badge` („anonym“/„Token“/„JWT“/„Admin“) an Kopf-Tabelle (Z48-53); `active:true`-Tabelle (Z114-120) als kanonischer Kasten mit `Aside caution` + Anker für andere Seiten.
- `sammlungen-anlegen.md`: `FileTree` + `Steps` bereits genutzt — zusätzlich `Tabs` (Minimal vs. Voll mit Suche/Hooks) statt einem Block; `Badge` an Modi-Tabelle (Z141-146, „Starter-Standard“).
- `ssr-website.md`: `FileTree` + `Tabs` bereits genutzt — zusätzlich `Badge` (`X-SSR-Cache`/`X-SSR-Cleared`) an Cache-Abschnitt (Z122-144); `Steps` für „Eigener Block“ ist gut, Code-Titel bereits mit Dateipfad.
- `suche.md`: `Steps` + `Tabs` bereits genutzt — zusätzlich `LinkCard`s zu den drei Index-Quellen (Starter-YAML, rankfusion, Test-Plan 41) unter „Neuaufbau“; RRF-Formel (Z69-71) als `Aside note` mit Formel-Hervorhebung.
## Unsichere Punkte (PRUEFEN, 7)
1. Nr. 13 — Job-`cron`-Pflicht und Schema-Felder (`job.schema.json` + Runner-Verhalten ungelesen).
2. Nr. 14 — `POST /api/ssr?clear=1` ohne Auth: `post_bind.js`-Wipe-Pfad nicht am Code verifiziert.
3. Nr. 15 — „kein Textindex nötig“ für `default`/`regex` (Wertung ohne Beleg).
4. Nr. 16 — „einzige Log-Hilfe im Starter“ (Volltextsuche im Starter fehlt).
5. `navigation-i18n.md:13` — „genau vier Singleton-Bäume“ + `maxLevel`-Regel (Header 2, Footer 1, Z28): `declaredTrees` nur teilweise gelesen (Header DE/EN gesehen, Footer nicht). Am vollständigen `navigation.yml`-Block belegen.
6. `rechte-tokens.md:130-142` — Org-/Team-Modell (`matrixorga`, `teama1`, Zugriffsregeln) aus `access_control_matrix_test.go` übernommen; Test-Namen als Doku-Beleg sind fragil — gegen Server-Docs oder Admin-Verhalten gegenlesen.
7. `pagebuilder-dev.md:62-86` und `mcp-nutzen.md:702-740` — Aussagen über Admin-Verhalten (`PagebuilderOverlay`, `de.json`-Schlüssel, `meta.agent`-Pflege) bzw. Server-Interna (`query.go`-Validierung) konnten in diesem Lauf nicht am jeweiligen Repo verifiziert werden; Stichprobe pro Datei empfohlen.
+776
View File
@@ -0,0 +1,776 @@
# Doku-Audit — Übersicht, Lücken, Fehler, Verbesserungen
**Zweck:** Vollständige Prüfung der Tibi-Doku (`tibi-docs`) gegen den Code von `tibi-server`, `tibi-admin-nova`, `tibi-types` und `tibi-svelte-starter` — mit Phasenplan und Checklisten zum Abarbeiten.
**Stand:** 2026-10-16, umgesetzt am 2026-10-17.
> **Zahlen in §1 sind der Audit-Stand vom 16.10.** (61 Seiten, 30 Screenshots) — bewusst als Schnappschuss stehen gelassen.
> **Ist-Stand nach der Umsetzung (17.10.):** 70 Inhaltsseiten / 8.264 Zeilen, 49 Screenshots (40 Anwender, 9 Admin), 71 Build-Seiten,
> 0 tote Links, 8 legitime „Platzhalter"-Marker, `npm run build` grün. Fortschritt siehe §18.
**Rahmen (aus der Zielsetzung):**
Die Doku **soll** beschreiben: (a) alle Funktionen des tibi-server für Entwickler und Administratoren beim Aufsetzen und Betreiben von Projekten, (b) alle tibi-admin-nova-Funktionen, die Entwickler zum korrekten Konfigurieren brauchen, (c) alle tibi-admin-nova-Funktionen aus Anwendersicht anhand von Screenshots (Mock-Projekt `corporate`, nie `t1`).
Die Doku **soll nicht**: den Quellcode der Tibi-Repos für die Core-Weiterentwicklung erklären, und den alten `tibi-admin` (vor Nova) beachten.
---
## 0. Artefakte dieses Audits
| Datei | Inhalt | Zeilen |
|---|---|---|
| `plans/doku-audit.md` | **dieses Übersichtsdokument** (Ergebnis + Phasenplan) | — |
| `plans/audit/01-server-features.md` | tibi-server: Feature-Matrix, Routen, Hooks, MCP, Config, Lücken, Fehler | 200 |
| `plans/audit/02-admin-interfaces.md` | tibi-admin-nova: Entwickler-Schnittstellen, fehlende Interface-Doku, UI-Inventar, Screenshot-Lücken | 128 |
| `plans/audit/03-types-starter.md` | tibi-types-Schemas + Starter: undokumentierte Properties, Starter-Lücken | 125 |
| `plans/audit/04-legacy-docs.md` | Alt-Doku-Inventar in den Nachbar-Repos, Wegfall-Kandidaten, Repo-Hygiene | 106 |
| `plans/audit/05-review-einstieg-server.md` | Seitenreview `einstieg/` + `server/` (35 Befunde) | 68 |
| `plans/audit/05-review-projekt.md` | Seitenreview `projekt/` (50 Befunde) | 88 |
| `plans/audit/05-review-admin-reference.md` | Seitenreview `admin/` + `reference/` (21 Befunde) | 55 |
| `plans/audit/05-review-anwender-adr.md` | Seitenreview `anwender/` + `adr/` (25 Befunde) | 58 |
| `plans/audit-report.json` | Maschinenlesbarer Struktur-Befund (Skript) | — |
| `scripts/audit-docs.mjs` | Audit-Skript: Frontmatter, Codeblöcke, Links, Anker, Bilder, Tabellen, Stil-Marker, Komponenten-Inventar | 268 |
| `plans/coverage-report.json` | Bestehendes Coverage-Inventar (Routen/Kontexte/Tools/Schemas) | — |
**Belegstatus-Legende** (durchgängig in diesem Dokument):
| Symbol | Bedeutung |
|---|---|
| ✅ | **selbst am Code/Doku geprüft** (Datei:Zeile liegt vor) |
| ⚠️ | aus Subagenten-Report übernommen, **nicht selbst nachgeprüft** |
| ❌ | Report-Behauptung, die ich **widerlegt** habe (siehe §15.3) |
| 🔍 | `PRUEFEN`-Punkt: offen, braucht Entscheidung/Beleg |
**Reproduzieren:**
```sh
cd /WM_Dev/src/gitbase.de/cms/tibi-docs
node scripts/audit-docs.mjs # schreibt plans/audit-report.json
npm run coverage # schreibt plans/coverage-report.json
npm run build # muss grün bleiben
```
---
## 1. Kennzahlen und Ampel
| Bereich | Zahl | Ampel |
|---|---|---|
| Seiten / Zeilen | 61 / 7.127 | 🟢 Gerüst steht, alle Bereiche vorhanden |
| Build | 62 HTML-Dateien (61 Inhaltsseiten + `404.html`), grün, Pagefind + Sitemap ok | 🟢 |
| Tote interne Links / Anker | 0 | 🟢 |
| Ungeschlossene Codeblöcke / Codeblöcke ohne Sprach-Tag | 0 / 0 | 🟢 |
| Frontmatter fehlt | 1 Seite ohne `description` (`reference/openapi.md`) | 🟡 |
| Kaputte Markdown-Tabellen | 1 (`admin/ki-funktionen.md:40`) | 🟡 |
| Screenshots | 30 (`anwender/`), 9 Admin-Seiten mit **Platzhalter-Kommentar** (0 Bilder) | 🟠 |
| Verifiziert-Stempel | 4 verschiedene Formate, 2 Stempelnamen („Verifiziert am" / „Geprüft an"), Hashlängen 7/8 Zeichen | 🟡 |
| Faktische Fehler (selbst geprüft) | 8 | 🔴 |
| Faktische Fehler (gemeldet, ungeprüft) | 7 | 🟠 |
| Nicht dokumentierte Server-Fähigkeiten | 15 | 🔴 |
| Fehlende Interface-Doku (Admin/Projekt) | 10 Kernpunkte | 🔴 |
| Undokumentierte Schema-Properties (`tibi-types`) | 10+ | 🔴 |
| UI-Bereiche ohne Screenshot | 25 | 🔴 |
| Anwender-Seiten, die komplett fehlen | 5 | 🔴 |
| Alt-Doku-Dateien, die sofort gelöscht werden können | **0** (nur Repo-Ebene) | ⚪ |
| Seitenreview-Befunde gesamt | 131 Tabellenzeilen (35 + 50 + 21 + 25) | 🟠 |
---
## 2. Phasenplan (Übersicht)
Reihenfolge nach Risiko/Nutzen — **P7 vor P6**, weil Stil-/Strukturarbeit Überschriften und Slugs ändert und damit Screenshots entwertet. P1 und P2 sind Fehlerkorrekturen (klein, hoher Effekt), P3–P5 sind Substanz, P6a ist Vorarbeit, P7 ordnet, P6b friert ein, P8/P9 sind Ordnung und Abriss. P3 und P4 sind parallelisierbar.
| Phase | Name | Inhalt | Aufwand | Abhängigkeit |
|---|---|---|---|---|
| **P1** | Falschaussagen tilgen | 8 selbst geprüfte + 7 gemeldete Inhaltsfehler | S (½ Tag) | — |
| **P2** | Markdown/Struktur/Hygiene | kaputte Tabelle, Hüllen-Rückstände, Stempel-Format, **Link-/Anker-Prüfung als fester Schritt, Redirect-Konzept** | S (1 Tag) | — |
| **P3** | Server-Lücken schließen | 16 Fähigkeiten: Feld-Level-Permissions, System-CRUD, `_meta`, MIME, Snapshots, Embedding-Gruppen, Scopes, Filecleanup | M–L (2–3 Tage) | P1 |
| **P4** | Admin-Interface-Lücken schließen | 12 Schnittstellen: `singleton`, `admin.tokens`, `query`, `agent`, `sections`, `MANUALLY`, Export-`ctx`, Action-`execution` | M (2 Tage) | P1 (parallel zu P3) |
| **P5** | tibi-types + Starter + Fremd-Repo-Doku | undokumentierte Schema-Properties, Playwright/Mock im Starter, Interface-Doku in den Repos selbst | M (1–2 Tage) | P4 |
| **P6a** | Fehlende Anwender-Seiten | 5 Seiten (Verlauf/Restore, Massenbearbeitung, Aktionen, Backup-Einspielen, Tastenkürzel) | S–M (1–2 Tage) | P2 |
| **P7** | Stil + Starlight + Struktur | Ton, UI-Text-Treue, Komponenten (`LinkCard`, `Tabs syncKey`, `Icon`), Astro-Config, Slug-/Sidebar-Umbau | M (1–2 Tage) | P6a, P3/P4 (UI-Texte können sich noch ändern) |
| **P6b** | Screenshots Anwender + Admin | 9 Admin-Motive + 25 UI-Bereiche, alles aus `corporate` | L (3–5 Tage) | P7 (erst nach Slug-/Text-Freeze) |
| **P8** | Meta/Betrieb | AGENTS.md-Widerspruch, Sync-Skripte, Coverage v2, Stempel-Norm, Staleness-Regel, Issues an die Repos | S (1 Tag) | P2 |
| **P9** | Alt-Doku-Abbau | Repo-Hygiene, Wegfall-Bedingungen je Alt-Datei | S–M | P3–P7 |
**Warum diese Reihenfolge (falsifizierbar):** P7 verschiebt Slugs und ändert Überschriften/Sidebar; jede Stunde Screenshot-Arbeit davor müsste danach teilweise wiederholt werden. Umgekehrt kostet die Verschiebung nichts, weil P6b keine Inhalte mehr ändert. Falls sich zeigt, dass P7 doch Inhalte anfasst, die Bilder zeigen, ist die Reihenfolge zu revidieren (Re-Shoot-Zähler als Messgröße).
---
## 3. P1 — Faktische Fehler (selbst geprüft ✅)
Diese acht Stellen widersprechen dem Code. Jede Zeile nennt Doku-Ort, Code-Beleg und Korrektur.
| # | Datei:Zeile | Doku sagt | Code sagt | Korrektur | Beleg |
|---|---|---|---|---|---|
| 1 | `server/konfiguration.md:21` und `:99-104` | `bge-m3` → `EMBEDDING_BGE-M3_*` (Bindestrich bleibt stehen) | `normalizeEnvKeyName`: `-` → `_`, dann UPPER → `BGE_M3` (`internal/config/server.go:245-251`) | Alle 6 Beispielzeilen auf `EMBEDDING_BGE_M3_*` ändern, Regel-Satz präzisieren | ✅ |
| 2 | `projekt/audit-backup.md:107,127,141` | Token-Recht heißt `AllowSnapshot` | Feld heißt `allowSnapshots` (yaml-Tag im Struct `AdminToken`, `internal/models/project.go:120`; Prüfung `AllowSnapshots`) | „`allowSnapshots` (Projekt-Admin-Recht)" durchgängig | ✅ |
| 3 | `reference/hook-kontexte.md:52` | `crypto.sha256`, `crypto.md5` „(plus weitere)" | Nur 2 Funktionen registriert (`internal/hook/context_crypto.go:13-16`) | „(plus weitere)" streichen | ✅ |
| 4 | `projekt/mcp-nutzen.md:63-71` | Scope-Tabelle: `.get`, `.post`, `.put`, `.delete` | `MethodToAction("GET")` → `read`, `POST` → `create`, `PUT` → `update` (`internal/config/admin_token.go:115-128`); Detailabschnitte derselben Seite (`:102,120,192,210`) nennen korrekt `.read/.create/.update/.delete` | Tabelle auf `.read/.create/.update/.delete` umstellen | ✅ |
| 5 | `admin/customizing.md:14` | „`meta.views` (Tabelle, Karten, Kalender …)" steuert die Ansichten | `collection-meta.schema.json` hat **kein** `views` und `additionalProperties:false`; `types/admin.d.ts:452-458`: Custom-Views „not yet supported" | Zeile streichen oder durch die tatsächlich vorhandenen Mittel (`viewHint`, `subNavigation`) ersetzen | ✅ |
| 6 | `admin/exporte.md:60` | Beispiel ruft `ctx.db.loadAll({ filter: ctx.filter })` | Signatur ist `loadAll(collection, options)` (`frontend/src/lib/utils/custom-export.types.ts:56`) | Beispiel auf `loadAll("blog", { filter: ctx.filter })` korrigieren | ✅ |
| 7 | `reference/openapi.md` (ganze Seite) | Quelle `TIBI_API_URL/openapi.json`, „Erzeugen mit `npm run sync:openapi`" | Server hat nur `GET /api/v1/_openapi/:project` (`internal/controller/setup.go:692-701`); **kein** `/openapi.json`. Das Skript `scripts/sync-openapi.mjs` ruft genau diesen nicht existenten Pfad → Platzhalter bleibt ewig stehen | Skript auf `_openapi/:project` umstellen (Env `TIBI_NAMESPACE`), Seite generieren | ✅ |
| 8 | `server/betrieb.md:21` | „`mongo` (MongoDB 7)" | Lokaler Stack: `image: gitbase.de/server/mongo:8` (`tibi-admin-nova/docker-compose-local.yml:133`, `tibi-svelte-starter/docker-compose-local.yml:139`) | „MongoDB 8" | ✅ |
**Gemeldet, aber nicht selbst geprüft** (⚠️ — vor dem Fixen kurz verifizieren):
| # | Datei:Zeile | Behauptung | Korrektur |
|---|---|---|---|
| 9 | `einstieg/neues-projekt.md:68,77` | `$CODING_TIBISERVER_URL` existiert in keinem `.env`/Skript | auf die realen Starter-Variablen umstellen |
| 10 | `server/schnittstellen.md:62` | „Alle Antworten gzip" absolut | Ausnahmen ergänzen: `.pdf/.mp4/.jpg/.jpeg/.png`, `/metrics` (`setup.go:151-154`) |
| 11 | `projekt/hooks-schreiben.md:85` | „Ohne `return` … **eingeloggte** Nutzer sehen dann alles" | „anonyme Nutzer" (Starter-Hook filtert nur Anonyme) |
| 12 | `projekt/sammlungen-anlegen.md:87` | Gruppen-Beispiel „`content`, `content`, `community`" | „`content`, `community`, `system`" (Starter: `content`, `community`, `system`) — ✅ Duplikat selbst bestätigt (`grep group: api/collections/*.yml`) |
| 13 | `projekt/audit-backup.md:29` | Audit-`defaultTTL: "0"` = ewig | im Code kein `0`-Sonderfall gefunden → Beleg nachreichen oder Aussage streichen (🔍) |
| 14 | `projekt/deploy.md:90-91` | `POST /api/ssr?clear=1` als Server-Pfad dargestellt | als **Starter-/Proxy-Pfad** kennzeichnen; Server hat keine `ssr`-Route |
| 15 | `anwender/orientierung.md:58` | Überschrift „Schnellsuche" | UI-String heißt „**Befehlspalette**" (`frontend/src/lib/i18n/locales/de.json:1207`; ✅ — „Schnellsuche" kommt dort nicht vor) |
| 16 | `admin/aktionen.md` / `admin/ki-funktionen.md` | „Kurzfassung" | Locale sagt „Zusammenfassung" (⚠️) |
---
## 4. P2 — Markdown, Struktur, Hygiene-Defekte
Automatisch gefunden (Skript) und im Review bestätigt:
| # | Ort | Defekt | Fix | Beleg |
|---|---|---|---|---|
| 1 | `admin/ki-funktionen.md:40` | Tabelle mit inkonsistenter Spaltenzahl (letzte Zeile 2 statt 3 Zellen) → bricht das Rendering der dritten Stufe | Zeile auf drei Zellen umbauen (Stufe, Schwelle, Verhalten) | ✅ Skript |
| 2 | `reference/hook-kontexte.md:65` | **Aufbau-Rückstand in Nutzer-Doku**: „Die Hülle nannte nur: db, http, …" | Absatz ersetzen durch: „Diese Pakete existieren zusätzlich zur Erstfassung: …" | ✅ |
| 3 | `reference/openapi.md` | kein `description` in Frontmatter, „Stand: Platzhalter" | nach P1-#7 generieren | ✅ Skript |
| 4 | `einstieg/neues-projekt.md:81`, `einstieg/schnellstart.md:8`, `server/uebersicht.md:12` | `:::caution` / `:::danger` / `:::note`-Direktiven — **3 Einzelfälle** im ganzen Repo (sonst 86× `<Aside>`) | auf `<Aside type="…">` vereinheitlichen | ⚠️/✅ (Vorkommen selbst bestätigt) |
| 5 | `admin/ansichten-listen.md:76`, `admin/exporte.md:40`, `projekt/…` | `#`-Überschriften **innerhalb** von Codeblöcken (Skript-Fehltreffer) → keine echten Fehler, aber Prüfregel dokumentieren | keine Änderung nötig | ✅ |
| 6 | `projekt/pagebuilder-dev.md:215` | Überschrift 100 Zeichen lang | kürzen, Rest in den Text | ✅ Skript |
| 7 | alle Seiten | „Suchbegriffe:"-Zeile in 7 Seiten, „Suche-Begriffe" (Bindestrich) in 4 Seiten, in 8 Seiten ganz ohne | einheitlich „Suchbegriffe:" überall oder nirgends | ⚠️ |
| 8 | Seitenfüße | Stempel-Format: 11× „Geprüft an 2b091d89" (ohne Datum), 15× „Verifiziert am <hash> <datum> (repo)", Hashlängen 7 und 8 | Norm „`Verifiziert am <kurzhash> <JJJJ-MM-TT> (<repo>[, …])`", AGENTS.md §2 ergänzen | ✅ (Inventar selbst erhoben) |
| 9 | alle Seiten | ungenutzte Starlight-Importe (z. B. 4 Server-Seiten teilen eine 8er-Importzeile) | Imports auf tatsächlich benutzte Komponenten kürzen | ⚠️ |
| 10 | `anwender/*.md` | Fußzeile „Geprüft an … (tibi-admin-nova)" **ohne Datum** (11×) | Datum ergänzen | ✅ |
Bilder: **0 verwaiste, 0 doppelte** Dateien — alle 30 `.webp` genau einmal referenziert, alle mit deutschem Alt-Text, alle ≤ 78 KB. 🟢
---
## 5. P3 — Lücken: tibi-server-Funktionen ohne Dokustelle
Aus `plans/audit/01-server-features.md`, sortiert nach Wirkung für Projektentwickler/Admins. Code-Belege dort im Detail.
| # | Fehlende Fähigkeit | Code-Beleg | Wo sie hingehört |
|---|---|---|---|
| 1 | **Feld-Level-Permissions** (`readonlyFields`, `hiddenFields`, Feldsichtbarkeit je Rolle) | `internal/models/field_permissions.go`, `http_status.go:53-58`; Server-Doc `17-field-level-permissions.md` (546 Z.) | eigene Seite `projekt/feld-rechte.md` |
| 2 | **System-CRUD per REST** (`user/project/org/team/token`, je 5 Routen) | `internal/dbapi/register.go:90-94` | `reference/rest-system.md` |
| 3 | **`GET /_meta`, `GET /_meta/<collection>`** | `internal/dbapi/register.go:100-114` | `reference/rest-system.md` + Querverweis `projekt/mcp-nutzen.md` |
| 4 | **`token`-Collection-Felder** (`scopes`, `expiresAt`) | `internal/models/token.go:39-76` | `projekt/rechte-tokens.md` |
| 5 | **Scope-Katalog** (System- vs. Namespace-Scopes, UND-Regel Token × Besitzer) | `internal/models/token_scope_catalog.go:47-90` | `projekt/rechte-tokens.md` |
| 6 | **`upload.allowedMimeTypes`** | `internal/config/server.go:46` | `server/konfiguration.md` |
| 7 | **Snapshot-Detailfelder** (`mode: include/exclude`, `collectionDedup`, `permissions`, `retention.maxAge/maxSizeBytes`) und Einzelrouten (`stats`, `prune`, `trigger`, `:id/download`) | `internal/controller/setup.go:436-495`, `snapshot_config.go:34-93` | `projekt/audit-backup.md` |
| 8 | **Backup-Restore-Token-Lebenszyklus** (Analyse → `:restoreToken` → Restore/Cleanup) + Admin-Auth-Reihenfolge | `internal/controller/admin_backup*.go`, `admin_auth.go:20-74` | `projekt/audit-backup.md` |
| 9 | **Embedding-Gruppen-Tuning** (Policies, Health, Race, Retry, Bulk) | `internal/config/embedding_group.go:44-190`, Env `server.go:489-625` | `server/konfiguration.md` + `projekt/suche.md` |
| 10 | **Suchmodi jenseits des Starters** (`text/regex/eval/filter`, `autoRegenerate`, Search-Worker, Such-Debug) | `internal/models/collection.go:314`, `controller/search_debug.go` | `projekt/suche.md` |
| 11 | **`aggregate`** an der Listen-Route (nur in MCP/SSR/Fehlercodes erwähnt) | `internal/controller/collection_get.go` | `projekt/abfragen-lesen.md` |
| 12 | **Realtime-Hub-Details** (Ringpuffer 256, `LastN/MaxAge/TTL`, Drop-Policy) | `internal/realtime/hub.go:94-179` | `server/schnittstellen.md` |
| 13 | **Gzip-Ausnahmen** + `_search`-Stripping/NaN-Sanitizing | `internal/controller/setup.go:151-154`, `:814-825` | `server/schnittstellen.md` |
| 14 | **Filecleanup** (Orphan-Dateien) hat gar keine Dokustelle | `internal/filecleanup/cleanup.go:21-149` | Einzeiler in `server/betrieb.md` + `projekt/dateien-bilder.md` |
| 15 | **Hook-Cache-Defaults** (MaxEntries 10000, TTL 0, LRU) | `internal/cache/cache.go:23-33` | `reference/hooks-daten.md` |
| 16 | **Ephemeral REST Tokens**: `tibi-server/docs/24-…md` beschreibt sie, im Code kein Treffer | nur `docs/24*` | 🔍 **Entscheidung nötig**: implementieren oder Server-Doc löschen — nicht in die Nutzer-Doku übernehmen, solange es die Fähigkeit nicht gibt |
**Positiv (vollständig abgedeckt):** alle 36 MCP-Tools, alle 23 Hook-Pakete/70 Funktionen, Jobs, Actions, Audit, LLM-Chat, Rate-Limit/Login-Backoff, CORS-Kaskade, suexec, SSR-Hooks, Deploy/Compose-Profile.
---
## 6. P4 — Lücken: tibi-admin-nova-Entwickler-Schnittstellen
Aus `plans/audit/02-admin-interfaces.md`. Alle Punkte fehlen **in tibi-docs und in `tibi-admin-nova/docs`**.
| # | Schnittstelle | Wirkung | Beleg |
|---|---|---|---|
| 1 | `meta.singleton` (`enabled`, `key`) | Ein-Eintrag-Sammlungen (Einstellungen, Startseite) | `collection-meta.schema.json` |
| 2 | `project.admin.tokens` (`allowReload`, `allowRegenerateSearch`, `allowBackup`, `allowSnapshots`) | wer Backup/Snapshot/Search-Rebuild per Token darf | `project.schema.json` |
| 3 | `meta.query` | deklarierte Zusatz-Querykeys für Hooks (`ctx.request().query`) | `collection-meta.schema.json` |
| 4 | `meta.agent` (`purpose/instructions/fieldHints/workflows`) | Sammlungs-Doku für Agents (MCP/REST `_meta`) | `collection-meta.schema.json` |
| 5 | `meta.sections` (`group/label/collapsible/collapsed`) | zweite Formular-Gruppierung neben `sidebar` | `collection-meta.schema.json` |
| 6 | `meta.defaultSort.order: MANUALLY` + Drag-Freigabe | manuelle Reihenfolge als **Config**, nicht nur Bedienung | `collection-meta.schema.json`, `types/admin.d.ts:426ff` |
| 7 | `meta.viewHint.navigation` (`nodesField`, `declaredTrees`) | Navigationsbaum-Ansicht konfigurieren | Schema, `NavigationTreeField.svelte` |
| 8 | Export-`ctx` vollständig (`loadAll`-Batching 500, `sort[0]`, `selector` vs. `select`, `csvCell`-Guard, Dateinamen-Sanitizing) | eigene Berichte korrekt schreiben | `frontend/src/lib/utils/custom-export.types.ts:17-100` |
| 9 | Action-`execution` (`kind`, `cancellable`) + Upload-Fortschritt | Actions abbrechbar/Fortschritt korrekt | `action.schema.json`, `ActionView.svelte:349-379` |
| 10 | Ladeordnung/Cache der Registry-Loader (Pagebuilder, Exporte), `{eval}`-Asset-Specifier | Assets/Registry korrekt verdrahten | `load-registry.ts:14-60`, `project-asset-specifier.ts:17-50` |
| 11 | Runtime-System-Config (`system.config.json`, Engine-Modi `auto/api/browser/none`, TTS-Stimmen) | KI-Laufzeit im Admin konfigurieren | `runtime-system-config.ts`, `schemas/config/system-config.schema.json` |
| 12 | `views`-Widerspruch auflösen (siehe P1 #5) | Doku darf nichts versprechen, was das Schema verbietet | ✅ |
🔍 **Nicht verifizierte Begriffe aus dem Ausbau-Auftrag:** `hooksTrigger` und `exportScript` existieren im Code **nicht** (grep ohne Treffer). Vor jeder Dokumentation klären, ob damit `hooks` bzw. `meta.exports` gemeint war.
---
## 7. P5 — Lücken in den anderen Repos
### 7.1 `tibi-types` (Schemas + Hook-Typen)
Kein Doku-Verzeichnis vorhanden — die einzige Referenz sind `index.d.ts` und `schemas/config/*.json`. Aus `plans/audit/03-types-starter.md`:
| Undokumentierte Property | Wirkung | Beleg |
|---|---|---|
| `collection.queryLimits` (`defaultLimit`/`maxLimit`) | erzwungene Limit-Grenzen je Sammlung | `collection.schema.json` |
| `collection.readonlyFields` / `hiddenFields` | pauschale Feld-Sperre/Verbergung | `collection.schema.json` |
| `collection.uploadPath` / `upload` | Upload-Ablage und Upload-Regeln je Sammlung | `collection.schema.json` |
| `collection.strictFields` | unbekannte Felder ablehnen | `collection.schema.json` |
| `collection.imageFilter` | Bildfilter-Defaults je Sammlung | `collection.schema.json` |
| `collection-meta.singleton` / `sections` | s. P4 | `collection-meta.schema.json` |
| `field-meta.hideLabel`, `imageEditor`, `containerProps`, `inputProps`, `schemaRef`, `protected`, `drillDown`, `pageSize` | Widget-/Formularverhalten | `field-meta.schema.json` |
| `permissionSet.validProjections` | erlaubte Feldprojektionen je Recht | `permissions.schema.json` |
| `action.output.mapping` (`summary`/`details`/`progress` mit index/count/status/message) | Ergebnis-Darstellung nach Action | `action.schema.json` |
**Strukturempfehlung:** `reference/schemas.md` von „Übersicht" zu einer **Property-Matrix** ausbauen (Schema → Property → Typ → Default → Wirkung), generiert aus `schemas/config/*.json` per Skript (analog `scripts/coverage-inventory.mjs`). Damit ist die Seite dauerhaft drift-frei.
### 7.2 `tibi-svelte-starter`
| Lücke | Beleg/Ort | Ziel in tibi-docs |
|---|---|---|
| Playwright-Setup fehlt komplett | `playwright.config.ts`, `tests/` | neues Kapitel `projekt/tests.md` oder Abschnitt in `einstieg/schnellstart.md` |
| Mock-Modus (`MOCK=1`) nur als Wort | `esbuild.config*.js`, `frontend/` | `einstieg/schnellstart.md` |
| `api/templates/`, `ssr.yml`, Cache-Regeln | `api/` | `projekt/ssr-website.md` |
| CI/Deploy-Env (`ci-deploy.sh`, `ci-staging.sh`, `.basic-auth-*`, `ADMIN_ASSET_VERSION`) | Repo-Root | `projekt/deploy.md` |
| `lib/`-Module, `widgets/`, Sentry | `frontend/src/` | `projekt/ssr-website.md` |
### 7.3 `tibi-admin-nova/docs/` und Test-Pläne
**Kein Löschkandidat.** 3 Dateien sind Single Source (`collection-config.md`, `custom-export-script.md`, `test-plans/39-preview-resolver.md`, `test-plans/41-search-index-status.md`), 18 weitere sind teilweise die einzige Quelle für Detailverhalten.
**Empfehlung statt Löschen:** die drei Vollreferenzen (`collection-config.md` 2.293 Z., `collection-export.md` 327 Z., `custom-export-script.md` 117 Z.) per Sync als **Referenz-Kapitel** in tibi-docs spiegeln (`reference/collection-config.md` …) und im Repo als Quelle belassen — gleiche Mechanik wie beim Server-Sync, nur mit Sidebar-Eintrag.
---
## 8. P6 — Screenshots und Anwender-UI-Abdeckung
### 8.1 Ist
- `anwender/`: 30 Bilder, 11 Seiten — inhaltlich vollständig illustriert. 🟢
- `admin/`: **9 Seiten mit Platzhalter-Kommentar `<!-- Screenshot-Platzhalter (T-SHOTS): … -->`, null Bilder.** 🔴
Fehlende Admin-Motive (aus den Kommentaren, ✅ selbst extrahiert):
| Seite | Motiv |
|---|---|
| `admin/aktionen.md:29` | Action-Ansicht mit Methoden-Reitern, Eingabeformular, Ausführen-Knopf |
| `admin/ansichten-listen.md:103` | Liste mit Suchfeld, Filter-Popover, Paginierung |
| `admin/benutzer-rechte.md:103` | Hub mit Benutzerliste + Detail-Drawer (Rollen-Badge, Chips) |
| `admin/customizing.md:87` | angepasste Listenansicht mit Spaltenwahl und eigenem Export-Reiter |
| `admin/eintrags-editor.md:103` | Editor mit Formular, Seitenleiste, Übersetzungsleiste |
| `admin/exporte.md:83` | Export-Dialog mit Formatwahl und Spaltenliste |
| `admin/ki-funktionen.md:132` | geöffneter KI-Chat mit Werkzeugstatus und Mikrofon-Knopf |
| `admin/medien.md:81` | Medienraster mit Warteschlange und Bildeditor-Dialog |
| `admin/setup.md:83` | frisch gestarteter Admin mit Projektliste nach Login |
### 8.2 UI-Bereiche ganz ohne Bild (Anwender-Perpektive, 25)
Command-Palette-Overlay, Tastaturkürzel-Hilfe, Suchindex-Banner, Bulk-Edit-Panel, Feldsuche/Sprungziele/Drill-Down, Diff-Dialog, JSON-Overlay, FK-Drawer/Inline-Anlage/Rückverweise, Übersetzungsleiste, Medien-Lightbox, Upload-Warteschlange, Fremddatei-Widget, Action-Ansicht (Anwender), KI-Werkzeug-Inspektor/Chat-Verlauf/Vision-Overlay, Audit-/Verlaufs-UI (komplett), Backup-Import-Dialog, Restore-Bestätigung, Benutzer-/Team-/Org-Drawer, Token-Seite, Projekt-Admin-Ansicht, Stellvertretungs-Banner, Session-Timeout-Dialog, Theme-Umschalter, Voice-Filter-Eingabe.
### 8.3 Fehlende Anwender-Seiten (mit Code-Beleg)
| Vorschlag | Beleg | Warum |
|---|---|---|
| `anwender/verlauf-wiederherstellen.md` | `AuditView.svelte`, `AuditHistoryPanel.svelte`, `AuditDiff.svelte`, `AuditRestoreDialog.svelte` | mächtigste ungeklärte UI; Restore ist destruktiv |
| `anwender/massenbearbeitung.md` | `BulkEditPanel.svelte` | versehentliche Änderungen an vielen Einträgen |
| `anwender/aktionen.md` | `ActionView.svelte` | Anwender startet Actions, weiß aber nicht, was passiert |
| `anwender/backup-einspielen.md` | `BackupView.svelte`, `backup-api.ts` | Import/Restore ohne Anleitung = Datenverlustrisiko |
| `anwender/tastenkombinationen.md` (Anwender-Sicht) | `shortcuts.ts`, `KeyboardShortcuts.svelte` | Referenz existiert, Anwender-Zugang fehlt |
### 8.4 Vorgehen (Playwright, Mock)
Bestehende Vorlage: `plans/t-shots-arbeitsauftrag.md` — Mock-Admin, Login `admin`/`admin` (`frontend/assets/mock/login.json`), Projekt **Corporate Website** (Mock-Projekte vorhanden: `blog`, `corporate`, `events`, `intranet`, `shop`; **`t1` ist verboten**), Zielpfad `tibi-docs/src/assets/anwender/`, max. 200 KB je Bild, deutscher Alt-Text.
Erweiterung für P6:
1. **Admin-Unterordner** `src/assets/admin/` anlegen (Trennung Anwender-/Admin-Bilder).
2. Motive 8.1 zuerst (9 Bilder), dann 8.2 in der Reihenfolge der Seiten (25 weitere).
3. Für die 5 neuen Seiten 8.3 zuerst Text, dann Bilder.
4. Danach `npm run build` + Alt-Text-Prüfung (`scripts/audit-docs.mjs` prüft automatisch).
### 8.5 Daten-/Rechte-Hinweis
Screenshots aus dem Mock sind unkritisch. Sobald echte Projekte fotografiert werden: keine Kunden-/Personendaten (AGENTS.md §5). 🔍 Für Review-Screenshots (Bulk, Restore, Stellvertretung) reicht der Mock — bitte bestätigen, dass Mock-Daten im Bild bleiben dürfen.
---
## 9. P7 — Stil
Aus den vier Seitenreviews (131 Befunde; hier die Muster, nicht jeder Einzelfall).
| Muster | Beispiel | Regel |
|---|---|---|
| **Erfundene UI-Namen** | „Schnellsuche" statt „**Befehlspalette**" (`orientierung.md:58`, `hilfe.md`); „Kurzfassung" statt „**Zusammenfassung**" | jeder fett/„zitierte" UI-String muss wörtlich in `frontend/src/lib/i18n/locales/de.json` stehen — als Prüfschritt in die Abnahme aufnehmen |
| **Anglizismen/Doppelbegriffe** | „Sichtbare Spalten" vs. „Spaltenwahl"; „Verlauf" vs. „Historie"; „Admin" vs. „tibi-admin" | Glossar in `einstieg/begriffe.md` als Norm, Seiten gleichen darauf ab |
| **Belehrende Binsen** | „Preise stehen nirgends im Code — erfinde keine" (`llm-nutzen.md:89`); „Übernimm die Liste nicht blind" (`audit-backup.md:96`); „Passiert nicht von selbst" (`dateien-bilder.md:105`) | Imperativ → sachlicher Satz |
| **Prosa-Wände** | Kennzahlen-Prosa in `ki-funktionen.md:30`; 4 Admin-UI-Absätze in `audit-backup.md:50-58` | Tabelle/`Badge`-Legende statt Fließtext; Admin-Verhalten nach `admin/` verschieben |
| **Wertungen ohne Beleg** | `suche.md:21` „kein Textindex nötig"; `hooks-schreiben.md:111` „einzige Log-Hilfe" | Absolut-Aussagen abschwächen oder belegen |
| **Inkonsistente Anführungszeichen** | `„…"` in 2 Dateien gemischt mit `"…"` | eine Form (deutsche Anführungszeichen) |
| **Platzhalter im Beispiel** | `LIVE_URL=https://www`, `__PROJECT_NAME__` (`deploy.md:15,79`) | `# TODO: echte Domain` in den Block |
| **Fehlende Ergebnis-Schritte** | Setup-Checkliste als `[ ]`-Codeblock (`neues-projekt.md:12-23`) | echte Task-Liste/`Steps`, damit Fortschritt sichtbar ist |
| **Duplikate statt Verweise** | `hilfe.md` wiederholt drei Seiten; `qName`-Tabelle wortgleich in `abfragen-lesen.md` und `suche.md`; Widget-Tabellen 3×; `active:true`-Falle in 5 Seiten | kanonische Stelle bestimmen, Rest kurz + Verweis |
**Positiv:** keine „man"-Form, saubere Umlaute, keine toten Links, alle Codeblöcke balanciert, keine Seite > 800 Zeilen außer `mcp-nutzen.md` (754).
---
## 10. P7 — Bessere Starlight-Komponenten und Astro-Konfiguration
### 10.1 Komponenten-Einsatz heute (Skript-Inventar ✅)
| Komponente | Vorkommen | Bewertung |
|---|---|---|
| `Aside` | 86 | gut genutzt; 3 Ausreißer als `:::`-Direktive |
| `Steps` | 78 | gut genutzt |
| `TabItem` / `Tabs` | 56 / 25 | **ohne `syncKey`** → Tab-Wahl geht beim Navigieren verloren |
| `Card` / `CardGrid` | 30 / 7 | Karten enthalten Markdown-Links statt echter Karten-Links |
| `Badge` | 21–30 | ok |
| `FileTree` | 3 | ok |
| `LinkCard` / `LinkButton` / `Icon` / `MarkdownContent` | **0** | ungenutztes Potenzial |
| Codeblöcke | 432 Zäune, 254 mit `title=` | **0 Zeilenmarkierungen**, **0 `frame=`** |
### 10.2 Konkrete Verbesserungen
| # | Maßnahme | Wirkung | Beleg |
|---|---|---|---|
| 1 | **`Tabs syncKey`** auf allen mehrsprachigen/alternativen Tab-Gruppen (`konfiguration.md:46-77`, `schnittstellen.md:21-60`, `felder-widgets.md`) | gleiche Auswahl bleibt beim Seitenwechsel | ⚠️ |
| 2 | **`LinkCard`** statt `Card` + Markdown-Link auf Übersichtsseiten (`einstieg/was-ist-tibi.md:30-35`, `server/uebersicht.md:19-33`, `index.mdx`) | ganze Karte klickbar, bessere Tastaturbedienung | ⚠️ |
| 3 | **`Icon`** in Tabellen/`Steps` bei wiederkehrenden Handlungstypen | schnellere Erfassung | ⚠️ |
| 4 | **Zeilenmarkierungen** (` ```sh {2,5} `) dort, wo ein Beispiel „die eine Zeile" zeigt (Konfiguration, `.env`, Hook-Beispiele) | Leser sieht die relevante Zeile sofort | ✅ (0 Vorkommen) |
| 5 | `references`/`frame="none"` für reine Textblöcke, `title=` konsequent (254 von 432 haben es) | einheitliche Optik | ✅ |
| 6 | `<Tabs>` statt zwei aufeinanderfolgender `Steps` bei Eingabe/Ausgabe (`anwender/ki-nutzen.md:58-75`) und bei Viewport-Presets (`anwender/seiten-bauen.md:89-109`, Werte aus `PagebuilderViewportControl.svelte:22-29`) | Zuordnung Bild ↔ Preset | ⚠️ |
| 7 | `<Tabs>` je Exportformat (`anwender/exportieren.md:24-34`) | Vergleich CSV/XLSX/JSON statt Klickstrecke | ⚠️ |
| 8 | **`<Badge>`** für Statusbegriffe statt Fettung (REST-only, Legacy, Pflicht) | konsistente Optik | ⚠️ |
| 9 | Frontmatter `sidebar.label` für abgekürzte Seitentitel (z. B. „Hook-Referenz: Daten" → „Daten") + `tableOfContents.minHeadingLevel` je Seite | kürzere Sidebar, längere ToC | 🟡 |
| 10 | `lastUpdated: true` in `astro.config.mjs` + `lastUpdated`-Frontmatter je Seite | Aktualität sichtbar statt Stempel im Fließtext | 🟡 |
| 11 | `expressiveCode`-Optionen (Theme passend zu `custom.css`, `frameType`) statt Default | einheitliches Code-Bild | ✅ |
| 12 | Suche: `pagefind`-Optionen (z. B. Sprach-/Stoppwort-Konfiguration), `social`-Einträge erweitern, `editLink` prüfen (zeigt aktuell auf `_edit/master/`) | Suchqualität, Beitragsweg | ✅ |
### 10.3 Astro-/Repo-Ebene
| # | Befund | Maßnahme |
|---|---|---|
| 1 | Sidebar ist **vollständig manuell** (61 Einträge in `astro.config.mjs`) — jede neue Seite braucht 2 Änderungen (Datei + Sidebar) | auf `autogenerate` je Gruppe umstellen, sobald die Ordner stabil sind; bis dahin Sidebar-Check in `scripts/audit-docs.mjs` (existiert bereits) als Pflicht-Abnahme |
| 2 | keine `src/components/`-Erweiterungen | optional: `TibiShot.astro` (Bild + Alt-Text + Breite), damit Screenshot-Einbau überall gleich aussieht |
| 3 | `sync-server-docs.mjs` erzeugt **verwaiste Seiten** (24+3), weil die Ziele nicht in der Sidebar stehen — siehe §11 | 🔍 Entscheidung D1 |
| 4 | `scripts/sync-openapi.mjs` zielt auf nicht existenten Endpunkt (P1 #7) | Skript korrigieren |
| 5 | `coverage-inventory.mjs` misst Topic-Level („282/282") und damit keine Tiefe | v2: Funktionskörnung + Depth-Gate (siehe `plans/doku-ausbau.md`, Abschnitt „v3") |
| 6 | keine `robots.txt`/`_headers`/Cache-Regeln für den statischen Betrieb | bei Live-Deploy ergänzen (nginx-Config existiert) |
### 10.4 Zusätzliche Dimensionen (aus der Zweitmeinung ergänzt)
| # | Dimension | Befund | Maßnahme | Phase |
|---|---|---|---|---|
| 1 | **Link-/Anker-Integrität + Redirects** | AGENTS.md §5 sagt selbst, dass der Build interne Links **nicht** prüft; es gibt keine `redirects`-Konfiguration. Jede Slug-Umbenennung in P7 reißt damit unbemerkt Links auf | `scripts/audit-docs.mjs` (prüft Links + Anker bereits) als Pflichtschritt vor jedem Push; Umbenennungen zusätzlich in `astro.config.mjs` → `redirects` eintragen | P2 / P7 |
| 2 | **Snippet-Verifikation** | „Code schlägt Doku" ist Regel, aber kein Beispiel wird ausgeführt — genau der Fehlertyp aus P1 (#6 `loadAll`, #4 Scope-Namen) | die 6 verifizierten Fehlerbeispiele in eine Prüfliste aufnehmen; wo möglich (Hooks, Export-Skripte) Beispiele gegen `tibi-types`/`demo-api` laufen lassen | P1 / P2 |
| 3 | **Staleness/Versionierung** | „Verifiziert am `<hash>` <datum>" altert ohne Mechanismus; 4 Formate, 2 Stempelnamen | Norm + Regel „bei jeder Änderung an der Quelle Stempel erneuern" in AGENTS.md; Audit-Skript meldet Stempel älter als N Tage | P8 |
| 4 | **Onboarding-Reihenfolge (Reise statt Fähigkeit)** | Sidebar ist fähigkeitsgetrieben (`projekt/` folgt dem Schema, nicht dem Ablauf klonen → konfigurieren → bauen → deploy) | `einstieg/schnellstart.md` als Reise ausbauen und die Sidebar-Reihenfolge in `projekt/` gegen diesen Ablauf prüfen | P7 |
| 5 | **Barrierefreiheit** | Alt-Texte sind vollständig ✅; Kontrast von `--tibi-nova: #c2410c` auf hellem Grund und Dark-Hover sind ungeprüft | Kontrast mit `a11y`-MCP oder Lighthouse prüfen, `--tibi-nova` nur als Deko zulassen | P7 |
| 6 | **Suche** | Pagefind läuft (62 Dateien, 185 ms) ✅; deutsche Stemming-Qualität und Synonyme („Suchbegriffe:"-Zeile) ungeprüft | Suche mit typischen Anwenderbegriffen testen, ggf. Synonym-Mapping je Seite vereinheitlichen | P7 |
| 7 | **Mehrsprachigkeit** | bewusst nur `de` (`astro.config.mjs` `locales.root`) | als ADR festhalten (`adr/0004-nur-deutsch.md`), damit die Frage nicht wiederkehrt | P8 |
| 8 | **Druck/PDF** | nicht vorgesehen | bewusst streichen; Dev-Doku wird online gelesen | — |
---
## 11. Struktur, Information Architecture, Zielbild
### 11.1 Ist-Struktur
```
einstieg/ 4 Seiten (Was ist Tibi, Schnellstart, Begriffe, Neues Projekt)
server/ 5 (Übersicht, Konfiguration, Betrieb, Sicherheit, Schnittstellen)
projekt/ 15 (Sammlungen → Deploy)
admin/ 10 (Konzept, Setup, Customizing, Ansichten, Editor, Medien, Exporte, Aktionen, Rechte, KI)
anwender/ 11 (Anmelden → KI nutzen)
reference/ 11 (OpenAPI, Schemas, Hook-Kontexte, 5 Hook-Referenzen, Token-Köpfe, Fehler, Tastatur)
adr/ 4
```
Abgleich Sidebar ↔ Dateien: **vollständig konsistent**, keine Seite ohne Sidebar-Eintrag. 🟢
### 11.2 Zielbild „3 Zielgruppen × 1 Ort pro Fähigkeit"
Der Rahmen verlangt drei Sichten auf **dieselben** Fähigkeiten. Heute sind sie teils vermischt:
| Fähigkeit | Entwickler (Projekt) | Anwender | Gehört heute wo | Soll |
|---|---|---|---|---|
| Editor/Widgets | `projekt/felder-widgets.md` | `anwender/eintrag-bearbeiten.md` | + 3 Widget-Tabellen in `admin/*` | Tabellen nur in `admin/customizing.md`, Rest verweist |
| Verlauf/Restore | — | fehlt | nur `projekt/audit-backup.md:50-58` (Admin-Verhalten in Projekt-Seite) | `admin/` + neue Anwender-Seite |
| Pagebuilder | `projekt/pagebuilder-dev.md` (660 Z., enthält 65 Z. Admin-UI) | `anwender/seiten-bauen.md` | vermischt | UI-Teil nach `admin/eintrags-editor.md` |
| MCP | `projekt/mcp-nutzen.md` (754 Z.) | — | de facto Referenz | Werkzeug-Referenz → `reference/mcp-werkzeuge.md` |
| Snapshots/Backup | `projekt/audit-backup.md` | `anwender/sichern.md` | ok | + `anwender/backup-einspielen.md` |
### 11.3 Konkrete Struktur-Maßnahmen
1. `reference/` erweitern: `mcp-werkzeuge.md`, `rest-system.md`, `collection-config.md` (Sync aus Nova), `schema-properties.md` (generiert).
2. `admin/` als **Entwickler-Sicht auf die Oberfläche** schärfen: alle UI-Verhaltensbeschreibungen aus `projekt/*` dorthin.
3. `anwender/` um die 5 fehlenden Seiten (8.3) erweitern; Ziel = **jede** Bildschirmfläche hat eine Seite.
4. `projekt/mcp-nutzen.md` und `projekt/pagebuilder-dev.md` auf Leitfaden-Länge kürzen (Ziel < 300 Zeilen), Details in `reference/`.
5. Querverweis-Konvention: jede Fähigkeit beschreibt **eine** kanonische Seite; andere Seiten nennen sie in 1–2 Zeilen mit Link.
---
## 12. P9 — Alt-Doku in den anderen Repos: was weg kann
**Ehrliche Antwort: als *Duplikat* von tibi-docs kann heute keine Datei weg — 0 Duplikate.** Grund: `tibi-server/docs/` (24 englische Dateien) und `tibi-admin-nova/docs/` (3 Referenzdateien) sind **Quellen**, nicht Kopien; die tibi-docs-Seiten sind davon abgeleitete Nutzer-Doku. Zusätzlich liest `scripts/sync-server-docs.mjs` genau diese Pfade als Sync-Quelle. Erst wenn eine Fähigkeit in tibi-docs **tiefer** steht als in der Quelle, ist die Quelle ein Duplikat.
Davon zu trennen sind **Stale-Kandidaten** (beschreiben etwas, das es nicht mehr/nicht gibt) — die dürfen sofort weg oder müssen korrigiert werden, unabhängig von tibi-docs. Es gibt genau einen: `tibi-server/docs/24-ephemeral-rest-tokens.md` (🔍 Entscheidung D3).
Und **Repo-Hygiene-Fälle** (Alt-Kopien, kaputte Arbeitsstände) — siehe §12.1.
### 12.1 Repo-Ebene: sofort weg (nach Backup-Abgleich)
| Pfad | Befund | Empfehlung |
|---|---|---|
| `cms/tibi-docs-kaputt/` | defekte Doku-Kopie, eigenes `.git` | löschen |
| `cms/tibi-admin.bak/`, `tibi-admin.bak2/`, `tibi-admin.kaputt/` | Alt-`tibi-admin` (vor Nova) mit eigenem `.git` | löschen — der alte Admin ist laut Zielsetzung ohnehin ausgeschlossen |
| `cms/tibi-server.bak/` | Server-Backup-Kopie | löschen |
| `cms/tibi-docs-legacy/` (inkl. eingechecktem `node_modules/`) | Docpress-Ära | **nach** Volltext-Abgleich gegen `einstieg/begriffe.md` löschen |
| `cms/tibi-admin/`, `tibi-admin-dev/`, `tibi-admin-master/`, `tibi-admin-server/`, `tibi-starter/` | alte Starter-/Admin-Varianten, READMEs verweisen auf `tibi-svelte-starter` | klären, dann archivieren; `tibi-svelte-starter` als kanonisch erklären |
| `tibi-server/docs/24-ephemeral-rest-tokens.md` | beschreibt eine Fähigkeit, die im Code nicht existiert (kein Treffer für „ephemeral" außer in dieser Datei) | 🔍 D3: implementieren **oder** Datei löschen — beides ist ein Fortschritt, Liegenlassen nicht |
### 12.2 Datei-Ebene: Wegfall **erst** nach Erfüllung dieser Bedingungen
| Alt-Datei | Wegfall möglich, wenn … | Heutiger Abdeckungsgrad |
|---|---|---|
| `tibi-server/docs/15-troubleshooting.md` | `reference/fehlercodes.md` alle dort gelisteten Fehler + Ursachen führt | teilweise |
| `tibi-server/docs/16-cookbook.md` | die Rezepte in `projekt/sammlungen-anlegen.md` stehen | teilweise |
| `tibi-server/docs/13-openapi-metrics.md` | `reference/openapi.md` generiert ist und Metrik-Labels dokumentiert sind | teilweise (P1 #7) |
| `tibi-server/docs/17-field-level-permissions.md` | `projekt/feld-rechte.md` existiert | **nicht abgedeckt** (P3 #1) |
| `tibi-server/docs/24-ephemeral-rest-tokens.md` | Fähigkeit im Code existiert (heute nicht) | **nicht abgedeckt** 🔍 |
| `tibi-server/docs/{01,02,03,04,05,06,07,08,10,12,14,18,19,20,21,22,23}.md` | **nicht absehbar** — Core-Referenz für Server-Entwicklung, nicht Ziel dieser Doku | teilweise/vollständig |
| `tibi-admin-nova/docs/collection-config.md`, `custom-export-script.md` | nach Spiegelung als `reference/collection-config.md` | nicht abgedeckt |
| `tibi-admin-nova/docs/test-plans/*` | nie — Test-Artefakte, keine Nutzer-Doku | — |
| `tibi-types/` (keine Doku) | es entsteht Doku **im Repo** (empfohlen, §7.1) | — |
### 12.3 Dubletten/Widersprüche zwischen Alt-Doku und tibi-docs
| # | Widerspruch | Auflösung |
|---|---|---|
| 1 | MCP-Route: `tibi-server/docs/23-mcp.md` (Env `MCP_ROUTE` steuerbar) vs. `server/schnittstellen.md:77` („wird ignoriert") | ✅ Code: Routing ist hart (`setup.go:754-755`), `mcp.Route()` ungenutzt → **tibi-docs hat recht**, Server-Doc veraltet |
| 2 | Sync-Behauptung in `tibi-docs/AGENTS.md` §2/§4 vs. Realität (keine Sync-Artefakte im Repo) | AGENTS.md korrigieren (P8) |
| 3 | Starter-`Token`-Header (`tibi-svelte-starter/AGENTS.md`) ohne Warnung vor reservierten Namen (`reference/token-koepfe.md:52`) | Starter-AGENTS.md ergänzen |
| 4 | `plans/doku-ausbau.md` führt `llm-nutzen`/`mcp-nutzen` noch als „Hülle" — beide sind fertig | Plan-Zeilen als erledigt markieren |
---
## 13. P8 — Prozess und Meta
| # | Befund | Maßnahme |
|---|---|---|
| 1 | `AGENTS.md` §2 sagt „`server/` = SYNC, nicht von Hand editieren"; tatsächlich sind es Handseiten (`npm run sync` würde 27 verwaiste Seiten erzeugen) | 🔍 **Entscheidung D1**: Sync reaktivieren **oder** Skript + AGENTS-Abschnitt entfernen |
| 2 | `AGENTS.md` §1 verbietet gleichzeitig das Duplizieren englischer Server-Docs — beißt sich mit §2 | Widerspruch auflösen (eine Regel streichen) |
| 3 | Prüfregeln (Links, UI-Text-Treue, Stempel) sind nirgends automatisiert | `scripts/audit-docs.mjs` um UI-String-Abgleich gegen `de.json` erweitern und in `npm run build`-Vorprüfung hängen |
| 4 | Kein Mechanismus, der Fähigkeiten ohne Dokustelle meldet | Coverage v2 mit Funktionskörnung + Depth-Gate (siehe `plans/doku-ausbau.md` v3) |
| 5 | Stempel-Format driftet (4 Formate) | Norm in AGENTS.md §2 festschreiben |
| 6 | Offene Punkte aus der letzten Runde wurden nie als Issues angelegt | Issues anlegen: (a) tibi-server: Typo `reponse.header` (`context_response.go`), (b) tibi-server: doppelter yaml-Tag `allowSnapshots` (`models/project.go:104`), (c) tibi-admin-nova: totes `u`-Kürzel in `de.json`, (d) tibi-server: `mgocrud.old/` toter Code, (e) tibi-server: Doku `24-ephemeral-rest-tokens.md` ohne Code |
**Fehler, die ich zusätzlich **im Server-Code** gefunden habe** (nicht Doku, aber gefunden beim Prüfen — gehören als Issue gemeldet):
| Code-Fund | Beleg |
|---|---|
| doppelter Struct-Tag `yaml:"allowSnapshots" yaml:"allowSnapshots"` (auch bei `token`, `allowReload` … — alle vier Felder doppelt getaggt) | `internal/models/project.go:120` (Struct `AdminToken:116-122`) ✅ |
| `Content-Range:` inkl. Doppelpunkt als Header-Name (Doku zitiert ihn korrekt als Code-Abweichung) | `internal/controller/collection_get.go:32` ✅ |
| Typos in Hook-Kontext: `reponse.header` | `internal/hook/context_response.go` (⚠️) |
| `MCP_ROUTE`-Env existiert, wird aber vom Routing ignoriert | `internal/config/server.go:221-224` vs. `setup.go:754` ✅ |
---
## 14. Phasen-Checklisten (zum Abhaken)
### P1 — Falschaussagen tilgen
- [ ] `EMBEDDING_BGE_M3_*` in `server/konfiguration.md` korrigieren (5 Stellen)
- [ ] `allowSnapshots` in `projekt/audit-backup.md` (3 Stellen)
- [ ] „(plus weitere)" bei `crypto` in `reference/hook-kontexte.md:52` streichen
- [ ] MCP-Scope-Tabelle in `projekt/mcp-nutzen.md:63-71` auf `.read/.create/.update/.delete`
- [ ] `meta.views` in `admin/customizing.md:14` streichen/ersetzen
- [ ] `loadAll`-Beispiel in `admin/exporte.md:60` korrigieren
- [ ] `scripts/sync-openapi.mjs` auf `_openapi/:project` umstellen, `reference/openapi.md` generieren
- [ ] „MongoDB 7" → „MongoDB 8" in `server/betrieb.md:21`
- [ ] Punkte 9–16 aus §3 prüfen und korrigieren (⚠️-Liste)
- [ ] Nach jedem Fix: `npm run build` grün + `Verifiziert am …`-Stempel erneuern
### P2 — Markdown/Struktur/Hygiene
- [ ] Tabelle `admin/ki-funktionen.md:40` reparieren
- [ ] Aufbau-Rückstand `reference/hook-kontexte.md:65` entfernen
- [ ] `:::note/caution/danger` → `<Aside>` (3 Stellen)
- [ ] lange Überschrift `projekt/pagebuilder-dev.md:215` kürzen
- [ ] Stempel-Format auf allen Seiten vereinheitlichen (Datum + Repo + Hashlänge)
- [ ] „Suchbegriffe:"-Zeile normieren (7/4/8-Verteilung auflösen)
- [ ] ungenutzte Starlight-Importe entfernen
- [ ] **Link-/Anker-Prüfung als fester Schritt etablieren** (`node scripts/audit-docs.mjs`, gibt 0 tote Links aus) + Redirect-Konzept in `astro.config.mjs` beschließen
- [ ] **Snippet-Prüfliste** aus den 6 verifizierten Fehlerbeispielen anlegen
- [ ] `npm run build` + `node scripts/audit-docs.mjs` → 0 Findings außer bekannten `PRUEFEN`
### P3 — Server-Lücken
- [ ] `projekt/feld-rechte.md` neu (Feld-Level-Permissions)
- [ ] `reference/rest-system.md` neu (System-CRUD + `_meta`)
- [ ] `projekt/rechte-tokens.md` um Scope-Katalog + `token`-Felder erweitern
- [ ] `server/konfiguration.md` um `upload.allowedMimeTypes` + Embedding-Gruppen-Tuning
- [ ] `projekt/audit-backup.md` um Snapshot-Detailfelder + Restore-Token-Flow
- [ ] `projekt/suche.md` um Suchmodi/Regenerate/Search-Worker
- [ ] Gzip-Ausnahmen + Filecleanup + Cache-Defaults einarbeiten
- [ ] 🔍 Entscheidung Ephemeral REST Tokens (implementieren oder Server-Doc löschen)
### P4 — Admin-Interfaces (parallel zu P3)
- [ ] `singleton`, `sections`, `query`, `agent`, `MANUALLY`, `viewHint.navigation` dokumentieren
- [ ] `project.admin.tokens`-Rechte dokumentieren (inkl. Querverweis Backup/Snapshot/Search)
- [ ] Export-`ctx` vollständig in `admin/exporte.md` (Beispiel korrigiert, Batching, Guards)
- [ ] Action-`execution.kind/cancellable` in `admin/aktionen.md`
- [ ] Registry-Ladeordnung/Cache + `{eval}`-Specifier in `projekt/pagebuilder-dev.md`
- [ ] Runtime-System-Config in `admin/setup.md`
- [ ] 🔍 `hooksTrigger`/`exportScript` klären (existieren nicht)
### P5 — tibi-types + Starter
- [ ] `reference/schemas.md` zur Property-Matrix ausbauen (generiert)
- [ ] Undokumentierte Properties (§7.1) je Projektseite ergänzen
- [ ] Playwright-/Mock-Kapitel anlegen
- [ ] CI/Deploy-Env + `api/templates` dokumentieren
- [ ] Doku **im** `tibi-types`-Repo anlegen (Schemas selbst erklären) + `index.d.ts`-Typen referenzieren
### P6a — Fehlende Anwender-Seiten (nach P2, parallel zu P3/P4)
- [ ] `anwender/verlauf-wiederherstellen.md`
- [ ] `anwender/massenbearbeitung.md`
- [ ] `anwender/aktionen.md`
- [ ] `anwender/backup-einspielen.md`
- [ ] `anwender/tastenkombinationen.md`
- [ ] Sidebar + `coverage-map.json` nachziehen
### P7 — Stil, Starlight, Struktur (vor P6b!)
- [ ] UI-Namen gegen `de.json` abgleichen (allen voran „Befehlspalette")
- [ ] §9-Muster abarbeiten (Binsen, Prosa-Wände, Wertungen, Anführungszeichen)
- [ ] `syncKey` für Tab-Gruppen, `LinkCard` auf Übersichten, `Icon`/`Badge` dort, wo es hilft
- [ ] Zeilenmarkierungen + `frame`/`title` in Codeblöcken vereinheitlichen
- [ ] `lastUpdated`, `expressiveCode`, `sidebar.label` in `astro.config.mjs`
- [ ] kanonische Stellen für Duplikate festlegen (Widgets, `qName`, `active:true`)
- [ ] Umbenennungen/Verschiebungen nur mit `redirects`-Eintrag
- [ ] Onboarding-Reihenfolge und Sidebar gegen die Reise prüfen (§10.4 #4)
- [ ] a11y-Kontrast + Pagefind-Suche testen (§10.4 #5/#6)
### P6b — Screenshots (nach P7, Slug-/Text-Freeze)
- [ ] `src/assets/admin/` anlegen
- [ ] 9 Admin-Motive aus §8.1 aufnehmen und Platzhalter-Kommentare ersetzen
- [ ] 25 UI-Bereiche aus §8.2 bebildern
- [ ] alle Bilder ≤ 200 KB, deutsche Alt-Texte, `npm run build` grün
- [ ] Re-Shoot-Zähler messen (Messgröße für die Reihenfolge-Entscheidung)
### P8 — Meta
- [ ] 🔍 D1 entscheiden: Sync reaktivieren oder entfernen (AGENTS.md §2/§4 anpassen)
- [ ] Stempel-Norm **und Staleness-Regel** in AGENTS.md
- [ ] `audit-docs.mjs` um UI-String-Abgleich + Stempel-Alter erweitern, in Prüf-Ablauf aufnehmen
- [ ] Coverage v2 (Funktionskörnung + Depth-Gate)
- [ ] ADR „nur deutsch" anlegen (§10.4 #7)
- [ ] 5 Issues in den Repos anlegen (§13)
### P9 — Abbau
- [ ] Repo-Hygiene (§12.1) nach Backup-Abgleich
- [ ] Volltext-Abgleich `tibi-docs-legacy` → `einstieg/begriffe.md`
- [ ] Wegfall-Bedingungen (§12.2) als Tickets anlegen, sobald P3–P6 fertig sind
---
## 15. Anti-Halluzination: Was geprüft ist und was nicht
### 15.1 Selbst am Code geprüft (✅)
`EMBEDDING_BGE-M3_*` · `allowSnapshots` · `crypto`-Funktionsumfang · `MethodToAction`/MCP-Scopes · `meta.views` im Schema · `loadAll`-Signatur · OpenAPI-Route (`setup.go:692`) · `mongo:8` im Compose · Routen-Zählung (44 gruppierte Registrierungen) · MCP-Tools 10+12+14=36 · Route-Reihenfolge `_count`/`_facet` vor `:id` · „Befehlspalette" in `de.json` · Screenshot-Platzhalter in 9 Admin-Seiten · 30 Bilder alle referenziert · Schema-Properties (`queryLimits`, `readonlyFields`, `hiddenFields`, `singleton`, `sections`, `hideLabel`, `imageEditor`) fehlen in der Doku · Sync-Skript-Quellen · Build grün (62 Seiten).
### 15.2 Aus Subagenten-Reports übernommen (⚠️)
Alle Zeilen mit ⚠️ — insbesondere: `$CODING_TIBISERVER_URL`, Gzip-Absolutaussage, „eingeloggte vs. anonyme Nutzer" im Hook, `hooks-schreiben.md`-Details, `action.execution`, `system.config.json`, Starter-CI-Details, Übersichtsliste der 25 UI-Bereiche.
### 15.3 Widerlegte Report-Behauptungen (❌)
| Behauptung | Widerlegung |
|---|---|
| „`…/comment/count` landet **nicht** auf `:id`" (Review Projekt #2) | ✅ Die Doku (`projekt/abfragen-lesen.md:99`) ist **richtig**: `_count` ist als Literal `/_/:project/:collection/_count` registriert (`setup.go:514`), `/comment/count` erfüllt dieses Muster nicht und fällt auf `/_/:project/:collection/:id` mit `id="count"` (`setup.go:536`). Kein Fix nötig. |
| „0 Wegfall-Kandidaten auf Datei-Ebene, daher nichts zu tun" (Report 04) | Teils richtig, teils zu absolut: die **Bedingungen**, unter denen Alt-Dateien wegfallen dürfen, sind in §12.2 jetzt explizit gemacht; auf Repo-Ebene gibt es sehr wohl Löschkandidaten (§12.1). |
### 15.4 Zweitmeinung (SecondOpinion) — eingearbeitet
Eine unabhängige Zweitmeinung zum Plan wurde eingeholt und hat drei Änderungen erzwungen (alle übernommen):
1. **Reihenfolgefehler korrigiert:** P7 (Stil/Slugs/Struktur) läuft jetzt **vor** P6, weil es Überschriften und Slugs ändert und Screenshots sonst entwertet. P6 ist in **P6a** (fehlende Seiten, früh) und **P6b** (Bilder, nach Slug-Freeze) geteilt. P3 und P4 sind als parallel markiert.
2. **Fehlende Dimensionen ergänzt** (§10.4): Link-/Redirect-Integrität, Snippet-Verifikation, Staleness-Regel, Onboarding-Reihenfolge, a11y, Suche, ADR „nur deutsch"; Druck/PDF bewusst ausgeschlossen.
3. **§12 entkräftet die eigene Absolutaussage:** „0 Duplikate" statt „0 Wegfall"; `tibi-server/docs/24-ephemeral-rest-tokens.md` ist ein **Stale-Kandidat von heute** (🔍 D3), kein „nach P3"-Fall.
### 15.5 Regel für die Umsetzung
Jeder Fix aus §3/§5/§6 wird **erst** am Code nachgeprüft, **dann** geschrieben, **dann** gestempelt (`Verifiziert am <hash> <datum> (<repo>)`). Kein Fix ohne `datei:zeile`. Keine Zahl ohne Zählbefehl.
---
## 16. Offene Entscheidungen und offene Prüfpunkte
| # | Frage | Betrifft | Vorschlag |
|---|---|---|---|
| D1 | Sync-Modell: `server/` aus `tibi-server/docs` spiegeln **oder** Handseiten behalten? | AGENTS.md, `sync-server-docs.mjs`, `sync-openapi.mjs` | **Handseiten behalten** (Nutzer-Doku, deutsch), Sync-Skript für Server entfernen, Nova-Referenzen als `reference/`-Spiegel behalten |
| D2 | Sind Mock-Daten in Review-Screenshots ok? | P6 | ja, `corporate` reicht |
| D3 | Ephemere REST-Tokens: Feature oder Doku-Fehler? | P3 #16 | prüfen, dann eins von beidem löschen |
| D4 | Übergangsbeleg für Audit-`defaultTTL: "0"` | P1 #13 | belegen oder Aussage streichen |
| D5 | `hooksTrigger`/`exportScript` — gemeint war `hooks` bzw. `meta.exports`? | P4 | bestätigen |
| D6 | Wie tief soll die Server-Referenz in tibi-docs gehen (Ziel: „Projekte aufsetzen und betreiben", nicht Core-Entwicklung)? | P3 | Referenz-Tabelle + Fehlerfälle ja, Go-Interna nein |
| D7 | Bleibt die Doku einsprachig (nur `de`)? | §10.4 #7 | ja → als ADR festhalten |
| D8 | Wo endet die Anwender-Doku (welche Admin-Flächen bekommen eine Anwender-Seite)? | P6a | jedes Bildschirmelement, das ein Redakteur ohne Entwicklerwissen bedient |
🔍 **Weitere `PRUEFEN`-Punkte:** 7 in `05-review-einstieg-server.md`, 7 in `05-review-projekt.md`, 5 in `05-review-admin-reference.md`, 5 in `05-review-anwender-adr.md` — dort verlinkt und einzeln mit Beleg nachzuziehen.
---
## 17. Anhang: Inventar
**Seiten je Bereich:** einstieg 4 · server 5 · projekt 15 · admin 10 · anwender 11 · reference 11 · adr 4 · index 1 → **61 Seiten / 7.127 Zeilen**.
**Bilder:** 30 `.webp` in `src/assets/anwender/` (≤ 78 KB je Datei) + 1 `admin-nova.png` + `favicon.svg`; Admin-Screenshots fehlen.
**Befunde:** Audit-Skript 20 Rohmeldungen (17 Marker, 1 kaputte Tabelle, 1 lange Überschrift, 1 fehlende `description`) → davon 2 echte Markdown-Defekte; Seitenreviews 131 Befundzeilen; Code-Reports 15 Lücken + 5 Fehler (Server), 10 Interface-Lücken + 25 Screenshot-Lücken (Admin), 10 Schema-Properties + 5 Starter-Lücken, 0 Datei-Wegfall-Kandidaten + 6 Hygienefälle.
**Erledigt in diesem Audit:** Audit-Skript geschrieben, 4 Code-Reports + 4 Seitenreviews erstellt, 10 Kernbefunde selbst am Code verifiziert, 1 Report-Behauptung widerlegt, dieses Übersichtsdokument erstellt.
**Nicht erledigt (bewusst):** kein Doku-Inhalt wurde geändert — die Umsetzung folgt über die Phasen P1–P9.
### Zählbefehle (jede Zahl in diesem Dokument nachprüfbar)
```sh
cd /WM_Dev/src/gitbase.de/cms/tibi-docs
find src/content/docs -name '*.md' -o -name '*.mdx' | wc -l # 61
find src/content/docs -name '*.md*' | xargs cat | wc -l # 7127
ls src/assets/anwender/*.webp | wc -l # 30
find dist -name '*.html' | wc -l # 62
grep -rln 'Screenshot-Platzhalter' src/content/docs/admin | wc -l # 9
grep -rho '^\s*```' src/content/docs | wc -l # 432
grep -rn 'title="' src/content/docs | wc -l # 254
grep -rc '<Aside type=' src/content/docs | awk -F: '{s+=$2} END{print s}' # 86
grep -rn '^:::' src/content/docs | wc -l # 3
wc -l src/content/docs/projekt/mcp-nutzen.md # 754
node scripts/audit-docs.mjs # 0 tote Links, 20 Rohmeldungen
```
---
## 18. Ausführungsprotokoll (live)
Statuslegende: ✅ erledigt und reviewed · 🔄 läuft · ⬜ offen · 🔴 blockiert
### Welle A — P1 (Fakten) + P2 (Markdown/Hygiene) · Stand 2026-10-17
| Punkt | Status | Belege/Ergebnis |
|---|---|---|
| P1 #1 `EMBEDDING_BGE_M3_*` (6 Stellen) | ✅ | `server/konfiguration.md`; Review A ok gegen `server.go:245-251` |
| P1 #2 `allowSnapshots` (3 Stellen) | ✅ | `projekt/audit-backup.md`; Review ok gegen `project.go:120` |
| P1 #3 `crypto` „plus weitere" | ✅ | `reference/hook-kontexte.md`; Review ok |
| P1 #4 MCP-Scopes `.read/.create/.update/.delete` | ✅ | `projekt/mcp-nutzen.md:63-71`; Review ok gegen `admin_token.go:115-128` |
| P1 #5 `meta.views` entfernt | ✅ | `admin/customizing.md`; `viewHint`+`subNavigation` sind im Schema belegt |
| P1 #6 `loadAll`-Signatur | ✅ | `admin/exporte.md`; Review ok gegen `custom-export.types.ts:56-60` |
| P1 #7 `reference/openapi.md` + `sync-openapi.mjs` | ✅ | Seite handfest, Skript auf `/_openapi/<namespace>`; **Review-Fund** falscher Default-Pfad nachgezogen (`/api/v1`) |
| P1 #8 „MongoDB 7" → 8 | ✅ | `server/betrieb.md:21`; Review ok gegen beide Composes |
| P1 #9 `$CODING_TIBISERVER_URL` | ✅ | ersetzt durch `$CODING_TIBIADMIN_URL/api/...`; im Starter belegt, Proxy `/api`→`/api/v1` belegt |
| P1 #10 Gzip-Absolutaussage | ✅ | `server/schnittstellen.md`; Ausnahmen = `setup.go:151-154` |
| P1 #11 „eingeloggte" → „anonyme" | ✅ | `projekt/hooks-schreiben.md`; belegt durch `filter_public.js` (`!context.user.auth()`) |
| P1 #12 Sammelgruppen | ✅ | `content`/`community`/`system` gegen Starter-YMLs |
| P1 #13 Audit-TTL `"0" = ewig` | ✅ **bleibt** | **Review-Fehler entkräftet**: `internal/mongocrud/mongo.go:477` (`if index.ExpireAfter > 0`) → bei 0 wird kein TTL-Index gesetzt = kein Ablauf. Aussage ist korrekt. |
| P1 #14 `POST /api/ssr?clear=1` | ✅ | `projekt/deploy.md`; als Starter-/Proxy-Pfad gekennzeichnet |
| P1 #15 „Schnellsuche" → „Befehlspalette" | ✅ | `anwender/orientierung.md`, `anwender/hilfe.md`; belegt `de.json:1207` |
| P1 #16 „Kurzfassung" | ⬜ | Locale sagt „Zusammenfassung" — beim Anwender-KI-Feinschliff (P7) prüfen |
| P2 #1 kaputte Tabelle | ✅ | `admin/ki-funktionen.md` (3 Zellen je Zeile) |
| P2 #2 „Hülle"-Rückstand | ✅ | `reference/hook-kontexte.md`; `grep "Hülle"` in `src/content/docs` = 0 |
| P2 #3 `description` in openapi.md | ✅ | Frontmatter vollständig |
| P2 #4 `:::` → `<Aside>` | ✅ teilweise | `server/uebersicht.md`, `einstieg/neues-projekt.md`; ⬜ `einstieg/schnellstart.md` (war im Auftrag nicht enthalten) |
| P2 #6 lange Überschrift | ✅ | `projekt/pagebuilder-dev.md` |
| P2 #7 „Suchbegriffe:" | ✅ | 4 Projekt-Seiten normiert |
| P2 #8 Stempel-Norm | ✅ teilweise | „Geprüft an" → „Verifiziert am" (11 Seiten), 8→7 Zeichen, Doppelungen zusammengefasst; ⬜ ~20 Restzeilen mit Datums-/Suffix-Abweichungen |
| P2 #9 ungenutzte Importe | ✅ | alle Seiten beider Bereiche geprüft |
| Welle-A-Review (general Subagent) | ✅ | Urteil: „abnehmbar nein — erst Punkt F fixen"; F wurde behoben, danach grün |
| Build | ✅ | 62 Seiten, 0 Fehler; `scripts/audit-docs.mjs`: 20 → **16** Findings (nur noch legitime „Platzhalter"-Wörter) |
### Welle B — P3 (Server-Lücken) + P4 (Admin-Interfaces) · Stand 2026-10-17 ✅
| Punkt | Status | Belege/Ergebnis |
|---|---|---|
| P3: `projekt/feld-rechte.md` (neu, 87 Z.) | ✅ | Kaskade Collection → PermissionSet → Feld-Def; `field_permissions.go:31-56,120,135,307,402-418`; `permissions.schema.json:55-72`; `collection.schema.json:201-212` |
| P3: `reference/rest-system.md` (neu, 65 Z.) | ✅ | 5 Modelle × 5 Routen + `_meta`; `dbapi/register.go:90-115`; `token.go:39-76`; `helper.go:64-167` |
| P3: `rechte-tokens.md` (Scope-Katalog, Token-Felder, Admin-Flags) | ✅ | `:144-208`; `token_scope_catalog.go:47-89`; `project.schema.json:227-261` |
| P3: `konfiguration.md` (`allowedMimeTypes`, Embedding-Gruppen) | ✅ | `:26,108-162`; `config/server.go:46,489-625`; `embedding_group.go:44-190` |
| P3: `audit-backup.md` (10 Snapshot-/6 Backup-Routen, Config-Felder, Restore-Token, Auth-Reihenfolge) | ✅ | `:95-198`; `setup.go:398-495`; `snapshot_config.go:11-58,89-94`; `admin_auth.go:20-74` |
| P3: `suche.md` (7 Suchmodi, Regenerate/Worker, Vektor-Pfad, Debug) | ✅ | `:44-129`; `collection.go:314-383`; `admin_regenerate_search.go:17-25`; `search_debug.go:145-159` |
| P3: `betrieb.md` + `dateien-bilder.md` (Filecleanup) | ✅ | `cleanup.go:21-149` |
| P3: `hooks-daten.md` (Cache-Defaults) | ✅ | `cache/cache.go:23-38`; `config/server.go:178-181` |
| P3: `abfragen-lesen.md` (`aggregate`) | ✅ | `:113-135`; Signatur/Ops/Fehler 400/403 |
| P4: Schema-Meta (`singleton`, `sections`, `query`, `agent`, `MANUALLY`, `viewHint.navigation`) | ✅ | `sammlungen-anlegen.md:87-116`; `collection-meta.schema.json:69-503` |
| P4: Feld-Meta (8 Optionen) | ✅ | `felder-widgets.md:201-216`; `field-meta.schema.json:50-235,303-317` |
| P4: `admin.tokens`-Rechte | ✅ | `admin/benutzer-rechte.md:83-108`; `project.go:116-122` |
| P4: Export-`ctx` vollständig + lauffähiges Beispiel | ✅ | `admin/exporte.md:50-103`; `custom-export.types.ts:17-102` |
| P4: Action-`execution` + Upload-Pfad | ✅ | `admin/aktionen.md:31-50`; `action.schema.json:274-288`; `ActionView.svelte:260-295,350-382` |
| P4: Registry-Ladeordnung + `{eval}`-Specifier | ✅ | `pagebuilder-dev.md:56-79`; `load-registry.ts:16-94`; `project-asset-specifier.ts:17-50` |
| P4: Runtime-System-Config | ✅ | `admin/setup.md:73-96`; `system-config.schema.json:11-28`; `system.config.json:1-49` |
| Welle-B-Review (general Subagent) | ✅ | 20 Stichproben, alle ok; Urteil „abnehmbar nach Fix von 2a+2b" — behoben |
| Review-Nacharbeit | ✅ | Quelle-Pfad in `feld-rechte.md` korrigiert; Stempel datiert (13 Seiten); Duplikat „Neuaufbau" in `suche.md` mit „Index aufbauen und prüfen" zusammengeführt; `**Kurzfassung**` → `**Zusammenfassung**` (`de.json:207`); letzte `:::`-Direktive → `<Aside>` |
| Build | ✅ | 64 Seiten (63 Inhaltsseiten + 404), 0 Fehler; Audit weiterhin 16 legitime „Platzhalter"-Marker |
### Welle C — P5 (types/Starter) + P6a (Anwender-Seiten) · Stand 2026-10-17 ✅
| Punkt | Status | Belege/Ergebnis |
|---|---|---|
| P5: `reference/schema-properties.md` (generiert, 384 Z.) | ✅ | 16/16 Schemas, 25 Tabellen, 164 Property-Zeilen; Generator `scripts/gen-schema-matrix.mjs` + `npm run gen:schemas`; idempotent (md5 identisch beim Zweitlauf) |
| P5: `reference/schemas.md` auf Übersicht + Verweise reduziert | ✅ | keine Doppelung; Dateinamen/Auflösungsreihenfolge ergänzt |
| P5: `projekt/tests.md` (neu, 83 Z.) | ✅ | Playwright-Scripts/Projekte, `MOCK=1` (`frontend/src/lib/mock.ts`, `esbuild.config.js:141f`), Artefaktordner |
| P5: `projekt/deploy.md` (CI-Variablen) | ✅ | `scripts/ci-deploy.sh:6-35,62-68`, `ci-staging.sh:4-42`; zwei Widersprüche bereinigt |
| P5: `projekt/ssr-website.md` (`api/templates`, Cache) | ✅ | `api/hooks/ssr/get_read.js`, `hooks/lib/utils.js` (`clearSSRCache`) |
| P6a: 5 neue Anwender-Seiten (319 Z. gesamt) | ✅ | Verlauf/Wiederherstellen, Massenbearbeitung, Aktionen, Backup einspielen, Tastenkürzel |
| P6a: 10 Screenshot-Platzhalter für P6b gesetzt | ✅ | 3 Verlauf, 2 Massenbearbeitung, 2 Aktionen, 2 Backup, 1 Tastenkürzel |
| Sidebar (`astro.config.mjs`) | ✅ | 7 neue Einträge; Sidebar ↔ Dateien konsistent (0 Abweichungen) |
| Welle-C-Review (general Subagent) | ✅ | Generator idempotent (md5 gleich), 15 Stichproben gegen JSON-Schemas ok, UI-Strings 25/26 wörtlich, tests.md korrekt |
| Review-Nacharbeit | ✅ | Generator schreibt jetzt „Generiert aus `tibi-types 4c55f82` (Stand 2026-10-12)" (deterministisch); Hash-Länge auf 7 Zeichen normiert (33 Stellen); Bildbeschriftung auf die echten Knopftexte gezogen |
| Build | ✅ | 71 Seiten (70 Inhaltsseiten + 404), 0 Fehler; Audit: 0 tote Links, 16 legitime „Platzhalter"-Marker |
### Welle D — P7 (Stil, Starlight, Struktur) · Stand 2026-10-17 ✅
| Punkt | Status | Ergebnis |
|---|---|---|
| UI-Text-Treue gegen `de.json` | ✅ | 21 geprüft, 20 wörtlich; korrigiert: `**Zusammenfassung**` (statt „Kurzfassung"), `noOutputFields`-Satz, „Erneut versuchen", `{width}×{height}`; „Datensatz öffnen" ohne UI-Beleg → wirkungsbeschreibend umformuliert |
| `LinkCard` statt Karte+Link | ✅ | `index.mdx`, `server/uebersicht.md`, `einstieg/was-ist-tibi.md`, `reference/hook-kontexte.md` (19 Karten) |
| `Tabs`/`Steps`/`syncKey` | ✅ | `syncKey` in 4 Seiten; `<Tabs>` für Exportformate, KI Ein-/Ausgabe, Viewport-Presets |
| Codeblöcke | ✅ | Zeilenmarkierungen (`{n}`) an 5 Stellen, `title=` flächendeckend |
| Checkliste statt Codeblock | ✅ | `einstieg/neues-projekt.md` Task-Liste |
| Begriffe-Glossar | ✅ | `einstieg/begriffe.md` mit 12 `##`-Überschriften (ToC-fähig) |
| Redundanz kanonisiert | ✅ | `qName` nur in `suche.md`, `active:true`-Falle nur in `rechte-tokens.md`, Widget-Tabelle nur in `felder-widgets.md` |
| Auslagerung Projekt → Admin | ✅ | Verlauf-/Pagebuilder-UI aus `projekt/*` nach `admin/eintrags-editor.md`, Details dort ergänzt |
| Prosa/Binsen | ✅ | `ki-funktionen.md` Schwellen jetzt Tabelle; 6 Binsen/Wertungen umformuliert |
| `mcp-nutzen.md` entschlackt | ✅ | 754 → 441 Zeilen, alle 36 Werkzeuge erhalten (Tabelle + Signatur + Beispiel + Fehler) |
| Kontrast/a11y (`custom.css`) | ✅ | nachgerechnet: alle Text-/Akzentwerte ≥ 4.5:1 (hell/dunkel), Deko-Farben bewusst unverändert |
| `lastUpdated: true` | ✅ | rendert ohne Fehler |
| Stempel normiert | ✅ | ein Format, 7-stellige Hashes, Punkt am Ende entfernt (~22 Stellen) |
### Welle E — P6b (Screenshots) · Stand 2026-10-17 ✅
| Punkt | Status | Ergebnis |
|---|---|---|
| Admin-Bilder | ✅ | **9/9** Motive aus `corporate` → `src/assets/admin/`, alle 1440×900, 21–78 KB |
| Anwender-Bilder | ✅ | **10 neue** (Verlauf, Massenbearbeitung, Aktionen, Backup, Tastenkürzel) → `src/assets/anwender/`, 18–89 KB |
| Platzhalter-Kommentare | ✅ | alle `<!-- Screenshot (T-SHOTS) … -->` ersetzt; 0 offene Kommentare |
| Mock-Fixture erweitert | ✅ | `corporate` hat jetzt eine Aktion („Suchindex neu aufbauen") und zwei Exporte („Seiten-Report", „Seiten-CSV") — vorher gab es Aktionen/Exporte nur in `t1`, das laut Regel verboten ist |
| Regelverstoß behoben | ✅ | die zwei Aktionen-Bilder des ersten Durchlaufs stammten aus `t1` und wurden durch `corporate`-Aufnahmen ersetzt |
| Textkorrektur nach UI-Abgleich | ✅ | Auswahl-Knopf heißt „**Bearbeiten**" (`admin.bulk.edit`) |
| offene UI-Mängel (nicht Doku) | ⚠️ | Mock-Overlay zeigt unübersetzten Key `admin.commandPalette.commands.toggleUsers`; Backup-Dialog teils englisch → als Issue notiert (`plans/offene-issues.md` A1/A2) |
### Welle F — P8 (Meta) + P9 (Abbau) · Stand 2026-10-17 ✅/⬜
| Punkt | Status | Ergebnis |
|---|---|---|
| D1 entschieden: Sync stillgelegt | ✅ | `AGENTS.md` §2/§4 richtiggestellt (server/ = Handseiten), `npm run sync` entfernt, `sync-server-docs.mjs` mit Stillegungs-Hinweis versehen |
| AGENTS.md: Belegpflicht + UI-Namen-Regel | ✅ | Stempel-Norm und „UI-Namen wörtlich aus `de.json`" als Pflichtregeln ergänzt |
| AGENTS.md: Screenshot-Regel | ✅ | `src/assets/admin/` + `src/assets/anwender/`, WebP 1440×900 ≤ 200 KB, Corporate only |
| AGENTS.md: Werkzeug-Abschnitt | ✅ | `audit-docs.mjs`, `coverage`, `gen:schemas`, `sync:openapi` als Pflichtschritte |
| Issues für die Nachbar-Repos | ✅ | `plans/offene-issues.md`: 7× tibi-server, 4× tibi-admin-nova, 2× tibi-types (mit Beleg) |
| P9 Repo-Hygiene (Löschungen) | ⬜ | **bewusst nicht ausgeführt** — Löschen in fremden Repos braucht Freigabe; Kandidatenliste steht in §12.1 |
### Abschluss-Review (general Subagent) und Nacharbeit · 2026-10-17 ✅
| Befund des Abschluss-Reviews | Nacharbeit |
|---|---|
| 4 Waisen-Seiten ohne eingehenden Link (Aktionen, Backup einspielen, Massenbearbeitung, Tastenkürzel) | ✅ verlinkt aus `admin/aktionen`, `anwender/sichern`, `projekt/audit-backup`, `anwender/lesen-suchen-filtern`, `admin/ansichten-listen`, `reference/tastatur-kurzbefehle`, `anwender/orientierung` |
| 8 Seiten ohne Stempel (4× `einstieg/`, `reference/openapi.md`, ADRs) | ✅ 5 Seiten gestempelt; ADRs + generierte Seite als Ausnahme in AGENTS.md definiert (Abdeckung jetzt 64/69, Rest = Ausnahmen) |
| `reference/rest-system.md` nannte nur `register.go` | ✅ vollständiger Pfad `internal/dbapi/register.go` |
| Export-Beispielpfad `_/assets/exports/reports.mjs` nicht belegt | ✅ auf `_/exports/reports.mjs` relativiert (Specifier-Semantik steht in `projekt/pagebuilder-dev.md`) |
| Kopfzahlen in `plans/doku-audit.md`/`plans/doku-ausbau.md` veraltet (61 Seiten/30 Shots/55 Seiten) | ✅ Ist-Stand als Block ergänzt, T-SHOTS- und T-FINISH-Zeile aktualisiert |
| Info-Verlust der Welle D | ✅ keiner gefunden (Verdichtung, kein Verlust prüfbarer Aussagen) |
| 20 Fakten-Stichproben | ✅ 17 ok, 2 Beanstandungen (s. o.), 1 Bestätigung (MCP-Scope-Fix korrekt) |
| 10 UI-String-Stichproben | ✅ 10/10 wörtlich in `de.json` |
| Bilder | ✅ 40 + 9, 0 verwaist, 0 leere Alt-Texte, alle < 200 KB, keine Aufnahme aus `t1` |
**Urteil des Reviews:** „abnehmbar nach ca. 30 Minuten Nacharbeit" — die Nacharbeit ist erledigt, Build grün.
### Review-Fehler (Anti-Halluzination)
| Behauptung eines Prüfers | Gegenbeleg | Ergebnis |
|---|---|---|
| „`…/comment/count` landet nicht auf `:id`" (Seitenreview Projekt) | `setup.go:514` (`/_count` als Literal) vs. `:536` (`:id`) | Doku war richtig, Prüfer falsch |
| „`sync-openapi.mjs` Pfad falsch" (Review Welle A) | `${base}/_openapi/…` mit Default `…/api` → `/api/_openapi/…` | **richtig**, Default auf `/api/v1` geändert |
| „`"0" = ewig` im Audit-TTL nicht belegt" (Review Welle A) | `mongocrud/mongo.go:477` (`> 0`-Guard) | Prüfer falsch, Aussage bleibt |
+25 -2
View File
@@ -26,9 +26,9 @@ Lebendes Dokument. Der Orga-Agent (Agent Warpfuchs) aktualisiert Status, Session
| T-ADMIN-B Admin II | aktionen, benutzer-rechte, ki-funktionen, setup, customizing | – | ⏳ offen | ⏳ | Build + Links | | T-ADMIN-B Admin II | aktionen, benutzer-rechte, ki-funktionen, setup, customizing | – | ⏳ offen | ⏳ | Build + Links |
| T-ANW-A Anwender I | anmelden, orientierung, lesen-suchen-filtern, eintrag-bearbeiten, medien | – | ⏳ offen | ⏳ | Build + Links | | T-ANW-A Anwender I | anmelden, orientierung, lesen-suchen-filtern, eintrag-bearbeiten, medien | – | ⏳ offen | ⏳ | Build + Links |
| T-ANW-B Anwender II | seiten-bauen, benutzer-teams, exportieren, sichern, hilfe | – | ⏳ offen | ⏳ | Build + Links | | T-ANW-B Anwender II | seiten-bauen, benutzer-teams, exportieren, sichern, hilfe | – | ⏳ offen | ⏳ | Build + Links |
| T-SHOTS Screenshots | `src/assets/anwender/*` NUR aus Mock-Projekt `corporate` (nie `t1`!), Auftrag: `plans/t-shots-arbeitsauftrag.md` | general ses_ebac0c7feffed3DYtWznC2bqzJ | ✅ fertig (24 webp, <200 KB, Vision-Spotcheck ok) | ✅ Text-Abweichungen eingearbeitet (Live-Schalter, OK-Button, Reiter, Corporate-Blöcke) | Build + Links ✅ | | T-SHOTS Screenshots | `src/assets/anwender/*` NUR aus Mock-Projekt `corporate` (nie `t1`!), Auftrag: `plans/t-shots-arbeitsauftrag.md` | general ses_ebac0c7feffed3DYtWznC2bqzJ | ✅ fertig — Stand 17.10.: 49 webp (40 Anwender, 9 Admin), <200 KB | ✅ Text-Abweichungen eingearbeitet (Live-Schalter, OK-Button, Reiter, Corporate-Blöcke) | Build + Links ✅ |
| T-PROJ-D Vollständigkeit | pagebuilder-dev, mcp-nutzen (36 Tools), llm-nutzen | general ses_ebac0c804ffe9vElWvaKTju4H9 | ✅ fertig | ✅ SO eingearbeitet (REST-only: sieben statt fünf) | Build + Links ✅ | | T-PROJ-D Vollständigkeit | pagebuilder-dev, mcp-nutzen (36 Tools), llm-nutzen | general ses_ebac0c804ffe9vElWvaKTju4H9 | ✅ fertig | ✅ SO eingearbeitet (REST-only: sieben statt fünf) | Build + Links ✅ |
| T-FINISH Finish | index-Karten, uebersicht/konzept-Stempel, Voll-Linkcheck, AGENTS-Spiegelung | Orga (direkt) | ✅ fertig (v1) | — (Linkcheck: alle ok, 0 Hüllen, ADR-0003 angelegt) | Build 55 Seiten ✅ | | T-FINISH Finish | index-Karten, uebersicht/konzept-Stempel, Voll-Linkcheck, AGENTS-Spiegelung | Orga (direkt) | ✅ fertig (v1) | — (Linkcheck: alle ok, 0 Hüllen, ADR-0003 angelegt) | Build 71 Seiten ✅ (Stand 17.10.) |
| T-PB Pagebuilder komplett | pagebuilder-dev + seiten-bauen: Overlay-Editor, Modi, Viewport, Overview/inline, drillDown | general ses_eb9584bafffekRtyPPBn7ORTeA | ✅ fertig | ✅ SO eingearbeitet (OK/blur-Pfad, <768-px-Hinweis) | Build + Links ✅ | | T-PB Pagebuilder komplett | pagebuilder-dev + seiten-bauen: Overlay-Editor, Modi, Viewport, Overview/inline, drillDown | general ses_eb9584bafffekRtyPPBn7ORTeA | ✅ fertig | ✅ SO eingearbeitet (OK/blur-Pfad, <768-px-Hinweis) | Build + Links ✅ |
| T-TON Ton-Rewrite | alle 10 anwender-Seiten: professionell-per-du, UI-Namen aus de.json | general ses_eb95443a5ffe8IrJVmt1DcAReX | ✅ fertig | ✅ SO eingearbeitet („Bild hochladen", Sitzungs-Hinweis, Live-Qualifier, KI-Beschreibung, Browser-Zurück) | Build + Links ✅ | | T-TON Ton-Rewrite | alle 10 anwender-Seiten: professionell-per-du, UI-Namen aus de.json | general ses_eb95443a5ffe8IrJVmt1DcAReX | ✅ fertig | ✅ SO eingearbeitet („Bild hochladen", Sitzungs-Hinweis, Live-Qualifier, KI-Beschreibung, Browser-Zurück) | Build + Links ✅ |
| T-SHOTS-2 Pagebuilder-Shots | Overlay + Viewport (corporate), Einbau in seiten-bauen | general ses_eb9503e20ffetq8gZU0Ff5U06T | ✅ fertig (3 webp, Icon-Tooltip korrigiert) | ✅ Abweichung eingearbeitet | Build ✅ | | T-SHOTS-2 Pagebuilder-Shots | Overlay + Viewport (corporate), Einbau in seiten-bauen | general ses_eb9503e20ffetq8gZU0Ff5U06T | ✅ fertig (3 webp, Icon-Tooltip korrigiert) | ✅ Abweichung eingearbeitet | Build ✅ |
@@ -165,3 +165,26 @@ Kontrollen: Depth-Gate im Skript (gemapptes Item < Stufe Signatur = Fail), Blind
- [ ] Offen an tibi-server: Typo `reponse.header` im Server-Code (`context_response.go`, von Doku wörtlich zitiert) → Issue - [ ] Offen an tibi-server: Typo `reponse.header` im Server-Code (`context_response.go`, von Doku wörtlich zitiert) → Issue
- [ ] Offen an tibi-admin-nova: totes `u`-Kürzel (`toggleUsers`-Key fehlt in `de.json`, nur Akkord `g → u` funktioniert) → Issue - [ ] Offen an tibi-admin-nova: totes `u`-Kürzel (`toggleUsers`-Key fehlt in `de.json`, nur Akkord `g → u` funktioniert) → Issue
- [ ] Abschlussmeldung Runde 3 an User - [ ] Abschlussmeldung Runde 3 an User
## Runde 4 — Audit und Umsetzung (2026-10-17)
Vollständige Prüfung von Code und Doku, dann Abarbeitung in Wellen. Übersicht, Befunde und Belege:
**`plans/doku-audit.md`** (mit Ausführungsprotokoll), Detailreports unter `plans/audit/`.
| Welle | Inhalt | Status |
|---|---|---|
| A | P1 Faktenfehler (8) + P2 Markdown/Hygiene | ✅ |
| B | P3 Server-Lücken (`feld-rechte.md`, `rest-system.md` neu, 8 Seiten vertieft) + P4 Admin-Interfaces | ✅ |
| C | P5 `reference/schema-properties.md` (generiert) + Starter-Kapitel + 5 neue Anwender-Seiten | ✅ |
| D | P7 Stil, Starlight, Struktur, UI-Text-Treue, Kontrast | ✅ |
| E | P6b Screenshots: 9 Admin- + 10 Anwender-Bilder aus `corporate` | ✅ |
| F | P8 Meta (AGENTS.md, Werkzeuge, Issues) | ✅ |
| — | P9 Repo-Hygiene (Löschungen in Nachbar-Repos) | ⬜ Freigabe nötig |
**Neue Werkzeuge:** `scripts/audit-docs.mjs` (Struktur-/Link-/Bildprüfung, `plans/audit-report.json`),
`scripts/gen-schema-matrix.mjs` (`npm run gen:schemas` → `reference/schema-properties.md`).
**Neue Seiten:** `projekt/feld-rechte`, `reference/rest-system`, `reference/schema-properties`,
`projekt/tests`, `anwender/{verlauf-wiederherstellen,massenbearbeitung,aktionen,backup-einspielen,tastenkombinationen}`.
**Stand:** 70 Inhaltsseiten, 49 Screenshots, Build grün, Audit: 0 tote Links, 0 kaputte Tabellen.
**Offen:** Issues für die Nachbar-Repos (`plans/offene-issues.md`), Löschkandidaten `tibi-docs-legacy`,
`tibi-docs-kaputt`, `tibi-admin.bak*`, `tibi-server.bak` (Freigabe), Coverage v2 (Funktionskörnung).
+37
View File
@@ -0,0 +1,37 @@
# Offene Punkte in den Nachbar-Repos (bereit zum Anlegen als Issue)
Stand: 2026-10-17. Alle Punkte sind am Code belegt und aus dem Doku-Audit entstanden
(`plans/doku-audit.md`). Je Eintrag: Titel für das Issue, Repo, Beleg, Auswirkung, Vorschlag.
## tibi-server
| # | Titel | Beleg | Auswirkung | Vorschlag |
|---|---|---|---|---|
| S1 | Doppelte Struct-Tags in `AdminToken` | `internal/models/project.go:116-122` — `Token string \`yaml:"token" yaml:"token"\`` (ebenso `AllowReload`, `AllowRegenerateSearch`, `AllowBackup`, `allowSnapshots`) | Der zweite Tag ist wirkungslos; Copy-Paste-Fehler bei künftigen Feldern | zweiten Tag entfernen |
| S2 | Header-Name mit Doppelpunkt | `internal/controller/collection_get.go:32` — `c.Header("Content-Range:", …)` | Antwort enthält einen Header mit ungültigem Namen; Clients können die Zeilenanzahl nicht lesen | auf `Content-Range` ändern (Breaking: Clients, die den Tippfehler kennen, prüfen) |
| S3 | Hook-Kontext: Typo `reponse.header` | `internal/hook/context_response.go` | Falsches Symbol in der Hook-API; nur der Alias hält es am Leben | umbenennen, Alias eine Minor-Version behalten |
| S4 | `mcp.route`/`MCP_ROUTE` wirkungslos | `internal/config/server.go:221-224` (Feld/Env) vs. `internal/controller/setup.go:754-755` (fest `/mcp`) | Konfiguration suggeriert Steuerbarkeit, die es nicht gibt | Env entfernen oder Routing darauf umstellen |
| S5 | `docs/24-ephemeral-rest-tokens.md` ohne Code | kein Treffer für `ephemeral` in `internal/` | Doku beschreibt eine Fähigkeit, die es nicht gibt | implementieren oder Doc löschen |
| S6 | `internal/mgocrud.old/` toter Code | Verzeichnis | Verwirrung bei Neueinsteigern | löschen |
| S7 | `audit.defaultTTL`-Semantik unklar dokumentiert | `internal/mongocrud/mongo.go:477` (`if index.ExpireAfter > 0`) — `0` ⇒ **kein** TTL-Index ⇒ Einträge bleiben ewig | Betreiber erwarten bei `0` sofortiges Verfallen | Kommentar + Server-Doc: `0` = kein Ablauf |
## tibi-admin-nova
| # | Titel | Beleg | Auswirkung | Vorschlag |
|---|---|---|---|---|
| A1 | Fehlende Übersetzung im Tastenkürzel-Overlay | sichtbar im Mock-Overlay: der Key `admin.commandPalette.commands.toggleUsers` erscheint unübersetzt; `frontend/src/lib/i18n/locales/de.json` | Anwender sieht einen technischen Schlüssel | String ergänzen |
| A2 | Teilweise englische Hinweise im Backup-Dialog | Mock-Backup-Import zeigt „target has new fields…" | Sprachbruch im deutschen UI | Strings übersetzen |
| A3 | Wiederherstellen-Dialog nur bei Änderungen sichtbar | `AuditRestoreDialog.svelte` — Feldauswahl erscheint nur bei ungespeicherten Änderungen, sonst „Keine Änderungen" | Doku muss den Sonderfall erklären; im Audit erwähnt | Hinweistext im UI prüfen |
| A4 | Actions fehlen im Mock-Projekt `corporate` | `frontend/assets/mock/_config/corporate/config.yml` hatte keinen `actions`-Block (nur `t1`) | Screenshots/Reviews ohne aussagekräftiges Demo-Projekt (erledigt in der Doku-Arbeit, s. u.) | übernommen: `actions/reindex-search.yml` + `meta.exports` in `corporate` ergänzt |
## tibi-types
| # | Titel | Beleg | Auswirkung | Vorschlag |
|---|---|---|---|---|
| T1 | Keine Doku im Repo | kein `docs/`-Verzeichnis, nur `index.d.ts` + `schemas/config/*.json` | Projektentwickler finden die Schemas nur über die zentrale Doku; Schema-Beschreibungen sind oft englisch und knapp | README mit Schema-Übersicht + Verweis auf `tibi-docs`; Beschreibungen pflegen |
| T2 | Schema-Beschreibungen teils englisch | `schemas/config/*.json` (`description`) | Deutsche Doku muss jeden Text selbst übersetzen | Feld-Beschreibungen zweisprachig oder deutsch |
**Bereits in der Doku-Arbeit umgesetzt (nur zur Info, kein Issue):** Mock-Projekt `corporate` hat jetzt
eine Aktion (`Suchindex neu aufbauen`) und zwei Export-Einträge (`Seiten-Report`, `Seiten-CSV`), damit
Screenshots und Reviews ohne `t1` auskommen. Betroffen sind
`frontend/assets/mock/_config/corporate/{config.yml,actions/reindex-search.yml,collections/pages.yml}`.
+277
View File
@@ -0,0 +1,277 @@
#!/usr/bin/env node
/**
* Struktur-Audit der Tibi-Doku.
* Prüft Mechanik (Frontmatter, Codeblöcke, Links, Anker, Bilder, Tabellen,
* Stil-Auffälligkeiten, Starlight-Komponenten) und schreibt plans/audit-report.json.
*
* Aufruf: node scripts/audit-docs.mjs
*/
import fs from 'node:fs';
import path from 'node:path';
const ROOT = path.resolve(import.meta.dirname, '..');
const DOCS = path.join(ROOT, 'src/content/docs');
const walk = (dir) =>
fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
const p = path.join(dir, e.name);
return e.isDirectory() ? walk(p) : [p];
});
const files = walk(DOCS).filter((f) => /\.mdx?$/.test(f)).sort();
const rel = (f) => path.relative(ROOT, f);
// --- Seitenregister für Link-/Ankerprüfung -------------------------------
const pages = new Map(); // slug -> {file, anchors:Set, title}
const slugOf = (file) => path.relative(DOCS, file).replace(/\.mdx?$/, '').replace(/\/index$/, '') || 'index';
const slugifyHeading = (h) =>
h
.toLowerCase()
.replace(/[`*_]/g, '')
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
.replace(/[^\p{L}\p{N}\s-]/gu, '')
.trim()
.replace(/\s+/g, '-')
.replace(/-+/g, '-');
const raw = new Map();
for (const f of files) raw.set(f, fs.readFileSync(f, 'utf8'));
for (const f of files) {
const text = raw.get(f);
const anchors = new Set();
const body = text.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '');
let inFence = false;
for (const line of body.split(/\r?\n/)) {
if (/^\s*(```|~~~)/.test(line)) { inFence = !inFence; continue; }
if (inFence) continue;
const m = /^(#{2,6})\s+(.*)$/.exec(line);
if (m) {
let s = slugifyHeading(m[2]);
let n = s, i = 1;
while (anchors.has(n)) n = `${s}-${i++}`;
anchors.add(s);
anchors.add(n);
}
}
const fmTitle = /^---\r?\n[\s\S]*?\btitle:\s*(.+)$/m.exec(text)?.[1]?.trim();
pages.set(slugOf(f), { file: rel(f), anchors, title: fmTitle });
}
// --- Prüfungen ------------------------------------------------------------
const findings = [];
const add = (file, kind, msg, line) => findings.push({ file: rel(file), kind, msg, line: line ?? null });
const STARLIGHT_COMPONENTS = [
'Aside', 'Badge', 'Card', 'CardGrid', 'Code', 'FileTree', 'Icon', 'LinkButton', 'LinkCard',
'Steps', 'TabItem', 'Tabs', 'Icon', 'MarkdownContent', 'LinkCard',
];
const stats = {
files: files.length,
byDir: {},
components: {},
lacksDescription: [],
emptyFenceLang: 0,
proseWalls: [],
asciiUmlauts: [],
hollowMarkers: [],
t1Refs: [],
tibiAdminRefs: [],
};
for (const f of files) {
const text = raw.get(f);
const lines = text.split(/\r?\n/);
const dir = path.relative(DOCS, f).split(path.sep)[0];
stats.byDir[dir] = (stats.byDir[dir] ?? 0) + 1;
// Frontmatter
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/.exec(text);
if (!fm) add(f, 'frontmatter', 'Kein Frontmatter-Block');
else {
if (!/\btitle:/.test(fm[1])) add(f, 'frontmatter', 'title fehlt');
const desc = /\bdescription:\s*(.*)$/m.exec(fm[1])?.[1]?.trim();
if (!desc) { add(f, 'frontmatter', 'description fehlt'); stats.lacksDescription.push(rel(f)); }
else if (desc.replace(/["']/g, '').length > 160) add(f, 'frontmatter', `description zu lang (${desc.length} Zeichen)`);
for (const key of ['sidebar', 'tableOfContents', 'hero', 'template', 'draft', 'lastUpdated', 'banner']) {
if (new RegExp(`^${key}:`, 'm').test(fm[1])) stats[`fm_${key}`] = (stats[`fm_${key}`] ?? 0) + 1;
}
}
// Codeblöcke
let fenceOpen = null;
let fenceCount = 0;
const fences = [];
for (let i = 0; i < lines.length; i++) {
const m = /^\s*(```|~~~)(.*)$/.exec(lines[i]);
if (!m) continue;
if (fenceOpen === null) {
fenceOpen = { line: i + 1, lang: m[2].trim() };
fenceCount++;
if (!m[2].trim()) { add(f, 'codefence', 'Codeblock ohne Sprach-Tag', i + 1); stats.emptyFenceLang++; }
} else {
fences.push({ start: fenceOpen.line, end: i + 1, lang: fenceOpen.lang });
if (m[2].trim()) add(f, 'codefence', `Schließender Zaun mit Inhalt: "${m[2].trim()}"`, i + 1);
fenceOpen = null;
}
}
if (fenceOpen !== null) add(f, 'codefence', 'Nicht geschlossener Codeblock', fenceOpen.line);
// Inline-Code unbalanciert (grobe Heuristik)
lines.forEach((line, i) => {
if (/^\s*(```|~~~)/.test(line)) return;
const ticks = (line.match(/(?<!`)`(?!`)/g) ?? []).length;
if (ticks % 2 === 1 && !line.includes('```')) add(f, 'inlinecode', 'Ungerade Zahl einfacher Backticks', i + 1);
});
// Überschriften-Hierarchie + leer
const fmOffset = fm ? fm[0].split(/\r?\n/).length - 1 : 0;
const bodyLines = lines.slice(fmOffset);
let prevLevel = 1;
let headFence = false;
bodyLines.forEach((line, i) => {
if (/^\s*(```|~~~)/.test(line)) { headFence = !headFence; return; }
if (headFence) return;
const m = /^(#{1,6})\s+(.*)$/.exec(line);
if (!m) return;
const lvl = m[1].length;
const cline = (fm ? fm[0].split(/\r?\n/).length - 1 : 0) + i + 1;
if (lvl === 1) add(f, 'heading', 'H1 im Body (Titel kommt aus Frontmatter)', cline);
if (lvl > prevLevel + 1) add(f, 'heading', `Sprung von H${prevLevel} auf H${lvl}`, cline);
if (!m[2].trim()) add(f, 'heading', 'Leere Überschrift', cline);
if (m[2].trim().length > 80) add(f, 'heading', `Sehr lange Überschrift (${m[2].trim().length})`, cline);
prevLevel = lvl;
});
// Links + Anker
const linkRe = /(\[)([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
let lm;
while ((lm = linkRe.exec(text))) {
if (text[lm.index - 1] === '!') continue; // Bild, separat geprüft
const target = lm[3];
if (/^(https?:|mailto:|#)/.test(target)) {
if (target.startsWith('#') && !pages.get(slugOf(f))?.anchors.has(target.slice(1)))
add(f, 'anchor', `Lokaler Anker ohne Ziel: ${target}`);
continue;
}
const line = text.slice(0, lm.index).split(/\r?\n/).length;
const [p, anchor] = target.split('#');
let slug = p.replace(/^\//, '').replace(/\/$/, '');
if (slug.endsWith('.md')) slug = slug.slice(0, -3);
if (slug === '') slug = 'index';
// relative Links
const targetPage = pages.get(slug) ?? pages.get(slug + '/index');
if (!targetPage) { add(f, 'link', `Interner Link ohne Ziel: ${target}`, line); continue; }
if (anchor && !targetPage.anchors.has(anchor))
add(f, 'anchor', `Anker nicht gefunden: ${target}`, line);
}
// Bilder
const imgRe = /!\[([^\]]*)\]\(([^)\s]+)/g;
let im;
while ((im = imgRe.exec(text))) {
const line = text.slice(0, im.index).split(/\r?\n/).length;
if (/^https?:/.test(im[2])) { add(f, 'image', `Externes Bild (unsicher): ${im[2]}`, line); continue; }
const p = path.resolve(path.dirname(f), im[2].split('#')[0].split('?')[0]);
if (!fs.existsSync(p)) add(f, 'image', `Bilddatei fehlt: ${im[2]}`, line);
if (!im[1].trim()) add(f, 'image', 'Bild ohne Alt-Text', line);
}
// Starlight-Komponenten
for (const c of new Set(STARLIGHT_COMPONENTS)) {
const n = (text.match(new RegExp(`<${c}[\\s/>]`, 'g')) ?? []).length;
if (n) stats.components[c] = (stats.components[c] ?? 0) + n;
}
const imports = text.match(/import\s*\{([^}]*)\}\s*from\s*'@astrojs\/starlight\/components'/);
if (imports) for (const c of imports[1].split(',').map((s) => s.trim())) stats.components[c] = (stats.components[c] ?? 0) + 0;
// Rohes HTML in .md (funktioniert, aber inkonsistent)
const htmlHits = text.match(/^\s*<(div|span|p|table|ul|br|img|details|summary|h[1-6])[\s>]/gm);
if (htmlHits) stats.rawHtml = (stats.rawHtml ?? 0) + htmlHits.length;
// Tabellen-Konsistenz
const tlines = lines.map((l, i) => ({ l, i: i + 1 })).filter(({ l }) => /^\s*\|.*\|\s*$/.test(l));
let tbl = [];
const flush = () => {
if (tbl.length >= 2) {
const cols = tbl.map((t) => t.l.trim().replace(/^\||\|$/g, '').split('|').length);
const uniq = new Set(cols);
if (uniq.size > 1) add(f, 'table', `Tabellenzeilen mit unterschiedlicher Spaltenzahl: ${cols.join(',')}`, tbl[0].i);
if (tbl.length > 1 && !/^\s*\|[\s:|-]+\|\s*$/.test(tbl[1].l)) add(f, 'table', 'Trenner-Zeile der Tabelle fehlt/ungültig', tbl[1].i);
}
tbl = [];
};
for (const t of tlines) {
if (tbl.length && t.i !== tbl[tbl.length - 1].i + 1) flush();
tbl.push(t);
}
flush();
// Absatz-Wände (Stil)
const stripped = text.split('\n\n').filter((b) => !/^\s*[|#>]/.test(b) && !/^---/.test(b.trim()) && !/^\s*</.test(b));
stripped.forEach((b) => {
const plain = b.replace(/`[^`]*`/g, '').trim();
if (plain.length > 900 && !plain.includes('\n- ') && (plain.match(/\.\s/g) ?? []).length > 8)
stats.proseWalls.push(`${rel(f)} (${plain.length} Zeichen)`);
});
// ASCII-Umlaute (nur häufige Wörter, wenig False Positives)
if (/\b(fuer|ueber|moeglich|waehrend|groesse|schluessel|zurueck|koennen|muessen|moechte|aendern|ausser|gemaeß|Grundeinstellung)\b/i.test(text))
stats.asciiUmlauts.push(rel(f));
if (/(?:waehl|einstell(?:ung)?en?|benutz(?:er)?|sc(?:h)luessel)/i.test('') ) {} // noop
// Hüllen-Reste (nur Fließtext — in Codeblöcken sind „Platzhalter"/„TODO" legitime Beispieltexte)
const proseText = (() => {
const out = [];
let f = false;
for (const line of lines) {
if (/^\s*(```|~~~)/.test(line)) { f = !f; continue; }
if (!f) out.push(line);
}
return out.join('\n');
})();
['Für spätere Agenten', 'TODO', 'FIXME', 'noch nicht', 'Hülle', 'Platzhalter', 'TBD', 'WIP']
.forEach((k) => {
const idx = proseText.indexOf(k);
if (idx < 0) return;
const line = proseText.slice(0, idx).split(/\r?\n/).length;
const ctx = proseText.split(/\r?\n/)[line - 1]?.trim().slice(0, 110) ?? '';
add(f, 'hollow', `Marker "${k}": ${ctx}`, line);
stats.hollowMarkers.push(`${rel(f)}:${line} ${k}`);
});
// Verbotene Namen
if (/\bt1\b/.test(text)) stats.t1Refs.push(rel(f));
const badAdmin = text.match(/tibi-admin(?!-nova)/g);
if (badAdmin) stats.tibiAdminRefs.push(`${rel(f)} (${badAdmin.length}×)`);
}
// --- Sidebar-Abgleich -----------------------------------------------------
const cfg = fs.readFileSync(path.join(ROOT, 'astro.config.mjs'), 'utf8');
const sidebarSlugs = [...cfg.matchAll(/slug:\s*'([^']+)'/g)].map((m) => m[1]);
const pageSlugs = [...pages.keys()].filter((s) => s !== 'index');
const notInSidebar = pageSlugs.filter((s) => !sidebarSlugs.includes(s));
const sidebarMissing = sidebarSlugs.filter((s) => !pages.has(s));
const report = {
generatedAt: new Date().toISOString(),
stats,
sidebar: { count: sidebarSlugs.length, notInSidebar, sidebarMissing },
findings,
summary: {
total: findings.length,
byKind: findings.reduce((a, f) => ({ ...a, [f.kind]: (a[f.kind] ?? 0) + 1 }), {}),
},
};
fs.mkdirSync(path.join(ROOT, 'plans'), { recursive: true });
fs.writeFileSync(path.join(ROOT, 'plans/audit-report.json'), JSON.stringify(report, null, 2));
console.log(JSON.stringify(report.summary, null, 2));
console.log('Seiten:', stats.files, JSON.stringify(stats.byDir));
console.log('Nicht in Sidebar:', notInSidebar);
console.log('Sidebar ohne Datei:', sidebarMissing);
console.log('Komponenten:', JSON.stringify(stats.components));
console.log('\nFindings:');
for (const f of findings) console.log(` ${f.file}:${f.line ?? '-'} [${f.kind}] ${f.msg}`);
+417
View File
@@ -0,0 +1,417 @@
#!/usr/bin/env node
/**
* gen-schema-matrix.mjs — erzeugt die Referenz-Seite
* src/content/docs/reference/schema-properties.md aus den JSON-Schemas
* in ../tibi-types/schemas/config/*.json.
*
* Aufruf: npm run gen:schemas (node scripts/gen-schema-matrix.mjs)
*
* Regeln:
* - ESM, Node-Bordmittel, keine neuen Dependencies.
* - Idempotent: zweiter Lauf = byte-identisches Ergebnis (sortierte,
* deterministische Ausgabe, kein Zeitstempel im Inhalt).
* - Bricht mit klarer Fehlermeldung ab, wenn ein Schema fehlt.
* - Keine erfundenen Properties: alles kommt aus den JSON-Schemas.
*/
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFileSync } from 'node:child_process';
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
const SCHEMA_DIR = join(ROOT, '..', 'tibi-types', 'schemas', 'config');
const OUT = join(ROOT, 'src', 'content', 'docs', 'reference', 'schema-properties.md');
// Herkunft der Quelle: Commit + Commit-Datum aus dem tibi-types-Repo.
// Bewusst deterministisch (kein Aufrufzeitpunkt), damit das Skript idempotent bleibt.
function sourceStamp() {
const repo = join(ROOT, '..', 'tibi-types');
if (!existsSync(join(repo, '.git'))) return '';
try {
const hash = execFileSync('git', ['-C', repo, 'rev-parse', '--short', 'HEAD'], { encoding: 'utf8' }).trim().slice(0, 7);
const date = execFileSync('git', ['-C', repo, 'log', '-1', '--format=%cs'], { encoding: 'utf8' }).trim();
return `Generiert aus \`tibi-types ${hash}\` (Stand ${date}) — Änderungen an den Schemas dort erfordern \`npm run gen:schemas\`.`;
} catch {
return '';
}
}
// Feste Liste: genau diese 16 Schemas erwartet der Generator.
const SCHEMAS = [
'action.schema.json',
'asset.schema.json',
'collection-meta.schema.json',
'collection.schema.json',
'defs.schema.json',
'field-list.schema.json',
'field-meta.schema.json',
'field.schema.json',
'hooks.schema.json',
'image-filter.schema.json',
'job.schema.json',
'openapi.schema.json',
'permissions.schema.json',
'project.schema.json',
'projections.schema.json',
'snapshot.schema.json',
];
// --- Laden ---------------------------------------------------------------
const missing = SCHEMAS.filter((f) => !existsSync(join(SCHEMA_DIR, f)));
if (missing.length > 0) {
console.error(`[gen:schemas] FEHLER: ${missing.length} Schema(s) fehlen in ${SCHEMA_DIR}:`);
for (const f of missing) console.error(` - ${f}`);
console.error('[gen:schemas] Abgebrochen — keine Datei geschrieben.');
process.exit(1);
}
const store = new Map();
for (const f of SCHEMAS) {
store.set(f, JSON.parse(readFileSync(join(SCHEMA_DIR, f), 'utf8')));
}
// --- Helfer ----------------------------------------------------------------
/** Zelle für Markdown-Tabellen säubern: Pipes escapen, Umbrüche glätten. */
function cell(text) {
return String(text ?? '—')
.replace(/\r?\n/g, '<br>')
.replace(/\|/g, '\\|')
.trim() || '—';
}
function fmtValue(v) {
const s = typeof v === 'string' ? v : JSON.stringify(v);
return s.length > 60 ? `${s.slice(0, 57)}…` : s;
}
/** Kurzname eines $ref-Ziels, z. B. "defs.schema.json#/definitions/boolOrEval" -> "boolOrEval (defs)". */
function refShort(ref) {
const [file, pointer] = ref.split('#');
const base = (file || '').replace('.schema.json', '') || 'dieses Schema';
const name = (pointer || '').split('/').pop() || file || ref;
if (!file) return `\`${name}\``;
if (!pointer || pointer === '') return `\`${file}\``;
return `\`${name}\` (${base})`;
}
/**
* $ref auflösen (max. 3 Ebenen, Zykelschutz). Gibt { file, node } oder null.
* Versteht "datei.json#/definitions/x", "datei.json" (ganzes Schema)
* und "#/definitions/x" (lokal).
*/
function resolveRef(ref, fromFile, seen = new Set()) {
if (seen.has(`${fromFile}::${ref}`)) return null;
seen.add(`${fromFile}::${ref}`);
const [file, pointer] = ref.split('#');
const targetFile = file || fromFile;
const target = store.get(targetFile);
if (!target) return null;
if (!pointer || pointer === '' || pointer === '#') return { file: targetFile, node: target };
const parts = pointer.replace(/^#/, '').split('/').filter(Boolean);
let node = target;
for (const p of parts) {
node = node?.[p];
if (node === undefined) return null;
}
if (node && typeof node === 'object' && node.$ref && seen.size < 4) {
return resolveRef(node.$ref, targetFile, seen) ?? { file: targetFile, node };
}
return { file: targetFile, node };
}
/** Trägt der Zweig eigene Typ-Info (vs. reine Pflicht-Constraints wie anyOf:[{required}])? */
function hasTypeInfo(node) {
if (!node || typeof node !== 'object') return false;
return ['type', 'enum', 'const', '$ref', 'oneOf', 'anyOf', 'properties', 'items', 'patternProperties']
.some((k) => node[k] !== undefined);
}
/** Typ-Kurzform eines Schema-Knotens ohne $ref/oneOf-Auflösung (Rekursionsschutz). */
function baseType(node) {
if (!node || typeof node !== 'object') return 'beliebig';
if (node.const !== undefined) return `fest: \`${fmtValue(node.const)}\``;
const opts = node.oneOf || node.anyOf;
if (Array.isArray(opts)) {
const typed = opts.filter(hasTypeInfo);
if (typed.length > 0) return typed.map((o) => baseType(o)).join(' oder ');
}
if (node.$ref) return refShort(node.$ref);
const t = node.type;
const tStr = Array.isArray(t) ? t.join(' oder ') : t;
if (node.enum) {
const vals = node.enum.map((v) => `\`${fmtValue(v)}\``).join(', ');
return `${tStr ? `${tStr}, ` : ''}eine von: ${vals}`;
}
if (tStr === 'array' && node.items) return `Liste von ${baseType(node.items)}`;
if (tStr === 'object' && node.properties) return 'object';
if (tStr) return tStr;
if (node.properties || node.patternProperties) return 'object';
if (node.items) return `Liste von ${baseType(node.items)}`;
if (node.additionalProperties) return 'object mit freien Werten';
return 'beliebig';
}
/**
* Menschenlesbarer Typ eines Schema-Knotens.
* Löst $ref (Zielname + dessen Typ) und oneOf/anyOf (Optionen) auf.
*/
function typeLabel(node, fromFile, depth = 0) {
if (!node || typeof node !== 'object') return 'beliebig';
if (node.$ref && depth < 8) {
const short = refShort(node.$ref);
const hit = resolveRef(node.$ref, fromFile);
if (hit && hit.node && typeof hit.node === 'object') {
const inner = baseType(hit.node);
const rest = { ...node };
delete rest.$ref;
delete rest.description;
delete rest.title;
const extra = Object.keys(rest).some((k) =>
['type', 'enum', 'const', 'oneOf', 'anyOf', 'items', 'properties'].includes(k))
? `, ${baseType(rest)}`
: '';
return `${short} — ${inner}${extra}`;
}
return short;
}
const opts = node.oneOf || node.anyOf;
if (Array.isArray(opts)) {
const typed = opts.filter(hasTypeInfo);
if (typed.length > 0) {
const joiner = node.oneOf ? ' oder ' : ' bzw. ';
const inner = typed.map((o) => typeLabel(o, fromFile, depth + 1)).join(joiner);
return depth > 0 ? `(${inner})` : inner;
}
}
if (node.const !== undefined) return `fest: \`${fmtValue(node.const)}\``;
const t = node.type;
const tStr = Array.isArray(t) ? t.join(' oder ') : t;
if (node.enum) {
const vals = node.enum.map((v) => `\`${fmtValue(v)}\``).join(', ');
return `${tStr ? `${tStr}, ` : ''}eine von: ${vals}`;
}
if (tStr === 'array' && node.items) return `Liste von ${typeLabel(node.items, fromFile, depth + 1)}`;
if (tStr) return tStr;
if (node.properties || node.patternProperties) return 'object';
if (node.items) return `Liste von ${typeLabel(node.items, fromFile, depth + 1)}`;
if (node.additionalProperties) return 'object mit freien Werten';
return 'beliebig';
}
function defaultLabel(node) {
if (!node || typeof node !== 'object') return '—';
if (node.default !== undefined) return `\`${fmtValue(node.default)}\``;
return '—';
}
function descLabel(node) {
if (!node || typeof node !== 'object') return '—';
return node.description || node.title || '—';
}
/** Markdown-Tabelle mit fester Spaltenzahl bauen. */
function table(header, rows) {
const lines = [`| ${header.join(' | ')} |`, `| ${header.map(() => '---').join(' | ')} |`];
for (const r of rows) lines.push(`| ${r.map(cell).join(' | ')} |`);
return lines.join('\n');
}
const PROP_HEADER = ['Property', 'Typ', 'Pflicht', 'Vorgabe/Default', 'Beschreibung'];
/**
* Effektives Objekt-Schema bestimmen: Top-Level-$ref (field.schema.json)
* wird zum Ziel in definitions/ aufgelöst.
*/
function effectiveSchema(file, schema) {
if (schema.$ref && !schema.properties) {
const hit = resolveRef(schema.$ref, file);
if (hit && hit.node && hit.node.properties) {
return { node: hit.node, via: schema.$ref };
}
}
return { node: schema, via: null };
}
// --- Seitenaufbau ------------------------------------------------------------
const out = [];
out.push('---');
out.push("title: 'Schema-Properties (generiert)'");
out.push("description: 'Alle Properties aller 16 Config-Schemas als Tabelle — generiert, nicht von Hand editieren.'");
out.push('---');
out.push('');
out.push('Diese Seite listet die Properties aller 16 Config-Schemas aus `tibi-types/schemas/config/`.');
out.push('');
out.push('> Generiert, nicht von Hand editieren — neu erzeugen mit `npm run gen:schemas`.');
out.push('');
out.push('Typnamen folgen JSON-Schema (`string`, `integer`, `number`, `boolean`, `object`, `Liste von …`);');
out.push('`$ref`-Verweise nennen den Baustein und dessen Typ, `oneOf`-Alternativen sind mit „oder“ verbunden.');
out.push('');
let tableCount = 0;
const perSchema = [];
for (const file of SCHEMAS) {
const schema = store.get(file);
const short = file.replace('.schema.json', '');
const required = schema.required || [];
out.push(`## ${short}`);
out.push('');
if (schema.title) out.push(`*${cell(schema.title)}* — \`${file}\``);
else out.push(`\`${file}\``);
out.push('');
// Sonderfall 1: definitions ohne Properties (defs.schema.json).
const { node: eff, via } = effectiveSchema(file, schema);
const props = eff.properties || {};
const propNames = Object.keys(props);
if (via) out.push(`Bildet den Baustein \`${via.split('/').pop()}\` dieses Schemas ab.`, '');
if (propNames.length > 0) {
if (required.length > 0) {
out.push(`Pflicht: ${required.map((r) => `\`${r}\``).join(', ')}.`);
out.push('');
}
// typeLabel braucht den Dateikontext für $ref-Auflösung: Zeilen mit file neu bauen.
const req = new Set(eff.required || required);
const rows = Object.entries(props).map(([name, sub]) => [
`\`${name}\``,
typeLabel(sub, file),
req.has(name) ? 'ja' : 'nein',
defaultLabel(sub),
descLabel(sub),
]);
out.push(table(PROP_HEADER, rows));
out.push('');
tableCount += 1;
perSchema.push({ schema: short, properties: rows.length, note: '' });
} else if (schema.type === 'array' && schema.items) {
// Sonderfall 2: Listen-Schema (field-list.schema.json).
out.push('Dieses Schema beschreibt eine **Liste** — keine Objekt-Properties, sondern Elemente:');
out.push('');
out.push(table(PROP_HEADER, [[
'`[]` (Elemente)',
typeLabel(schema.items, file),
'—',
'—',
'Jedes Element ist entweder ein Feldname (`string`) oder ein Feld-Objekt nach `field.schema.json`.',
]]));
out.push('');
tableCount += 1;
perSchema.push({ schema: short, properties: 1, note: 'Listen-Elemente' });
} else if (schema.patternProperties) {
// Sonderfall 3: freie Schlüssel (image-filter, projections).
const patterns = Object.entries(schema.patternProperties).filter(([k]) => k !== '^x-');
const rows = patterns.map(([pat, sub]) => [
`\`<name>\` (Muster \`${pat}\`)`,
typeLabel(sub, file),
'—',
'—',
descLabel(sub) !== '—' ? descLabel(sub) : 'Freier Schlüssel, Wert siehe Untertabelle.',
]);
out.push('Dieses Schema nutzt **freie Schlüssel** (Pattern):');
out.push('');
out.push(table(PROP_HEADER, rows));
out.push('');
tableCount += 1;
let innerProps = 0;
for (const [pat, sub] of patterns) {
let inner = sub;
if (sub.type === 'array' && sub.items && sub.items.properties) inner = sub.items;
if (inner && inner.properties) {
const req = new Set(inner.required || []);
const irows = Object.entries(inner.properties).map(([name, s]) => [
`\`${name}\``,
typeLabel(s, file),
req.has(name) ? 'ja' : 'nein',
defaultLabel(s),
descLabel(s),
]);
out.push(`### Wertefelder zu \`${pat}\``);
out.push('');
out.push(table(PROP_HEADER, irows));
out.push('');
tableCount += 1;
innerProps += irows.length;
} else if (inner && inner.properties === undefined && sub.properties) {
const req = new Set(sub.required || []);
const irows = Object.entries(sub.properties).map(([name, s]) => [
`\`${name}\``,
typeLabel(s, file),
req.has(name) ? 'ja' : 'nein',
defaultLabel(s),
descLabel(s),
]);
out.push(`### Wertefelder zu \`${pat}\``);
out.push('');
out.push(table(PROP_HEADER, irows));
out.push('');
tableCount += 1;
innerProps += irows.length;
}
}
perSchema.push({ schema: short, properties: rows.length + innerProps, note: 'patternProperties + Wertefelder' });
} else if (Object.keys(schema.definitions || {}).length > 0 || Object.keys(schema.$defs || {}).length > 0) {
// Sonderfall 4: nur Bausteine (defs.schema.json).
const defs = schema.definitions || schema.$defs;
const rows = Object.entries(defs).map(([name, sub]) => [
`\`${name}\``,
baseType(sub),
sub.required ? `Pflicht: ${sub.required.map((r) => `\`${r}\``).join(', ')}` : '—',
sub.description || '—',
]);
out.push('Dieses Schema definiert nur **`$ref`-Bausteine** (kein eigenes Config-Ziel):');
out.push('');
out.push(table(['Baustein', 'Typ', 'Pflicht', 'Beschreibung'], rows));
out.push('');
tableCount += 1;
perSchema.push({ schema: short, properties: rows.length, note: '$ref-Bausteine' });
} else {
throw new Error(`[gen:schemas] Schema ohne Properties/Items/patternProperties/definitions: ${file}`);
}
// Baustein-Übersicht, wenn das Schema eigene definitions mit Properties hat.
const defs = schema.definitions || schema.$defs;
if (defs && propNames.length > 0) {
const withProps = Object.entries(defs).filter(([, d]) => d && d.properties);
if (withProps.length > 0) {
out.push(`### Bausteine in ${short}`);
out.push('');
const drows = withProps.map(([name, d]) => [
`\`${name}\``,
Object.keys(d.properties).map((p) => `\`${p}\``).join(', '),
d.required ? d.required.map((r) => `\`${r}\``).join(', ') : '—',
]);
out.push(table(['Baustein', 'Felder', 'Pflicht'], drows));
out.push('');
tableCount += 1;
}
}
}
mkdirSync(dirname(OUT), { recursive: true });
const content = `${out.join('\n').replace(/\n{3,}/g, '\n\n').trim()}\n`;
const herkunft = sourceStamp();
const final = herkunft ? content.replace(/\n$/, `\n\n${herkunft}\n`) : content;
writeFileSync(OUT, final);
// --- Bericht ------------------------------------------------------------------
const total = perSchema.reduce((a, p) => a + p.properties, 0);
console.log(`[gen:schemas] geschrieben: ${OUT}`);
console.log(`[gen:schemas] Schemas: ${perSchema.length}, Tabellen: ${tableCount}, Properties gesamt: ${total}`);
for (const p of perSchema) console.log(` - ${p.schema}: ${p.properties} Zeilen${p.note ? ` (${p.note})` : ''}`);
// Selbstprüfung: keine leeren Tabellen, keine [object Object]-Reste.
if (/\| *\| *\|/.test(content)) {
console.error('[gen:schemas] FEHLER: leere Tabellenzellen gefunden.');
process.exit(1);
}
if (content.includes('[object Object]')) {
console.error('[gen:schemas] FEHLER: [object Object]-Reste gefunden.');
process.exit(1);
}
+22 -8
View File
@@ -3,26 +3,40 @@
* sync-openapi.mjs — holt die OpenAPI-Spec vom laufenden Tibi-Server * sync-openapi.mjs — holt die OpenAPI-Spec vom laufenden Tibi-Server
* und legt sie als Referenz-Seite ab. Ohne laufenden Server: Platzhalter. * und legt sie als Referenz-Seite ab. Ohne laufenden Server: Platzhalter.
* Aufruf: npm run sync:openapi * Aufruf: npm run sync:openapi
* Env: TIBI_API_URL (default http://localhost:8080/api), TIBI_NAMESPACE * Env: TIBI_API_URL (default http://localhost:8080/api/v1), TIBI_NAMESPACE
*/ */
import { mkdirSync, writeFileSync } from 'node:fs'; import { mkdirSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..'); const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const base = process.env.TIBI_API_URL ?? 'http://localhost:8080/api'; const base = process.env.TIBI_API_URL ?? 'http://localhost:8080/api/v1';
const namespace = process.env.TIBI_NAMESPACE;
const out = join(root, 'src', 'content', 'docs', 'reference', 'openapi.md'); const out = join(root, 'src', 'content', 'docs', 'reference', 'openapi.md');
const frontmatter = `---\ntitle: 'OpenAPI (generiert)'\ndescription: 'Projektspezifische OpenAPI-Spec vom laufenden tibi-server: Endpunkt, Abruf per curl und Sync.'\n---\n`;
const erklaerung = `Die Spec erzeugt der Server frisch aus Config und Collections deines Projekts: \`GET /api/v1/_openapi/:project\`. Es gibt keine statische \`/openapi.json\` — jede Spec gehört zu genau einem Projekt (Namespace).\n\n\`\`\`bash title="Spec eines Projekts abrufen"\ncurl http://localhost:8080/api/v1/_openapi/meinprojekt\n\`\`\`\n\n\`npm run sync:openapi\` ruft diesen Endpunkt für \`$TIBI_NAMESPACE\` auf (Basis \`TIBI_API_URL\`, Default \`http://localhost:8080/api/v1\`) und schreibt das Ergebnis in diese Seite.\n\nLäuft der Admin hinter seinem Proxy, erreichst du dieselbe Spec auch über \`$CODING_TIBIADMIN_URL/api/_openapi/<namespace>\` — der Proxy schreibt \`/api\` auf \`/api/v1\` um.\n`;
function schreiben(stand, specBlock) {
mkdirSync(dirname(out), { recursive: true });
writeFileSync(out, `${frontmatter}\n> Stand: ${stand} — nicht von Hand editieren.\n\n${erklaerung}${specBlock}`);
}
if (!namespace) {
console.warn('[sync:openapi] TIBI_NAMESPACE fehlt — Platzhalter bleibt.');
process.exit(0);
}
try { try {
const res = await fetch(`${base}/openapi.json`); const url = `${base}/_openapi/${namespace}`;
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const spec = await res.text(); const spec = await res.text();
mkdirSync(dirname(out), { recursive: true }); schreiben(
writeFileSync( `${new Date().toISOString()} · Quelle: \`${url}\` · Erzeugen mit \`npm run sync:openapi\``,
out, `\n\`\`\`json\n${spec.slice(0, 20000)}\n\`\`\`\n\n_Volle Spec: \`${url}\`_\n`,
`---\ntitle: 'OpenAPI (generiert)'\n---\n\n> Stand: ${new Date().toISOString()} · Quelle: \`${base}/openapi.json\` — nicht von Hand editieren.\n\n\`\`\`json\n${spec.slice(0, 20000)}\n\`\`\`\n\n_Volle Spec: \`${base}/openapi.json\`_\n`,
); );
console.log(`[sync:openapi] geschrieben: ${out}`); console.log(`[sync:openapi] geschrieben: ${out}`);
} catch (err) { } catch (err) {
console.warn(`[sync:openapi] Server nicht erreichbar (${base}): ${err.message} — Platzhalter bleibt.`); console.warn(`[sync:openapi] Server nicht erreichbar (${base}/_openapi/${namespace}): ${err.message} — Platzhalter bleibt.`);
} }
+9 -1
View File
@@ -1,6 +1,14 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* sync-server-docs.mjs — Single Source bleibt in den Quell-Repos, * sync-server-docs.mjs — STILLGELEGT (2026-10-17).
*
* Grund: Der Sync kopierte die englischen Server-Docs nach src/content/docs/server/ und die
* Nova-Docs nach src/content/docs/admin/. Diese Ziele stehen nicht in der Sidebar, es entstanden
* verwaiste Seiten, die nie verlinkt wurden. Die deutschen Handseiten sind seither kanonisch.
*
* Nicht mehr aufrufen (kein npm-Script mehr). Datei bleibt nur als Nachweis/Referenz liegen.
*
* ALT: sync-server-docs.mjs — Single Source bleibt in den Quell-Repos,
* diese Doku rendert nur. Kopiert: * diese Doku rendert nur. Kopiert:
* - ../tibi-server/docs/*.md -> src/content/docs/server/ (ohne uebersicht.md zu überschreiben) * - ../tibi-server/docs/*.md -> src/content/docs/server/ (ohne uebersicht.md zu überschreiben)
* - ../tibi-admin-nova/docs/*.md -> src/content/docs/admin/ (+ test-plans/) * - ../tibi-admin-nova/docs/*.md -> src/content/docs/admin/ (+ test-plans/)
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+29 -6
View File
@@ -3,7 +3,7 @@ title: 'Aktionen'
description: 'Actions im Admin starten: Parameter ausfüllen, ausführen, Ausgabe und Fortschritt lesen.' description: 'Actions im Admin starten: Parameter ausfüllen, ausführen, Ausgabe und Fortschritt lesen.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps, Badge } from '@astrojs/starlight/components';
Du stößt im **tibi-admin** (Verwaltungs-Backend, Release Nova) eine **Action** an — z. B. Versand, Import oder Aufräum-Job. Du füllst Parameter aus, klickst Ausführen und liest das Ergebnis. Diese Seite beschreibt die Bedienung — das Schreiben von Actions steht unter [Jobs und Actions](/projekt/jobs-actions/). Du stößt im **tibi-admin** (Verwaltungs-Backend, Release Nova) eine **Action** an — z. B. Versand, Import oder Aufräum-Job. Du füllst Parameter aus, klickst Ausführen und liest das Ergebnis. Diese Seite beschreibt die Bedienung — das Schreiben von Actions steht unter [Jobs und Actions](/projekt/jobs-actions/).
@@ -26,7 +26,28 @@ Du stößt im **tibi-admin** (Verwaltungs-Backend, Release Nova) eine **Action**
| Upload-Toast | Enthält die Eingabe Datei-Bytes, zeigt der Admin einen Byte-Fortschritt (`upload-progress`) | | Upload-Toast | Enthält die Eingabe Datei-Bytes, zeigt der Admin einen Byte-Fortschritt (`upload-progress`) |
| Verlauf | Werkzeug-Menü (Schild-Icon) → Audit mit Filter `collection=_actions/<name>` | | Verlauf | Werkzeug-Menü (Schild-Icon) → Audit mit Filter `collection=_actions/<name>` |
<!-- Screenshot-Platzhalter (T-SHOTS): Action-Ansicht mit Methoden-Reitern, Eingabeformular und Ausführen-Knopf. --> ### Ausführung: `kind`, `cancellable`, Upload-Pfad
Jede Methode darf `execution` tragen (Schema: `action.schema.json:274-288`, eingebunden unter `methods.<name>.execution`):
```yaml title="Action-Config — execution-Meta"
meta:
methods:
post:
execution:
kind: "long-running" # freier String, nur Doku-Charakter
cancellable: true # geplant, vom Admin aktuell nicht ausgewertet
```
| Feld | Stand im Admin (Release Nova) |
|---|---|
| `execution.kind` | Freier String — der Admin wertet ihn **nicht** aus (kein kind-spezifischer Runner; `ActionView.svelte` kennt nur ausführbar vs. gesperrt) |
| `execution.cancellable` | Schema-seitig vorgesehen, im Frontend **ohne Verwendung** (kein Abbruch-Knopf; `grep` über `frontend/src` findet keine Auswertung) — lang laufende Actions brechen nur über SSE-Ende oder Netzwerkfehler ab |
| Upload-Fortschritt | Enthält `data` Datei-Bytes (`hasInlineUploadPayload`), erzeugt `runAction` einen Byte-Toast (`createByteProgressToast`, `ActionView.svelte:350-357`) und füttert ihn per `onUploadProgress` (`ActionView.svelte:377`) — Erfolgs-Toast erst nach Server-Antwort |
<Aside type="note">Fortschritt während der Ausführung kommt ausschließlich aus SSE-Events plus `output.mapping.progress` (siehe Reiter oben) — `execution` steuert daran nichts.</Aside>
![Action-Ansicht im tibi-admin: Methoden-Reiter GET und POST, Eingabeformular mit den Feldern Kollektion und Versteckte einschliessen sowie Ausführen-Knopf](../../../assets/admin/admin-aktionen.webp)
## Parameter ## Parameter
@@ -47,13 +68,13 @@ Das Formular (`ActionMethodScaffold.svelte`) trennt zwei Blöcke:
## Ausgabe und Fortschritt ## Ausgabe und Fortschritt
<Tabs> <Tabs>
<TabItem label="Kurfassung und Details"> <TabItem label="Zusammenfassung und Details">
`ActionOutputMapping.svelte` zeigt gemappte Felder statt Roh-JSON: `ActionOutputMapping.svelte` zeigt gemappte Felder statt Roh-JSON:
- **Kurzfassung** (`output.mapping.summary.fields`) — immer sichtbar, Werte per `source`-Pfad aus dem Ergebnis gelesen (`deepGet`, z. B. `totals.grandTotal`). - **Zusammenfassung** (`output.mapping.summary.fields`, UI-String `outputSummary`) — immer sichtbar, Werte per `source`-Pfad aus dem Ergebnis gelesen (`deepGet`, z. B. `totals.grandTotal`).
- **Details** (`output.mapping.details.fields`) — aufklappbar, als Widgets gerendert. - **Details** (`output.mapping.details.fields`) — aufklappbar, als Widgets gerendert.
- Ohne Mapping zeigt der Admin das Ergebnis als formatiertes JSON; kennt die Methode nur einen Titel ohne Felder, steht dort „keine Ausgabefelder“. - Ohne Mapping zeigt der Admin das Ergebnis als formatiertes JSON; kennt die Methode nur einen Titel ohne Felder, steht dort „Fuer diese Aktion sind keine Ergebnisfelder konfiguriert.“ (`admin.actions.noOutputFields`).
</TabItem> </TabItem>
<TabItem label="Fortschritt (SSE)"> <TabItem label="Fortschritt (SSE)">
@@ -88,4 +109,6 @@ Formular (query + data) → Ausführen → [SSE-Events → Balken] → Ergebnis
Klick-Abläufe dazu stehen im Test-Plan `docs/test-plans/40-actions.md` (Methoden-Tabs, ausführbar vs. gesperrt, Ergebnis und Fortschritt). Klick-Abläufe dazu stehen im Test-Plan `docs/test-plans/40-actions.md` (Methoden-Tabs, ausführbar vs. gesperrt, Ergebnis und Fortschritt).
Verifiziert am 2b091d899590b392491c59a54f50de43f1057280 2026-10-16 (tibi-admin-nova) Anwender-Sicht zum Starten einer Aktion: [Aktionen ausführen](/anwender/aktionen/).
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova); `execution`-Abschnitt verifiziert am 4c55f82 (tibi-types: action.schema.json:274-288, 401-403), am 2b091d8 (ActionView.svelte:260-295, 350-382)
+5 -3
View File
@@ -3,7 +3,7 @@ title: 'Ansichten und Listen'
description: 'Tabellen-, Karten-, Medien- und Navigations-Ansichten, Suche, Sortierung, Filter, Blättern.' description: 'Tabellen-, Karten-, Medien- und Navigations-Ansichten, Suche, Sortierung, Filter, Blättern.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Badge } from '@astrojs/starlight/components';
Du öffnest eine Sammlung im **tibi-admin** (Verwaltungs-Backend, Release Nova) und siehst eine Liste aller Einträge. Suche, Sortierung, Filter und Blättern funktionieren in allen Ansichten gleich. Diese Seite beschreibt die Bedienung aus Redakteurs- und Projekt-Sicht — Konzept und Einordnung stehen unter [Admin-Konzept](/admin/konzept/). Du öffnest eine Sammlung im **tibi-admin** (Verwaltungs-Backend, Release Nova) und siehst eine Liste aller Einträge. Suche, Sortierung, Filter und Blättern funktionieren in allen Ansichten gleich. Diese Seite beschreibt die Bedienung aus Redakteurs- und Projekt-Sicht — Konzept und Einordnung stehen unter [Admin-Konzept](/admin/konzept/).
@@ -100,6 +100,8 @@ Teile eine gefilterte Liste per URL — Empfänger sehen exakt dieselbe Auswahl,
| Gesamtzahl fehlt oder lädt lange | `/_count` auf altem Server nicht verfügbar | Direkt laden lassen (Fallback) oder Server aktualisieren | | Gesamtzahl fehlt oder lädt lange | `/_count` auf altem Server nicht verfügbar | Direkt laden lassen (Fallback) oder Server aktualisieren |
| Ansicht startet „falsch“ | `viewHint` der Sammlung schlägt die Startansicht vor | Ansicht einmal wechseln — `view=…` in der URL gewinnt | | Ansicht startet „falsch“ | `viewHint` der Sammlung schlägt die Startansicht vor | Ansicht einmal wechseln — `view=…` in der URL gewinnt |
<!-- Screenshot-Platzhalter (T-SHOTS): Listenansicht mit Suchfeld, Filter-Popover und Paginierung, Alt-Text deutsch, max. 200 KB unter src/assets/. --> ![Seitenliste im tibi-admin: Suchfeld mit Suchbegriff, geöffnetes Filter-Popover mit Feld-Filtern und Paginierung in der Fußzeile](../../../assets/admin/admin-ansichten-listen.webp)
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova) Anwender-Sicht zum Ändern vieler Einträge auf einmal: [Massenbearbeitung](/anwender/massenbearbeitung/).
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+30 -4
View File
@@ -3,7 +3,7 @@ title: 'Benutzer, Teams, Tokens'
description: 'Anmelden, Personen, Teams und Orgs verwalten, Rollen und Berechtigungen, PATs, Stellvertretung.' description: 'Anmelden, Personen, Teams und Orgs verwalten, Rollen und Berechtigungen, PATs, Stellvertretung.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps, Badge } from '@astrojs/starlight/components';
Du verwaltest im **tibi-admin** (Verwaltungs-Backend, Release Nova), wer sich anmelden und was jede Person darf: Benutzerkonten, Teams, Organisationen, Rollen, persönliche Token (PATs) und die Stellvertretung. Server-Regeln dazu stehen unter [Rechte und Tokens](/projekt/rechte-tokens/), die vier HTTP-Köpfe unter [Token-Köpfe](/reference/token-koepfe/). Du verwaltest im **tibi-admin** (Verwaltungs-Backend, Release Nova), wer sich anmelden und was jede Person darf: Benutzerkonten, Teams, Organisationen, Rollen, persönliche Token (PATs) und die Stellvertretung. Server-Regeln dazu stehen unter [Rechte und Tokens](/projekt/rechte-tokens/), die vier HTTP-Köpfe unter [Token-Köpfe](/reference/token-koepfe/).
@@ -54,7 +54,7 @@ Die Kette lautet **Org → Team → Benutzer → Projekt**. Du pflegst sie im Ad
| Benutzer | `role: 1` | Zugriff nur über Org-/Team-Mitgliedschaft, neutrales Badge | | Benutzer | `role: 1` | Zugriff nur über Org-/Team-Mitgliedschaft, neutrales Badge |
- Die Rolle stellst du per Auswahl um (mit Admin-Warnung); im Nur-Lesen-Modus zeigt der Admin nur das Badge. - Die Rolle stellst du per Auswahl um (mit Admin-Warnung); im Nur-Lesen-Modus zeigt der Admin nur das Badge.
- Feinrechte vergibst du als **Chips** (`PermissionChipsInput.svelte`): Eintippen, Vorschlag anklicken oder Entfernen per ×. Vorschläge kommen aus der Projekt-Meta (`project-meta`) oder als Team-Rückfall (`team-fallback`); Platzhalter-Beispiel: `pages, media`. - Feinrechte vergibst du als **Chips** (`PermissionChipsInput.svelte`): Eintippen, Vorschlag anklicken oder per × entfernen. Die Vorschläge gruppiert der Admin nach Herkunft — aus der Projekt-Meta (`project-meta`) oder als Team-Rückfall (`team-fallback`); das Eingabefeld zeigt als Beispiel `pages, media`.
## Tokens ## Tokens
@@ -78,6 +78,32 @@ Persönliche Token (PATs) pflegst du auf zwei Wegen: im eigenen **Profil** (`Tok
</TabItem> </TabItem>
</Tabs> </Tabs>
## Projekt-Admin-Token
Der Admin ruft Reload, Suchindex-Aufbau, Backup und Snapshots mit einem **Projekt-Token** auf.
Welche dieser vier Operationen das Token darf, steuern vier Flags in `api/config.yml` (`admin.tokens[]`):
| Flag | Erlaubt | Wofür im Admin |
|---|---|---|
| `allowReload` | Projekt neu laden | Nach Config-Änderungen |
| `allowRegenerateSearch` | Suchindex neu aufbauen | Nach Index-Problemen (siehe [Suche](/projekt/suche/)) |
| `allowBackup` | Backup herunterladen und wiederherstellen | Sicherung (siehe [Audit und Backup](/projekt/audit-backup/)) |
| `allowSnapshots` | Snapshots auslösen, listen, löschen, wiederherstellen, herunterladen | Versionierte Sicherung (siehe [Audit und Backup](/projekt/audit-backup/)) |
Schema: `tibi-types/schemas/config/project.schema.json:227-261` (alle vier optional, nur `token` ist Pflicht); Server-Modell: `tibi-server/internal/models/project.go:116-120`.
```yaml title="api/config.yml — Projekt-Admin-Token mit allen Rechten"
admin:
tokens:
- token: "REDACTED_CREDENTIALS"
allowReload: true
allowRegenerateSearch: true
allowBackup: true
allowSnapshots: true
```
<Aside type="caution">Fehlt ein Flag, scheitert die Operation serverseitig — der Admin zeigt dann einen Rechte-Fehler. Vergib nur, was das Projekt wirklich braucht.</Aside>
## Stellvertretung (Impersonation) ## Stellvertretung (Impersonation)
Admins können sich als ein Benutzerkonto anmelden, um dessen Sicht zu prüfen: Admins können sich als ein Benutzerkonto anmelden, um dessen Sicht zu prüfen:
@@ -100,8 +126,8 @@ Admins können sich als ein Benutzerkonto anmelden, um dessen Sicht zu prüfen:
| Token-Bereich leer | Server ohne Token-Feature (`404`) | Normal — kein Fehler, Bereich bleibt verborgen | | Token-Bereich leer | Server ohne Token-Feature (`404`) | Normal — kein Fehler, Bereich bleibt verborgen |
| `permission denied` | Kopf oder Recht falsch | [Token-Köpfe](/reference/token-koepfe/) und Chips prüfen | | `permission denied` | Kopf oder Recht falsch | [Token-Köpfe](/reference/token-koepfe/) und Chips prüfen |
<!-- Screenshot-Platzhalter (T-SHOTS): Hub mit Benutzerliste und Detail-Drawer inkl. Rollen-Badge und Chips. --> ![Benutzerverwaltung im tibi-admin: Benutzerliste mit Rollen-Badges und Team-Chips, rechts der geöffnete Detail-Drawer mit Rollenauswahl und Organisations-Zuordnung](../../../assets/admin/admin-benutzer-rechte.webp)
Klick-Abläufe zur Stellvertretung stehen im Test-Plan `docs/test-plans/42-impersonation.md` (Auslöser, Bestätigungsdialog, Banner, Rückkehr zur Admin-Sitzung — nur im Mock-Modus testen). Klick-Abläufe zur Stellvertretung stehen im Test-Plan `docs/test-plans/42-impersonation.md` (Auslöser, Bestätigungsdialog, Banner, Rückkehr zur Admin-Sitzung — nur im Mock-Modus testen).
Verifiziert am 2b091d899590b392491c59a54f50de43f1057280 2026-10-16 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova); Projekt-Admin-Token verifiziert am 4c55f82 (tibi-types: project.schema.json:227-261), am 68b4278 (tibi-server: models/project.go:116-120)
+6 -17
View File
@@ -3,7 +3,7 @@ title: 'Admin anpassen'
description: 'Sammlungen, Ansichten, Widgets, Fremdschlüssel, Vorschau, Aktionen, Suche und eigene Exporte im Admin.' description: 'Sammlungen, Ansichten, Widgets, Fremdschlüssel, Vorschau, Aktionen, Suche und eigene Exporte im Admin.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';
Du passt das **tibi-admin** (Verwaltungs-Backend, Release Nova) an dein Projekt an: Was Listen zeigen, welche Eingabeelemente Formulare nutzen, wie Datensätze verknüpft und gesucht werden. Das meiste steuerst du über die Sammlungs-Config — Schema-Details stehen unter [Sammlungen anlegen](/projekt/sammlungen-anlegen/) und [Felder und Widgets](/projekt/felder-widgets/). Du passt das **tibi-admin** (Verwaltungs-Backend, Release Nova) an dein Projekt an: Was Listen zeigen, welche Eingabeelemente Formulare nutzen, wie Datensätze verknüpft und gesucht werden. Das meiste steuerst du über die Sammlungs-Config — Schema-Details stehen unter [Sammlungen anlegen](/projekt/sammlungen-anlegen/) und [Felder und Widgets](/projekt/felder-widgets/).
@@ -11,26 +11,15 @@ Du passt das **tibi-admin** (Verwaltungs-Backend, Release Nova) an dein Projekt
| Mittel | Wirkung | | Mittel | Wirkung |
|---|---| |---|---|
| `meta.views` (Tabelle, Karten, Kalender …) | Welche [Ansichten](/admin/ansichten-listen/) die Sammlung anbietet | | `meta.viewHint` (Tabelle, Karten, Medien, Navigation) | Startansicht der [Ansichten](/admin/ansichten-listen/) für die Sammlung |
| `meta.subNavigation` (Reiter-Filter) | Filter-Reiter über der Liste (z. B. nach Status oder Gruppe) |
| Sichtbare Spalten | Per Checkbox im Listen-Kopf; [Exporte](/admin/exporte/) übernehmen sie als Vorauswahl | | Sichtbare Spalten | Per Checkbox im Listen-Kopf; [Exporte](/admin/exporte/) übernehmen sie als Vorauswahl |
| URL-Parameter | Ansicht ist teilbar: `?view=cards&q=hero&qm=combined&sort=name&order=asc&page=2&size=25` — `qm` ist der Suchmodus aus dem `search`-Block | | URL-Parameter | Ansicht ist teilbar: `?view=cards&q=hero&qm=combined&sort=name&order=asc&page=2&size=25` — `qm` ist der Suchmodus aus dem `search`-Block |
| Sortierung | Klick auf Spaltenkopf; manuelle Reihenfolge per Drag, wo freigegeben | | Sortierung | Klick auf Spaltenkopf; manuelle Reihenfolge per Drag, wo freigegeben |
## Widgets und Felder ## Widgets und Felder
Der `WidgetDispatcher` wählt das Eingabeelement: zuerst `meta.widget`, sonst der Feldtyp. Alle **25 Schema-Enum-Werte** kommen vor (`pagebuilder` und `pageBuilder` meinen dasselbe): Der `WidgetDispatcher` wählt das Eingabeelement: zuerst `meta.widget`, sonst der Feldtyp (`pagebuilder` und `pageBuilder` meinen dasselbe). Die vollständige Liste aller Schema-Widgets mit Beispielen steht unter [Felder und Widgets](/projekt/felder-widgets/) — hier nur die Bedienung: Pflicht, Formate und `dependsOn`-Sichtbarkeit kommen aus dem Feld-Schema (siehe [Eintrags-Editor](/admin/eintrags-editor/)), mehrsprachige Felder bekommen automatisch Sprachtabs (`I18nLanguageTabs`).
| Gruppe | Widgets |
|---|---|
| Text | `text`, `textarea`, `richtext`, `html`, `json` |
| Zahl/Auswahl | `number`, `checkbox`, `switch`, `select`, `selectArray`, `chipArray`, `color` |
| Datum | `date`, `datetime` |
| Dateien | `file`, `image`, `foreignFile`, `foreignMedia` |
| Verknüpfung | `foreignKey`, `foreignKeyChipArray` |
| Struktur | `pagebuilder`/`pageBuilder`, `templateProps`, `containerLessObject`, `containerLessObjectArray` |
- Pflicht, Formate und `dependsOn`-Sichtbarkeit kommen aus dem Feld-Schema — Bedienung siehe [Eintrags-Editor](/admin/eintrags-editor/).
- Mehrsprachige Felder bekommen automatisch Sprachtabs (`I18nLanguageTabs`).
## Fremdschlüssel und Vorschau ## Fremdschlüssel und Vorschau
@@ -84,6 +73,6 @@ Volle Kontext-Tabelle in der Repo-Doku `custom-export-script.md`; Bedienung unte
<Aside type="caution">Anpassungen an Sammlungs-Config und Skripten erst im lokalen Stack ([einrichten](/admin/setup/)) testen — falsche `meta`-Einträge blenden Ansichten oder Export-Reiter ersatzlos aus.</Aside> <Aside type="caution">Anpassungen an Sammlungs-Config und Skripten erst im lokalen Stack ([einrichten](/admin/setup/)) testen — falsche `meta`-Einträge blenden Ansichten oder Export-Reiter ersatzlos aus.</Aside>
<!-- Screenshot-Platzhalter (T-SHOTS): angepasste Listenansicht mit Spaltenwahl und eigenem Export-Reiter. --> ![Angepasste Listenansicht im tibi-admin: Seitenliste mit Filter-Reitern und geöffnetem Export-Dialog mit den eigenen Export-Reitern Seiten-Report und Seiten-CSV](../../../assets/admin/admin-customizing.webp)
Verifiziert am 2b091d899590b392491c59a54f50de43f1057280 2026-10-16 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova); Widerspruchsprüfung am 2026-10-17: veraltete `meta.views`-Zeile parallel zu `meta.viewHint` korrigiert, kein Custom-Views-Widerspruch mehr.
+20 -19
View File
@@ -3,7 +3,7 @@ title: 'Eintrags-Editor'
description: 'Felder ausfüllen, Widgets, Fremdschlüssel, i18n-Tabs, Verlauf, Tastaturkürzel.' description: 'Felder ausfüllen, Widgets, Fremdschlüssel, i18n-Tabs, Verlauf, Tastaturkürzel.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Du öffnest einen Eintrag aus einer [Liste](/admin/ansichten-listen/) und bearbeitest seine Felder im Editor des **tibi-admin** (Verwaltungs-Backend, Release Nova). Links liegt das Formular, rechts die Seitenleiste mit Aktionen und Gruppen (`meta.position`: `main`, `sidebar:<gruppe>`, `sidebar:settings`). Diese Seite beschreibt die Bedienung — Feldtypen und Schema-Details stehen unter [Felder und Widgets](/projekt/felder-widgets/). Du öffnest einen Eintrag aus einer [Liste](/admin/ansichten-listen/) und bearbeitest seine Felder im Editor des **tibi-admin** (Verwaltungs-Backend, Release Nova). Links liegt das Formular, rechts die Seitenleiste mit Aktionen und Gruppen (`meta.position`: `main`, `sidebar:<gruppe>`, `sidebar:settings`). Diese Seite beschreibt die Bedienung — Feldtypen und Schema-Details stehen unter [Felder und Widgets](/projekt/felder-widgets/).
@@ -26,21 +26,7 @@ Du öffnest einen Eintrag aus einer [Liste](/admin/ansichten-listen/) und bearbe
## Widgets ## Widgets
Der `WidgetDispatcher` wählt das Eingabeelement aus `meta.widget` — sonst aus dem Feldtyp. Alle 25 Schema-Widgets aus [Felder und Widgets](/projekt/felder-widgets/) werden unterstützt (`text`, `textarea`, `number`, `checkbox`, `switch`, `select`, `selectArray`, `chipArray`, `date`, `datetime`, `file`, `image`, `json`, `richtext`, `html`, `foreignKey`, `foreignKeyChipArray`, `foreignFile`, `foreignMedia`, `pagebuilder`/`pageBuilder`, `templateProps`, `containerLessObject`, `containerLessObjectArray`, `color`). Der `WidgetDispatcher` wählt das Eingabeelement aus `meta.widget` — sonst aus dem Feldtyp. Welcher Widget-Name zu welchem Feldtyp gehört und was jeder Widget kann, steht vollständig unter [Felder und Widgets](/projekt/felder-widgets/). Zur Bedienung genügt: Kurztext trägst du in einer Zeile ein, Langtext im mehrzeiligen Feld (mit Werkzeugleiste bei `richtext`), Zahlen über Pfeile oder Eingabe, Ja/Nein über Schalter oder Kästchen, Datum und Uhrzeit im Kalenderfeld, Dateien und Bilder über Vorschau plus [Bildeditor](/admin/medien/), Verweise über den Auswahl-Dialog unten, Seiten über Blöcke mit Typ-Umschalter.
| Eingabe | Widgets | Hinweis |
|---|---|---|
| Kurztext | `text` | Eine Zeile, Standard für `string` |
| Langtext | `textarea`, `richtext`, `html` | Editor mit Werkzeugleiste bei `richtext` |
| Zahl | `number` | Auch für `number[]` |
| Ja/Nein | `checkbox`, `switch` | Reine Flags brauchen kein Widget |
| Auswahl | `select`, `selectArray`, `chipArray` | Optionen aus `meta.choices` |
| Datum | `date`, `datetime` | Zeitanteil nur bei `datetime` |
| Datei/Bild | `file`, `image` | Vorschau plus [Bildeditor](/admin/medien/) |
| Struktur | `json`, `templateProps`, `containerLessObject(-Array)` | Freies JSON ohne `subFields` |
| Seite | `pagebuilder` | Blöcke mit Typ-Umschalter und `dependsOn`-Feldern |
| Farbe | `color` | Farbwähler |
| Verweis | `foreignKey`, `foreignKeyChipArray`, `foreignFile`, `foreignMedia` | Siehe unten |
<Aside type="note"> <Aside type="note">
Felder mit `meta.dependsOn.eval` blendet der Editor per JavaScript ein oder aus (z. B. Block-Optionen nur beim passenden Blocktyp). `meta.hide: true` versteckt Technikfelder dauerhaft. Felder mit `meta.dependsOn.eval` blendet der Editor per JavaScript ein oder aus (z. B. Block-Optionen nur beim passenden Blocktyp). `meta.hide: true` versteckt Technikfelder dauerhaft.
@@ -74,6 +60,21 @@ Jede Sprache ist ein eigener Eintrag mit gleichem Gruppenschlüssel (`meta.i18n.
Sprachwechsel-Tastenkürzel: `Strg + L` (zyklisch durch die Inhaltssprachen). Sprachwechsel-Tastenkürzel: `Strg + L` (zyklisch durch die Inhaltssprachen).
## Verlauf im Editor
Über das Verlaufs-Symbol am Eintrag öffnest du die Audit-Historie (`AuditHistoryPanel`): Sie listet Zeitpunkt, Aktion (**erstellt**, **geändert**, **gelöscht**), **Benutzer** und **Quelle** — aufklappen zeigt den Unterschied **Vorher** gegen **Nachher** (`AuditDiff`: **hinzugefügt**, **entfernt**, **geändert**). Über **Wiederherstellen** wählst du im Dialog (`AuditRestoreDialog`) unter **Felder zur Wiederherstellung** aus, was übernommen wird (**Alle auswählen**, **Keine**, einzeln pro Feld, **Ganzes Array übernehmen** für ganze Listen); **Media-Dateien werden nicht wiederhergestellt**. Übernehmen trägt die Werte nur ins Formular ein — gespeichert wird erst mit **Speichern**. In der Seitenleiste zeigt `AuditSidebarHistory` denselben Verlauf als Ausschnitt mit **Ältere Einträge**. Schritt für Schritt aus Redakteurs-Sicht steht unter [Verlauf wiederherstellen](/anwender/verlauf-wiederherstellen/).
| Detail | Verhalten | Quelle |
|---|---|---|
| Blättern | **25 Einträge pro Seite** (`PAGE_SIZE`) | `AuditView.svelte:37,107-108,376` |
| Aufklappen | Pfeil pro Zeile (`mdiChevronRight`/`mdiChevronDown`, `toggleExpand`) zeigt den Vorher/Nachher-Diff | `AuditHistoryPanel.svelte:75-76,158-162`, `AuditView.svelte:322` |
| Kopieren | Kopieren-Schalter je Diff-Wert (`copyValue`, `navigator.clipboard`, Titel „Kopieren") | `AuditDiff.svelte:81-83,276,309` |
| Zähler | Änderungsanzeige „sichtbar / gesamt" (`selection.size` / `diffs.length`) | `AuditDiff.svelte:168` |
## Pagebuilder-Ansicht
Seitenfelder (`pagebuilder`) rendert `PagebuilderWidget` als Stapel aus Block-Karten mit Block-Label und Farbpunkt. Ein Klick öffnet den Overlay-Editor (`PagebuilderOverlay`): links das Formular, rechts die Live-Vorschau, oben der Modus-Umschalter (**Nur Formular**, **Formular und Vorschau**, **Nur Vorschau**) und **OK** (oder `Esc`) zum Schließen. Das Übersichts-Overlay (`PagebuilderOverviewOverlay`, Knopf **Pagebuilder-Overlay öffnen**) listet alle Blöcke als Blueprint-Karten (`BlueprintCard`: Typ, Bilder, Kind-Elemente, weitere Felder) zum Anspringen und Umsortieren. Unter der Vorschau steuert `PagebuilderViewportControl` die simulierte Breite: **Desktop** (1280), **Tablet** (768), **Mobil** (375), per Slider frei von 320 bis 1920 px. Blöcke anlegen und füllen steht unter [Seiten bauen](/anwender/seiten-bauen/).
## Tastatur ## Tastatur
| Kürzel | Wirkung | | Kürzel | Wirkung |
@@ -88,7 +89,7 @@ Sprachwechsel-Tastenkürzel: `Strg + L` (zyklisch durch die Inhaltssprachen).
| `?` | Tastaturhilfe öffnen | | `?` | Tastaturhilfe öffnen |
<Aside type="tip"> <Aside type="tip">
Drücke `?` im Editor — die Hilfe listet alle Kürzel inklusive Listen-Kürzel (`s`, `f`, `x`, `o`, `v`, `p` + Ziffer). Drücke `?` im Editor — die Hilfe listet alle Kürzel inklusive Listen-Kürzel (`s`, `f`, `x`, `o`, `v`, `p` + Ziffer). Die vollständige Referenz aller Kürzel steht unter [Tastatur-Kurzbefehle](/reference/tastatur-kurzbefehle/).
</Aside> </Aside>
## Häufige Fehler ## Häufige Fehler
@@ -100,6 +101,6 @@ Drücke `?` im Editor — die Hilfe listet alle Kürzel inklusive Listen-Kürzel
| Übersetzung fehlt in der Leiste | Kein Gruppenschlüssel oder Sprache nicht konfiguriert | `translationKey`/`lang` prüfen, fehlende Sprache per `+` anlegen | | Übersetzung fehlt in der Leiste | Kein Gruppenschlüssel oder Sprache nicht konfiguriert | `translationKey`/`lang` prüfen, fehlende Sprache per `+` anlegen |
| Änderung „verschwunden“ | Drawer geschlossen ohne Speichern | Auf Änderungs-Warnung achten, erst Drawer-, dann Haupt-Speichern | | Änderung „verschwunden“ | Drawer geschlossen ohne Speichern | Auf Änderungs-Warnung achten, erst Drawer-, dann Haupt-Speichern |
<!-- Screenshot-Platzhalter (T-SHOTS): Editor mit Formular, Seitenleiste und Übersetzungsleiste, Alt-Text deutsch, max. 200 KB unter src/assets/. --> ![Eintrags-Editor im tibi-admin: Pagebuilder-Blöcke im Formular, Übersetzungsleiste mit DE- und EN-Reitern und Seitenleiste mit Einstellungen und Metadaten](../../../assets/admin/admin-eintrags-editor.webp)
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+40 -13
View File
@@ -3,7 +3,7 @@ title: 'Exporte'
description: 'CSV, XLSX, JSON aus Listen, vordefinierte und eigene Skript-Exporte.' description: 'CSV, XLSX, JSON aus Listen, vordefinierte und eigene Skript-Exporte.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge } from '@astrojs/starlight/components'; import { Aside, Steps } from '@astrojs/starlight/components';
Du exportierst Einträge einer Sammlung im **tibi-admin** (Verwaltungs-Backend, Release Nova) direkt aus der [Liste](/admin/ansichten-listen/): als Datei für Tabellen­kalkulation, Datenaustausch oder eigene Berichte. Exportiert wird, was du siehst — Filter und Suche der Liste gelten auch für den Export. Du exportierst Einträge einer Sammlung im **tibi-admin** (Verwaltungs-Backend, Release Nova) direkt aus der [Liste](/admin/ansichten-listen/): als Datei für Tabellen­kalkulation, Datenaustausch oder eigene Berichte. Exportiert wird, was du siehst — Filter und Suche der Liste gelten auch für den Export.
@@ -41,13 +41,36 @@ Stehen im Export-Dialog eigene Reiter **vor** dem Standard-Tab, hat das Projekt
exports: exports:
- name: bestellreport - name: bestellreport
label: { de: "Bestellreport", en: "Order report" } label: { de: "Bestellreport", en: "Order report" }
file: "_/assets/exports/reports.mjs" file: "_/exports/reports.mjs"
filename: "bestellreport-{date}.xlsx" filename: "bestellreport-{date}.xlsx"
``` ```
## Eigenes Skript ## Eigenes Skript
Projekt-Entwickler legen eigene Exporte als ES-Modul unter `meta.exports[].file` ab (Pfad mit `_/`-Präfix = projekt-relativ, empfohlen). Das Modul exportiert **eine Standard-Funktion**, die Kontext (Filter, Auswahl, Fortschritt) bekommt und genau ein Ergebnis zurückgibt: Projekt-Entwickler legen eigene Exporte als ES-Modul unter `meta.exports[].file` ab (Pfad mit `_/…`-Präfix = projekt-relativ, empfohlen; daneben `/_/…`-Alias, `/…` host-absolut, `https://…`, `data:`, `blob:` und `{ eval }` — gleiche Regeln wie bei der [Block-Registry](/projekt/pagebuilder-dev/)). Das Modul exportiert **eine** Standard-Funktion, die `ctx` bekommt und genau ein Ergebnis zurückgibt.
### `ctx` vollständig
Quelle: `frontend/src/lib/utils/custom-export.types.ts:72-102` (einzige Typ-Wahrheit).
| Feld | Inhalt |
|---|---|
| `api`, `project`, `collection` | Config-Kopien in GetMap-Form (lesen ja, ändern sinnlos) |
| `namespace`, `$project`, `$namespace`, `$method ("export")`, `context` (selbst) | Wie Server-Hook-`jsData` — **kein `$auth`** (läuft als eingeloggte Admin-Sitzung, Server prüft Rechte) |
| `export`, `exportName` | Eigene `meta.exports`-Zeile bzw. deren `name` (ein Modul bedient N Exporte) |
| `view` | Listenzustand: `search`, `searchMode`, `sortField`, `sortOrder`, `manualSort`, `fieldFilters`, `subNavFilter`, `filter` (gemischt) |
| `db.find(coll, opts)` | `{ filter, select, selector, sort, offset, limit, lookup }` — `select` exklusiv mit `selector` (nur `1`-Werte, `0` → Fehler); **nur `sort[0]`** wird genutzt (REST-Grenze); kein `projection` |
| `db.count(coll, { filter })` | Trefferzahl für Fortschritt und Batch-Planung |
| `db.loadAll(coll, opts, onProgress?)` | Zählt, lädt in 500er-Paketen (Retry-once pro Batch, Abbruch per `signal`), meldet pro Batch |
| `xlsx` | Gebündelte `xlsx-js-style`-Lib — kein Eigen-Import nötig |
| `helpers` | `deepGet`, `formatValue`, `csvCell` (Formel-Injektionsschutz: führende `=+-@` → `'…'`), `csvWithBom` (UTF-8-BOM für Excel-Umlaute) |
| `lang`, `languageCodes`, `defaultLanguage` | i18n |
| `progress(done, total, label?)` | Schreibt auf die Dialog-Fortschrittsleiste |
| `signal` | `AbortSignal` des Abbrechen-Knopfs (`fetch` wirft `AbortError`) |
Nur lesend: kein `create`/`update`/`delete` über `db`.
### Rückgabe und Grenzen
| Rückgabe | Inhalt | | Rückgabe | Inhalt |
|---|---| |---|---|
@@ -56,19 +79,23 @@ Projekt-Entwickler legen eigene Exporte als ES-Modul unter `meta.exports[].file`
| `text` | Text wie CSV oder HTML | | `text` | Text wie CSV oder HTML |
| `bytes` | Binärdaten wie PDF (`Uint8Array`/`ArrayBuffer`) | | `bytes` | Binärdaten wie PDF (`Uint8Array`/`ArrayBuffer`) |
Dazu Pflicht: `filename` und `contentType` (z. B. `text/csv`, `application/pdf`) — ohne `contentType` wirft der Admin einen Fehler. Genau ein Payload pro Ergebnis. Dazu Pflicht: `filename` und `contentType` (z. B. `text/csv`, `application/pdf`) — ohne `contentType` wirft der Admin einen Fehler. Genau ein Payload pro Ergebnis. Dateinamen werden sanitiert (`/:*?"<>|` → `_`, `{date}` → `YYYY-MM-DD`; `custom-export-result.ts:14-18`).
```js title="reports.mjs (vereinfacht)" ```js title="reports.mjs — lauffähiges Minimalbeispiel (CSV)"
// reports.mjs — stark vereinfacht // _/exports/reports.mjs
export default async function (ctx) { export default async function (ctx) {
const rows = await ctx.db.loadAll({ filter: ctx.filter }); const rows = await ctx.db.loadAll(
ctx.progress(1, 1, "Fertig"); "orders",
const wb = ctx.xlsx.utils.json_to_sheet(rows); { select: ["orderNo", "total"], filter: ctx.view.filter, sort: [`-${ctx.view.sortField || "orderNo"}`] },
return { workbook: { SheetNames: ["Report"], Sheets: { Report: wb } }, filename: "bestellreport-{date}.xlsx", contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }; (done, total) => ctx.progress(done, total, "Lade Bestellungen …")
);
const lines = [["orderNo", "total"], ...rows.map((r) => [r.orderNo, r.total ?? ""])];
const csv = lines.map((cols) => cols.map((c) => ctx.helpers.csvCell(c)).join(";")).join("\n");
return { text: ctx.helpers.csvWithBom(csv), filename: "bestellreport-{date}.csv", contentType: "text/csv;charset=utf-8" };
} }
``` ```
Details für Entwickler (Kontext-Felder, Dateinamen-Platzhalter, Sanitizing) stehen in der Admin-Repo-Doku `custom-export-script.md` — im Zweifel gilt der Code (`utils/load-custom-export.ts`, `utils/custom-export-context.ts`, `utils/export.ts`). Details für Entwickler (Styling per `cell.s`, Spaltenbreiten, Autofilter) stehen in der Admin-Repo-Doku `custom-export-script.md` — im Zweifel gilt der Code (`utils/load-custom-export.ts`, `utils/custom-export-context.ts`, `utils/export.ts`).
## Häufige Fehler ## Häufige Fehler
@@ -80,6 +107,6 @@ Details für Entwickler (Kontext-Felder, Dateinamen-Platzhalter, Sanitizing) ste
| Skript-Fehler im Dialog | Modul ohne Standard-Export oder Laufzeitfehler | Browser-Konsole lesen, Modul-Export und Rückgabeformat prüfen | | Skript-Fehler im Dialog | Modul ohne Standard-Export oder Laufzeitfehler | Browser-Konsole lesen, Modul-Export und Rückgabeformat prüfen |
| Leere Datei trotz Treffern | Leserecht auf Zielfelder fehlt | Rechte unter [Rechte und Tokens](/projekt/rechte-tokens/) klären | | Leere Datei trotz Treffern | Leserecht auf Zielfelder fehlt | Rechte unter [Rechte und Tokens](/projekt/rechte-tokens/) klären |
<!-- Screenshot-Platzhalter (T-SHOTS): Export-Dialog mit Formatwahl und Spaltenliste, Alt-Text deutsch, max. 200 KB unter src/assets/. --> ![Export-Dialog im tibi-admin: Formatwahl zwischen JSON, CSV und XLSX sowie Spaltenliste mit vorausgewählten Feldern](../../../assets/admin/admin-exporte.webp)
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova); `ctx`-Tabelle und Beispiel verifiziert am 2b091d8 (custom-export.types.ts:17-102, custom-export-script.md, custom-export-result.ts:14-18)
+13 -6
View File
@@ -3,7 +3,7 @@ title: 'KI-Funktionen'
description: 'Chat, Zusammenfassen, Stimme und Werkzeuge im Admin: was die KI kann und wo ihre Grenzen liegen.' description: 'Chat, Zusammenfassen, Stimme und Werkzeuge im Admin: was die KI kann und wo ihre Grenzen liegen.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Die KI im **tibi-admin** (Verwaltungs-Backend, Release Nova) hilft dir beim Redigieren: Sie chattet im Kontext der aktuellen Ansicht, fasst lange Verläufe zusammen, hört zu (STT) und liest vor (TTS). **Wichtig vorab: Im Frontend wählst du kein Modell** — alle Modellentscheidungen trifft der Proxy (`config/ai-proxy.config.json`); das Frontend ruft nur Endpunkte aus `frontend/assets/system.config.json` auf. Die KI im **tibi-admin** (Verwaltungs-Backend, Release Nova) hilft dir beim Redigieren: Sie chattet im Kontext der aktuellen Ansicht, fasst lange Verläufe zusammen, hört zu (STT) und liest vor (TTS). **Wichtig vorab: Im Frontend wählst du kein Modell** — alle Modellentscheidungen trifft der Proxy (`config/ai-proxy.config.json`); das Frontend ruft nur Endpunkte aus `frontend/assets/system.config.json` auf.
@@ -27,7 +27,14 @@ Was die KI dabei sieht (`aiController.ts`, `AiChat.svelte`): Neben dem UI-Snapsh
| Werkzeug-Definitionen | `lib/aiTools.ts` | Funktionsaufrufe für den Chat (nächster Abschnitt) | | Werkzeug-Definitionen | `lib/aiTools.ts` | Funktionsaufrufe für den Chat (nächster Abschnitt) |
| Schätzung | `lib/aiTokenEstimator.ts` | Heuristik ~4 Zeichen ≈ 1 Token, nur für Vorab-Entscheidungen | | Schätzung | `lib/aiTokenEstimator.ts` | Heuristik ~4 Zeichen ≈ 1 Token, nur für Vorab-Entscheidungen |
**„Token-Status“** (Knopf im Chat, `showTokenStatus`): Das Popover zeigt **„Kontext-Auslastung“** (Prozent mit Farbstufe: grün < 40 %, gelb < 70 %, orange < 85 %, rot darüber — `getContextStatus`), **„Letzter Turn“** (**„Prompt-Tokens“**, **„Completion-Tokens“**, **„Gesamt-Tokens“**, **„Geschätzte Kosten“**), **„Konversation gesamt“** (inkl. Zusammenfassungs-, Spracheingabe- und Sprachausgabe-Kosten) sowie **„Modell“** und **„Kontextfenster“**. Gezählt wird zweigleisig: Die Schätzung (`estimateTokens`, `estimateSystemOverhead`) steuert Vorab-Entscheidungen, die echten Zähler kommen aus der Stream-Antwort (`stream_options`) und landen in **„Geschätzte Kosten“** über die Anzeigetabelle `TEXT_TOKEN_PRICING` — reine Anzeige, keine Abrechnung. **„Token-Status“** (Knopf im Chat) öffnet ein Popover: Oben steht die **„Kontext-Auslastung“** in Prozent mit Farbstufe, darunter **„Letzter Turn“** (**„Prompt-Tokens“**, **„Completion-Tokens“**, **„Gesamt-Tokens“**, **„Geschätzte Kosten“**), **„Konversation gesamt“** (inkl. Zusammenfassungs-, Spracheingabe- und Sprachausgabe-Kosten) sowie **„Modell“** und **„Kontextfenster“**. Gezählt wird zweigleisig: Die Schätzung steuert Vorab-Entscheidungen, die echten Zähler kommen aus der Stream-Antwort und landen in **„Geschätzte Kosten“** über die Anzeigetabelle `TEXT_TOKEN_PRICING` — reine Anzeige, keine Abrechnung.
| Schwelle | Farbe | Bedeutung |
|---|---|---|
| unter 40 % | Grün | Reichlich Platz — normal weiterarbeiten |
| 40 bis unter 70 % | Gelb | Füllstand im Blick behalten |
| 70 bis unter 85 % | Orange | Bald zusammenfassen oder neuen Chat starten |
| ab 85 % | Rot | Kritisch — Zusammenfassen oder hartes Kürzen steht an |
Das **„Kontextfenster“** liest der Chat aus dem Modellkatalog (Endpunkt `/api/ai/models`, `configured.chat.contextWindow`); antwortet der Katalog nicht, gelten 96 000 Token (`MODEL_CONTEXT_WINDOW`). Aktuell meldet der Proxy 262 144 (siehe `config/ai-proxy.config.json`, `chat.contextWindow`). Das **„Kontextfenster“** liest der Chat aus dem Modellkatalog (Endpunkt `/api/ai/models`, `configured.chat.contextWindow`); antwortet der Katalog nicht, gelten 96 000 Token (`MODEL_CONTEXT_WINDOW`). Aktuell meldet der Proxy 262 144 (siehe `config/ai-proxy.config.json`, `chat.contextWindow`).
@@ -41,7 +48,7 @@ Wächst ein Gespräch Richtung Kontextgrenze, komprimiert `lib/aiSummarizer.ts`
|---|---|---| |---|---|---|
| Verdichten (kostenlos, synchron) | Mehr als 12 Werkzeug-Aktivitäten | `condenseHistoricalToolMessages`: abgeschlossene alte Werkzeug-Gruppen werden zu je einer Historie-Notiz (`[HISTORY-CONDENSED: …]`, Rolle `user`), die neuesten 12 Aktivitäten bleiben roh | | Verdichten (kostenlos, synchron) | Mehr als 12 Werkzeug-Aktivitäten | `condenseHistoricalToolMessages`: abgeschlossene alte Werkzeug-Gruppen werden zu je einer Historie-Notiz (`[HISTORY-CONDENSED: …]`, Rolle `user`), die neuesten 12 Aktivitäten bleiben roh |
| Zusammenfassen per LLM | 60 % des Fensters (`SUMMARIZE_THRESHOLD`) | Ältere Nachrichten per LLM zusammenfassen, neueste ~6 Nachrichten (max. ~8 000 Token) roh behalten | | Zusammenfassen per LLM | 60 % des Fensters (`SUMMARIZE_THRESHOLD`) | Ältere Nachrichten per LLM zusammenfassen, neueste ~6 Nachrichten (max. ~8 000 Token) roh behalten |
| 85 % (`HARD_TRUNCATE_THRESHOLD`) | Hart kürzen: älteste Nachrichten verwerfen | | Hart kürzen | 85 % (`HARD_TRUNCATE_THRESHOLD`) | Älteste Nachrichten verwerfen |
- Reserviert sind 10 000 Token für System-Prompt, Werkzeug-Definitionen und Antwort (`RESERVED_TOKENS`). - Reserviert sind 10 000 Token für System-Prompt, Werkzeug-Definitionen und Antwort (`RESERVED_TOKENS`).
- Zusammenfassungs-Modell: `proxy-managed` — auch hier entscheidet der Proxy, nicht das Frontend. Der Aufruf geht an den Zusammenfassungs-Endpunkt mit Profil `summarizer` und liest einen SSE-Strom (inkl. Zähler); Antwort leer oder Netzfehler → Rückfall auf hartes Kürzen. - Zusammenfassungs-Modell: `proxy-managed` — auch hier entscheidet der Proxy, nicht das Frontend. Der Aufruf geht an den Zusammenfassungs-Endpunkt mit Profil `summarizer` und liest einen SSE-Strom (inkl. Zähler); Antwort leer oder Netzfehler → Rückfall auf hartes Kürzen.
@@ -88,7 +95,7 @@ Die KI ruft UI-Werkzeuge statt zu raten (`aiTools.ts` → `aiController.ts`). La
|---|---| |---|---|
| Navigation | `navigate` (**„Navigieren“**), `guide_to` (**„Geführte Tour“**), `list_projects` (**„Projekte auflisten“**), `select_project` (**„Projekt öffnen“**), `select_collection` (**„Collection öffnen“**) | | Navigation | `navigate` (**„Navigieren“**), `guide_to` (**„Geführte Tour“**), `list_projects` (**„Projekte auflisten“**), `select_project` (**„Projekt öffnen“**), `select_collection` (**„Collection öffnen“**) |
| Liste | `search` (**„Suchen“**), `set_search_mode` (**„Suchmodus wechseln“**), `set_filter` (**„Filter setzen“**), `clear_filters` (**„Filter löschen“**), `sort_by` (**„Sortieren“**), `set_page` (**„Seite wechseln“**), `set_page_size` (**„Seitengröße ändern“**) | | Liste | `search` (**„Suchen“**), `set_search_mode` (**„Suchmodus wechseln“**), `set_filter` (**„Filter setzen“**), `clear_filters` (**„Filter löschen“**), `sort_by` (**„Sortieren“**), `set_page` (**„Seite wechseln“**), `set_page_size` (**„Seitengröße ändern“**) |
| Auswahl | `select_records` (**„Datensätze auswählen“**), `select_all` (**„Alle auswählen“**), `deselect_all` (**„Auswahl aufheben“**), `open_collection_record` (Datensatz öffnen), `set_view_mode` (**„Ansicht wechseln“**) | | Auswahl | `select_records` (**„Datensätze auswählen“**), `select_all` (**„Alle auswählen“**), `deselect_all` (**„Auswahl aufheben“**), `open_collection_record` (öffnet den Datensatz, Tool-Name als Anzeige), `set_view_mode` (**„Ansicht wechseln“**) |
| Stapel | `open_bulk_edit` (**„Massenbearbeitung“**), `prepare_bulk_delete` (**„Löschung vorbereiten“**) | | Stapel | `open_bulk_edit` (**„Massenbearbeitung“**), `prepare_bulk_delete` (**„Löschung vorbereiten“**) |
| Eintrag & Formular | `get_entry_context` (**„Eintrag-Kontext abrufen“**), `fill_field` (**„Feld ausfüllen“**), `fill_form` (**„Formular ausfüllen“**), `focus_field` (**„Feld fokussieren“**), `save_entry` (**„Eintrag speichern“**), `cancel_entry` (**„Bearbeitung abbrechen“**), `create_entry` (**„Neuen Eintrag anlegen“**), `duplicate_entry` (**„Eintrag duplizieren“**) | | Eintrag & Formular | `get_entry_context` (**„Eintrag-Kontext abrufen“**), `fill_field` (**„Feld ausfüllen“**), `fill_form` (**„Formular ausfüllen“**), `focus_field` (**„Feld fokussieren“**), `save_entry` (**„Eintrag speichern“**), `cancel_entry` (**„Bearbeitung abbrechen“**), `create_entry` (**„Neuen Eintrag anlegen“**), `duplicate_entry` (**„Eintrag duplizieren“**) |
| Array-Felder | `add_array_item` (**„Array-Element hinzufügen“**), `remove_array_item` (**„Array-Element entfernen“**), `duplicate_array_item` (**„Array-Element duplizieren“**), `reorder_array_items` (**„Array-Elemente sortieren“**), `clear_array` (**„Array leeren“**) | | Array-Felder | `add_array_item` (**„Array-Element hinzufügen“**), `remove_array_item` (**„Array-Element entfernen“**), `duplicate_array_item` (**„Array-Element duplizieren“**), `reorder_array_items` (**„Array-Elemente sortieren“**), `clear_array` (**„Array leeren“**) |
@@ -129,6 +136,6 @@ Wichtig zur Einordnung: Der Admin-Chat läuft über den Proxy (`/api/ai/chat`, `
| **„Transkription fehlgeschlagen: {error}“** | Rate-Limit, Netz oder Proxy-Fehler | Kürzeren Abschnitt sprechen, erneut versuchen | | **„Transkription fehlgeschlagen: {error}“** | Rate-Limit, Netz oder Proxy-Fehler | Kürzeren Abschnitt sprechen, erneut versuchen |
| Stimme klingt falsch | Andere Stimme aktiv | Stimme in den KI-Einstellungen zurück auf `eva-de` setzen | | Stimme klingt falsch | Andere Stimme aktiv | Stimme in den KI-Einstellungen zurück auf `eva-de` setzen |
<!-- Screenshot-Platzhalter (T-SHOTS): geöffneter KI-Chat mit Werkzeugstatus und Mikro-Knopf. --> ![Geöffneter Nova-Chat im tibi-admin: Begrüßungsnachricht, Eingabefeld mit Mikrofon-Knopf und Sammlungsübersicht des Corporate-Projekts dahinter](../../../assets/admin/admin-ki-funktionen.webp)
Verifiziert am 2b091d89 2026-10-16 (tibi-admin-nova: AiChat/AiButton/AiConversationList/AiToolStatus/AiToolInspector/AiDebugContext, aiChatHistory/aiController/aiTools/aiTokenEstimator/aiVoiceInput/aiSummarizer/aiTTS/aiModelCatalog, config/ai-proxy.config.json, frontend/assets/system.config.json, de.json admin.ai + Befehle) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova: AiChat/AiButton/AiConversationList/AiToolStatus/AiToolInspector/AiDebugContext, aiChatHistory/aiController/aiTools/aiTokenEstimator/aiVoiceInput/aiSummarizer/aiTTS/aiModelCatalog, config/ai-proxy.config.json, frontend/assets/system.config.json, de.json admin.ai + Befehle)
+1 -1
View File
@@ -33,4 +33,4 @@ import { Card, CardGrid } from '@astrojs/starlight/components';
</Card> </Card>
</CardGrid> </CardGrid>
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova). Verifiziert am 2b091d8 2026-10-13 (tibi-admin-nova)
+3 -3
View File
@@ -3,7 +3,7 @@ title: 'Medien im Admin'
description: 'Raster, Upload-Warteschlange, Bildeditor, Vorschau aus Admin-Sicht.' description: 'Raster, Upload-Warteschlange, Bildeditor, Vorschau aus Admin-Sicht.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge } from '@astrojs/starlight/components'; import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Bilder, PDFs und andere Dateien verwaltest du im **tibi-admin** (Verwaltungs-Backend, Release Nova) in einer Medien-Sammlung — meist `medialib` mit `viewHint.media`. Dieselben Werkzeuge stecken auch in Datei-Feldern des [Eintrags-Editors](/admin/eintrags-editor/). Projekt-Grundlagen (Pfade, Größen, Formate) stehen unter [Dateien und Bilder](/projekt/dateien-bilder/). Bilder, PDFs und andere Dateien verwaltest du im **tibi-admin** (Verwaltungs-Backend, Release Nova) in einer Medien-Sammlung — meist `medialib` mit `viewHint.media`. Dieselben Werkzeuge stecken auch in Datei-Feldern des [Eintrags-Editors](/admin/eintrags-editor/). Projekt-Grundlagen (Pfade, Größen, Formate) stehen unter [Dateien und Bilder](/projekt/dateien-bilder/).
@@ -78,6 +78,6 @@ Bilder im Fließtext verwaltet das `richtext`-Widget des Editors (siehe [Eintrag
| Bild-Einbettung bleibt leer | Verweis ohne `lookup` gelesen oder Eintrag nicht öffentlich | `lookup` auf `medialib` prüfen, `active`-Status und Rechte unter [Rechte und Tokens](/projekt/rechte-tokens/) klären | | Bild-Einbettung bleibt leer | Verweis ohne `lookup` gelesen oder Eintrag nicht öffentlich | `lookup` auf `medialib` prüfen, `active`-Status und Rechte unter [Rechte und Tokens](/projekt/rechte-tokens/) klären |
| Editor-Änderung weg | Dialog ohne Übernehmen geschlossen | Erst im Bildeditor übernehmen, dann den Eintrag speichern | | Editor-Änderung weg | Dialog ohne Übernehmen geschlossen | Erst im Bildeditor übernehmen, dann den Eintrag speichern |
<!-- Screenshot-Platzhalter (T-SHOTS): Medienraster mit Warteschlange und Bildeditor-Dialog, Alt-Text deutsch, max. 200 KB unter src/assets/. --> ![Bildeditor im tibi-admin als Vollbild-Dialog: Werkzeugleiste mit Zuschnitt, Anpassung und Skalierung über dem geöffneten Foto, dahinter das abgedunkelte Medienraster](../../../assets/admin/admin-medien.webp)
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+21 -3
View File
@@ -3,7 +3,7 @@ title: 'Admin einrichten'
description: 'tibi-admin lokal starten und mit dem Server verbinden: Installieren, Docker-Stack, URLs, Mock-Modus.' description: 'tibi-admin lokal starten und mit dem Server verbinden: Installieren, Docker-Stack, URLs, Mock-Modus.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Du richtest das **tibi-admin** (Verwaltungs-Backend, Release Nova) lokal ein und verbindest es mit dem Tibi-Server. Code-Repo ist ausschließlich `tibi-admin-nova` — alte `tibi-admin`-Repos ignorieren. Stack: **Svelte 5**, **esbuild**-Build, **Tailwind 4**, **Playwright**-Tests (siehe `package.json`). Du richtest das **tibi-admin** (Verwaltungs-Backend, Release Nova) lokal ein und verbindest es mit dem Tibi-Server. Code-Repo ist ausschließlich `tibi-admin-nova` — alte `tibi-admin`-Repos ignorieren. Stack: **Svelte 5**, **esbuild**-Build, **Tailwind 4**, **Playwright**-Tests (siehe `package.json`).
@@ -70,6 +70,24 @@ Nützliche Ziele: `make docker-ps`, `make docker-logs`, `make docker-down`, `mak
- Laufzeitwerte liegen in `frontend/assets/system.config.json` (KI-Endpunkte, TTS-Stimmen); Proxy-Ziele und Modelle in `config/ai-proxy.config.json` — siehe [KI-Funktionen](/admin/ki-funktionen/). - Laufzeitwerte liegen in `frontend/assets/system.config.json` (KI-Endpunkte, TTS-Stimmen); Proxy-Ziele und Modelle in `config/ai-proxy.config.json` — siehe [KI-Funktionen](/admin/ki-funktionen/).
- Fehlerbilder bei falscher Verbindung: Login-Fehlerbox, Netzwerkfehler bei Listen/Actions — dann `API_BASE`, Server-Container (`make docker-ps`) und [Token-Köpfe](/reference/token-koepfe/) prüfen. - Fehlerbilder bei falscher Verbindung: Login-Fehlerbox, Netzwerkfehler bei Listen/Actions — dann `API_BASE`, Server-Container (`make docker-ps`) und [Token-Köpfe](/reference/token-koepfe/) prüfen.
## Runtime-System-Config
Die Datei `frontend/assets/system.config.json` steuert zur Laufzeit KI-Endpunkte, TTS-Stimmen und Engine-Modi.
Schema: `schemas/config/system-config.schema.json` (Pflichtblock `ai` mit `chat`, `summarizer`, `translation`, `vision`, `stt`, `tts`); geladen per `loadRuntimeSystemConfig` (`frontend/src/lib/runtime-system-config.ts:205-225`).
| Bereich | Felder | Engine-Modi |
|---|---|---|
| `ai.chat` | `enabled`, `endpoint` (`/api/ai/chat`), `modelsEndpoint` (`/api/ai/models`) | — |
| `ai.summarizer` | `endpoint` (teilt sich den Chat-Endpunkt) | — |
| `ai.translation` | `enabled`, `endpoint`, `systemPrompt` | — |
| `ai.vision` | `enabled`, `endpoint` (`/api/ai/vision`), `defaultPrompt`, `image` (`maxWidth`/`maxHeight`/`quality`) | — |
| `ai.stt` | `enabled`, `endpoint` (`/api/ai/transcribe`), `engine` | `auto` / `api` / `browser` / `none` |
| `ai.tts` | `enabled`, `endpoint` (`/api/ai/tts`), `engine`, `defaultVoice`, `voices`, `speed` (`min`/`max`/`step`/`default`) | `auto` / `api` / `browser` / `none` |
Stimmen (Schema-Enum): `eva-de` (Standard), `kerstin-de`, `ramona-de`; Tempo-Defaults `0.5–2.0`, Schritt `0.1`, Standard `1.3`. Beleg: `frontend/assets/system.config.json:35-39` (`defaultVoice`, `voices`), `schemas/config/system-config.schema.json:20-26` (`ttsVoice`-Enum), Anzeige-Labels `frontend/src/lib/i18n/locales/de.json:1130-1136` (`ttsVoice`, `ttsSourceAi`).
Fehlt die Datei oder ist sie ungültig, fällt der Admin still auf die eingebauten Defaults zurück (`console.warn`, `runtime-system-config.ts:219-222`) — die Werte entsprechen der ausgelieferten `system.config.json`. Eigene Werte werden tief mit den Defaults verschmolzen (`deepMerge`) und danach sanitiert (unbekannte Engine → `auto`, leere Stimmenliste → Defaults, TTS-Tempo auf `min`/`max` geklemmt).
## Prüfen ## Prüfen
```sh title="Build und Tests" ```sh title="Build und Tests"
@@ -80,6 +98,6 @@ yarn test:e2e # Playwright E2E
<Aside type="tip">Läuft `yarn build` grün und zeigt der Admin nach Login die Projektliste, ist das Setup fertig. Weiter geht es mit [Admin anpassen](/admin/customizing/).</Aside> <Aside type="tip">Läuft `yarn build` grün und zeigt der Admin nach Login die Projektliste, ist das Setup fertig. Weiter geht es mit [Admin anpassen](/admin/customizing/).</Aside>
<!-- Screenshot-Platzhalter (T-SHOTS): frisch gestarteter Admin mit Projektliste nach Login. --> ![Projektliste im tibi-admin nach dem Login: Karten für Blog Platform, Corporate Website, Event Portal, Intranet, Online Shop und Test Core mit Online-Status](../../../assets/admin/admin-setup.webp)
Verifiziert am 2b091d899590b392491c59a54f50de43f1057280 2026-10-16 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova); Runtime-System-Config verifiziert am 2b091d8 (runtime-system-config.ts:205-222, frontend/assets/system.config.json:35-39, schemas/config/system-config.schema.json:20-26)
+61
View File
@@ -0,0 +1,61 @@
---
title: 'Aktionen'
description: 'Aktionen starten: Eingaben ausfüllen, ausführen, Ergebnis lesen.'
---
import { Aside, Steps } from '@astrojs/starlight/components';
**Aktionen** sind Knöpfe deiner Redaktion für wiederkehrende Aufgaben — zum Beispiel einen Newsletter versenden oder Preise neu berechnen. Du füllst die **Eingaben** aus, klickst auf **Ausführen** und liest danach das Ergebnis ab.
![Geöffnete Aktion „Suchindex neu aufbauen" mit Eingabeformular und Ausführen-Knopf](../../../assets/anwender/aktionen-formular.webp)
## Aktion ausführen
<Steps>
1. Öffne das Projekt und wähle die Aktion aus der Liste.
2. Lies oben, was die Aktion tut (Abschnitt **Methoden**).
3. Fülle unter **Eingaben** alle benötigten Felder aus.
4. Korrigiere markierte Felder, solange die Meldung **Bitte korrigiere {count} Feldfehler vor dem Ausfuehren.** steht.
5. Klicke auf **Ausführen**.
6. Warte, bis statt **Läuft…** wieder **Ausführen** dasteht.
</Steps>
<Aside type="note">
Steht statt **Ausführen** der Knopf **Gesperrt**, fehlt dir die Berechtigung: **Du hast aktuell keine erlaubte ausfuehrbare Methode fuer diese Aktion.** Bitte deine Teamleitung um Freischaltung.
</Aside>
![Abgeschlossene Aktion mit vollständigem Fortschrittsbalken und Anzeige „100% abgeschlossen"](../../../assets/anwender/aktionen-fortschritt.webp)
## Fortschritt und Ergebnis lesen
Während der **Ausführung** zeigt der Bereich **Fortschritt** einen Balken mit **{percent}% abgeschlossen** oder einer Zählung wie **{index} von {count}**. Danach steht das Ergebnis unter **Letztes Ergebnis** mit **Zusammenfassung** und **Details**.
<Steps>
1. Beobachte den Balken unter **Fortschritt**, bis der Lauf fertig ist.
2. Lies unter **Letztes Ergebnis** die **Zusammenfassung**.
3. Öffne bei Bedarf die **Details** für die vollständige Ausgabe.
4. Bei **Aktion erfolgreich ausgeführt.** ist alles erledigt.
</Steps>
Ein gestarteter Lauf lässt sich nicht abbrechen — plane die **Eingaben** daher vorher und starte erst, wenn alle Felder stimmen.
## Wenn etwas schiefgeht
<Steps>
1. Bei **Aktion konnte nicht ausgeführt werden.** lies zuerst die Meldung unter **Letztes Ergebnis**.
2. Prüfe deine **Eingaben** auf Tippfehler und unvollständige Felder.
3. Korrigiere sie und klicke erneut auf **Ausführen**.
4. Bleibt der Fehler bestehen, notiere Aktion, Uhrzeit und Fehlermeldung und bitte deine Betreuung um Prüfung.
</Steps>
<Aside type="caution">
Starte eine Aktion bei unklarer Fehlermeldung nicht wiederholt neu — manche Aktionen versenden oder berechnen bei jedem Lauf erneut. Kläre erst die Ursache.
</Aside>
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+1 -1
View File
@@ -70,4 +70,4 @@ Ungespeicherte Eingaben gehen beim automatischen Abmelden verloren. Speichere de
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
@@ -0,0 +1,63 @@
---
title: 'Backup einspielen'
description: 'Backup-Datei hochladen, Analyse lesen und Wiederherstellung starten.'
---
import { Aside, Steps } from '@astrojs/starlight/components';
Über **Backup & Wiederherstellung** spielst du eine Backup-Datei (ZIP) zurück ins Projekt. Du lädst die Datei hoch, liest das **Analyse-Ergebnis** und startest erst danach die **Wiederherstellung starten**.
![Backup-Ansicht mit den Reitern „Backup erstellen" und „Wiederherstellen"](../../../assets/anwender/backup-reiter.webp)
## Datei hochladen
<Steps>
1. Öffne den Bereich **Admin** und dort **Backup & Wiederherstellung**.
2. Wechsle auf den Reiter **Wiederherstellen**.
3. Lege die ZIP-Datei auf die Fläche **ZIP-Datei hier ablegen oder klicken zum Auswählen**.
4. Warte, bis **Backup wird hochgeladen…** und danach **Backup wird analysiert…** verschwunden sind.
</Steps>
<Aside type="note">
Ohne Administrator-Recht sperrt die Ansicht mit **Keine Administrator-Berechtigung für Backup-Funktionen**. Bitte den Betreiber, das Backup für dich einzuspielen.
</Aside>
## Analyse lesen
Das **Analyse-Ergebnis** nennt zuerst die Zahl der gefundenen Sammlungen (**{count} Collections gefunden**) sowie Quell-Projekt und Erstelldatum der Datei. Die Tabelle darunter listet pro **Collection** die Spalten **Dokumente**, **Medien** und **Hinweise**.
<Steps>
1. Prüfe Quell-Projekt und Erstelldatum — stimmt die Datei mit dem Stand überein, den du brauchst?
2. Lies pro Zeile **Dokumente** und **Medien**, um den Umfang einzuschätzen.
3. Beachte die **Hinweise**: Markierungen wie „nicht wiederherstellbar“ bedeuten, dass diese Sammlung übersprungen wird.
4. Wähle aus, welche Sammlungen zurückgespielt werden.
5. Entscheide über **Zusammenführen (Merge)**: eingeschaltet gilt **Zusammenführen – vorhandene Dokumente werden aktualisiert, neue hinzugefügt**; ausgeschaltet gilt **Ersetzen – bestehende Dokumente werden gelöscht und durch Backup ersetzt**.
</Steps>
<Aside type="caution">
Lies die Analyse vollständig, bevor du fortfährst. Bei ausgeschalteter Zusammenführung (**Ersetzen – bestehende Dokumente werden gelöscht und durch Backup ersetzt**) gehen aktuelle Inhalte verloren.
</Aside>
![Analyse-Ergebnis mit Collection-Tabelle und Zusammenführen-Option im Ersetzen-Modus](../../../assets/anwender/backup-analyse.webp)
## Wiederherstellung starten und prüfen
<Steps>
1. Klicke auf **Wiederherstellung starten** (mit Auswahl: **Wiederherstellung starten ({n} Collections)**).
2. Verfolge den Fortschritt (**Stelle {name} wieder her…**) und warte auf **Wiederherstellung abgeschlossen**.
3. Lies die Zusammenfassung: **{docs} Dokumente in {n} Collections wiederhergestellt**, dazu ggf. **{n} Mediendateien kopiert**.
4. Beachte übersprungene Sammlungen (**{n} Collections übersprungen (existieren nicht im Ziel)**).
5. Öffne danach die betroffenen Sammlungen und prüfe Stichproben.
</Steps>
<Aside type="caution">
Das Einspielen überschreibt Daten im Zielprojekt. Spiele nur Dateien ein, deren Herkunft du kennst, und stimme dich bei geteilten Projekten vorher mit dem Team ab.
</Aside>
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+1 -1
View File
@@ -65,4 +65,4 @@ Meldet sich der Support mit deinem Konto an, um dir zu helfen (Stellvertretung
Was du über das Verwaltungs-Backend wissen musst, steht unter [Admin-Konzept](/admin/konzept/). Was du über das Verwaltungs-Backend wissen musst, steht unter [Admin-Konzept](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
@@ -85,4 +85,4 @@ Schlägt ein Verweis fehl, ist häufig der Filter im Fenster **Durchsuchen** zu
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+54 -10
View File
@@ -3,7 +3,7 @@ title: 'Exportieren'
description: 'Listen als CSV, XLSX oder JSON exportieren.' description: 'Listen als CSV, XLSX oder JSON exportieren.'
--- ---
import { Aside, Steps } from '@astrojs/starlight/components'; import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Du lädst eine Liste aus dem **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**), als Datei herunter. Exportiert wird genau das, was du in der Liste siehst — Filter gelten auch für die Datei. Du lädst eine Liste aus dem **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**), als Datei herunter. Exportiert wird genau das, was du in der Liste siehst — Filter gelten auch für die Datei.
@@ -27,20 +27,64 @@ Du lädst eine Liste aus dem **tibi-admin**, dem Verwaltungs-Backend (Release **
1. Klicke auf ein Format (CSV, XLSX oder JSON). 1. Klicke auf ein Format (CSV, XLSX oder JSON).
2. Wähle die benötigten Felder (**Nur sichtbare** oder **Alle auswählen**). 2. Wähle die benötigten Felder (**Nur sichtbare** oder **Alle auswählen**).
3. Lege bei Arrays die Array-Behandlung fest (**JSON** oder **Dynamische Spalten**). 3. Klicke auf **Exportieren**.
4. Klicke auf **Exportieren**. 4. Warte, bis der Fortschritt abgeschlossen ist.
5. Warte, bis der Fortschritt abgeschlossen ist. 5. Öffne die Datei auf deinem Rechner.
6. Öffne die Datei auf deinem Rechner.
</Steps>
<Tabs>
<TabItem label="CSV">
Einfache Tabelle für Kalkulation und Skripte.
<Steps>
1. Wähle **CSV** als Format.
2. Lege bei Aufzählungen (Arrays) die Behandlung fest: **JSON** schreibt die Liste in eine Zelle, **Dynamische Spalten** fächert sie auf.
3. Klicke auf **Exportieren**.
</Steps>
<Aside type="caution">
Erscheint die Warnung **Viele Spalten** (Feld mit unterschiedlich langen Aufzählungen), wechsle über **Auf JSON umschalten** zum JSON-Format oder wähle weniger Spalten. Bei Abbruch bleibt der Dialog offen; es geht nichts verloren.
</Aside>
</TabItem>
<TabItem label="XLSX">
Aufbereitete Tabelle zum Weitergeben und Ausdrucken.
<Steps>
1. Wähle **XLSX** als Format.
2. Lege bei Aufzählungen die Behandlung fest (**JSON** oder **Dynamische Spalten**).
3. Klicke auf **Exportieren**.
</Steps>
<Aside type="caution">
Auch hier kann die Warnung **Viele Spalten** erscheinen — wechsle dann über **Auf JSON umschalten** oder wähle weniger Spalten.
</Aside>
</TabItem>
<TabItem label="JSON">
Verlustfreier Austausch mit anderen Programmen, Array-Behandlung **JSON**.
<Steps>
1. Wähle **JSON** als Format.
2. Klicke auf **Exportieren**.
</Steps> </Steps>
<Aside type="note"> <Aside type="note">
CSV eignet sich für einfache Tabellen, XLSX für aufbereitete Tabellen, JSON für den verlustfreien Austausch mit anderen Programmen (Array-Behandlung **JSON**). JSON behält auch ungleich lange Aufzählungen ohne Zusatzspalten — daher die Empfehlung, wenn die Tabellenformate warnen.
</Aside> </Aside>
<Aside type="caution"> </TabItem>
Erscheint die Warnung **Viele Spalten** (Feld mit unterschiedlich langen Aufzählungen), wechsle auf **JSON** (**Auf JSON umschalten**) oder wähle weniger Spalten. Bei Abbruch bleibt der Dialog offen; es geht nichts verloren. </Tabs>
</Aside>
## Datei prüfen ## Datei prüfen
@@ -62,4 +106,4 @@ Bei sehr großen Listen warnt dich tibi-admin vorab (**Große Sammlung**). Wiede
Wie du Listen filterst, steht unter [Lesen, Suchen, Filtern](/anwender/lesen-suchen-filtern/). Wie du Listen filterst, steht unter [Lesen, Suchen, Filtern](/anwender/lesen-suchen-filtern/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+7 -48
View File
@@ -5,22 +5,11 @@ description: 'Häufige Fragen und was du dann tust.'
import { Aside, Steps } from '@astrojs/starlight/components'; import { Aside, Steps } from '@astrojs/starlight/components';
Diese Seite fasst die häufigsten Probleme im **tibi-admin** (Release **Nova**) und ihre Lösung zusammen. Gehe die Punkte der Reihe nach durch. Diese Seite hilft dir einzuordnen, wo ein Problem liegt — die ausführliche Anleitung steht jeweils auf der verlinkten Seite.
## Anmeldung schlägt fehl ## Anmeldung schlägt fehl
<Steps> Prüfe Benutzername und Passwort Zeichen für Zeichen (Feststelltaste beachten) und klicke erneut auf **Anmelden**. Nach höchstens drei Versuchen bitte deine Betreuung um ein neues Passwort, statt weiter zu raten. Die genaue Abfolge steht unter [Anmelden](/anwender/anmelden/).
1. Prüfe die Adresse im Browser (Staging und Live nicht verwechseln).
2. Gib Benutzername und Passwort erneut ein.
3. Klicke auf **Anmelden**.
4. Warte die Rückmeldung ab.
</Steps>
<Aside type="note">
„Ungültiger Benutzername oder Passwort" bedeutet: Name oder Passwort stimmen nicht. Das System nennt aus Sicherheitsgründen nicht, welches von beiden. Details stehen unter [Anmelden](/anwender/anmelden/).
</Aside>
<Aside type="caution"> <Aside type="caution">
Lautet die Meldung „Keine Berechtigung für diese Aktion", fehlt dir das Recht für dieses Projekt. Bitte deine Teamleitung um Freischaltung. Lautet die Meldung „Keine Berechtigung für diese Aktion", fehlt dir das Recht für dieses Projekt. Bitte deine Teamleitung um Freischaltung.
@@ -28,49 +17,19 @@ Lautet die Meldung „Keine Berechtigung für diese Aktion", fehlt dir das Recht
## Eintrag wird nicht gefunden ## Eintrag wird nicht gefunden
<Steps> Häufige Ursachen: falsche Sammlung, aktiver Filter oder Tippfehler. Setze alle Filter zurück (**Alle zurücksetzen**) und prüfe die Sammlung in der linken Liste. Wie Suche, Filter und Sortierung zusammenspielen, steht unter [Lesen, Suchen, Filtern](/anwender/lesen-suchen-filtern/).
1. Drücke „/" und gib einen kurzen Begriff in der Schnellsuche ein.
2. Wechsle in die passende Sammlung in der linken Liste.
3. Setze alle Filter zurück (**Alle zurücksetzen**).
4. Blättere eine Seite weiter.
</Steps>
<Aside type="note">
Häufige Ursachen: falsche Sammlung, aktiver Filter oder Tippfehler. Suchtipps stehen unter [Lesen, Suchen, Filtern](/anwender/lesen-suchen-filtern/).
</Aside>
## Speichern ist blockiert ## Speichern ist blockiert
<Steps> Rot markierte Felder bedeuten: Eine Pflichtangabe (Feld mit Stern) fehlt oder ist ungültig — fülle sie und klicke erneut auf **Speichern**. Details zu Pflichtfeldern und Formaten stehen unter [Einträge anlegen und bearbeiten](/anwender/eintrag-bearbeiten/).
1. Lies die rote Meldung am Feld.
2. Fülle das markierte Pflichtfeld aus.
3. Kürze zu lange Texte.
4. Klicke erneut auf **Speichern**.
</Steps>
<Aside type="note"> <Aside type="note">
„Dieses Feld ist erforderlich" bedeutet: Ein Feld mit Stern darf nicht leer bleiben. „Eintrag nicht gefunden" bedeutet: Der Eintrag wurde gelöscht oder du bist in der falschen Sammlung — lege ihn nicht doppelt an, sondern bitte deine Teamleitung um Prüfung. „Eintrag nicht gefunden" bedeutet: Der Eintrag wurde gelöscht oder du bist in der falschen Sammlung — lege ihn nicht doppelt an, sondern bitte deine Teamleitung um Prüfung.
</Aside> </Aside>
## Bild fehlt ## Bild fehlt
<Steps> Öffne den Eintrag, wähle das Bild im Bildfeld erneut aus der Mediathek und klicke auf **Speichern**. Bleibt es danach leer, bitte deine Betreuung um Prüfung der Freigabe. Hochladen und Bildeditor stehen unter [Medien](/anwender/medien/).
1. Öffne den Eintrag mit dem Bild.
2. Klicke das Bildfeld an.
3. Prüfe, ob dort ein Bild gewählt ist.
4. Wähle das Bild erneut aus der Mediathek.
5. Klicke auf **Speichern**.
</Steps>
<Aside type="note">
Bleibt das Bild nach dem Speichern leer, bitte deine Betreuung um Prüfung der Freigabe. Mehr dazu unter [Medien](/anwender/medien/).
</Aside>
## An wen du dich wendest ## An wen du dich wendest
@@ -88,4 +47,4 @@ Bleibt das Bild nach dem Speichern leer, bitte deine Betreuung um Prüfung der F
Ist niemand erreichbar, fertige einen Screenshot der Meldung an, notiere Uhrzeit und Projekt und sende alles per Nachricht weiter. Ist niemand erreichbar, fertige einen Screenshot der Meldung an, notiere Uhrzeit und Projekt und sende alles per Nachricht weiter.
</Aside> </Aside>
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+11 -2
View File
@@ -3,7 +3,7 @@ title: 'KI nutzen'
description: 'Nova im Admin: chatten, zusammenfassen, diktieren und vorlesen lassen.' description: 'Nova im Admin: chatten, zusammenfassen, diktieren und vorlesen lassen.'
--- ---
import { Aside, Steps } from '@astrojs/starlight/components'; import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Die KI **Nova** im **tibi-admin** (Release **Nova**) hilft dir beim Redigieren: Sie beantwortet Fragen zur geöffneten Ansicht, fasst lange Gespräche zusammen und versteht gesprochene Eingaben. So nutzt du sie. Die KI **Nova** im **tibi-admin** (Release **Nova**) hilft dir beim Redigieren: Sie beantwortet Fragen zur geöffneten Ansicht, fasst lange Gespräche zusammen und versteht gesprochene Eingaben. So nutzt du sie.
@@ -57,6 +57,9 @@ Geht dir der Zusammenhang verloren, beginne über **Neuer Chat** von vorn und st
## Sprechen statt tippen ## Sprechen statt tippen
<Tabs>
<TabItem label="Eingabe">
<Steps> <Steps>
1. Klicke auf **Spracheingabe** und sprich deine Frage aus. 1. Klicke auf **Spracheingabe** und sprich deine Frage aus.
@@ -66,6 +69,9 @@ Geht dir der Zusammenhang verloren, beginne über **Neuer Chat** von vorn und st
</Steps> </Steps>
</TabItem>
<TabItem label="Ausgabe">
<Steps> <Steps>
1. Lasse dir eine Antwort mit **Vorlesen** vortragen (**Audio wird geladen…** während des Ladens). 1. Lasse dir eine Antwort mit **Vorlesen** vortragen (**Audio wird geladen…** während des Ladens).
@@ -74,6 +80,9 @@ Geht dir der Zusammenhang verloren, beginne über **Neuer Chat** von vorn und st
</Steps> </Steps>
</TabItem>
</Tabs>
<Aside type="note"> <Aside type="note">
Für die Spracheingabe benötigt der Browser Zugriff auf dein Mikrofon. Verweigert er ihn, erlaube ihn in den Browser-Einstellungen. Für die Spracheingabe benötigt der Browser Zugriff auf dein Mikrofon. Verweigert er ihn, erlaube ihn in den Browser-Einstellungen.
</Aside> </Aside>
@@ -100,6 +109,6 @@ Nova kennt nur, was im Admin geöffnet ist, und kann irren. Prüfe wichtige Anga
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Kommt die KI nicht weiter, hilft [Hilfe, wenn es klemmt](/anwender/hilfe/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Kommt die KI nicht weiter, hilft [Hilfe, wenn es klemmt](/anwender/hilfe/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
Screenshots: Mock-Projekt Corporate Website; KI-Antworten und Statusanzeigen simuliert, kein echter Dienst. Die Zusammenfassung lief ohne sichtbares Banner durch — der Shot zeigt den zugehörigen Token-Status. Screenshots: Mock-Projekt Corporate Website; KI-Antworten und Statusanzeigen simuliert, kein echter Dienst. Die Zusammenfassung lief ohne sichtbares Banner durch — der Shot zeigt den zugehörigen Token-Status.
@@ -95,9 +95,9 @@ Bei vielen Einträgen erscheinen unten Seitenzahlen.
</Steps> </Steps>
<Aside type="tip"> <Aside type="tip">
Suche und Seitenposition bleiben erhalten, wenn du zurückkehrst. Weiter geht es mit [Einträge anlegen und bearbeiten](/anwender/eintrag-bearbeiten/). Suche und Seitenposition bleiben erhalten, wenn du zurückkehrst. Weiter geht es mit [Einträge anlegen und bearbeiten](/anwender/eintrag-bearbeiten/); viele Einträge gleichzeitig änderst du über die [Massenbearbeitung](/anwender/massenbearbeitung/).
</Aside> </Aside>
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
@@ -0,0 +1,67 @@
---
title: 'Massenbearbeitung'
description: 'Viele Einträge auf einmal ändern: auswählen, Operationen anlegen, anwenden.'
---
import { Aside, Steps } from '@astrojs/starlight/components';
Die **Massenbearbeitung** ändert viele Einträge in einem Durchgang — zum Beispiel allen Artikeln einer Kategorie denselben Status geben. Du wählst Einträge aus, legst **Operationen** fest, prüfst die **Vorschau** und wendest sie mit **Änderungen anwenden** an.
![Journal-Liste mit zwei ausgewählten Einträgen und geöffnetem Massenbearbeitung-Dialog](../../../assets/anwender/massenbearbeitung-auswahl.webp)
## Einträge auswählen und öffnen
<Steps>
1. Öffne die Sammlung mit den Einträgen.
2. Markiere die Einträge einzeln, oder nutze **Alle auswählen** für die ganze Seite.
3. Klicke auf **Bearbeiten** (Taste `e` geht auch).
4. Der Dialog zeigt oben **Massenoperationen** und wie viele Einträge betroffen sind (**{count} ausgewählt**).
</Steps>
<Aside type="note">
Ohne Auswahl betrifft die Aktion alle Einträge der Sammlung. Brich in dem Fall lieber mit **Abbrechen** ab und wähle zuerst gezielt aus.
</Aside>
## Ablauf: Bedingungen und Operationen
<Steps>
1. Wechsle bei Bedarf oben zwischen **Bearbeiten** und **Löschen** — standardmäßig steht **Bearbeiten**.
2. Schränke unter **Bedingungen** die Auswahl weiter ein (**Bedingung hinzufügen**, z. B. Status **Gleich** „Entwurf“).
3. Lege unter **Operationen** fest, was passieren soll (**Operation hinzufügen**, Feld wählen und Aktion wie **Setzen auf** oder **Suchen & Ersetzen** bestimmen).
4. Öffne die **Vorschau** und lies, wie viele der ausgewählten Einträge betroffen sind.
5. Klicke auf **Änderungen anwenden** und bestätige die Nachfrage.
6. Warte die Meldung ab: **{count} Einträge aktualisiert** bedeutet Erfolg.
</Steps>
Die Nachfrage warnt ehrlich: **{count} Einträge werden aktualisiert. Diese Aktion kann nicht rückgängig gemacht werden.** Lies sie, bevor du bestätigst.
![Massenbearbeitung-Dialog mit einer Bedingung, zwei Operationen und geöffneter Vorschau](../../../assets/anwender/massenbearbeitung-vorschau.webp)
## Welche Felder gehen — und welche nicht
Nicht jedes Feld steht zur Wahl. Zur Auswahl stehen einfache Felder wie Text, Zahl, Datum, Auswahl und Schalter — jeweils mit passenden Aktionen wie **Setzen auf**, **Erhöhen um**, **Verringern um**, **Suchen & Ersetzen**, **Hinzufügen**, **Entfernen** und **Umschalten**.
Ausgenommen sind:
- Systemfelder (interne Felder, deren Name mit Unterstrich beginnt),
- formatierte Langtexte (Richtext-Felder),
- Datei-Felder (Bilder und Dateien lädst du pro Eintrag hoch),
- zusammengesetzte Felder ohne Unterfelder.
Steht ein Feld nicht unter **Feld wählen…**, bearbeite die betroffenen Einträge einzeln.
<Aside type="caution">
Die Massenbearbeitung lässt sich nicht rückgängig machen. Prüfe die **Vorschau** und teste neue Operationen zuerst an wenigen Einträgen.
</Aside>
## Wenn nicht alles klappt
- Einzelne Einträge können scheitern (z. B. Pflichtfeld leer, keine Berechtigung). Dann meldet die Anzeige sinngemäß „{done} von {total} aktualisiert, {failed} fehlgeschlagen“ — notiere die Zahl und prüfe die übrigen Einträge einzeln.
- Steht dort **Füge mindestens eine Operation hinzu.**, hast du noch keine **Operation** angelegt.
- Zum Löschen vieler Einträge wechsele oben auf **Löschen** — dort heißt der Knopf **Löschen** statt **Änderungen anwenden**, und die Nachfrage lautet **Möchtest du {count} Einträge wirklich unwiderruflich löschen?**.
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+3 -3
View File
@@ -74,9 +74,9 @@ Ohne **OK** geht die Anpassung verloren. Klicke zuerst auf **OK** und speichere
## Direkt bearbeiten und KI-Beschreibung ## Direkt bearbeiten und KI-Beschreibung
In der Upload-Warteschlange („{done} von {total} hochgeladen") bearbeitest du Einträge vor dem Hochladen: **Bearbeiten & hochladen** öffnet den Eintrag im Dialog (**Medieneintrag bearbeiten**), **Schnell-Upload** lädt ohne Bearbeitung. Große Bilder verkleinert tibi-admin automatisch („Bild auf Breite×Höhe verkleinert"). In der Upload-Warteschlange („{done} von {total} hochgeladen") bearbeitest du Einträge vor dem Hochladen: **Bearbeiten & hochladen** öffnet den Eintrag im Dialog (**Medieneintrag bearbeiten**), **Schnell-Upload** lädt ohne Bearbeitung. Große Bilder verkleinert tibi-admin automatisch („Bild auf {width}×{height} verkleinert").
Fehlt die Bildbeschreibung, erstellt die KI sie selbst („KI beschreibt das Bild…" → „KI-Beschreibung bereit"). Bereits ausgefüllte Beschreibungen bleiben stehen. Bei Bedarf brichst du ab (**KI-Beschreibung abbrechen**) oder stößt sie erneut an. Fehlt die Bildbeschreibung, erstellt die KI sie selbst („KI beschreibt das Bild…" → „KI-Beschreibung bereit"). Bereits ausgefüllte Beschreibungen bleiben stehen. Bei Bedarf brichst du ab (**KI-Beschreibung abbrechen**) oder stößt sie über **Erneut versuchen** erneut an.
## Bild in einen Eintrag einbinden ## Bild in einen Eintrag einbinden
@@ -92,4 +92,4 @@ Fehlt die Bildbeschreibung, erstellt die KI sie selbst („KI beschreibt das Bil
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+8 -8
View File
@@ -5,7 +5,7 @@ description: 'Start, Navigation und Übersicht im tibi-admin.'
import { Aside, Steps } from '@astrojs/starlight/components'; import { Aside, Steps } from '@astrojs/starlight/components';
Nach dem [Anmelden](/anwender/anmelden/) landest du im **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**). Diese Seite beschreibt Projektübersicht, Navigation und Schnellsuche. Nach dem [Anmelden](/anwender/anmelden/) landest du im **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**). Diese Seite beschreibt Projektübersicht, Navigation und Befehlspalette.
## Projektübersicht ## Projektübersicht
@@ -55,23 +55,23 @@ Eine Sammlung ist eine Liste gleichartiger Inhalte, beispielsweise alle Seiten o
Wie du in Listen suchst und filterst, steht unter [Lesen, suchen, filtern](/anwender/lesen-suchen-filtern/). Wie du in Listen suchst und filterst, steht unter [Lesen, suchen, filtern](/anwender/lesen-suchen-filtern/).
## Schnellsuche ## Befehlspalette
Die Schnellsuche springt zu Zielen wie Sammlungen und Projekten. Inhalte suchst du in der jeweiligen Liste. Die **Befehlspalette** springt zu Zielen wie Sammlungen und Projekten — die Taste `/` öffnet sie. Inhalte suchst du in der jeweiligen Liste.
<Steps> <Steps>
1. Drücke die Taste „/" (Schrägstrich). 1. Drücke die Taste „/" (Schrägstrich), um die Befehlspalette zu öffnen.
2. Gib einen Suchbegriff ein, beispielsweise einen Titel. 2. Gib einen Suchbegriff ein, beispielsweise einen Titel.
3. Wähle einen Vorschlag, um dorthin zu springen. 3. Wähle einen Vorschlag, um dorthin zu springen.
</Steps> </Steps>
<Aside type="note"> <Aside type="note">
Öffnet sich das Fenster nicht, klicke einmal in die Seite und drücke „/" erneut. Im Text-Editor fügt Strg + K einen Link ein und öffnet keine Suche — die Schnellsuche reagiert ausschließlich auf „/". Öffnet sich die Befehlspalette nicht, klicke einmal in die Seite und drücke „/" erneut. Im Text-Editor fügt Strg + K einen Link ein und öffnet keine Befehlspalette — sie reagiert ausschließlich auf „/".
</Aside> </Aside>
![Geöffnete Schnellsuche mit Treffer Corporate Website öffnen](../../../assets/anwender/orientierung-schnellsuche.webp) ![Geöffnete Befehlspalette mit Treffer Corporate Website öffnen](../../../assets/anwender/orientierung-schnellsuche.webp)
## Ausgangspunkt wiederfinden ## Ausgangspunkt wiederfinden
@@ -83,6 +83,6 @@ Die Schnellsuche springt zu Zielen wie Sammlungen und Projekten. Inhalte suchst
</Steps> </Steps>
Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/). Für Tastengriffe im Alltag siehe [Tastenkürzel](/anwender/tastenkombinationen/). Mehr Details zur Technik stehen unter [Mehr Details](/admin/konzept/).
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+25 -5
View File
@@ -3,7 +3,7 @@ title: 'Seiten bauen'
description: 'Einfache Seiten mit dem Pagebuilder zusammenstellen.' description: 'Einfache Seiten mit dem Pagebuilder zusammenstellen.'
--- ---
import { Aside, Steps } from '@astrojs/starlight/components'; import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Du baust eine Seite im **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**). Die Seite erscheint erst auf der Projekt-Website, wenn du sie per **Live**-Schalter freigibst. Du baust eine Seite im **tibi-admin**, dem Verwaltungs-Backend (Release **Nova**). Die Seite erscheint erst auf der Projekt-Website, wenn du sie per **Live**-Schalter freigibst.
@@ -88,17 +88,37 @@ Das Block-Label mit Farbpunkt zeigt Typ und Nummer des Blocks, beispielsweise He
## Responsiv prüfen ## Responsiv prüfen
Unter der Vorschau steuert der Viewport-Umschalter die simulierte Seitenbreite. So prüfst du, wie der Block auf kleineren Geräten wirkt. Unter der Vorschau steuert der Viewport-Umschalter die simulierte Seitenbreite (`PagebuilderViewportControl`: drei Geräte-Symbole plus Slider für freie Breiten von 320 bis 1920 px). Die Namen **Desktop**, **Tablet** und **Mobil** mit Breite erscheinen beim Darüberfahren; die aktive Breite steht als px-Zahl in der Leiste.
<Tabs>
<TabItem label="Desktop">
Standard-Ansicht mit 1280 px Breite — so sieht die Seite auf großen Bildschirmen aus.
![Seite mit ausgefüllten Corporate-Blöcken im Pagebuilder in Desktop-Breite](../../../assets/anwender/seiten-bloecke.webp)
</TabItem>
<TabItem label="Tablet">
Simulierte Breite 768 px — prüfe, ob Text lesbar bleibt und Bilder nicht abgeschnitten sind.
![Overlay-Editor mit Tablet-Vorschau (768 px) des Hero-Blocks, Viewport-Umschalter unter der Vorschau](../../../assets/anwender/seiten-viewport-tablet.webp) ![Overlay-Editor mit Tablet-Vorschau (768 px) des Hero-Blocks, Viewport-Umschalter unter der Vorschau](../../../assets/anwender/seiten-viewport-tablet.webp)
</TabItem>
<TabItem label="Mobil">
Simulierte Breite 375 px — engste Ansicht, hier fallen Umbruch- und Schnittfehler zuerst auf.
![Overlay-Editor mit Mobil-Vorschau (375 px) des Hero-Blocks, Viewport-Umschalter unter der Vorschau](../../../assets/anwender/seiten-viewport-mobil.webp) ![Overlay-Editor mit Mobil-Vorschau (375 px) des Hero-Blocks, Viewport-Umschalter unter der Vorschau](../../../assets/anwender/seiten-viewport-mobil.webp)
</TabItem>
</Tabs>
<Steps> <Steps>
1. Öffne einen Block im Overlay-Editor. 1. Öffne einen Block im Overlay-Editor.
2. Klicke unter der Vorschau auf eines der drei Geräte-Symbole (Bildschirm, Tablet, Telefon — die Namen Desktop, Tablet und Mobil mit Breite erscheinen beim Darüberfahren; die aktive Breite steht als px-Zahl in der Leiste). 2. Klicke unter der Vorschau auf eines der drei Geräte-Symbole.
3. Ziehe bei Bedarf den Slider für eine freie Breite (320–1920 px). 3. Ziehe bei Bedarf den Slider für eine freie Breite.
4. Prüfe, ob Text lesbar bleibt und Bilder nicht abgeschnitten sind. 4. Prüfe, ob Text lesbar bleibt und Bilder nicht abgeschnitten sind.
5. Schließe mit **OK** und klicke auf **Speichern**, wenn alles stimmt. 5. Schließe mit **OK** und klicke auf **Speichern**, wenn alles stimmt.
@@ -126,4 +146,4 @@ Erst mit **Live** wird die Seite für Besucher sichtbar. Ohne diesen Schalter bl
Weiter geht es mit [Einträge anlegen und bearbeiten](/anwender/eintrag-bearbeiten/). Dort lernst du alle Felder kennen. Weiter geht es mit [Einträge anlegen und bearbeiten](/anwender/eintrag-bearbeiten/). Dort lernst du alle Felder kennen.
Geprüft an 2b091d89 (tibi-admin-nova) Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+3 -1
View File
@@ -64,4 +64,6 @@ Du spielst nichts selbst zurück. Eine Wiederherstellung (**Wiederherstellung st
Stoße keine Wiederherstellung ohne Auftrag an. Stoße keine Wiederherstellung ohne Auftrag an.
</Aside> </Aside>
Geprüft an 2b091d89 (tibi-admin-nova) Willst du eine bestehende Sicherungsdatei einlesen statt eine neue zu erstellen, siehe [Backup einspielen](/anwender/backup-einspielen/).
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
@@ -0,0 +1,52 @@
---
title: 'Tastenkombinationen'
description: 'Die wichtigsten Tasten- und Griffkürzel für den Arbeitsalltag.'
---
import { Aside, Steps } from '@astrojs/starlight/components';
Viele Klicks gehen auch per Tastatur — schneller und ohne Maus. Die Übersicht rufst du jederzeit mit `?` auf (**Tastenkürzel**, **Shortcuts anzeigen**). Es gilt: **Shortcuts funktionieren nur außerhalb von Eingabefeldern**.
![Geöffnete Tastenkürzel-Übersicht mit gruppierten Kürzeln für Liste, Einträge, Editor, Nova und Allgemein](../../../assets/anwender/tastenkombinationen-overlay.webp)
## Die wichtigsten Griffe
| Taste | Wirkung | Wo |
| ----- | ------- | -- |
| `/` | Befehlspalette öffnen (**Befehl eingeben…**) | überall |
| `?` | **Tastenkürzel**-Übersicht öffnen (**Shortcuts anzeigen**) | überall |
| `s` | **Suche fokussieren** (Einträge suchen) | Liste |
| `f` | **Filter öffnen** | Liste |
| `x` | **Filter zurücksetzen** | Liste |
| `v` | **Ansicht umschalten** (Tabelle/Karten) | Liste |
| `a` | **Alle auswählen / aufheben** | Liste |
| `e` | **Massenbearbeitung** öffnen | Liste |
| `n` | **Neues Element** anlegen | Liste |
| `↑` `↓` | **Einträge navigieren** | Liste |
| `Enter` | **Eintrag öffnen** | Liste |
| `[` `]` | **Vorherige Seite** / **Nächste Seite** | Liste |
| `Strg`+`S` | **Eintrag speichern** | Editor |
| `Strg`+`F` | **Feld suchen** | Editor |
| `g` dann `p` | **Zu Projekte** springen | überall |
| `c` | Assistenten ein-/ausblenden (**Nova ein-/ausblenden**) | überall |
## Befehlspalette nutzen
Die Palette ersetzt langes Suchen in Menüs: Taste `/` drücken (**Drücke / zum Öffnen**), Befehl tippen, mit `Enter` bestätigen.
<Steps>
1. Drücke `/` — die Palette mit **Befehl eingeben…** öffnet sich.
2. Tippe wenige Buchstaben, z. B. `mass` für die **Massenbearbeitung**.
3. Wähle mit `↑` `↓` und bestätige mit `Enter`.
4. Schließe die Palette ohne Aktion mit `Esc`.
</Steps>
<Aside type="note">
Manche Befehle stehen nur je nach Stelle zur Verfügung (z. B. **Massenbearbeitung** nur in einer Liste mit Auswahl). Steht ein Befehl nicht in der Palette, wechsle zuerst an den passenden Ort.
</Aside>
Die vollständige Liste aller Kürzel — inklusive seltener Griffe für Sortierung, Seitengröße und Assistent — steht in der Entwickler-Referenz unter [Tastatur-Kurzbefehle](/reference/tastatur-kurzbefehle/).
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
@@ -0,0 +1,76 @@
---
title: 'Verlauf wiederherstellen'
description: 'Änderungsverlauf lesen und einen früheren Stand zurückspielen.'
---
import { Aside, Steps } from '@astrojs/starlight/components';
Jeder gespeicherte Eintrag bekommt automatisch einen **Verlauf**: wer was wann geändert hat. Du siehst dort jede Änderung und spielst bei Bedarf einen früheren Stand zurück — Feld für Feld oder alles auf einmal.
![Verlaufsbereich in der Seitenleiste eines Eintrags mit einer Änderung von admin](../../../assets/anwender/verlauf-seitenleiste.webp)
## Verlauf in der Seitenleiste lesen
Öffne einen gespeicherten Eintrag. Unterhalb der Felder liegt der Bereich **Verlauf** — er erscheint nur, wenn bereits Änderungen gespeichert wurden.
<Steps>
1. Öffne einen Eintrag in einer Sammlung.
2. Scrolle in der Seitenleiste nach unten zum Bereich **Verlauf**.
3. Lies pro Zeile Zeitpunkt, Aktion (**erstellt**, **geändert** oder **gelöscht**) und wer die Änderung gemacht hat (**Benutzer** mit Name oder Kürzel sowie **Quelle**).
4. Klicke eine Zeile an, um die Unterschiede (**Diff anzeigen**) zu öffnen.
5. Reichen die letzten zehn Zeilen nicht, klicke auf **Ältere Einträge**.
</Steps>
Steht dort **Kein Verlauf für diesen Datensatz**, wurde der Eintrag noch nie geändert — dann gibt es nichts zurückzuspielen.
<Aside type="note">
Der Verlauf zeigt nur Einträge dieser Sammlung, die du lesen darfst. Fehlt der Bereich ganz, frage deine Teamleitung nach dem Leserecht.
</Aside>
## Unterschiede (Diff) lesen
Aufgeklappte Zeilen zeigen den Vergleich **Vorher** gegen **Nachher** — bei der Wiederherstellung zusätzlich **Aktuell** gegen **Danach**.
<Steps>
1. Klicke auf eine Verlaufszeile, um sie aufzuklappen.
2. Lies pro Feld die Markierung: **hinzugefügt**, **entfernt** oder **geändert**.
3. Vergleiche die Werte unter **Vorher** und **Nachher**.
4. Klappe weitere Zeilen auf, bis du den Stand gefunden hast, den du brauchst.
</Steps>
![Aufgeklappter Verlaufseintrag mit Vorher-Nachher-Vergleich des Seitentitels](../../../assets/anwender/verlauf-diff.webp)
## Stand zurückspielen
Zurückspielen trägt alte Werte ins Formular ein — gespeichert wird erst, wenn du danach auf **Speichern** klickst.
<Steps>
1. Öffne den **Verlauf** des Eintrags und klappe den gewünschten Stand auf.
2. Klicke auf **Wiederherstellen**.
3. Wähle unter **Felder zur Wiederherstellung** aus, was übernommen wird — mit **Alle auswählen** alles, mit **Keine** nichts, oder einzeln pro Feld.
4. Aktiviere bei Bedarf **Ganzes Array übernehmen**, um eine ganze Liste als Block zurückzuspielen.
5. Klicke auf **Wiederherstellen**, um die Werte ins Formular zu übernehmen.
6. Prüfe die Felder im Formular und klicke auf **Speichern**.
</Steps>
<Aside type="note">
Nach dem Übernehmen meldet die Anzeige **Felder übernommen — bitte speichern**. Ohne **Speichern** bleibt der Eintrag unverändert. Beachte außerdem: **Media-Dateien werden nicht wiederhergestellt** — Bilder und Dateien musst du bei Bedarf neu hochladen.
</Aside>
<Aside type="caution">
Zurückspielen überschreibt den aktuellen Stand. Prüfe im Diff genau, welche Felder du übernimmst — nach **Speichern** ist der alte Stand nur noch über einen neueren Verlaufseintrag erreichbar.
</Aside>
![Wiederherstellen-Dialog mit Feldauswahl und den Knöpfen „Alle auswählen" und „Keine"](../../../assets/anwender/verlauf-wiederherstellen.webp)
## Eigene Ansicht: Audit-Log
Für den Überblick über alle Sammlungen gibt es die Ansicht **Audit-Log** (**Alle Audit-Einträge anzeigen**). Dort filterst du nach **Alle Projekte**, **Alle Collections**, **Alle Aktionen** und **Alle Benutzer** und liest die Spalten **Zeit**, **Collection**, **Aktion**, **Benutzer** und **Quelle**. Steht dort **Keine Audit-Einträge** oder **Audit ist nicht aktiviert**, wende dich an den Betreiber.
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
+50
View File
@@ -17,3 +17,53 @@ description: Die wichtigsten Tibi-Begriffe in einem Glossar.
| **Tibi-Projekt** | Eigenständige Website auf Tibi-Basis aus `tibi-svelte-starter` — normale kleine Websites, Shops, Intranet-Anwendungen usw. Läuft getrennt vom Backend, bezieht Inhalte per API/SSR. | | **Tibi-Projekt** | Eigenständige Website auf Tibi-Basis aus `tibi-svelte-starter` — normale kleine Websites, Shops, Intranet-Anwendungen usw. Läuft getrennt vom Backend, bezieht Inhalte per API/SSR. |
| **Nova** | Release-Name des aktuellen tibi-admin (Repo `tibi-admin-nova`). | | **Nova** | Release-Name des aktuellen tibi-admin (Repo `tibi-admin-nova`). |
| **Mock-Modus** | tibi-admin läuft lokal gegen gemockte Daten statt gegen den echten Server. | | **Mock-Modus** | tibi-admin läuft lokal gegen gemockte Daten statt gegen den echten Server. |
## Namespace / Projekt
Ein Mandant im Tibi-Server: Der `namespace:` in der Projekt-YAML legt fest, zu welchem Projekt Daten gehören. Jeder Namespace nutzt eine eigene MongoDB-Datenbank (`DB_PREFIX_Namespace`).
## Collection
Eine Dokument-Sammlung mit Feldschema, Validatoren und Permissions — zum Beispiel `articles`. Definiert in `api/collections/*.yml`, geprüft gegen `collection.schema.json`.
## Dokument
Ein einzelner Datensatz in einer Collection (JSON, mit `_id`). Angelegt, gelesen, geändert und gelöscht über die Collection-Endpunkte.
## Hook
JavaScript-Lebenszyklus pro Collection und Route (`bind → validate → create/update/delete → return`). Hier steht eigene Logik — vom Validieren bis zum Rendern.
## Permission-Set
`public` / `user` / Rollen: welche Methoden (`get`/`post`/`put`/`delete`) mit welchem Vorfilter erlaubt sind. Details stehen in der Collection-YAML.
## PAT
Personal Access Token für API-Zugriff ohne Login. Immer aktiv, Scopes wie Admin-Tokens — es gilt zusätzlich das Besitzer-Recht (UND-Regel, siehe [Token-Köpfe](/reference/token-koepfe/)).
## SSE-Kanal
Server-Sent-Events-Kanal für Echtzeit: Pub/Sub mit Replay und TTL. Volle Kanäle verwerfen Alt oder Neu — langsame Leser blockieren den Server nicht.
## SSR-Hook
Server-Side-Rendering für Kunden-SPAs (`api/hooks/ssr/`): Der Server liefert fertiges HTML, der Cache liegt in der `ssr`-Collection.
## Tibi-Admin
Das Verwaltungs-Backend: Bedienoberfläche zum Tibi-Server (aktuelles Release Nova, Repo `tibi-admin-nova`). Hier pflegst du Inhalte, Medien, Benutzer und Exporte.
## Tibi-Projekt
Eigenständige Website auf Tibi-Basis aus `tibi-svelte-starter` — normale kleine Websites, Shops, Intranet-Anwendungen usw. Läuft getrennt vom Backend und bezieht Inhalte per API/SSR.
## Nova
Release-Name des aktuellen tibi-admin (Repo `tibi-admin-nova`).
## Mock-Modus
tibi-admin läuft lokal gegen gemockte Daten statt gegen den echten Server.
Verifiziert am 68b4278 2026-10-17 (tibi-server), am 2b091d8 2026-10-17 (tibi-admin-nova)
+18 -18
View File
@@ -3,7 +3,7 @@ title: Neues Projekt aufsetzen
description: Vom geklonten Starter zum erreichbaren Projekt — inklusive Registrierung und Abnahme. description: Vom geklonten Starter zum erreichbaren Projekt — inklusive Registrierung und Abnahme.
--- ---
import { FileTree } from '@astrojs/starlight/components'; import { Aside, FileTree } from '@astrojs/starlight/components';
Anleitung für **Projekt-Entwickler**: du klonst `tibi-svelte-starter` und baust daraus eine eigenständige Website auf Tibi-Basis — normale kleine Websites, Shops, Intranet-Anwendungen usw. Das Verwaltungs-Backend (Tibi-Server + tibi-admin) entwickelst du nicht weiter, du nutzt es. Anleitung für **Projekt-Entwickler**: du klonst `tibi-svelte-starter` und baust daraus eine eigenständige Website auf Tibi-Basis — normale kleine Websites, Shops, Intranet-Anwendungen usw. Das Verwaltungs-Backend (Tibi-Server + tibi-admin) entwickelst du nicht weiter, du nutzt es.
@@ -11,16 +11,14 @@ Verifiziert gegen: Starter-`README.md`, Skill `tibi-project-setup/SKILL.md`, `Ma
## Setup-Checkliste ## Setup-Checkliste
```text title="Setup-Checkliste" - [ ] Geklont, `origin` zeigt aufs Projekt-Repo (`template` heißt das Starter-Remote)
[ ] Geklont, `origin` zeigt aufs Projekt-Repo (`template` heißt das Starter-Remote) - [ ] `__PROJECT_NAME__` (kebab-case) und `__TIBI_NAMESPACE__` (snake_case) ersetzt
[ ] `__PROJECT_NAME__` (kebab-case) und `__TIBI_NAMESPACE__` (snake_case) ersetzt - [ ] `__ORG__` / `__PROJECT__` ersetzt, kein `__*__` mehr per `rg` findbar
[ ] `__ORG__` / `__PROJECT__` ersetzt, kein `__*__` mehr per `rg` findbar - [ ] `.env`-URLs gesetzt (`CODING_URL`, `CODING_TIBIADMIN_URL`, `STAGING_URL`, `LIVE_URL`)
[ ] `.env`-URLs gesetzt (`CODING_URL`, `CODING_TIBIADMIN_URL`, `STAGING_URL`, `LIVE_URL`) - [ ] `api/config.yml.env` mit frischem `ADMIN_TOKEN` + `ADMIN_ASSET_VERSION`
[ ] `api/config.yml.env` mit frischem `ADMIN_TOKEN` + `ADMIN_ASSET_VERSION` - [ ] `App.svelte` hat `<svelte:head>` mit `<title>`
[ ] `App.svelte` hat `<svelte:head>` mit `<title>` - [ ] `yarn install`, `make docker-up`, Website + Admin + API antworten
[ ] `yarn install`, `make docker-up`, Website + Admin + API antworten - [ ] `yarn validate`, `yarn build`, `yarn build:server` fehlerfrei
[ ] `yarn validate`, `yarn build`, `yarn build:server` fehlerfrei
```
## 1. Klonen und Remotes ## 1. Klonen und Remotes
@@ -58,29 +56,29 @@ Dateien auf Platte definieren das Projekt. Erst die **Registrierung** macht es d
## 5. Registrieren (wenn `project not found`) ## 5. Registrieren (wenn `project not found`)
Im lokalen Standard-Setup per `admin` / `admin` einloggen und JWT nutzen: Im lokalen Standard-Setup per `admin` / `admin` einloggen und JWT nutzen — der Admin-Host leitet `/api` an den tibi-server weiter, ein eigener Roh-Server-Host ist nicht nötig:
```sh title="JWT holen und Projekt registrieren" ```sh title="JWT holen und Projekt registrieren" {7}
jwt=$(curl -s -X POST "$CODING_TIBIADMIN_URL/api/login" \ jwt=$(curl -s -X POST "$CODING_TIBIADMIN_URL/api/login" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' | jq -r '.token') -d '{"username":"admin","password":"admin"}' | jq -r '.token')
curl -s -X POST "$CODING_TIBISERVER_URL/api/v1/project" \ curl -s -X POST "$CODING_TIBIADMIN_URL/api/project" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "X-Auth-Token: $jwt" \ -H "X-Auth-Token: $jwt" \
-d '{"name":"<PROJECT_NAME>","description":"...","configFile":"/data/api/config.yml","enabled":true}' -d '{"name":"<PROJECT_NAME>","description":"...","configFile":"/data/api/config.yml","enabled":true}'
``` ```
Danach erscheint das Projekt unter `GET /api/v1/project`, und `/_/<namespace>/...` löst auf. Nach Config-Änderungen neu laden: Danach erscheint das Projekt unter `GET /api/v1/project` am Server (über den Admin-Proxy: `GET /api/project`), und `/_/<namespace>/...` löst auf. Nach Config-Änderungen neu laden:
```sh title="Projekt-Reload anstoßen" ```sh title="Projekt-Reload anstoßen"
curl -s -X POST "$CODING_TIBISERVER_URL/api/v1/_/<PROJECT_NAME>/_/admin/reload" \ curl -s -X POST "$CODING_TIBIADMIN_URL/api/_/<PROJECT_NAME>/_/admin/reload" \
-H "X-Auth-Token: $jwt" -H "X-Auth-Token: $jwt"
``` ```
:::caution title="Token nicht verwechseln" <Aside type="caution" title="Token nicht verwechseln">
Verwechsle `ADMIN_TOKEN` aus `api/config.yml.env` nicht mit System-Rechten: er schreibt Sammlungen (Kopf `Token`), registriert aber keine Projekte. Deploy-Skripte nutzen ihn separat als `Authorization: Bearer` auf dem Projekt-Reload. Verwechsle `ADMIN_TOKEN` aus `api/config.yml.env` nicht mit System-Rechten: er schreibt Sammlungen (Kopf `Token`), registriert aber keine Projekte. Deploy-Skripte nutzen ihn separat als `Authorization: Bearer` auf dem Projekt-Reload.
::: </Aside>
## 6. Inhalte und URLs verstehen ## 6. Inhalte und URLs verstehen
@@ -113,3 +111,5 @@ curl -I "$CODING_TIBIADMIN_URL"
``` ```
Anonyme SSR-Seite prüfen (Navigation vorhanden, Bilder mit `_lookup`), nicht nur Lesen mit Token. Erst dann ist das Projekt wirklich aufgesetzt. Anonyme SSR-Seite prüfen (Navigation vorhanden, Bilder mit `_lookup`), nicht nur Lesen mit Token. Erst dann ist das Projekt wirklich aufgesetzt.
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server)
+6 -2
View File
@@ -5,9 +5,11 @@ description: Eigenes Tibi-Projekt aus dem Starter klonen, starten und prüfen
Du entwickelst **Projekte mit Tibi**, nicht Tibi selbst. Go-Code vom tibi-server und Svelte-Code vom tibi-admin fasst du dafür nicht an. Dein Startpunkt ist immer der Starter. Du entwickelst **Projekte mit Tibi**, nicht Tibi selbst. Go-Code vom tibi-server und Svelte-Code vom tibi-admin fasst du dafür nicht an. Dein Startpunkt ist immer der Starter.
:::danger title="Kern-Regel aus dem Starter" import { Aside } from '@astrojs/starlight/components';
<Aside type="danger" title="Kern-Regel aus dem Starter">
Dev-Server laufen immer in Docker. Nie `yarn dev` oder `yarn start` lokal. Dev-Server laufen immer in Docker. Nie `yarn dev` oder `yarn start` lokal.
::: </Aside>
## Voraussetzungen ## Voraussetzungen
@@ -92,3 +94,5 @@ Alle drei müssen fehlerfrei durchlaufen. `yarn` nutzt du nur für diese drei
| Deploy-Reload auf Live/Staging | `Authorization: Bearer` | `ADMIN_TOKEN` | | Deploy-Reload auf Live/Staging | `Authorization: Bearer` | `ADMIN_TOKEN` |
Weiter: [Neues Projekt aufsetzen](/einstieg/neues-projekt/) mit Registrierung, Pfad A/B und Checkliste. Weiter: [Neues Projekt aufsetzen](/einstieg/neues-projekt/) mit Registrierung, Pfad A/B und Checkliste.
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server)
+5 -7
View File
@@ -3,7 +3,7 @@ title: Was ist Tibi?
description: Überblick über Tibi-Server, Tibi-Admin und Tibi-Projekte. description: Überblick über Tibi-Server, Tibi-Admin und Tibi-Projekte.
--- ---
import { Card, CardGrid } from '@astrojs/starlight/components'; import { CardGrid, LinkCard } from '@astrojs/starlight/components';
**Tibi** ist unser Baukasten aus Verwaltungs-Backend plus eigenständigen Projekt-Websites: **Tibi** ist unser Baukasten aus Verwaltungs-Backend plus eigenständigen Projekt-Websites:
@@ -27,10 +27,8 @@ Backend verwalten, Website bauen: als Projekt-Entwickler pflegst du Inhalte im A
## Wo geht es weiter? ## Wo geht es weiter?
<CardGrid stagger> <CardGrid stagger>
<Card title="Schnellstart"> <LinkCard title="Schnellstart" href="/einstieg/schnellstart/" description="Starter klonen, starten, prüfen." />
Starter klonen, starten, prüfen. [Loslegen](/einstieg/schnellstart/). <LinkCard title="Begriffe" href="/einstieg/begriffe/" description="Die wichtigsten Tibi-Begriffe im Glossar." />
</Card>
<Card title="Begriffe">
Die wichtigsten Tibi-Begriffe im Glossar. [Nachschlagen](/einstieg/begriffe/).
</Card>
</CardGrid> </CardGrid>
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova), am 68b4278 2026-10-17 (tibi-server)
+9 -25
View File
@@ -13,33 +13,17 @@ hero:
variant: minimal variant: minimal
--- ---
import { Card, CardGrid } from '@astrojs/starlight/components'; import { CardGrid, LinkCard } from '@astrojs/starlight/components';
## Wo finde ich was? ## Wo finde ich was?
<CardGrid stagger> <CardGrid stagger>
<Card title="Was ist Tibi?"> <LinkCard title="Was ist Tibi?" href="/einstieg/was-ist-tibi/" description="Backend plus eigenständige Projekt-Websites im Überblick." />
Backend plus eigenständige Projekt-Websites im Überblick. [Lesen](/einstieg/was-ist-tibi/). <LinkCard title="Schnellstart" href="/einstieg/schnellstart/" description="Starter klonen, starten, prüfen — in 6 Schritten." />
</Card> <LinkCard title="Neues Projekt" href="/einstieg/neues-projekt/" description="Vom Klon zum registrierten Projekt inkl. Checkliste." />
<Card title="Schnellstart"> <LinkCard title="Projekt bauen" href="/projekt/sammlungen-anlegen/" description="Sammlungen, Felder, Hooks, Suche, SSR, Pagebuilder, LLM, MCP, Deploy." />
Starter klonen, starten, prüfen — in 6 Schritten. [Starten](/einstieg/schnellstart/). <LinkCard title="Server betreiben" href="/server/uebersicht/" description="Konfiguration, Betrieb, Sicherheit, Schnittstellen." />
</Card> <LinkCard title="Admin bedienen" href="/admin/konzept/" description="Ansichten, Editor, Medien, Exporte, Aktionen, Rechte, KI." />
<Card title="Neues Projekt"> <LinkCard title="Anwender-Anleitung" href="/anwender/anmelden/" description="Anmelden bis Sichern — für Nicht-Profis mit Bildern." />
Vom Klon zum registrierten Projekt inkl. Checkliste. [Aufsetzen](/einstieg/neues-projekt/). <LinkCard title="Referenz" href="/reference/schemas/" description="Schemas, Hook-Kontexte, Token-Köpfe, Fehlerbilder, OpenAPI." />
</Card>
<Card title="Projekt bauen">
Sammlungen, Felder, Hooks, Suche, SSR, Pagebuilder, LLM, MCP, Deploy. [Bauen](/projekt/sammlungen-anlegen/).
</Card>
<Card title="Server betreiben">
Konfiguration, Betrieb, Sicherheit, Schnittstellen. [Betreiben](/server/uebersicht/).
</Card>
<Card title="Admin bedienen">
Ansichten, Editor, Medien, Exporte, Aktionen, Rechte, KI. [Verwalten](/admin/konzept/).
</Card>
<Card title="Anwender-Anleitung">
Anmelden bis Sichern — für Nicht-Profis mit Bildern. [Anleiten](/anwender/anmelden/).
</Card>
<Card title="Referenz">
Schemas, Hook-Kontexte, Token-Köpfe, Fehlerbilder, OpenAPI. [Nachschlagen](/reference/schemas/).
</Card>
</CardGrid> </CardGrid>
+36 -14
View File
@@ -31,7 +31,7 @@ Einzelner Eintrag per ID.
</Tabs> </Tabs>
<Aside type="caution"> <Aside type="caution">
`?count=1` liefert zusätzlich die Header `X-Results-Count` und — Achtung, Tippfehler im Code — `Content-Range:` **mit Doppelpunkt** (`collection_get.go`). `?count=1` liefert zusätzlich die Header `X-Results-Count` und — Abweichung im Code (mit Doppelpunkt) — `Content-Range:` (`collection_get.go`).
</Aside> </Aside>
## Filtern, Sortieren, Blättern ## Filtern, Sortieren, Blättern
@@ -87,9 +87,7 @@ GET …/content?lookup=blocks.heroImage.image:medialib,blocks.image:medialib
- Ziel ohne Leserecht oder unbekannte Sammlung → Fehler (`lookup permission denied`, Ziel nicht gefunden). - Ziel ohne Leserecht oder unbekannte Sammlung → Fehler (`lookup permission denied`, Ziel nicht gefunden).
<Aside type="caution"> <Aside type="caution">
Ein `lookup` löst nur auf, wenn der Zieldatensatz für dich lesbar ist. Ein `lookup` löst nur auf, wenn der Zieldatensatz für dich lesbar ist — dazu gehört `active: true` am Ziel (kanonische Erklärung unter [Rechte und Tokens](/projekt/rechte-tokens/)).
Fehlt dort `active:true`, bleibt `_lookup` öffentlich leer — Bilder und Navigation verschwinden.
Details: [Rechte und Tokens](/projekt/rechte-tokens/).
</Aside> </Aside>
## Facetten und Zählen (eigene Endpunkte!) ## Facetten und Zählen (eigene Endpunkte!)
@@ -109,9 +107,40 @@ Details: [Rechte und Tokens](/projekt/rechte-tokens/).
Die Kommentar-Anzahl je Seite (`commentCount`) kommt aus so einer Abfrage über `contentId`. Die Kommentar-Anzahl je Seite (`commentCount`) kommt aus so einer Abfrage über `contentId`.
## Aggregate: Rückwärts zählen und rechnen (Listen-Route!)
`aggregate` hängt an der normalen `GET`-Liste — **kein** eigener Endpunkt wie `_count`/`_facet`.
Es zählt oder verrechnet Dokumente einer **anderen** Sammlung, die auf deine Listeneinträge zeigen, und legt das Ergebnis unter `_aggregate` ab.
```text title="Kommentar-Anzahl je Content (Rückwärtszählung)"
GET …/content?aggregate=comment:contentId:count:commentCount
# Antwort je Eintrag: {"_id": "…", "name": "…", "_aggregate": {"commentCount": 3}}
```
| Baustein (`ziel:feld:op:wert:als`) | Bedeutung | Beispiel |
|---|---|---|
| `ziel` (Pflicht) | Ziel-Sammlung, in der gezählt wird | `comment` |
| `feld` (Pflicht) | Feld dort, das auf deinen Eintrag zeigt | `contentId` (dein `_id` ist die Quelle) |
| `op` | `count` (Standard), `exists`, `sum`, `avg`, `min`, `max` | `count` |
| `wert` | Nur für `sum`/`avg`/`min`/`max` Pflicht: Zahlenfeld im Ziel (`number`, `float`, `int`, `any`) | `price` |
| `als` | Ergebnis-Schlüssel unter `_aggregate`; ohne Angabe erzeugt der Server einen (`commentCount`, `commentPriceSum`, …) | `commentCount` |
Formate: Kurzform mit Doppelpunkten, mehrere per Komma (`?aggregate=a:x:count,b:y:count`), JSON-Objekt/Array, wiederholtes `?aggregate=` oder `?aggregate[]=`.
Nur lesend: Quell-Feld, Ziel-Feld (`foreignField`), `valueField` und Filter-Felder müssen für deine Rolle lesbar sein — sonst `400`.
Fehlt dir das `get`-Recht auf der Ziel-Sammlung, kommt `403` (`aggregate permission denied`).
Unbekannte Sammlung, unbekanntes Feld oder unbekannte Op → `400`; mehrere Specs mit gleichem Ergebnis-Schlüssel → `400` (`aggregate result key conflict`).
Leere Treffer haben feste Werte: `count` → `0`, `exists` → `false`, `sum` → `0`, `avg`/`min`/`max` → `null`.
<Aside type="note">
`aggregate` löst **Rückwärts**-Beziehungen (viele zeigen auf einen) — für **Vorwärts**-Verweise (einer zeigt auf einen) nimmst du `lookup` weiter oben.
Beides lässt sich in einer Abfrage kombinieren.
</Aside>
## Suche ## Suche
`q` sucht, `qName` wählt den Suchindex aus dem `search`-Block der Sammlung. `q` sucht, `qName` wählt den Suchindex aus dem `search`-Block der Sammlung.
Die vollständige Index-Tabelle (Modi, Gewichte, Aufbau) steht unter [Suche](/projekt/suche/) — hier nur die Abfrage:
```text title="Suche mit q und qName" ```text title="Suche mit q und qName"
# kombinierte Suche in content (RRF aus semantisch + fuzzy) # kombinierte Suche in content (RRF aus semantisch + fuzzy)
@@ -121,13 +150,6 @@ GET …/content?q=kontakt&qName=combined
GET …/tag?q=hero GET …/tag?q=hero
``` ```
| `qName` | Sammlung | Modus |
|---|---|---|
| `combined` | `content` | Hybrid (RRF, `k: 60`, `topK: 100`) |
| `fuzzy` | `content` | n-gram, fehlertolerant |
| `semantic` | `content` | Vektor (`bge_m3`) |
| `default` | `tag`, `navigation`, `comment`, `medialib` | regex |
`?qExplain=1` erklärt, wie die Suche gewichtet hat — nützlich beim Debuggen. `?qExplain=1` erklärt, wie die Suche gewichtet hat — nützlich beim Debuggen.
## Fehlerbilder ## Fehlerbilder
@@ -137,7 +159,7 @@ GET …/tag?q=hero
| `400` | Ungültiges `filter`-JSON, unbekanntes `select`-Feld, falsche `lookup`-Syntax | JSON prüfen, Feldnamen gegen die Sammlungs-YAML halten | | `400` | Ungültiges `filter`-JSON, unbekanntes `select`-Feld, falsche `lookup`-Syntax | JSON prüfen, Feldnamen gegen die Sammlungs-YAML halten |
| `permission denied` | Kein `get`-Recht für deine Rolle | [Rechte und Tokens](/projekt/rechte-tokens/) prüfen | | `permission denied` | Kein `get`-Recht für deine Rolle | [Rechte und Tokens](/projekt/rechte-tokens/) prüfen |
| `invalid projection` | `projection` heißt anders als in der YAML | Namen im `projections`-Block nachschlagen | | `invalid projection` | `projection` heißt anders als in der YAML | Namen im `projections`-Block nachschlagen |
| Leere Liste trotz Daten | `filter_public`-Hook filtert `active:true` + Zeitraum | `active:true` setzen, `publication`-Fenster prüfen | | Leere Liste trotz Daten | `filter_public`-Hook filtert `active:true` + Zeitraum ([Rechte und Tokens](/projekt/rechte-tokens/)) | `active:true` setzen, `publication`-Fenster prüfen |
| `_lookup` leer | Zieldatensatz nicht lesbar (meist `active:true` fehlt) | Zieldatensatz prüfen, nicht die Abfrage | | `_lookup` leer | Zieldatensatz nicht lesbar (meist `active:true` fehlt, siehe [Rechte und Tokens](/projekt/rechte-tokens/)) | Zieldatensatz prüfen, nicht die Abfrage |
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 68b42785 2026-10-15 (tibi-server, collection_get/select/sort/facet/count/lookup). Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server, collection_get/select/sort/facet/count/lookup), am 68b4278 2026-10-17 (tibi-server, aggregate: dbapi/aggregate.go, controller/collection_aggregate.go)
+111 -14
View File
@@ -47,15 +47,7 @@ Die Sammlung `ssr` (Cache) ist bewusst ausgenommen (`enabled: false`).
## Verlauf im Admin ## Verlauf im Admin
Du musst nicht per API lesen: Der Admin zeigt den Verlauf an zwei Stellen — pro Eintrag im Editor und global als „Audit-Log“ (`/admin/audit` bzw. `/projects/:id/audit`, mit Filtern für Projekt, Collection, Aktion und Benutzer, 25 Zeilen pro Seite). Du musst nicht per API lesen: Der Admin zeigt den Verlauf pro Eintrag (letzte 50 Änderungen mit Vorher/Nachher-Vergleich und Wiederherstellen einzelner Felder) sowie global als Audit-Log. Details zur Bedienung — Verlaufspanel, Diff-Ansicht, Wiederherstellen-Dialog, Seitenleiste — stehen unter [Eintrags-Editor](/admin/eintrags-editor/).
**Verlaufspanel pro Eintrag.** Im Editor lädt das Panel die letzten 50 Änderungen dieses Datensatzes, neueste zuerst. Jede Zeile zeigt Zeit, Aktion („erstellt“, „geändert“, „gelöscht“), Person und Quelle; bei „geändert“ steht zusätzlich, welche Felder betroffen sind. Ein Pfeil klappt die Zeile auf und zeigt den Vorher/Nachher-Vergleich. Bei „geändert“ und „gelöscht“ erscheint — nur mit Schreibrecht — der Schalter „Wiederherstellen“, der den alten Stand ins Formular übernimmt (danach noch speichern).
**Diff-Ansicht.** Jeder Vergleich zeigt pro Feld eine Karte mit Status-Badge: „hinzugefügt“, „entfernt“ oder „geändert“. Geänderte Texte stehen als „Vorher“ und „Nachher“ nebeneinander, bei kurzen Texten sind einzelne Wörter markiert. Lange Werte sind eingeklappt und lassen sich per Pfeil ganz aufklappen; jeder Wert hat einen Kopieren-Schalter. Feldnamen kommen aus der Sammlungs-Definition, der technische Pfad steht daneben. Gibt es nichts zu zeigen, meldet die Ansicht „Keine Änderungen“.
**Wiederherstellen-Dialog.** Der Dialog öffnet sich als seitliches Panel („Wiederherstellen“) und stellt „Aktuell“ (aus dem geöffneten Formular) und „Danach“ (aus dem Verlauf) gegenüber. Per Haken wählst du einzelne Felder aus — „Alle auswählen“ und „Keine“ helfen, der Zähler (z. B. 3 / 5) zeigt den Stand. Beachte den Hinweis „Media-Dateien werden nicht wiederhergestellt“. Bestätigen übernimmt die gewählten Felder ins Formular („Felder übernommen — bitte speichern“).
**Seitenleisten-Verlauf.** Unter dem Editor zeigt eine kompakte Liste die letzten 10 Änderungen mit Zeit, Aktion und Person. Sie blendet sich bei leerem Verlauf komplett aus — kein Titel, kein Hinweistext. Ein Klick auf eine Zeile öffnet den vollen Vergleich, bei mehr als 10 Einträgen führt „Ältere Einträge“ zur Gesamtansicht. Nach dem Speichern lädt die Liste automatisch neu.
## Snapshots ## Snapshots
@@ -92,8 +84,59 @@ snapshots:
| `retention.maxCount` | Älteste fliegen automatisch raus | | `retention.maxCount` | Älteste fliegen automatisch raus |
| Speicherung | `json` (BSON-Extended-JSON auf Platte, Standard); `db`/`both` sind Zukunft | | Speicherung | `json` (BSON-Extended-JSON auf Platte, Standard); `db`/`both` sind Zukunft |
### Snapshot-Config-Felder
Alle Felder aus `snapshot_config.go:11-58` — nur diese Namen verwenden:
| Feld | YAML-Name | Bedeutung |
|---|---|---|
| Name | `name` | Eindeutige Profil-Kennung (Pflicht, sonst `snapshot config: name is required`) |
| Beschreibung | `description` | Freitext, optional |
| Sammlungen | `collections` | `[]` = alle; String-Kurzform `"content"` = `include`; Objektform `{name, mode}` (`include` oder `exclude`) — gemischt `include` + `exclude` ist ein Validierungsfehler |
| Medien | `includeMedia` | Datei-Felder per Hardlink sichern (Standard: `true`) |
| Deduplizierung | `collectionDedup` | SHA256-Dedup identischer Collection-Exporte über `.store/collections/` per Hardlink — nur für große, selten geänderte Sammlungen |
| Modus | `mode` | `""`/`json` = Platte (Standard, implementiert); `db`/`both` = MongoDB-`$out`-Kopie (Zukunft) — Medien immer Hardlink, egal welcher Modus |
| Aufbewahrung | `retention: { maxCount, maxAge, maxSizeBytes }` | Auto-Prune-Regeln, alle optional |
| Zeitplan | `schedule` | Cron-Ausdruck; leer = nur manuell per Trigger-API |
| Team-Rechte | `permissions` | Liste reiner Berechtigungsnamen — Phase 2, aktuell gilt `AllowSnapshots` |
| An/Aus | `enabled` | `false` = Scheduler überspringt das Profil; manueller Trigger geht trotzdem |
```yaml title="Snapshot-Profil — alle Felder (Beispiel)"
snapshots:
- name: redaktionell
description: "Täglich, ohne Systemdaten"
collections:
- content # Kurzform = include
- name: ssr # Objektform
mode: exclude
includeMedia: true
collectionDedup: false
mode: json
retention: { maxCount: 14, maxAge: "336h", maxSizeBytes: 10737418240 }
schedule: "0 12 * * *"
permissions: []
enabled: true
```
### Snapshot-Routen
Alle Routen aus `setup.go:436-495` (`GET` liest, `POST` löst aus, `DELETE` löscht):
| Methode | Pfad (hinter `/api/v1/_/:project/_/admin`) | Zweck |
|---|---|---|
| `GET` | `/snapshots/configs` | Alle Profile aus der Projekt-YAML lesen (`AdminSnapshotConfigs`) |
| `POST` | `/snapshots/configs/:name/trigger` | Profil manuell auslösen — geht auch bei `enabled: false` |
| `GET` | `/snapshots` | Alle Einträge auflisten (Scannt das `entries`-Verzeichnis) |
| `GET` | `/snapshots/download/:file` | Datei-Download (vor `:id` registriert, damit `download` kein `:id` matcht) |
| `GET` | `/snapshots/:id` | Einzelnen Eintrag ansehen |
| `DELETE` | `/snapshots/:id` | Eintrag löschen |
| `POST` | `/snapshots/:id/restore?merge=1&collections=a,b` | Zurückspielen (`merge=1` = zusammenführen; `collections` = Auswahl) |
| `GET` | `/snapshots/:id/download` | Eintrag als ZIP laden |
| `GET` | `/snapshots/stats` | Belegung prüfen |
| `POST` | `/snapshots/prune` | Retention-Aufräumen manuell anstoßen |
<Aside type="caution" title="Typo im Starter: tags statt tag"> <Aside type="caution" title="Typo im Starter: tags statt tag">
Das `daily`-Profil oben ist vereinfacht aus der Starter-`config.yml` abgeschrieben (`description`/`enabled` weggelassen) — aber der Name `tags` darin steht wirklich so im Starter, obwohl die Sammlung `tag` heißt (siehe `api/collections/tag.yml`, `name: tag`). Übernimm die Liste nicht blind: prüfe jeden Namen gegen deine `api/collections/*.yml`, sonst sichert das Profil eine Sammlung, die es nicht gibt. Das `daily`-Profil oben ist vereinfacht aus der Starter-`config.yml` abgeschrieben (`description`/`enabled` weggelassen) — aber der Name `tags` darin steht wirklich so im Starter, obwohl die Sammlung `tag` heißt (siehe `api/collections/tag.yml`, `name: tag`). Prüfe jeden Namen gegen deine `api/collections/*.yml`, bevor du das Profil übernimmst — sonst sichert das Profil eine Sammlung, die es nicht gibt.
</Aside> </Aside>
Im **tibi-admin** (`/projects/:id/snapshots`, drei Tabs): Im **tibi-admin** (`/projects/:id/snapshots`, drei Tabs):
@@ -104,9 +147,57 @@ Im **tibi-admin** (`/projects/:id/snapshots`, drei Tabs):
| Entries | Auslösen (mit Fortschritt), herunterladen (ZIP), zurückspielen, löschen, aufräumen | | Entries | Auslösen (mit Fortschritt), herunterladen (ZIP), zurückspielen, löschen, aufräumen |
| Stats | Belegung prüfen | | Stats | Belegung prüfen |
Zum Zurückspielen braucht dein Token `AllowSnapshot: true`. Zum Zurückspielen braucht dein Token `allowSnapshots: true`.
Übe das Zurückspielen **nur mit Testdaten** — es überschreibt den Live-Stand. Übe das Zurückspielen **nur mit Testdaten** — es überschreibt den Live-Stand.
### Backup-Routen
Alle Routen aus `setup.go:398-432`:
| Methode | Pfad (hinter `/api/v1/_/:project/_/admin`) | Zweck |
|---|---|---|
| `GET` | `/backup/analyze-download?collections=a,b` | Schätzung (Dokument-/Medien-Zahlen) **ohne** Voll-Export — vor dem Download prüfen |
| `GET` | `/backup` | Voll-ZIP erzeugen und laden (`?collections=` schränkt ein, `?sse=1` streamt) |
| `GET` | `/backup/download/:file` | Fertiges Temp-ZIP aus dem SSE-Modus laden (**One-Time-Use**: zweiter Request = `404`) |
| `POST` | `/backup?sse=1` | ZIP hochladen und **analysieren** → antwortet mit `restoreToken` |
| `POST` | `/backup/:restoreToken?collections=…&merge=true&sse=1` | **Restore** aus dem analysierten Temp-Verzeichnis ausführen |
| `DELETE` | `/backup/:restoreToken` | Temp-Daten zum Token manuell aufräumen |
### Restore-Token-Lebenszyklus
`POST /backup` erzeugt Token plus Temp-Verzeichnis (`admin_backup.go:204-234`), `POST /backup/:restoreToken` verbraucht es, `DELETE` räumt auf.
<Steps>
1. **Analyse:** ZIP per `POST /backup` hochladen — Antwort enthält `restoreToken`, `sourceNamespace`, `createdAt`, `expiresAt` (`admin_backup.go:378-384`). Mit `?sse=1` kommt der Fortschritt als Stream (`init` → `done`, jeweils mit `restoreToken`).
2. **Prüfen:** `collections`-Analyse in der Antwort lesen (Quelle gegen Ziel abgleichen, ggf. `?collections=` für Teil-Restore vormerken).
3. **Restore:** `POST /backup/:restoreToken` ausführen — bei Erfolg landet `restoredCollections`/`totalDocuments`/`mediaFiles` in der Antwort, alles wird als Audit-Action `backup/restore` protokolliert. Danach ist das Token **verbraucht** (Eintrag wird vor dem Restore aus dem Store entfernt).
4. **Cleanup:** Läuft der Restore ohne `sse`, löscht der Server das Temp-Verzeichnis selbst (`os.RemoveAll`). Bei Abbruch oder abgelaufenem Token per `DELETE /backup/:restoreToken` aufräumen — Antwort `404 restore token not found or already cleaned up` heißt: schon weg. Abgelaufene Tokens räumt zusätzlich ein Hintergrund-Job weg.
</Steps>
### Rechte und Auth-Reihenfolge
| Ebene | Schalter | Beleg |
|---|---|---|
| Server-Token | Scope `project.backup` / `project.snapshot` (`scope.go:53-55`) | `X-Admin-Token` oder `Authorization: Bearer` |
| Projekt-Token | `allowBackup` / `allowSnapshots` in der Projekt-Config (`project.go:118-120`) | Nur mit passendem Flag |
| JWT-Login | Rolle `AdminRole` plus Scope-Check (`EnforceScope`) | Persönliche Tokens bleiben durch ihre Scopes begrenzt |
Der Check läuft in `admin_auth.go:20-74` **immer in dieser Reihenfolge**:
<Steps>
1. Server-Token mit passendem Scope (`config.GetAdminTokenInfo`).
2. Projekt-Token mit passendem Flag (`AllowBackup` / `AllowSnapshots` per `ConstantTimeCompare`).
3. JWT-Login mit Admin-Rolle plus Scope-Prüfung — sonst `403 permission denied`.
</Steps>
<Aside type="caution">
Fehlt am Token das richtige Flag, antwortet der Endpunkt mit `403` — auch wenn du Projekt-Admin bist. `permissions: []` im Snapshot-Profil hilft dir heute nicht weiter (Phase 2); maßgeblich ist `allowSnapshots` am Token.
</Aside>
## Backup und Restore (mit Betreiber) ## Backup und Restore (mit Betreiber)
Backups sind der Voll-Export für Umzug und Desaster-Fall — im Admin unter `/projects/:id/backup`: Backups sind der Voll-Export für Umzug und Desaster-Fall — im Admin unter `/projects/:id/backup`:
@@ -124,7 +215,7 @@ Backups sind der Voll-Export für Umzug und Desaster-Fall — im Admin unter `/p
|---|---|---| |---|---|---|
| Wofür | Schneller Rücksprung im Alltag | Umzug, Desaster, Weitergabe | | Wofür | Schneller Rücksprung im Alltag | Umzug, Desaster, Weitergabe |
| Wo | `snapshotPath` auf dem Server | ZIP-Datei bei dir | | Wo | `snapshotPath` auf dem Server | ZIP-Datei bei dir |
| Recht | `AllowSnapshot` | Projekt-Admin plus `AllowBackup` | | Recht | `allowSnapshots` | Projekt-Admin plus `AllowBackup` |
| Fortschritt | Ja (Stream) | Ja (SSE-Stream) | | Fortschritt | Ja (Stream) | Ja (SSE-Stream) |
<Aside type="caution"> <Aside type="caution">
@@ -138,8 +229,14 @@ Fortschritt läuft als Stream (`text/event-stream`) — bei Abbrüchen nichts do
|---|---|---| |---|---|---|
| Audit bleibt leer | `audit.enabled` im Projekt fehlt oder Server-Flag aus | Beide Schalter prüfen, Änderung erneut auslösen | | Audit bleibt leer | `audit.enabled` im Projekt fehlt oder Server-Flag aus | Beide Schalter prüfen, Änderung erneut auslösen |
| Alte Einträge verschwinden | TTL (`720h`) abgelaufen | Wichtige Stände als Snapshot sichern, TTL ggf. auf `"0"` | | Alte Einträge verschwinden | TTL (`720h`) abgelaufen | Wichtige Stände als Snapshot sichern, TTL ggf. auf `"0"` |
| Snapshot-Button fehlt | Token ohne `AllowSnapshot` | Betreiber um Recht bitten | | Snapshot-Button fehlt | Token ohne `allowSnapshots` | Betreiber um Recht bitten |
| Backup-Button fehlt | Kein Projekt-Admin oder `AllowBackup` fehlt | Betreiber um Recht bitten | | Backup-Button fehlt | Kein Projekt-Admin oder `AllowBackup` fehlt | Betreiber um Recht bitten |
| `400 missing restoreToken` | Restore ohne Token aufgerufen | Erst `POST /backup` (Analyse), Token aus der Antwort nehmen |
| `404 restore token not found or expired` | Token verbraucht, aufgeräumt oder TTL abgelaufen | Analyse erneut hochladen, neues Token holen |
| `403 invalid entry id` / `404 snapshot not found` | Snapshot-Restore mit falscher/gelöschter ID | ID aus `GET /snapshots` neu lesen |
| `snapshot config: name is required` | Profil ohne `name` | `name` in der YAML ergänzen |
| Restore zeigt keine Wirkung | Cache alt | Reload plus `?clear=1` (siehe [Veröffentlichen](/projekt/deploy/)) | | Restore zeigt keine Wirkung | Cache alt | Reload plus `?clear=1` (siehe [Veröffentlichen](/projekt/deploy/)) |
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter, config.yml audit- und snapshots-Blöcke), am 68b42785 2026-10-15 (tibi-server, audit-, backup- und snapshot-docs), am 2b091d89 2026-10-13 (tibi-admin-nova, test-pläne 43 und 44), am 2b091d89 2026-10-13 (tibi-admin-nova, AuditView, AuditHistoryPanel, AuditDiff, AuditRestoreDialog, AuditSidebarHistory). Wie ein Redakteur eine Sicherung einspielt: [Backup einspielen](/anwender/backup-einspielen/).
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter, config.yml audit- und snapshots-Blöcke), am 68b4278 2026-10-17 (tibi-server, audit-, backup- und snapshot-docs), am 2b091d8 2026-10-17 (tibi-admin-nova, test-pläne 43 und 44, AuditView, AuditHistoryPanel, AuditDiff, AuditRestoreDialog, AuditSidebarHistory)
+8 -6
View File
@@ -7,7 +7,7 @@ import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Dateien liegen in der Sammlung `medialib`. Dateien liegen in der Sammlung `medialib`.
Du lädst sie per `POST` hoch, verknüpfst sie per ID und zeigst sie nur über die Komponente `MedialibImage` an. Du lädst sie per `POST` hoch, verknüpfst sie per ID und zeigst sie nur über die Komponente `MedialibImage` an.
Suche-Begriffe: Datei hochladen, Bild anzeigen, Bildfilter. Suchbegriffe: Datei hochladen, Bild anzeigen, Bildfilter.
## Upload-Format ## Upload-Format
@@ -44,9 +44,7 @@ Gespeichert wird `{src: '<feld>/name.jpg', path, type}` — also relativer Pfad
Die Widgets `file`, `file[]` und `foreignFile` schreiben dieses Format; `foreignMedia` in `content.yml` (`blocks.heroImage.image`, `blocks.image`) verweist per Medialib-ID darauf. Die Widgets `file`, `file[]` und `foreignFile` schreiben dieses Format; `foreignMedia` in `content.yml` (`blocks.heroImage.image`, `blocks.image`) verweist per Medialib-ID darauf.
<Aside type="caution" title="active:true nicht vergessen"> <Aside type="caution" title="active:true nicht vergessen">
`filter_public.js` erzwingt anonym `active:true` — `medialib.yml` deklariert aber kein `active`-Feld. Anonyme Abfragen sehen nur Dokumente mit `active: true` (kanonische Erklärung unter [Rechte und Tokens](/projekt/rechte-tokens/)) — `medialib.yml` deklariert dafür kein eigenes Feld, speichere das Flag trotzdem auf jedem Dokument.
Ohne gespeichertes `active:true` löst **kein** öffentlicher Lookup auf (Vorfall 2026-10-05).
Speichere `active:true` auf jedem Medialib-Dokument.
</Aside> </Aside>
## Anzeige-Regel: nur MedialibImage ## Anzeige-Regel: nur MedialibImage
@@ -102,8 +100,12 @@ Die Größen stehen in `api/collections/lib/imageFilter.yml` (`imageFilter`-Bloc
| Hero zeigt Verlauf statt Bild | Lookup unaufgelöst (`file.src` fehlt) | Eintrag + `active:true` + Lookup prüfen | | Hero zeigt Verlauf statt Bild | Lookup unaufgelöst (`file.src` fehlt) | Eintrag + `active:true` + Lookup prüfen |
| Richtext-Bild verschwindet öffentlich | Gleicher Grund | Gleicher Fix | | Richtext-Bild verschwindet öffentlich | Gleicher Grund | Gleicher Fix |
| Riesige Antworten | `file.data` mitgelesen | `select` ohne Dateifelder nutzen | | Riesige Antworten | `file.data` mitgelesen | `select` ohne Dateifelder nutzen |
| Dateien bleiben nach Löschen übrig? | Passiert nicht von selbst | Beim Löschen räumt der Server das Upload-Verzeichnis des Dokuments automatisch weg (`filecleanup`, unter der Projekt-UID) — Waisen prüfst du per Dateisuche im Upload-Pfad | | Dateien bleiben nach Löschen übrig? | Reste bleiben nur bei Fehlern liegen — beim Löschen räumt der Server das Upload-Verzeichnis automatisch weg | `warning` in Delete-Antwort bzw. Server-Log prüfen, Rest per Dateisuche im Upload-Pfad löschen. Details: [Betrieb](/server/betrieb/) |
<Aside type="note">
Es gibt keinen Schalter für das Cleanup und keinen Zeitplan: Einzelne entfernte oder ersetzte Dateien löscht der Server sofort beim `PUT` (pro Dateipfad, abgesichert wie oben); das ganze Dokument-Verzeichnis fällt erst beim Löschen des Dokuments.
</Aside>
Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/), [Sammlungen anlegen](/projekt/sammlungen-anlegen/). Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/), [Sammlungen anlegen](/projekt/sammlungen-anlegen/).
Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter) Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter)
+28 -5
View File
@@ -12,7 +12,8 @@ Suchbegriffe: Deploy, Staging, Live, Produktion, Reload, Freischaltung.
## URL-Bild ## URL-Bild
```bash title=".env — die drei Bühnen (Platzhalter im Starter ersetzen)" ```bash title=".env — die drei Bühnen (Platzhalter im Starter ersetzen)"
LIVE_URL=https://www # Produktion (im echten Projekt: echte Domain) # TODO: echte Domain eintragen
LIVE_URL=https://example.com # Produktion (im echten Projekt: echte Domain)
STAGING_URL=https://dev-<projekt>.staging.testversion.online STAGING_URL=https://dev-<projekt>.staging.testversion.online
CODING_URL=https://<projekt>.code.testversion.online CODING_URL=https://<projekt>.code.testversion.online
``` ```
@@ -76,11 +77,33 @@ curl -X POST "${LIVE_URL}/api/ssr?clear=1" -H "Content-Type: application/json" -
``` ```
Zugang per `RSYNC_USER`, `RSYNC_PASS`, `RSYNC_HOST`, `RSYNC_PORT` aus `.env`. Zugang per `RSYNC_USER`, `RSYNC_PASS`, `RSYNC_HOST`, `RSYNC_PORT` aus `.env`.
Auf `master` fliegen Quellen und Karten raus (`--exclude=src --exclude=*.map`). Auf `master` fliegen im Frontend-Sync Quellen und Karten raus (`--exclude=src --exclude=*.map`); der `api/`-Sync läuft ohne Excludes.
</TabItem> </TabItem>
</Tabs> </Tabs>
## CI-Umgebung und Variablen
Beide Skripte liegen in `scripts/` und lesen per `allexport` erst `.env`, dann `api/config.yml.env` ein.
`ci-deploy.sh` bricht ohne `RSYNC_USER`, `RSYNC_PASS`, `RSYNC_HOST`, `RSYNC_PORT` ab; `ci-staging.sh` verlangt `API_BASEDIR` und `COMPOSE_PROJECT_NAME` (Schrägstriche werden zu Bindestrichen).
Erkennt `ci-deploy.sh` die Starter-Vorlage (`PROJECT_NAME=__PROJECT_NAME__`, `_CUSTOMER_ID_` in `PRODUCTION_PATH` oder `LIVE_URL=https://www`), steigt es mit Exit 0 aus — kein Deploy, kein Fehler.
| Variable | Woher | Wofür |
|---|---|---|
| `RSYNC_USER`, `RSYNC_PASS`, `RSYNC_HOST`, `RSYNC_PORT` | CI-Umgebung | SSH-Zugang und Ziel für den Live-rsync |
| `BRANCH` | CI-Umgebung | `master` schaltet `--exclude=src --exclude=*.map` (nur Frontend-Sync) |
| `ADMIN_TOKEN` | `api/config.yml.env` | `Bearer`-Kopf für den Proxy-Reload |
| `LIVE_URL`, `STAGING_URL` | `.env` | Ziele für Reload und SSR-Clear |
| `API_BASEDIR`, `COMPOSE_PROJECT_NAME` | CI-Umgebung | Staging-Zielverzeichnis und Compose-Projektname |
| `PREVIEW_URL` | wird geschrieben | `ci-modify-config.sh` setzt `LIVE_URL/preview`, `ci-staging.sh` überschreibt mit `STAGING_URL/preview` |
| `PROJECT_NAME`, `PRODUCTION_PATH` | `.env` | Nur Template-Schutz (siehe oben) |
Was das Skript tut: Live per rsync (`frontend/`, `api/`, `media/`) auf `RSYNC_HOST`, danach Reload plus SSR-Clear; Staging per rsync nach `API_BASEDIR` (`api`, `frontend/dist`, `frontend/assets`), danach `docker compose up -d --build` mit Wartezeit, Reload plus SSR-Clear.
Nicht als Deploy-Variablen listen: `ADMIN_ASSET_VERSION` steht zwar in `api/config.yml.env`, wird aber von keinem CI-Skript gelesen.
`CODING_URL` und `CODING_TIBIADMIN_URL` sind Test-Basis-URLs (siehe [Tests](/projekt/tests/)), keine Deploy-Ziele.
`.basic-auth-code` und `.basic-auth-web` liegen im Repo, sind in `docker-compose-local.yml` aber derzeit auskommentiert; Playwright umgeht Basic-Auth per User-Agent-Marker.
## Reload-Endpunkte ## Reload-Endpunkte
Nach jedem Deploy: Config neu laden **und** SSR-Cache wischen — sonst siehst du alte Seiten auf neuer Config. Nach jedem Deploy: Config neu laden **und** SSR-Cache wischen — sonst siehst du alte Seiten auf neuer Config.
@@ -88,7 +111,7 @@ Nach jedem Deploy: Config neu laden **und** SSR-Cache wischen — sonst siehst d
| Endpunkt | Kopf | Wirkung | | Endpunkt | Kopf | Wirkung |
|---|---|---| |---|---|---|
| `POST /api/_/admin/reload` (Live/Staging-URL) | `Authorization: Bearer <ADMIN_TOKEN>` | Projekt-Config über den Reverse-Proxy neu laden | | `POST /api/_/admin/reload` (Live/Staging-URL) | `Authorization: Bearer <ADMIN_TOKEN>` | Projekt-Config über den Reverse-Proxy neu laden |
| `POST /api/ssr?clear=1` | keiner nötig (`public.post: true`) | Ganzen SSR-Cache löschen | | `POST /api/ssr?clear=1` (Starter-URL über Proxy/Webserver, kein tibi-server-Pfad — siehe `scripts/ci-staging.sh` und `scripts/ci-deploy.sh`) | keiner nötig (`public.post: true` in `api/collections/ssr.yml`) | Ganzen SSR-Cache löschen (Hook `hooks/ssr/post_bind.js` reagiert auf `?clear`) |
| `POST /api/v1/_/:project/_/admin/reload` | `X-Admin-Token` (Server-Ebene) | Gleicher Reload direkt am Server | | `POST /api/v1/_/:project/_/admin/reload` | `X-Admin-Token` (Server-Ebene) | Gleicher Reload direkt am Server |
| `POST /api/v1/_/:project/_/admin/regenerate-search` | `X-Admin-Token` | Suchindex neu aufbauen ([Suche](/projekt/suche/)) | | `POST /api/v1/_/:project/_/admin/regenerate-search` | `X-Admin-Token` | Suchindex neu aufbauen ([Suche](/projekt/suche/)) |
@@ -122,10 +145,10 @@ Im Deploy-Skript steht er nur hinter `Bearer` für den Proxy-Reload — Projekt-
| Fehlerbild | Ursache | Lösung | | Fehlerbild | Ursache | Lösung |
|---|---|---| |---|---|---|
| Deploy bricht mit „starter template detected“ ab | Platzhalter noch drin | `.env` und `PRODUCTION_PATH` vervollständigen | | Deploy steigt mit „starter template detected" aus | Guard greift (Exit 0, kein Deploy, kein Fehler) | `.env` und `PRODUCTION_PATH` vervollständigen |
| Neue Config wirkt nicht | Reload vergessen | Reload-Endpunkt pro Bühne aufrufen | | Neue Config wirkt nicht | Reload vergessen | Reload-Endpunkt pro Bühne aufrufen |
| Alte Seite trotz neuer Daten | SSR-Cache alt | `?clear=1`, Köpfe `X-SSR-Cache`/`X-SSR-Cleared` prüfen | | Alte Seite trotz neuer Daten | SSR-Cache alt | `?clear=1`, Köpfe `X-SSR-Cache`/`X-SSR-Cleared` prüfen |
| `project not found` | Namespace falsch | [Neues Projekt](/einstieg/neues-projekt/) Schritt 5 | | `project not found` | Namespace falsch | [Neues Projekt](/einstieg/neues-projekt/) Schritt 5 |
| 401/403 beim Reload | Falscher Kopf (z. B. `Token:` statt `Bearer`) | Tabelle oben: Deploy nimmt `Bearer`, Server-API nimmt `X-Admin-Token` | | 401/403 beim Reload | Falscher Kopf (z. B. `Token:` statt `Bearer`) | Tabelle oben: Deploy nimmt `Bearer`, Server-API nimmt `X-Admin-Token` |
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter, .env, ci-deploy.sh, ci-staging.sh, deploy.yml). Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter, .env, ci-deploy.sh, ci-staging.sh, deploy.yml)
+87
View File
@@ -0,0 +1,87 @@
---
title: 'Feld-Rechte'
description: 'Einzelne Felder pro Rolle ausblenden oder sperren: readonlyFields, hiddenFields, validProjections.'
---
import { Aside } from '@astrojs/starlight/components';
Manche Felder dürfen nicht alle sehen oder schreiben — z. B. interne Notizen oder Preise.
Dafür sperrst du einzelne Felder pro Rolle, statt ganze Sammlungen zu schließen.
Grundlage sind `readonlyFields`, `hiddenFields` und `validProjections`.
## Wofür welcher Schalter
| Schalter | Wo | Wirkung |
|---|---|---|
| `readonlyFields` | Collection, PermissionSet, Feld-Definition | Schreiben verboten: `POST`/`PUT` mit dem Feld → Fehler; Lesen bleibt erlaubt |
| `hiddenFields` | Collection, PermissionSet, Feld-Definition | Schreiben verboten **und** Lesen unsichtbar: Feld wird aus jeder `GET`-Antwort und aus `fields[]` der Config-Auslieferung entfernt |
| `validProjections` | nur PermissionSet | Erlaubte `?projection=`-Namen für diese Rolle; fremde Namen → `permission denied for given projection` |
| `readonly` / `hidden` | Feld-Definition (`fields[]`, auch `eval`) | Feldweit für alle Nicht-Admins (`true` = immer, `false` = niemals, `eval` = pro Dokument) |
Die Kaskade lautet: **Collection → PermissionSet → Feld-Definition**.
Die Feld-Definition hat das letzte Wort: `readonly: false` entfernt das Feld aus jedem effektiven Set, `readonly: true` fügt es immer hinzu.
Ein `-`-Prefix im PermissionSet zieht ab: `readonlyFields: [-createdBy]` entfernt ein Collection-Eintrag nur für diese Rolle.
Elternpfade expandieren auf alle Unterfelder (`payment` schließt `payment.cardNumber` ein); einzelne Felder adressierst du per Dot-Notation (`payment.cardNumber`).
Quelle der Wahrheit: `tibi-types/schemas/config/permissions.schema.json` und `collection.schema.json` (Feld-Definitionen); die Statuscode-Logik steht im Server (`internal/controller/http_status.go`).
## So konfigurierst du es
```yaml title="orders.yml — Feld-Rechte für zwei Rollen"
readonlyFields: [createdBy] # Ebene 1: gilt für alle Rollen
hiddenFields: [costPrice]
permissions:
public:
methods:
get: true
readonlyFields: [status, customerName]
hiddenFields: [internalMargin, costPrice, payment.cardNumber]
validProjections: [teaser] # nur GET mit ?projection=teaser
user:
methods:
get: true
post: true
put: true
readonlyFields: [customerName]
hiddenFields: [internalMargin]
fields:
- name: createdBy
type: string
- name: costPrice
type: number
- name: lockedNote
type: string
readonly:
eval: "$.status === 'confirmed'" # nur gesperrt, wenn das Dokument bestätigt ist
```
- Schema-Belege: `permissions.schema.json` (`permissions.*.validProjections`, `*.readonlyFields`, `*.hiddenFields`), `collection.schema.json` (Top-Level `readonlyFields`, `hiddenFields`).
- `eval` kennt `$` (Dokument), `$this` (Feldwert), `$auth`, `$method`, `$project`, `$namespace`.
- Feld-Rechte gelten auch für `"token:…"`-PermissionSets — Tokens unterliegen denselben Listen wie Nutzer.
## Verhalten und Fehler
| Vorgang | readonly-Feld | hidden-Feld |
|---|---|---|
| `POST` / Bulk-`POST` (je Zeile) | Fehler | Fehler |
| `PUT` / Bulk-Update, inkl. Operatoren (`$set`, `$inc`, …) | Fehler | Fehler |
| `GET` Liste und Einzel | sichtbar | gestripped |
| Config-Auslieferung (`yourPermissions`) | Liste steht in `readonlyFields` | Liste wird **nicht** ausgeliefert; Feld-Definitionen fehlen in `fields[]` |
Fehlerantwort (Status hängt von der Projekt-Config ab — modern `403`, Legacy `400`):
```json title="POST mit gesperrtem Feld"
{"error": "readonly fields must not be sent", "fields": ["customerName"]}
// oder: {"error": "hidden fields must not be sent", "fields": ["costPrice"]}
// falsche Projektion: 400 {"error": "permission denied for given projection"}
```
<Aside type="note">
`readonly`/`hidden` ersetzen keine `validations` (Pflicht, Format) und keine Methoden-Rechte: Ein Feld mit `required: true`, das für eine Rolle `hidden` ist, kann diese Rolle nie anlegen — kombiniere `hidden` daher nur mit optionalen Feldern.
Und: Admin (Rolle `0`) sowie Admin-Token umgehen alle Feld-Rechte — `GetEffectivePermissionSet()` liefert dann `nil` (keine Einschränkung).
</Aside>
Verwandt: [Rechte und Tokens](/projekt/rechte-tokens/) (Permission-Blöcke, Token-Köpfe), [Lesen und suchen](/projekt/abfragen-lesen/) (`select`, `projection`, `lookup`).
Verifiziert am 68b4278 2026-10-17 (tibi-server)
+41 -3
View File
@@ -53,9 +53,32 @@ Der Server akzeptiert zur Laufzeit zusätzlich die Schreibweisen `bool`, `float`
In neuem YAML nutzt du trotzdem nur die elf Schema-Typen — so bleibt die Datei gegen das Schema prüfbar. In neuem YAML nutzt du trotzdem nur die elf Schema-Typen — so bleibt die Datei gegen das Schema prüfbar.
</Aside> </Aside>
Alle prüfbaren Widgets aus dem Schema (25 Enum-Werte — `pagebuilder` und `pageBuilder` stehen beide drin und meinen dasselbe): Alle prüfbaren Widgets aus dem Schema (`field-meta.schema.json`, `widget`-Enum, 25 Werte):
`text`, `textarea`, `number`, `checkbox`, `switch`, `select`, `selectArray`, `chipArray`, `date`, `datetime`, `file`, `image`, `json`, `richtext`, `html`, `foreignKey`, `foreignKeyChipArray`, `foreignFile`, `foreignMedia`, `pagebuilder`, `pageBuilder`, `templateProps`, `containerLessObject`, `containerLessObjectArray`, `color`. | Widget | Wofür |
|---|---|
| `text` | Einzeiliger Text (Standard) |
| `textarea` | Mehrzeiliger Text |
| `number` | Zahleneingabe |
| `checkbox`, `switch` | Ja/Nein-Umschalter |
| `select` | Dropdown aus `meta.choices` |
| `selectArray`, `chipArray` | Mehrfach-Auswahl als Liste bzw. Chips |
| `date`, `datetime` | Datum bzw. Datum mit Uhrzeit |
| `file` | Datei-Upload (auch `file[]`) |
| `image` | Bild-Upload mit Vorschau |
| `json` | Freies JSON (`any`-Felder) |
| `richtext`, `html` | Formatierter Text bzw. HTML-Quelltext |
| `foreignKey`, `foreignKeyChipArray` | Verweis auf andere Sammlung (einfach / mehrfach) |
| `foreignFile` | Datei-Verweis auf andere Sammlung |
| `foreignMedia` | Bild-Verweis auf die Mediathek |
| `pagebuilder` | Block-Editor für `object[]` |
| `templateProps` | Vorlagen-Parameter |
| `containerLessObject`, `containerLessObjectArray` | Gruppe(n) ohne umgebenden Kasten |
| `color` | Farbauswahl |
<Aside type="note">
`pagebuilder` und `pageBuilder` stehen beide im Schema-Enum (`field-meta.schema.json`) und meinen dasselbe Widget — nutze in neuem YAML die Kleinschreibung `pagebuilder`.
</Aside>
## Validatoren ## Validatoren
@@ -196,4 +219,19 @@ Jede Sprache bekommt einen eigenen Eintrag mit gleichem `translationKey`.
Setze es bei Feldern, die Nutzer nicht nachträglich ändern dürfen. Setze es bei Feldern, die Nutzer nicht nachträglich ändern dürfen.
</Aside> </Aside>
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 4c55f82 2026-10-12 (tibi-types), am 68b42785 2026-10-15 (tibi-server, validator.go). ## Weitere Meta-Optionen
Je Option eine Zeile (Quelle: `tibi-types/schemas/config/field-meta.schema.json`):
| Option | Wirkung | Typ / Beispiel |
|---|---|---|
| `hideLabel` | Blendet das Feld-Label im Formular aus | `boolean`; `hideLabel: true` |
| `imageEditor` | Bildbearbeitung im Uploader | `boolean` oder `{ crop, rotate, flip, focalPoint, adjust, palette }`; `imageEditor: { crop: true, rotate: true }` |
| `containerProps` | Feld-Wrapper (`class`, `layout` mit `breakBefore`, `breakAfter`, `size` mit `default`/`small`/`large`) | Objekt; `containerProps: { layout: { breakBefore: true } }` |
| `inputProps` | Wird ans Eingabeelement durchgereicht (widget-spezifisch) | Objekt (`additionalProperties: true`) |
| `schemaRef` | Schema des Felds aus einem anderen Feld übernehmen | `{ field }` + optional `ref`; `schemaRef: { field: blocks }` |
| `protected` | Schreibschutz vor fremdem Überschreiben (Bool oder UI-Bedingung) | `boolOrUiCondition`; `protected: true` |
| `drillDown` | `false` rendert den Pagebuilder inline statt im Overlay | `boolean`; `drillDown: false` (siehe [Pagebuilder-Anbindung](/projekt/pagebuilder-dev/)) |
| `pageSize` | Seitengröße für paginierte Feldlisten | `integer ≥ 0`; `pageSize: 20` |
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 4c55f82 2026-10-17 (tibi-types), am 68b4278 2026-10-17 (tibi-server, validator.go); Meta-Ergänzungen verifiziert am 4c55f82 2026-10-17 (tibi-types: field-meta.schema.json)
+16 -6
View File
@@ -3,11 +3,11 @@ title: 'Hooks schreiben'
description: 'Lifecycle get/post/put/delete, Datei-Hooks, SSR- und Cache-Hooks aus dem Starter.' description: 'Lifecycle get/post/put/delete, Datei-Hooks, SSR- und Cache-Hooks aus dem Starter.'
--- ---
import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components'; import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Hooks sind JavaScript-Dateien, die der tibi-server vor oder nach einem Zugriff ausführt. Hooks sind JavaScript-Dateien, die der tibi-server vor oder nach einem Zugriff ausführt.
Du filterst damit öffentliche Lesezugriffe, räumst den SSR-Cache weg oder renderst Seiten. Du filterst damit öffentliche Lesezugriffe, räumst den SSR-Cache weg oder renderst Seiten.
Suche-Begriffe: Hook schreiben, Lifecycle, Cache invalidieren. Suchbegriffe: Hook schreiben, Lifecycle, Cache invalidieren.
## Lifecycle: welche Stufe wann läuft ## Lifecycle: welche Stufe wann läuft
@@ -82,7 +82,7 @@ Die `ssr`-Sammlung nutzt eigene Stufen (`api/collections/ssr.yml`): `get.read`
})() })()
``` ```
Ohne `return` läuft der Hook ins Leere — eingeloggte Nutzer sehen dann alles. Ohne `return` läuft der Hook ins Leere — anonyme Nutzer sehen dann alles.
2. Verstehe `clear_cache.js`: Er liest Sammlung, Methode und Entry-ID aus `context` und ruft `utils.clearSSRCache()` auf. 2. Verstehe `clear_cache.js`: Er liest Sammlung, Methode und Entry-ID aus `context` und ruft `utils.clearSSRCache()` auf.
@@ -108,7 +108,7 @@ Die `ssr`-Sammlung nutzt eigene Stufen (`api/collections/ssr.yml`): `get.read`
3. Merke dir die Invalidierungs-Regel aus `lib/utils.js`: `POST` löscht nur Listen-Abhängigkeiten (`col:*`), `PUT`/`DELETE` löschen Detail plus Liste (`col:id` oder `col:*`). Ohne Argumente folgt ein Komplett-Wipe. 3. Merke dir die Invalidierungs-Regel aus `lib/utils.js`: `POST` löscht nur Listen-Abhängigkeiten (`col:*`), `PUT`/`DELETE` löschen Detail plus Liste (`col:id` oder `col:*`). Ohne Argumente folgt ein Komplett-Wipe.
4. Logge mit `utils.log()` (`console.log` mit `JSON.stringify`). Das ist die einzige Log-Hilfe im Starter. 4. Logge mit `utils.log()` (`console.log` mit `JSON.stringify`) — der übliche Weg, um aus Hooks zu protokollieren.
</Steps> </Steps>
@@ -124,7 +124,17 @@ Vollständige Liste: [Hook-Kontexte](/reference/hook-kontexte/). Hier die fünf,
| `context.db` | `config.js`, `get_read.js`, `utils.js` | `find`, `create`, `delete`, `deleteMany` | | `context.db` | `config.js`, `get_read.js`, `utils.js` | `find`, `create`, `delete`, `deleteMany` |
| `context.tpl` / `context.http` | per [Hook-Kontexte](/reference/hook-kontexte/) | Templates rendern, externe Aufrufe | | `context.tpl` / `context.http` | per [Hook-Kontexte](/reference/hook-kontexte/) | Templates rendern, externe Aufrufe |
Weitere im Starter genutzte Zugriffe: `context.filter`, `context.data`, `context.collection()`, `context.config.server().api.port`, `context.api().namespace`, `context.fs.readFile()`, `context.debug.sentryTraceId()`, `context.ssrCache` / `context.ssrDeps` (nur SSR). Weitere im Starter genutzte Zugriffe:
| Zugriff | Wo |
|---|---|
| `context.filter`, `context.data` | Anfrage-Filter bzw. Dokument |
| `context.collection()` | Sammlung des aktuellen Zugriffs |
| `context.config.server().api.port` | Server-Port aus der Config |
| `context.api().namespace` | Aktueller Namespace |
| `context.fs.readFile()` | Datei lesen |
| `context.debug.sentryTraceId()` | Trace-ID für Fehlerberichte |
| `context.ssrCache` / `context.ssrDeps` | SSR-Zwischenspeicher (nur SSR) |
## Kontext in get.read: filter, aber kein data ## Kontext in get.read: filter, aber kein data
@@ -231,4 +241,4 @@ Nur `get.read`-Hooks wie `filter_public.js` geben ein Objekt mit `filter` zurüc
Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/), [Hook-Kontexte](/reference/hook-kontexte/). Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/), [Hook-Kontexte](/reference/hook-kontexte/).
Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter), am 68b42785 2026-10-15 (tibi-server), am 4c55f824 2026-10-12 (tibi-types) Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server), am 4c55f82 2026-10-17 (tibi-types)
+1 -1
View File
@@ -141,4 +141,4 @@ Zwei MCP-Werkzeuge spiegeln diese Seite: `actions_list` (Aktionen auflisten, bra
| Action läuft, schreibt aber nichts | `validate`/`handle`-Hook fehlt oder wirft | Hook-Kette Stufe für Stufe testen | | Action läuft, schreibt aber nichts | `validate`/`handle`-Hook fehlt oder wirft | Hook-Kette Stufe für Stufe testen |
| `project not found` | Namespace falsch | [Neues Projekt](/einstieg/neues-projekt/) Schritt 5 | | `project not found` | Namespace falsch | [Neues Projekt](/einstieg/neues-projekt/) Schritt 5 |
Verifiziert am 4c55f82 2026-10-12 (tibi-types, job- und action-schema), am 68b42785 2026-10-15 (tibi-server, jobs- und actions-docs), am 2b091d89 2026-10-13 (tibi-admin-nova, actions-test-plan 40). Verifiziert am 4c55f82 2026-10-12 (tibi-types, job- und action-schema), am 68b4278 2026-10-15 (tibi-server, jobs- und actions-docs), am 2b091d8 2026-10-13 (tibi-admin-nova, actions-test-plan 40)
+2 -2
View File
@@ -86,7 +86,7 @@ Provider-Mechanik (`registry.go`, `openai.go`, `server.go`):
- `maxTokensPerRequest` (> 0) deckelt still: Fehlt `maxTokens` oder liegt es darüber, sendet der Server den konfigurierten Wert an den Provider (`buildRequest`). Das schützt vor Riesen-Antworten, steht aber nicht in der API-Antwort — plane deine Budgets mit dem konfigurierten Wert, nicht mit deinem Wunschwert. - `maxTokensPerRequest` (> 0) deckelt still: Fehlt `maxTokens` oder liegt es darüber, sendet der Server den konfigurierten Wert an den Provider (`buildRequest`). Das schützt vor Riesen-Antworten, steht aber nicht in der API-Antwort — plane deine Budgets mit dem konfigurierten Wert, nicht mit deinem Wunschwert.
<Aside type="note"> <Aside type="note">
Preise stehen nirgends im Code — erfinde keine. Modellnamen bezieht der Betrieb aus der Server-Config — die gültigen Werte listet `GET /llm/providers` (nächster Abschnitt). Lege keine festen Namen in deiner Doku ab.
Kosten steuerst du über Budgets (nächster Abschnitt), nicht über Modellnamen. Kosten steuerst du über Budgets (nächster Abschnitt), nicht über Modellnamen.
</Aside> </Aside>
@@ -169,4 +169,4 @@ Dazu die zweite Fehlerlage: Scheitert schon der Stream-Aufbau (Provider down, Mo
Budget-Verwaltung (wer darf was): `llmBudgets` stehen am Benutzer (`models/user.go`) und an der Org. Nur System-Admins ändern sie frei — Org-Admins verlieren das Feld still (`llmBudgets` wird auf `nil` gesetzt bzw. aus Änderungen gestrichen), Team-Admins und Mitglieder bekommen beim Schreibversuch `updating llmBudgets only allowed for admins`. Wende dich also für Budget-Erhöhungen an den Betrieb. Budget-Verwaltung (wer darf was): `llmBudgets` stehen am Benutzer (`models/user.go`) und an der Org. Nur System-Admins ändern sie frei — Org-Admins verlieren das Feld still (`llmBudgets` wird auf `nil` gesetzt bzw. aus Änderungen gestrichen), Team-Admins und Mitglieder bekommen beim Schreibversuch `updating llmBudgets only allowed for admins`. Wende dich also für Budget-Erhöhungen an den Betrieb.
Verifiziert am 68b42785 2026-10-16 (tibi-server: setup.go LLM-Routen + Providers-Antwort ohne Secrets, llm_chat.go inkl. 429/502/413-Mapping und RecordUsage, registry.go Typ-Filter, provider.go, models.go EstimateUsage, openai.go resolveModel + buildRequest-Clamp + include_usage, quota.go CheckBudget/CheckBudgetIfSet/RecordUsage, usage_model.go llm_usage, user.go LLMBudget + Feldschutz, config/scope.go llm.chat, server.go ConfigLLMProvider). Verifiziert am 68b4278 2026-10-17 (tibi-server: setup.go LLM-Routen + Providers-Antwort ohne Secrets, llm_chat.go inkl. 429/502/413-Mapping und RecordUsage, registry.go Typ-Filter, provider.go, models.go EstimateUsage, openai.go resolveModel + buildRequest-Clamp + include_usage, quota.go CheckBudget/CheckBudgetIfSet/RecordUsage, usage_model.go llm_usage, user.go LLMBudget + Feldschutz, config/scope.go llm.chat, server.go ConfigLLMProvider)
+290 -603
View File
@@ -3,7 +3,7 @@ title: 'MCP nutzen'
description: 'KI-Werkzeugzugriff auf Tibi: Transport, Anmeldung, alle Werkzeuge und die Agenten-Anleitung.' description: 'KI-Werkzeugzugriff auf Tibi: Transport, Anmeldung, alle Werkzeuge und die Agenten-Anleitung.'
--- ---
import { Aside, Tabs, TabItem, Steps, Badge } from '@astrojs/starlight/components'; import { Tabs, TabItem, Steps, Badge } from '@astrojs/starlight/components';
MCP gibt KI-Agenten Werkzeugzugriff auf Tibi — statt Klicks ruft der Agent `tools/list` und `tools/call` auf. MCP gibt KI-Agenten Werkzeugzugriff auf Tibi — statt Klicks ruft der Agent `tools/list` und `tools/call` auf.
Transport: Streamable-HTTP mit JSON-RPC 2.0. Transport: Streamable-HTTP mit JSON-RPC 2.0.
@@ -60,15 +60,15 @@ Jede Antwort ist ein JSON-RPC-Envelope mit der Request-`id`.
| Werkzeug | Wofür | Scope | | Werkzeug | Wofür | Scope |
|---|---|---| |---|---|---|
| `documents_list` | Einträge listen (Filter, Sort, Limit wie [Lesen und suchen](/projekt/abfragen-lesen/)) | `namespace.<ns>.<col>.get` | | `documents_list` | Einträge listen (Filter, Sort, Limit wie [Lesen und suchen](/projekt/abfragen-lesen/)) | `namespace.<ns>.<col>.read` |
| `document_get` | Ein Eintrag per ID | `.get` | | `document_get` | Ein Eintrag per ID | `.read` |
| `documents_count` | Nur Anzahl | `.get` | | `documents_count` | Nur Anzahl | `.read` |
| `documents_facet` | Werte je Feld zählen | `.get` | | `documents_facet` | Werte je Feld zählen | `.read` |
| `queue_status` | Suchindex-Warteschlange | Projekt-Leserecht | | `queue_status` | Suchindex-Warteschlange | Projekt-Leserecht |
| `document_create` | Eintrag anlegen | `.post` | | `document_create` | Eintrag anlegen | `.create` |
| `document_update` | Eintrag ändern | `.put` | | `document_update` | Eintrag ändern | `.update` |
| `document_delete` | Eintrag löschen | `.delete` | | `document_delete` | Eintrag löschen | `.delete` |
| `file_read` | <Badge text="REST-only" variant="caution" /> Datei lesen: `GET /_/:project/:collection/:id/:field/*path` | `.get` | | `file_read` | <Badge text="REST-only" variant="caution" /> Datei lesen: `GET /_/:project/:collection/:id/:field/*path` | `.read` |
| `action_call` | Aktion aufrufen (`method: get/post`) | Aktions-Scope | | `action_call` | Aktion aufrufen (`method: get/post`) | Aktions-Scope |
| `projects_list` / `project_get` | Projekte auflisten / ein Projekt | `project.read` | | `projects_list` / `project_get` | Projekte auflisten / ein Projekt | `project.read` |
| `collections_list` | Sammlungen eines Projekts | `project.read` | | `collections_list` | Sammlungen eines Projekts | `project.read` |
@@ -92,599 +92,9 @@ Jede Antwort ist ein JSON-RPC-Envelope mit der Request-`id`.
Methoden-Mapping (`method.go`) ist sicherheitsrelevant: Lese-Werkzeuge laufen als `GET`, `document_create` als `POST`, `document_update` als `PUT`, `document_delete` als `DELETE`. Methoden-Mapping (`method.go`) ist sicherheitsrelevant: Lese-Werkzeuge laufen als `GET`, `document_create` als `POST`, `document_update` als `PUT`, `document_delete` als `DELETE`.
So greifen deine [Hooks](/projekt/hooks-schreiben/) (Stufen je Methode, Kontexte in [Hook-Kontexte](/reference/hook-kontexte/)) genau wie bei REST. So greifen deine [Hooks](/projekt/hooks-schreiben/) (Stufen je Methode, Kontexte in [Hook-Kontexte](/reference/hook-kontexte/)) genau wie bei REST.
## Werkzeug-Referenz (alle 36) ## Grenzen: REST-only-Werkzeuge
Pro Werkzeug: Signatur (Pflichtparameter mit `*`, Stand `tools_crud.go`, `tools_discovery.go`, `tools_system.go`), genau ein `tools/call`-Beispiel und ein Fehlerfall aus dem Code (`mcp_tools_impl.go`, `guards.go`, `query.go`, `method.go`). Neun Werkzeuge antworten nur mit einem Verweis auf REST — sie führen per MCP nichts aus (erkennbar an `"REST-only in this phase"` in der Beschreibung von `tools/list`). `allowAdminActions` steuert nur, ob Admin-Werkzeuge überhaupt sichtbar sind — die Sperre bleibt.
Ohne PAT scheitert jedes Werkzeug mit 403 (`mcp requires a personal access token (X-Auth-Token: tibi_pat_...)`, `guards.go`); ein Tippfehler im Namen gibt `-32601 unknown tool` (`mcp_handler.go`).
### documents_list
Signatur: `namespace*`, `collection*`, optional `filter`, `sort`, `limit`, `offset`, `select`, `projection`, `lookup`, `aggregate`, `q`, `qName`, `count`, `extra`. Scope: `namespace.<ns>.<col>.read` (bei `lookup`/`aggregate` zusätzlich Lese-Recht auf das Ziel). Methode: `GET`.
```json title="documents_list — tools/call"
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "documents_list",
"arguments": {"namespace": "<ns>", "collection": "content", "limit": 5}
}
}
```
Fehler: ohne `namespace`/`collection` → 400 (`namespace and collection are required`); lange Listen kappt `maxResultItems` (Standard 100).
### document_get
Signatur: `namespace*`, `collection*`, `id*`, optional `select`, `lookup`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
```json title="document_get — tools/call"
{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "document_get",
"arguments": {"namespace": "<ns>", "collection": "content", "id": "<id>"}
}
}
```
Fehler: unbekannte `id` → 404 (`not found`); unlesbare Hex-ID → 400.
### documents_count
Signatur: `namespace*`, `collection*`, optional `filter`, `q`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
```json title="documents_count — tools/call"
{
"jsonrpc": "2.0",
"id": 12,
"method": "tools/call",
"params": {
"name": "documents_count",
"arguments": {"namespace": "<ns>", "collection": "content"}
}
}
```
Fehler: ohne Leserecht am Ziel → 403 (`permission denied` bzw. Scope-Fehler).
### documents_facet
Signatur: `namespace*`, `collection*`, `fields*`, optional `filter`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
```json title="documents_facet — tools/call"
{
"jsonrpc": "2.0",
"id": 13,
"method": "tools/call",
"params": {
"name": "documents_facet",
"arguments": {"namespace": "<ns>", "collection": "content", "fields": "status"}
}
}
```
Fehler: ohne `fields` → 400 (`missing required parameter: facet` auf Controller-Ebene).
### queue_status
Signatur: `namespace*`, `collection*`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
```json title="queue_status — tools/call"
{
"jsonrpc": "2.0",
"id": 14,
"method": "tools/call",
"params": {
"name": "queue_status",
"arguments": {"namespace": "<ns>", "collection": "content"}
}
}
```
Fehler: unbekannte Sammlung → 404 auf Controller-Ebene.
### document_create
Signatur: `namespace*`, `collection*`, `data*`, optional `items[]` (Bulk). Datei-Felder als `{src: "data:<mime>;base64,…", path: "name.jpg"}` hochladen (Typ wird serverseitig erschnüffelt). Scope: `namespace.<ns>.<col>.create`. Methode: `POST`.
```json title="document_create — tools/call"
{
"jsonrpc": "2.0",
"id": 15,
"method": "tools/call",
"params": {
"name": "document_create",
"arguments": {"namespace": "<ns>", "collection": "content", "data": {"name": "Neu"}}
}
}
```
Fehler: ohne Schreibrecht → 403; Pflichtfeld-Verletzung → 400 aus der Validierung.
### document_update
Signatur: `namespace*`, `collection*`, dazu `id` (einzeln) oder `filter` (Bulk), plus `data`. Gespeicherte Datei-`src` zurückgeben behält die Datei. Scope: `namespace.<ns>.<col>.update` (+ Bulk-Freigabe). Methode: `PUT`.
```json title="document_update — tools/call"
{
"jsonrpc": "2.0",
"id": 16,
"method": "tools/call",
"params": {
"name": "document_update",
"arguments": {"namespace": "<ns>", "collection": "content", "id": "<id>", "data": {"name": "Neu"}}
}
}
```
Fehler: unbekannte `id` → 404; Bulk ohne `filter` → 400 (`filter query parameter is required`).
### document_delete
Signatur: `namespace*`, `collection*`, dazu `id` (einzeln) oder `filter` (Bulk). Scope: `namespace.<ns>.<col>.delete` (+ Bulk-Freigabe). Methode: `DELETE`.
```json title="document_delete — tools/call"
{
"jsonrpc": "2.0",
"id": 17,
"method": "tools/call",
"params": {
"name": "document_delete",
"arguments": {"namespace": "<ns>", "collection": "content", "id": "<id>"}
}
}
```
Fehler: ohne Löschrecht → 403; Bulk ohne `filter` → 400 (`filter query parameter is required for bulk delete`).
### file_read
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`, `collection*`, `id*`, `field*`, optional `path`. Scope: `namespace.<ns>.<col>.read`.
```json title="file_read — tools/call"
{
"jsonrpc": "2.0",
"id": 18,
"method": "tools/call",
"params": {
"name": "file_read",
"arguments": {"namespace": "<ns>", "collection": "content", "id": "<id>", "field": "image"}
}
}
```
Fehler: immer 400 (`use GET file endpoint (public like web)`) — nutze stattdessen `GET /_/:project/:collection/:id/:field/*path`.
### action_call
Signatur: `namespace*`, `action*`, optional `method` (`get` oder `post`, Standard `post`), `data`. Scope: `namespace.<ns>._actions.<name>.read` bzw. `.create`. Methode: `GET` oder `POST` je nach `method`.
```json title="action_call — tools/call"
{
"jsonrpc": "2.0",
"id": 19,
"method": "tools/call",
"params": {
"name": "action_call",
"arguments": {"namespace": "<ns>", "action": "versand", "method": "post", "data": {}}
}
}
```
Fehler: `method: "put"` → 400 (`action method must be get or post`, `method.go`).
### projects_list
Signatur: optional `limit`, `offset`. Scope: `project.read`. Methode: `GET`.
```json title="projects_list — tools/call"
{
"jsonrpc": "2.0",
"id": 20,
"method": "tools/call",
"params": {"name": "projects_list", "arguments": {"limit": 10}}
}
```
Fehler: ohne `project.read`-Scope → 403; fremde Orgs filtert die Sichtbarkeit still heraus (leere Liste trotz Daten).
### project_get
Signatur: `id*`. Scope: `project.read`. Methode: `GET`.
```json title="project_get — tools/call"
{
"jsonrpc": "2.0",
"id": 21,
"method": "tools/call",
"params": {"name": "project_get", "arguments": {"id": "<id>"}}
}
```
Fehler: ohne `id` → 400 (`id is required`); unsichtbar/ unbekannt → 404 (`not found`).
### collections_list
Signatur: `namespace*`. Scope: `project.read` plus Lese-Recht je Sammlung. Methode: `GET`.
```json title="collections_list — tools/call"
{
"jsonrpc": "2.0",
"id": 22,
"method": "tools/call",
"params": {"name": "collections_list", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: ohne `namespace` → 400 (`namespace is required`); unbekanntes Projekt → 404 (`project not found`).
### collection_describe
Signatur: `namespace*`, `collection*`, optional `lang`. Gibt Schema, `agent`- und `query`-Meta (bereinigt, ohne Secrets) plus `yourPermissions` zurück. Scope: `project.read` plus Lese-Recht auf die Sammlung. Methode: `GET`.
```json title="collection_describe — tools/call"
{
"jsonrpc": "2.0",
"id": 23,
"method": "tools/call",
"params": {
"name": "collection_describe",
"arguments": {"namespace": "<ns>", "collection": "content"}
}
}
```
Fehler: unbekannte Sammlung → 404 (`collection not found`); ohne Leserecht → 403 (`permission denied`).
### actions_list
Signatur: `namespace*`. Scope: Lese-Recht auf den Namensraum oder je Aktion. Methode: `GET`.
```json title="actions_list — tools/call"
{
"jsonrpc": "2.0",
"id": 24,
"method": "tools/call",
"params": {"name": "actions_list", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: ohne `namespace` → 400 (`namespace is required`); Aktionen ohne Recht fehlen still in der Liste.
### action_describe
Signatur: `namespace*`, `action*`. Scope: `namespace.<ns>._actions.<name>.read`. Methode: `GET`.
```json title="action_describe — tools/call"
{
"jsonrpc": "2.0",
"id": 25,
"method": "tools/call",
"params": {
"name": "action_describe",
"arguments": {"namespace": "<ns>", "action": "versand"}
}
}
```
Fehler: unbekannte Aktion → 404 (`action not found`); ohne Aktions-Scope → 403.
### jobs_list
Signatur: `namespace*`. Nur Configs, kein Trigger. Scope: `project.read`. Methode: `GET`.
```json title="jobs_list — tools/call"
{
"jsonrpc": "2.0",
"id": 26,
"method": "tools/call",
"params": {"name": "jobs_list", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: ohne `project.read`-Scope → 403; unbekanntes Projekt → 404 (`project not found`).
### openapi_get
Signatur: `namespace*`, optional `lang`. Laut Tool-Beschreibung öffentlich wie `GET /_openapi/:project`. Methode: `GET`.
```json title="openapi_get — tools/call"
{
"jsonrpc": "2.0",
"id": 27,
"method": "tools/call",
"params": {"name": "openapi_get", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: unbekanntes Projekt → 404 (`project not found`).
### system_meta_get
Signatur: optional `model` (`user`/`project`). Laut Tool-Beschreibung öffentlich wie `GET /_meta`. Methode: `GET`.
```json title="system_meta_get — tools/call"
{
"jsonrpc": "2.0",
"id": 28,
"method": "tools/call",
"params": {"name": "system_meta_get", "arguments": {"model": "user"}}
}
```
Fehler: ohne PAT → 403 (`mcp requires a personal access token`) — sonst antwortet das Werkzeug immer mit 200.
### agent_guide_get
Signatur: `namespace*`, optional `collection`, `action`, `lang`. Liest kuratiertes `meta.agent` (Projekt → Sammlung/Aktion) plus `yourPermissions`. Scope: je Ebene das passende Lese-Recht. Methode: `GET`.
```json title="agent_guide_get — tools/call"
{
"jsonrpc": "2.0",
"id": 29,
"method": "tools/call",
"params": {
"name": "agent_guide_get",
"arguments": {"namespace": "<ns>", "collection": "content"}
}
}
```
Fehler: ohne Sammlungs-Leserecht → 403 (`permission denied`); unbekanntes Projekt → 404 (`project not found`).
### assets_list
Signatur: `namespace*`. Scope: `project.read`. Methode: `GET`.
```json title="assets_list — tools/call"
{
"jsonrpc": "2.0",
"id": 30,
"method": "tools/call",
"params": {"name": "assets_list", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: unbekanntes Projekt → 404 (`project not found`).
### asset_read
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`, `name*`, `path*`. Methode: `GET`.
```json title="asset_read — tools/call"
{
"jsonrpc": "2.0",
"id": 31,
"method": "tools/call",
"params": {
"name": "asset_read",
"arguments": {"namespace": "<ns>", "name": "bilder", "path": "logo.svg"}
}
}
```
Fehler: immer 400 (`use GET /_/:project/_/assets/:name/*path (public like web)`) — nutze stattdessen den Asset-Endpunkt.
### users_list
Signatur: optional `filter`, `limit`, `offset` (alle als String typisiert, `tools_system.go`). Scope: `user.read`. Methode: `GET`.
```json title="users_list — tools/call"
{
"jsonrpc": "2.0",
"id": 32,
"method": "tools/call",
"params": {"name": "users_list", "arguments": {"limit": "10"}}
}
```
Fehler: ohne `user.read`-Scope → 403; fremde Benutzer filtert die Eigentümer-Sicht heraus.
### user_get
Signatur: `id*`. Scope: `user.read`. Methode: `GET`.
```json title="user_get — tools/call"
{
"jsonrpc": "2.0",
"id": 33,
"method": "tools/call",
"params": {"name": "user_get", "arguments": {"id": "<id>"}}
}
```
Fehler: ohne `id` → 400 (`id is required`); unsichtbar → 403 über den Scope-Filter.
### user_create
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: optional `data`. Scope: `user.create`.
```json title="user_create — tools/call"
{
"jsonrpc": "2.0",
"id": 34,
"method": "tools/call",
"params": {"name": "user_create", "arguments": {"data": {"username": "neu"}}}
}
```
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### user_update
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: optional `id`, `data`. Scope: `user.update`.
```json title="user_update — tools/call"
{
"jsonrpc": "2.0",
"id": 35,
"method": "tools/call",
"params": {
"name": "user_update",
"arguments": {"id": "<id>", "data": {"username": "neu"}}
}
}
```
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### user_delete
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `id*`. Scope: `user.delete`.
```json title="user_delete — tools/call"
{
"jsonrpc": "2.0",
"id": 36,
"method": "tools/call",
"params": {"name": "user_delete", "arguments": {"id": "<id>"}}
}
```
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### orgs_list
Signatur: optional `limit` (als String typisiert). Scope: `org.read`. Methode: `GET`.
```json title="orgs_list — tools/call"
{
"jsonrpc": "2.0",
"id": 37,
"method": "tools/call",
"params": {"name": "orgs_list", "arguments": {}}
}
```
Fehler: ohne `org.read`-Scope → 403; fremde Orgs filtert die Mitgliedschaft heraus.
### org_get
Signatur: `id*`. Scope: `org.read`. Methode: `GET`.
```json title="org_get — tools/call"
{
"jsonrpc": "2.0",
"id": 38,
"method": "tools/call",
"params": {"name": "org_get", "arguments": {"id": "<id>"}}
}
```
Fehler: ohne `id` → 400 (`id is required`); fremde Org → 403 über den Scope-Filter.
### teams_list
Signatur: optional `limit` (als String typisiert). Scope: `team.read`. Methode: `GET`.
```json title="teams_list — tools/call"
{
"jsonrpc": "2.0",
"id": 39,
"method": "tools/call",
"params": {"name": "teams_list", "arguments": {}}
}
```
Fehler: ohne `team.read`-Scope → 403.
### team_get
Signatur: `id*`. Scope: `team.read`. Methode: `GET`.
```json title="team_get — tools/call"
{
"jsonrpc": "2.0",
"id": 40,
"method": "tools/call",
"params": {"name": "team_get", "arguments": {"id": "<id>"}}
}
```
Fehler: ohne `id` → 400 (`id is required`).
### audit_query
Signatur: optional `filter`, `limit` (als String typisiert). Scope: `audit.read` plus Lese-Recht je Sammlung; System-Ereignisse bleiben für Nicht-Admins verborgen. Methode: `GET`.
```json title="audit_query — tools/call"
{
"jsonrpc": "2.0",
"id": 41,
"method": "tools/call",
"params": {"name": "audit_query", "arguments": {"limit": "20"}}
}
```
Fehler: ohne `audit.read`-Scope → 403.
### project_reload
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.reload`.
```json title="project_reload — tools/call"
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {"name": "project_reload", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "project_reload" is REST-only in this phase, use REST admin endpoints`).
### regenerate_search
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.regenerate-search`.
```json title="regenerate_search — tools/call"
{
"jsonrpc": "2.0",
"id": 43,
"method": "tools/call",
"params": {"name": "regenerate_search", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "regenerate_search" is REST-only in this phase, use REST admin endpoints`).
### backup_create
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.backup`.
```json title="backup_create — tools/call"
{
"jsonrpc": "2.0",
"id": 44,
"method": "tools/call",
"params": {"name": "backup_create", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "backup_create" is REST-only in this phase, use REST admin endpoints`).
### snapshots_list
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.snapshot`. Methode: `GET`.
```json title="snapshots_list — tools/call"
{
"jsonrpc": "2.0",
"id": 45,
"method": "tools/call",
"params": {"name": "snapshots_list", "arguments": {"namespace": "<ns>"}}
}
```
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "snapshots_list" is REST-only in this phase, use REST admin endpoints`).
## REST-only-Ausnahmen
Neun Werkzeuge antworten nur mit einem Verweis auf REST — sie führen per MCP nichts aus:
| Werkzeug | Stattdessen | | Werkzeug | Stattdessen |
|---|---| |---|---|
@@ -696,8 +106,285 @@ Neun Werkzeuge antworten nur mit einem Verweis auf REST — sie führen per MCP
| `file_read` | `GET /_/:project/:collection/:id/:field/*path` | | `file_read` | `GET /_/:project/:collection/:id/:field/*path` |
| `asset_read` | `GET /_/:project/_/assets/:name/*path` | | `asset_read` | `GET /_/:project/_/assets/:name/*path` |
Erkennbar an `"REST-only in this phase"` in der Beschreibung von `tools/list`. ## Werkzeug-Referenz (alle 36)
`allowAdminActions` steuert nur, ob Admin-Werkzeuge überhaupt sichtbar sind — die Sperre bleibt.
Pro Werkzeug: Signatur (Pflichtparameter mit `*`), genau ein `tools/call`-Beispiel als Einzeiler und der wichtigste Fehlerfall aus dem Code (`mcp_tools_impl.go`, `guards.go`, `query.go`, `method.go`).
Ohne PAT scheitert jedes Werkzeug mit 403 (`mcp requires a personal access token (X-Auth-Token: tibi_pat_...)`, `guards.go`); ein Tippfehler im Namen gibt `-32601 unknown tool` (`mcp_handler.go`).
### documents_list — Einträge listen
Signatur: `namespace*`, `collection*`, optional `filter`, `sort`, `limit`, `offset`, `select`, `projection`, `lookup`, `aggregate`, `q`, `qName`, `count`, `extra`. Scope: `namespace.<ns>.<col>.read` (bei `lookup`/`aggregate` zusätzlich Lese-Recht auf das Ziel). Methode: `GET`.
`tools/call`: `{"name":"documents_list","arguments":{"namespace": "<ns>", "collection": "content", "limit": 5}}`
Fehler: ohne `namespace`/`collection` → 400 (`namespace and collection are required`); lange Listen kappt `maxResultItems` (Standard 100).
### document_get — Ein Eintrag per ID
Signatur: `namespace*`, `collection*`, `id*`, optional `select`, `lookup`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
`tools/call`: `{"name":"document_get","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>"}}`
Fehler: unbekannte `id` → 404 (`not found`); unlesbare Hex-ID → 400.
### documents_count — Nur Anzahl
Signatur: `namespace*`, `collection*`, optional `filter`, `q`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
`tools/call`: `{"name":"documents_count","arguments":{"namespace": "<ns>", "collection": "content"}}`
Fehler: ohne Leserecht am Ziel → 403 (`permission denied` bzw. Scope-Fehler).
### documents_facet — Werte je Feld zählen
Signatur: `namespace*`, `collection*`, `fields*`, optional `filter`, `extra`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
`tools/call`: `{"name":"documents_facet","arguments":{"namespace": "<ns>", "collection": "content", "fields": "status"}}`
Fehler: ohne `fields` → 400 (`missing required parameter: facet` auf Controller-Ebene).
### queue_status — Suchindex-Warteschlange
Signatur: `namespace*`, `collection*`. Scope: `namespace.<ns>.<col>.read`. Methode: `GET`.
`tools/call`: `{"name":"queue_status","arguments":{"namespace": "<ns>", "collection": "content"}}`
Fehler: unbekannte Sammlung → 404 auf Controller-Ebene.
### document_create — Eintrag anlegen
Signatur: `namespace*`, `collection*`, `data*`, optional `items[]` (Bulk). Datei-Felder als `{src: "data:<mime>;base64,…", path: "name.jpg"}` hochladen (Typ wird serverseitig erschnüffelt). Scope: `namespace.<ns>.<col>.create`. Methode: `POST`.
`tools/call`: `{"name":"document_create","arguments":{"namespace": "<ns>", "collection": "content", "data": {"name": "Neu"}}}`
Fehler: ohne Schreibrecht → 403; Pflichtfeld-Verletzung → 400 aus der Validierung.
### document_update — Eintrag ändern
Signatur: `namespace*`, `collection*`, dazu `id` (einzeln) oder `filter` (Bulk), plus `data`. Gespeicherte Datei-`src` zurückgeben behält die Datei. Scope: `namespace.<ns>.<col>.update` (+ Bulk-Freigabe). Methode: `PUT`.
`tools/call`: `{"name":"document_update","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>", "data": {"name": "Neu"}}}`
Fehler: unbekannte `id` → 404; Bulk ohne `filter` → 400 (`filter query parameter is required`).
### document_delete — Eintrag löschen
Signatur: `namespace*`, `collection*`, dazu `id` (einzeln) oder `filter` (Bulk). Scope: `namespace.<ns>.<col>.delete` (+ Bulk-Freigabe). Methode: `DELETE`.
`tools/call`: `{"name":"document_delete","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>"}}`
Fehler: ohne Löschrecht → 403; Bulk ohne `filter` → 400 (`filter query parameter is required for bulk delete`).
### file_read — Datei lesen (REST-only)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`, `collection*`, `id*`, `field*`, optional `path`. Scope: `namespace.<ns>.<col>.read`.
Fehler: immer 400 (`use GET file endpoint (public like web)`) — nutze stattdessen `GET /_/:project/:collection/:id/:field/*path`.
### action_call — Aktion aufrufen (`method: get/post`)
Signatur: `namespace*`, `action*`, optional `method` (`get` oder `post`, Standard `post`), `data`. Scope: `namespace.<ns>._actions.<name>.read` bzw. `.create`. Methode: `GET` oder `POST` je nach `method`.
`tools/call`: `{"name":"action_call","arguments":{"namespace": "<ns>", "action": "versand", "method": "post", "data": {}}}`
Fehler: `method: "put"` → 400 (`action method must be get or post`, `method.go`).
### projects_list — Projekte auflisten / ein Projekt
Signatur: optional `limit`, `offset`. Scope: `project.read`. Methode: `GET`.
`tools/call`: `{"name":"projects_list","arguments":{"limit": 10}}`
Fehler: ohne `project.read`-Scope → 403; fremde Orgs filtert die Sichtbarkeit still heraus (leere Liste trotz Daten).
### project_get — Projekte auflisten / ein Projekt
Signatur: `id*`. Scope: `project.read`. Methode: `GET`.
`tools/call`: `{"name":"project_get","arguments":{"id": "<id>"}}`
Fehler: ohne `id` → 400 (`id is required`); unsichtbar/ unbekannt → 404 (`not found`).
### collections_list — Sammlungen eines Projekts
Signatur: `namespace*`. Scope: `project.read` plus Lese-Recht je Sammlung. Methode: `GET`.
`tools/call`: `{"name":"collections_list","arguments":{"namespace": "<ns>"}}`
Fehler: ohne `namespace` → 400 (`namespace is required`); unbekanntes Projekt → 404 (`project not found`).
### collection_describe — Schema + `meta` einer Sammlung
Signatur: `namespace*`, `collection*`, optional `lang`. Gibt Schema, `agent`- und `query`-Meta (bereinigt, ohne Secrets) plus `yourPermissions` zurück. Scope: `project.read` plus Lese-Recht auf die Sammlung. Methode: `GET`.
`tools/call`: `{"name":"collection_describe","arguments":{"namespace": "<ns>", "collection": "content"}}`
Fehler: unbekannte Sammlung → 404 (`collection not found`); ohne Leserecht → 403 (`permission denied`).
### actions_list — Aktionen auflisten / beschreiben
Signatur: `namespace*`. Scope: Lese-Recht auf den Namensraum oder je Aktion. Methode: `GET`.
`tools/call`: `{"name":"actions_list","arguments":{"namespace": "<ns>"}}`
Fehler: ohne `namespace` → 400 (`namespace is required`); Aktionen ohne Recht fehlen still in der Liste.
### action_describe — Aktionen auflisten / beschreiben
Signatur: `namespace*`, `action*`. Scope: `namespace.<ns>._actions.<name>.read`. Methode: `GET`.
`tools/call`: `{"name":"action_describe","arguments":{"namespace": "<ns>", "action": "versand"}}`
Fehler: unbekannte Aktion → 404 (`action not found`); ohne Aktions-Scope → 403.
### jobs_list — Jobs auflisten
Signatur: `namespace*`. Nur Configs, kein Trigger. Scope: `project.read`. Methode: `GET`.
`tools/call`: `{"name":"jobs_list","arguments":{"namespace": "<ns>"}}`
Fehler: ohne `project.read`-Scope → 403; unbekanntes Projekt → 404 (`project not found`).
### openapi_get — OpenAPI-Ausschnitt
Signatur: `namespace*`, optional `lang`. Laut Tool-Beschreibung öffentlich wie `GET /_openapi/:project`. Methode: `GET`.
`tools/call`: `{"name":"openapi_get","arguments":{"namespace": "<ns>"}}`
Fehler: unbekanntes Projekt → 404 (`project not found`).
### system_meta_get — System-Metadaten
Signatur: optional `model` (`user`/`project`). Laut Tool-Beschreibung öffentlich wie `GET /_meta`. Methode: `GET`.
`tools/call`: `{"name":"system_meta_get","arguments":{"model": "user"}}`
Fehler: ohne PAT → 403 (`mcp requires a personal access token`) — sonst antwortet das Werkzeug immer mit 200.
### agent_guide_get — Agent-Anleitung (`meta.agent`) lesen
Signatur: `namespace*`, optional `collection`, `action`, `lang`. Liest kuratiertes `meta.agent` (Projekt → Sammlung/Aktion) plus `yourPermissions`. Scope: je Ebene das passende Lese-Recht. Methode: `GET`.
`tools/call`: `{"name":"agent_guide_get","arguments":{"namespace": "<ns>", "collection": "content"}}`
Fehler: ohne Sammlungs-Leserecht → 403 (`permission denied`); unbekanntes Projekt → 404 (`project not found`).
### assets_list — Projekt-Assets listen
Signatur: `namespace*`. Scope: `project.read`. Methode: `GET`.
`tools/call`: `{"name":"assets_list","arguments":{"namespace": "<ns>"}}`
Fehler: unbekanntes Projekt → 404 (`project not found`).
### asset_read — Asset lesen (REST-only: `GET /_/:project/_/assets/:name/*path`)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`, `name*`, `path*`. Methode: `GET`.
Fehler: immer 400 (`use GET /_/:project/_/assets/:name/*path (public like web)`) — nutze stattdessen den Asset-Endpunkt.
### users_list — Benutzer listen / lesen
Signatur: optional `filter`, `limit`, `offset` (alle als String typisiert, `tools_system.go`). Scope: `user.read`. Methode: `GET`.
`tools/call`: `{"name":"users_list","arguments":{"limit": "10"}}`
Fehler: ohne `user.read`-Scope → 403; fremde Benutzer filtert die Eigentümer-Sicht heraus.
### user_get — Benutzer listen / lesen
Signatur: `id*`. Scope: `user.read`. Methode: `GET`.
`tools/call`: `{"name":"user_get","arguments":{"id": "<id>"}}`
Fehler: ohne `id` → 400 (`id is required`); unsichtbar → 403 über den Scope-Filter.
### user_create — Benutzer anlegen (REST-only: `REST /api/v1/user`)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: optional `data`. Scope: `user.create`.
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### user_update — Benutzer ändern (REST-only: `REST /api/v1/user`)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: optional `id`, `data`. Scope: `user.update`.
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### user_delete — Benutzer löschen (REST-only: `REST /api/v1/user`)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `id*`. Scope: `user.delete`.
Fehler: immer 400 (`user write via MCP is REST-only in this phase, use REST /api/v1/user`) — nutze stattdessen `REST /api/v1/user`.
### orgs_list — Orgs listen / lesen
Signatur: optional `limit` (als String typisiert). Scope: `org.read`. Methode: `GET`.
`tools/call`: `{"name":"orgs_list","arguments":{}}`
Fehler: ohne `org.read`-Scope → 403; fremde Orgs filtert die Mitgliedschaft heraus.
### org_get — Orgs listen / lesen
Signatur: `id*`. Scope: `org.read`. Methode: `GET`.
`tools/call`: `{"name":"org_get","arguments":{"id": "<id>"}}`
Fehler: ohne `id` → 400 (`id is required`); fremde Org → 403 über den Scope-Filter.
### teams_list — Teams listen / lesen
Signatur: optional `limit` (als String typisiert). Scope: `team.read`. Methode: `GET`.
`tools/call`: `{"name":"teams_list","arguments":{}}`
Fehler: ohne `team.read`-Scope → 403.
### team_get — Teams listen / lesen
Signatur: `id*`. Scope: `team.read`. Methode: `GET`.
`tools/call`: `{"name":"team_get","arguments":{"id": "<id>"}}`
Fehler: ohne `id` → 400 (`id is required`).
### audit_query — Audit-Log abfragen
Signatur: optional `filter`, `limit` (als String typisiert). Scope: `audit.read` plus Lese-Recht je Sammlung; System-Ereignisse bleiben für Nicht-Admins verborgen. Methode: `GET`.
`tools/call`: `{"name":"audit_query","arguments":{"limit": "20"}}`
Fehler: ohne `audit.read`-Scope → 403.
### project_reload — Projekt neu laden (REST-only: Admin-Reload-Endpunkt)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.reload`.
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "project_reload" is REST-only in this phase, use REST admin endpoints`).
### regenerate_search — Suchindex neu aufbauen (REST-only: Admin-Suchindex-Endpunkt)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.regenerate-search`.
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "regenerate_search" is REST-only in this phase, use REST admin endpoints`).
### backup_create — Backup erzeugen (REST-only: Admin-Backup-Endpunkt)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.backup`.
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "backup_create" is REST-only in this phase, use REST admin endpoints`).
### snapshots_list — Snapshots auflisten (REST-only: Admin-Snapshots-Endpunkt)
<Badge text="REST-only" variant="caution" /> Führt per MCP nichts aus. Signatur: `namespace*`. Scope: `project.snapshot`. Methode: `GET`.
Fehler: bei `allowAdminActions: false` → 403 (`admin actions disabled`); sonst immer 400 (`admin tool "snapshots_list" is REST-only in this phase, use REST admin endpoints`).
## REST-only-Ausnahmen
Siehe [Grenzen: REST-only-Werkzeuge](#grenzen-rest-only-werkzeuge) weiter oben — dort steht die vollständige Liste.
## Eigene Agent-Anleitung pflegen (`meta.agent`) ## Eigene Agent-Anleitung pflegen (`meta.agent`)
@@ -751,4 +438,4 @@ REST ignoriert das Schema (offen per Design) und nutzt es als Doku; MCP validier
| `-32602 invalid params` | Pflicht-Argument fehlt | `collection_describe`-Schema bzw. [Fehlerbilder](/reference/fehlercodes/) lesen | | `-32602 invalid params` | Pflicht-Argument fehlt | `collection_describe`-Schema bzw. [Fehlerbilder](/reference/fehlercodes/) lesen |
| Leere Listen trotz Daten | `maxResultItems` (Standard 100) oder `filter_public`-Hook | Limit erhöhen bzw. `active:true` + Zeitraum prüfen | | Leere Listen trotz Daten | `maxResultItems` (Standard 100) oder `filter_public`-Hook | Limit erhöhen bzw. `active:true` + Zeitraum prüfen |
Verifiziert am 68b42785 2026-10-16 (tibi-server: tools_crud, tools_discovery, tools_system, method, query, guards, registry, server, mcp_handler, mcp_tools_impl, setup), am e3f92eb 2026-10-14 (tibi-svelte-starter: config.yml und content.yml meta.agent). Verifiziert am 68b4278 2026-10-17 (tibi-server: tools_crud, tools_discovery, tools_system, method, query, guards, registry, server, mcp_handler, mcp_tools_impl, setup), am e3f92eb 2026-10-17 (tibi-svelte-starter: config.yml und content.yml meta.agent)
+5 -11
View File
@@ -6,7 +6,7 @@ description: 'Navigations-Collection, Sprachfelder, translationKey, Pfad-Regeln.
import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Navigation und Sprachen hängen zusammen: Die `navigation`-Sammlung hält je Sprache einen Baum, `content` hält je Sprache einen Eintrag. Navigation und Sprachen hängen zusammen: Die `navigation`-Sammlung hält je Sprache einen Baum, `content` hält je Sprache einen Eintrag.
Suche-Begriffe: Navigation anlegen, Sprache umschalten, translationKey. Suchbegriffe: Navigation anlegen, Sprache umschalten, translationKey.
## Navigations-Dokumente ## Navigations-Dokumente
@@ -41,9 +41,7 @@ fields:
</Steps> </Steps>
<Aside type="caution" title="active:true auch hier"> <Aside type="caution" title="active:true auch hier">
`filter_public.js` filtert anonym `active:true` — `navigation.yml` deklariert kein solches Feld. Anonyme Abfragen sehen nur Dokumente mit `active: true` (kanonische Erklärung unter [Rechte und Tokens](/projekt/rechte-tokens/)) — `navigation.yml` deklariert dafür kein eigenes Feld, lege das Flag trotzdem auf jedem der vier Bäume ab.
Ohne gespeichertes `active:true` liefert die öffentliche Abfrage `count 0` (Vorfall 2026-10-05), mit Token sieht alles gut aus.
Lege `active:true` auf jedem Navigations-Dokument ab.
</Aside> </Aside>
## Sprachmodell: lang + translationKey ## Sprachmodell: lang + translationKey
@@ -63,7 +61,7 @@ meta:
|---|---| |---|---|
| `lang` | Sprachkürzel, z. B. `de`, `en` | | `lang` | Sprachkürzel, z. B. `de`, `en` |
| `translationKey` | **Pflicht**, z. B. `home`, `ueber-uns` — pro Sprache eindeutig (Unique-Index `translationKey` + `lang`) | | `translationKey` | **Pflicht**, z. B. `home`, `ueber-uns` — pro Sprache eindeutig (Unique-Index `translationKey` + `lang`) |
| Zweiter Datensatz ohne Schlüssel | Kollidiert auf `null` — kein „geht schon gut" | | Zweiter Datensatz ohne Schlüssel | Kollidiert auf `null` — vergebe immer einen eindeutigen `translationKey` je Sprache |
| `path` | Ohne Sprachpräfix gespeichert (`/ueber-uns`), URL mit Präfix (`/de/ueber-uns`) | | `path` | Ohne Sprachpräfix gespeichert (`/ueber-uns`), URL mit Präfix (`/de/ueber-uns`) |
| `alternativePaths[].path` | Alte Pfade, gleiches Muster wie `path` | | `alternativePaths[].path` | Alte Pfade, gleiches Muster wie `path` |
@@ -81,11 +79,7 @@ Die Feld-i18n (Tabs je Feld, Test-Plan `34-i18n-fields`) und Entry-i18n (eine Sp
## active:true auch hier (Checkliste) ## active:true auch hier (Checkliste)
| Sammlung | Ohne `active:true` öffentlich … | Ohne `active: true` bleiben Abfragen öffentlich leer — je Sammlung wirkt sich das anders aus (kanonische Tabelle unter [Rechte und Tokens](/projekt/rechte-tokens/)): `content` wird weggefiltert, `medialib`-Lookups bleiben leer ([Dateien und Bilder](/projekt/dateien-bilder/)), Header/Footer sind leer.
|---|---|
| `content` | … filtern `filter_public` + SSR-Prüfung den Eintrag weg |
| `medialib` | … bleibt `_lookup` leer, Bilder verschwinden (siehe [Dateien und Bilder](/projekt/dateien-bilder/)) |
| `navigation` | … ist Header/Footer leer |
## Prüfung: anonyme SSR-Nav-Zahl ## Prüfung: anonyme SSR-Nav-Zahl
@@ -116,4 +110,4 @@ GET …/ssr?url=/de/
Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Dateien und Bilder](/projekt/dateien-bilder/), [Rechte und Tokens](/projekt/rechte-tokens/). Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Dateien und Bilder](/projekt/dateien-bilder/), [Rechte und Tokens](/projekt/rechte-tokens/).
Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter), am 2b091d89 2026-10-13 (tibi-admin-nova, Test-Pläne 34-i18n-fields, 35-entry-i18n) Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 2b091d8 2026-10-17 (tibi-admin-nova, Test-Pläne 34-i18n-fields, 35-entry-i18n)
+32 -59
View File
@@ -53,8 +53,23 @@ export { registry as blockRegistry }
| `css` | Array mit Stylesheet-URLs, wird vor dem Mount vorgeladen | | `css` | Array mit Stylesheet-URLs, wird vor dem Mount vorgeladen |
| Fehlt der Typ | `render()` gibt `false` zurück → Blueprint-Karte statt Vorschau | | Fehlt der Typ | `render()` gibt `false` zurück → Blueprint-Karte statt Vorschau |
Laden ist lazy und gecacht (`loadBlockRegistry`). Laden ist lazy und pro URL gecacht (`loadBlockRegistry`, `load-registry.ts:68-94`: `Map`-Cache pro aufgelöstem Specifier; Import via dynamischem `import()`, vor esbuild versteckt).
Schlägt der Import fehl, zeigt der Editor den Blueprint-Modus — kein Fehler, nur keine Vorschau. Schlägt der Import fehl oder fehlt der Export (`default`/`blockRegistry`), zeigt der Editor den Blueprint-Modus — kein Fehler, nur keine Vorschau.
Registry-Pfad-Auflösung in drei Schritten (`resolveBlockRegistrySpecifier`, `load-registry.ts:18-35`):
1. Quelle: Feld-`meta.pagebuilder.blockRegistry.file`, Rückfall auf Collection-`meta.pagebuilder…` — jeweils String **oder** `{ eval }`-Ausdruck (ausgewertet per `resolveMetaEval` mit Namespace und API-Basis).
2. Ergebnis durch `resolveProjectAssetSpecifier` (Projekt-Basis) — siehe Tabelle unten.
3. CSS-Präzedenz (`resolveBlockCssTransform`, `load-registry.ts:50-61`): Block-Definition → Feld-`meta.pagebuilder.cssTransform` → Collection → Default `true` (`undefined` heißt „nicht gesetzt", nicht `false`).
| Specifier | Auflösung (`project-asset-specifier.ts:17-50`) |
|---|---|
| `https://…`, `http://…`, `//…`, `data:`, `blob:` | Unverändert (extern/inline) |
| `_/…` | Projekt-relativ zur Namespace-Basis (empfohlen) |
| `/_/…` | Alias für projekt-relativ (rückwärtskompatibel) |
| `/…` | Host-absolut, bleibt wie geschrieben |
| relativ (`assets/x.js`) | Gegen die Projekt-Basis aufgelöst |
| `{ eval: "…" }` | JS-Ausdruck **vor** dem Specifier ausgewertet (`resolveMetaEval`, `load-registry.ts:26-31`), Ergebnis läuft danach durch dieselbe Tabelle |
## CSS-Umschreibung ## CSS-Umschreibung
@@ -178,64 +193,22 @@ Seed für Mock-Daten: `scripts/seed-mock-pagebuilder-screenshots.js` (Playwright
Design-Regeln aus dem Starter (`api/config.yml`, `meta.agent.designGuide`): Svelte-5-Runes (`$props`, nie `block` destrukturieren), Tailwind v4 kanonisch, i18n per `$_`, Bilder nur über `MedialibImage`. Design-Regeln aus dem Starter (`api/config.yml`, `meta.agent.designGuide`): Svelte-5-Runes (`$props`, nie `block` destrukturieren), Tailwind v4 kanonisch, i18n per `$_`, Bilder nur über `MedialibImage`.
## Admin-Keys und Verhalten
| Key | Wirkung | Quelle |
|---|---|---|
| `viewForm` / `viewBoth` / `viewPreview` | Modus-Umschalter im Overlay: Nur Formular, Formular und Vorschau, Nur Vorschau | `PagebuilderOverlay.svelte:293,310,326`, `de.json:598-600` |
| `closeTitle` | Schließen-Knopf mit ESC-Hinweis („Schließen (ESC)") | `PagebuilderOverlay.svelte:348`, `PagebuilderOverviewOverlay.svelte:725`, `de.json:596` |
| `viewportDesktop` / `viewportTablet` / `viewportPhone` | Breiten-Presets 1280 / 768 / 375 in der Viewport-Steuerung | `PagebuilderViewportControl.svelte:23-25`, `de.json:605-607` |
| Drill-Down-Modi `overlay` / `inline` | `overlay` (Standard): Fullscreen-Overlay mit Layer-Stack; `inline`: direkte Blockliste im Formular bei `meta.drillDown: false` | `PagebuilderOverviewOverlay.svelte:4-8,34,61,83`, `PagebuilderWidget.svelte:205,238` |
| `blueprintMoreFields` | „+{count} weitere" — eingeklappte Restfelder auf der Blueprint-Karte | `BlueprintCard.svelte:125`, `de.json:611` |
| `blueprintChildren` | Kind-Zähler („1 Kind" / „n Kinder") | `BlueprintCard.svelte:101`, `de.json:610` |
| `blueprintNoData` | Leertext „Keine Daten" bei fehlenden Block-Daten | `BlueprintCard.svelte:132`, `de.json:612` |
| ESC / Blur / Drilldown | `Escape` schließt nur den obersten Layer (`isTopLayer`), vorher `activeElement.blur()`; Drilldown-Navigation übernimmt Blur selbst | `PagebuilderOverlay.svelte:190-198,244`, `PagebuilderOverviewOverlay.svelte:378-382` |
## Fehler ## Fehler
### Editor-Overlay (`PagebuilderOverlay.svelte`) Der Block-Editor (Fullscreen-Split mit Formular und Live-Vorschau), die Viewport-Steuerung (Desktop/Tablet/Mobil-Presets plus Slider) sowie Übersichts-Overlay und Blueprint-Fallback (Schema-Karte bei fehlendem Registry-Typ) sind Admin-Oberfläche — Bedienung siehe [Eintrags-Editor](/admin/eintrags-editor/). Aus Entwickler-Sicht zählen die Ursachen dahinter:
Der Klick auf einen Block öffnet einen Fullscreen-Split-Editor auf dunklem Hintergrund (`backdrop-blur-md`, eigenes Layer per `pushLayer`, `focusTrap`, `role="dialog"` mit `aria-label` aus `admin.pagebuilder.editorAria`).
| Bereich | Inhalt |
|---|---|
| Links | Formular-Karte (`FieldRenderer` mit `subFields` des Blocktyps) |
| Rechts | Live-Vorschau (`RowPreview`) im skalierten Rahmen, passt sich automatisch an Breite und Höhe an |
| Toolbar links | Block-Label mit Farbpunkt plus Blocknummer (`#1`, `#2`, …) |
| Toolbar Mitte | Modus-Umschalter: **Nur Formular** (`viewForm`), **Formular und Vorschau** (`viewBoth`), **Nur Vorschau** (`viewPreview`) |
| Toolbar rechts | **OK** mit `ESC`-Badge, Titel **Schließen (ESC)** (`closeTitle`) |
Verhalten:
- **Speichern** gibt es im Overlay nicht — jede Eingabe schreibt per `onchange` sofort in die Zeile. **OK** und `ESC` schließen nur. `ESC` löst vorher `blur` aus; die eigentliche Prüfung läuft beim Verlassen der Drilldown-Ebene und vor dem Speichern (`entry:before-save`) über `validateAllFields`.
- **Validierung:** Beim Verlassen einer Drilldown-Ebene und vor dem Speichern (`entry:before-save`) prüft `validateAllFields` den aktuellen Pfad-Scope.
- **Breadcrumbs:** `DrawerBreadcrumb` (Layer-Stapel) oben, `DrillDownBreadcrumb` im Formular bei verschachtelten Objekten. `ESC` geht zuerst eine Drilldown-Ebene zurück und schließt erst am Root.
- **Schmal (< 768 px):** `both` entfällt, der Editor fällt auf **Nur Formular** zurück.
### Viewport-Steuerung (`PagebuilderViewportControl.svelte`)
Sit-observer unter der Vorschau (Editor) bzw. in der Übersichts-Toolbar. Sie simuliert die Seitenbreite, damit `@container`-Queries wie auf der echten Seite greifen.
| Preset | Breite |
|---|---|
| **Desktop** (`viewportDesktop`) | 1280 px |
| **Tablet** (`viewportTablet`) | 768 px |
| **Mobil** (`viewportPhone`) | 375 px |
| Freier Slider | 320–1920 px, Anzeige in `px`, Skalierung in `%` bei Verkleinerung |
Startwert ist `defaultViewport` (Feld-`meta.pagebuilder.defaultViewport` → Collection → `1280`); Preset-Namen oder Zahlen sind erlaubt.
### Übersichts-Overlay und `drillDown` (`PagebuilderOverviewOverlay.svelte`, `PagebuilderWidget.svelte`)
| Modus | Wann | Darstellung |
|---|---|---|
| `overlay` (Standard) | `meta.drillDown` gesetzt oder fehlt | Kompakt-Thumbnail (`PagebuilderThumbnail`) im Formular; Klick öffnet das Fullscreen-Übersichts-Overlay mit Blockliste, **Block hinzufügen**-Knopf und Viewport-Steuerung |
| `inline` | `meta.drillDown: false` am Feld | Blockliste rendert direkt im Formular, kein Thumbnail, kein Overlay |
Der Block-Editor (`PagebuilderOverlay`) öffnet sich in beiden Modi identisch obendrauf.
Block-Management in der Übersicht:
| Aktion | Wie |
|---|---|
| Hinzufügen | **Block hinzufügen** → Typ im Picker wählen — der neue Block öffnet sofort im Editor |
| Öffnen | Klick auf die Block-Vorschau |
| Sortieren | Drag-Handle (**Ziehen zum Sortieren**) oder Menü (**Aktionen**): **Nach oben**, **Nach unten** |
| Duplizieren / Löschen | Gleiches Menü: **Duplizieren**, **Löschen** |
| Schließen | **OK** oder `ESC` (oberstes Layer schließt zuerst) |
Leere Liste zeigt **Keine Inhaltsblöcke vorhanden**; die Toolbar zählt **Keine Blöcke** bzw. **N Blöcke** (`noBlocks`, `blocks`).
### BlueprintCard-Fallback (`BlueprintCard.svelte`)
Liefert `render()` `false` (Typ fehlt in der Registry) oder schlägt der Registry-Import fehl, zeigt `RowPreview` statt der Vorschau eine Schema-Karte: Block-Label als Badge, bis zu vier befüllte Felder als kompakte Werte (Text gekürzt, Richtext ohne HTML, Datei als Pfad, Farbe als Swatch), Rest als **+N weitere** (`blueprintMoreFields`), Kinder-Zähler (`blueprintChildren`), ohne Werte **Keine Daten** (`blueprintNoData`). Kein Fehlerzustand — nur keine Grafik.
| Symptom | Ursache | Lösung | | Symptom | Ursache | Lösung |
|---|---|---| |---|---|---|
@@ -245,4 +218,4 @@ Liefert `render()` `false` (Typ fehlt in der Registry) oder schlägt der Registr
| Altes Thumbnail | Screenshot-Regel fehlt oder `fileField` falsch | `meta.pagebuilder.screenshot` in `content.yml` prüfen | | Altes Thumbnail | Screenshot-Regel fehlt oder `fileField` falsch | `meta.pagebuilder.screenshot` in `content.yml` prüfen |
| Felder erscheinen nicht | `dependsOn`-Eval auf `$parent.type` fehlt | Eval wie oben setzen, Typ-Schreibweise exakt | | Felder erscheinen nicht | `dependsOn`-Eval auf `$parent.type` fehlt | Eval wie oben setzen, Typ-Schreibweise exakt |
Verifiziert am 2b091d89 2026-10-16 (tibi-admin-nova: PagebuilderOverlay, PagebuilderViewportControl, PagebuilderOverviewOverlay, BlueprintCard, PagebuilderWidget, de.json admin.pagebuilder.*), am e3f92eb 2026-10-14 (tibi-svelte-starter: content.yml, BlockRenderer, config.yml designGuide). Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova: de.json admin.pagebuilder.*), am e3f92eb 2026-10-17 (tibi-svelte-starter: content.yml, BlockRenderer, config.yml designGuide); Registry-Ladeordnung und Asset-Specifier verifiziert am 2b091d8 2026-10-17 (load-registry.ts:14-94, project-asset-specifier.ts:17-50)
+51 -1
View File
@@ -141,6 +141,56 @@ Mitglieder von Team A1 kommen in Projekt A, aber nicht in Projekt B.
Ohne Org-Zugehörigkeit geht gar nichts. Ohne Org-Zugehörigkeit geht gar nichts.
Details regelt der Server-Code (`access_control_matrix_test.go`) — für normale Projekte reicht: ein Projekt, ein Team, `ADMIN_TOKEN` für Skripte. Details regelt der Server-Code (`access_control_matrix_test.go`) — für normale Projekte reicht: ein Projekt, ein Team, `ADMIN_TOKEN` für Skripte.
## Scope-Katalog (PATs)
Persönliche Tokens (`POST /api/v1/token`, siehe [System-Routen](/reference/rest-system/)) tragen **Scopes** — Engpässe, die du pro Token vergibst.
`GET /api/v1/token/_scopes` listet, was du aktuell vergeben darfst (gleiche Quelle wie die Anlage-Prüfung).
| Gruppe | Beispiele | Bedeutung |
|---|---|---|
| System | `user.read`, `project.create`, `team.delete`, `org.read`, `audit.read`, `llm.chat` | CRUD je Domäne (`user`, `project`, `team`, `org`); `audit` nur `read`, `llm` nur `chat`; Projekt-Admin zusätzlich `project.reload`, `project.backup`, `project.regenerate-search`, `project.snapshot` |
| Namespace | `namespace` → alles; `namespace.shop` → alle Sammlungen dort; `namespace.shop.content` → alle Aktionen dort; `namespace.shop.content.read` → genau eine Aktion; `namespace.shop._actions.name.post` → Aktions-Endpunkt | Je länger, desto enger; `*` matcht genau ein Segment |
| Besitzer-Kürzel | Sammlungs-Kurzform ohne Aktion (z. B. `namespace.shop.content`) | Alle Aktionen, die der Besitzer dort aktuell darf |
<Aside type="caution">
Es gilt immer **UND**: Token-Scope **und** Besitzer-Recht.
Ein weites Token (`namespace`) gibt nie mehr her, als der Besitzer im Moment darf — verliert er das Team, verliert das Token die Wirkung, ohne dass du es anfassen musst.
Umgekehrt scheitert ein enges Token (`…content.read`) an `POST`, selbst wenn der Besitzer schreiben dürfte.
</Aside>
Die `token`-Collection-Felder im Überblick (`token.go`):
| Feld | Inhalt |
|---|---|
| `userId` | Besitzer (Nutzer-ID) |
| `name` | Anzeigename (`required`, 1–100 Zeichen) |
| `token` | Klartext — nur in der Anlage-Antwort (`tibi_pat_<id>_<secret>`), danach nie wieder |
| `tokenHash` / `tokenPrefix` | SHA-256-Hash (gespeichert) / Anzeige-Präfix (`tibi_pat_` + 4 Zeichen) |
| `scopes` | String-Liste aus dem Katalog oben |
| `expiresAt` | Ablaufzeitpunkt; leer = kein Ablauf |
| `lastUsedAt` | Letzte Nutzung (wird bei Gebrauch aktualisiert) |
## Projekt-Admin-Tokens
Skripte mit `X-Admin-Token` brauchen je Aktion ein Flag im Projekt (`tokens[]` in der Projekt-Config, Schema `project.schema.json`):
| Flag | Erlaubt |
|---|---|
| `allowReload` | `POST …/_/admin/reload` (Config neu laden) |
| `allowRegenerateSearch` | `POST …/_/admin/regenerate-search` (Suchindex neu aufbauen) |
| `allowBackup` | Backup herunter- und wiederherstellen |
| `allowSnapshots` | Snapshots auslösen, listen, löschen, wiederherstellen, herunterladen |
```yaml title="Projekt-Config — Token mit Reload- und Backup-Recht"
tokens:
- token: ersetze-mich-durch-zufall
allowReload: true
allowBackup: true
```
Ohne das passende Flag antwortet der Endpunkt mit `403` — das Token ist dann zwar gültig, aber für diese Aktion nicht freigeschaltet.
Feld-Rechte pro Rolle (`readonlyFields`, `hiddenFields`, `validProjections`) stehen auf der eigenen Seite [Feld-Rechte](/projekt/feld-rechte/).
## Fehlerbilder ## Fehlerbilder
| Antwort | Ursache | Lösung | | Antwort | Ursache | Lösung |
@@ -151,4 +201,4 @@ Details regelt der Server-Code (`access_control_matrix_test.go`) — für normal
| Öffentlich leer, mit Token voll | `active:true` fehlt oder `publication`-Fenster liegt in der Zukunft/Vergangenheit | `active:true` setzen, `publication.from`/`publication.to` prüfen | | Öffentlich leer, mit Token voll | `active:true` fehlt oder `publication`-Fenster liegt in der Zukunft/Vergangenheit | `active:true` setzen, `publication.from`/`publication.to` prüfen |
| Falscher Kopf, richtige Rolle | z. B. `Token:` statt `X-Auth-Token:` bei Nutzer-Rechten | Tabelle oben: Kopf an Rolle anpassen | | Falscher Kopf, richtige Rolle | z. B. `Token:` statt `X-Auth-Token:` bei Nutzer-Rechten | Tabelle oben: Kopf an Rolle anpassen |
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter, Hooks und Permissions), am 68b42785 2026-10-15 (tibi-server, access_control_matrix_test.go, Lookup-/Projektions-Prüfung). Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter, Hooks und Permissions), am 68b4278 2026-10-17 (tibi-server, access_control_matrix_test.go, Lookup-/Projektions-Prüfung), am 68b4278 2026-10-17 (tibi-server, Scopes: scope.go, token.go, token_scope_catalog.go; Projekt-Token-Flags: project.go, project.schema.json)
+19 -4
View File
@@ -70,7 +70,7 @@ fields:
1. Datei `api/collections/<name>.yml` anlegen, Schema-Zeile aus dem Beispiel kopieren. 1. Datei `api/collections/<name>.yml` anlegen, Schema-Zeile aus dem Beispiel kopieren.
2. `name` kleinschreiben, keine Umlaute und keine Leerzeichen (`content`, `tag`, `comment` als Vorbild). 2. `name` kleinschreiben, keine Umlaute und keine Leerzeichen (`content`, `tag`, `comment` als Vorbild).
3. `permissions` setzen — sonst liest niemand die Daten (Details: [Rechte und Tokens](/projekt/rechte-tokens/)). 3. `permissions` setzen — sonst liest niemand die Daten (Details: [Rechte und Tokens](/projekt/rechte-tokens/)).
4. `uploadPath` setzen, auch wenn die Sammlung keine Dateien enthält (`../media/<name>` wie in allen Starter-Sammlungen). 4. `uploadPath` setzen (`../media/<name>` wie in allen Starter-Sammlungen) — empfohlen auch ohne Datei-Felder: Datei-Uploads landen dann in einem eigenen Verzeichnis pro Sammlung, und der Server räumt beim Löschen gezielt auf. Das Schema verlangt es nicht (`required` sind nur `name` und `permissions`), der Starter pflegt es aber überall.
5. Projekt neu laden, damit der tibi-server die Datei einliest. 5. Projekt neu laden, damit der tibi-server die Datei einliest.
6. Testen: `POST` anlegen, doppelten Wert schicken → `409` bei Unique-Index. 6. Testen: `POST` anlegen, doppelten Wert schicken → `409` bei Unique-Index.
@@ -84,13 +84,18 @@ Pflicht ist nichts davon — aber ohne `label` rätst du im Admin.
| Schlüssel | Wirkung | Beispiel aus dem Starter | | Schlüssel | Wirkung | Beispiel aus dem Starter |
|---|---|---| |---|---|---|
| `label` | Anzeigename (`de`/`en`) | `tag.yml`: `{ de: "Schlagwörter", en: "Tags" }` | | `label` | Anzeigename (`de`/`en`) | `tag.yml`: `{ de: "Schlagwörter", en: "Tags" }` |
| `group` | Gruppierung in der Navigation | `content`, `content`, `community` (`comment.yml`) | | `group` | Gruppierung in der Navigation | `content` (content, medialib, navigation, tag), `community` (comment), `system` (ssr) |
| `muiIcon` | Icon im Admin | `label`, `menu`, `chat`, `folder-multiple-image` | | `muiIcon` | Icon im Admin | `label`, `menu`, `chat`, `folder-multiple-image` |
| `imageUrl` | Vorschaubild für Karten | Unsplash-URL in `content.yml` | | `imageUrl` | Vorschaubild für Karten | Unsplash-URL in `content.yml` |
| `preview` | Listenvorschau: `label`, `secondary`, `tertiary`, `image` | `content.yml`: `label: name`, `secondary: path`, `tertiary: lang` | | `preview` | Listenvorschau: `label`, `secondary`, `tertiary`, `image` | `content.yml`: `label: name`, `secondary: path`, `tertiary: lang` |
| `viewHint` | Darstellungsart im Admin | siehe unten | | `viewHint` | Darstellungsart im Admin | siehe unten |
| `sidebar` | Eigene Seitenleiste mit Gruppen | `content.yml`: `publishing`, `seo` | | `sidebar` | Eigene Seitenleiste mit Gruppen | `content.yml`: `publishing`, `seo` |
| `i18n.entry` | Übersetzungs-Kopplung | `content.yml`: `languageField: lang`, `groupField: translationKey` | | `i18n.entry` | Übersetzungs-Kopplung | `content.yml`: `languageField: lang`, `groupField: translationKey` |
| `singleton` | Genau ein Eintrag (`enabled`, optional `key`) | siehe unten |
| `sections` | Formular-Abschnitte (`group`, `label`, `collapsible`, `collapsed`) | siehe unten |
| `query` | Deklarierte Zusatz-Querykeys für Hooks | siehe unten |
| `agent` | Zweck, Hinweise und Workflows für Editor-Agenten | siehe unten |
| `defaultSort.order` | Sortierung, inkl. `MANUALLY` (manuelle Reihenfolge per Drag) | siehe unten |
Felder positionierst du mit `meta.position`: `main`, `sidebar:<gruppe>` oder `sidebar:settings`. Felder positionierst du mit `meta.position`: `main`, `sidebar:<gruppe>` oder `sidebar:settings`.
Beispiel: `lang` liegt in `content.yml` auf `sidebar:settings`, `active` auf `sidebar:publishing`. Beispiel: `lang` liegt in `content.yml` auf `sidebar:settings`, `active` auf `sidebar:publishing`.
@@ -99,7 +104,17 @@ Beispiel: `lang` liegt in `content.yml` auf `sidebar:settings`, `active` auf `si
- `viewHint: cards` — Kartenansicht (`content.yml`). - `viewHint: cards` — Kartenansicht (`content.yml`).
- `viewHint.media` — Medienansicht mit Upload und KI-Alt-Text (`medialib.yml`). - `viewHint.media` — Medienansicht mit Upload und KI-Alt-Text (`medialib.yml`).
- `viewHint.navigation` — Baumansicht mit `nodesField` und `declaredTrees` (`navigation.yml`, Bäume: Header/Footer je Sprache). - `viewHint.navigation` — Baumansicht (`navigation.yml`, Bäume: Header/Footer je Sprache): `nodesField` nennt das `object[]`-Unterfeld mit den Kind-Knoten (fällt es weg, gilt der Feldname selbst — `NavigationTreeField.svelte:106`), `declaredTrees` listet die Bäume (je Eintrag Pflicht: `singleton`, optional `label`, `maxLevel`).
### Singleton, Sections, Query, Agent, DefaultSort
| Schlüssel | Wirkung | YAML-Beispiel |
|---|---|---|
| `meta.singleton.enabled: true` (+ optional `key`) | Genau ein Eintrag pro Sammlung (bzw. pro `key`) | `singleton: { enabled: true }` |
| `meta.sections[]` | Formular-Abschnitte; `group` ist Pflicht, dazu `label` (`de`/`en`), `collapsible`, `collapsed` | `sections: [{ group: seo, label: { de: "SEO", en: "SEO" }, collapsible: true }]` |
| `meta.query.<name>` | Deklariert Zusatz-Querykeys, die Hooks per `ctx.request().query(name)` lesen (`type`: `string`/`number`/`boolean`, optional `enum`, `maxLength`, `description`) | `query: { preview: { type: boolean } }` |
| `meta.agent` | Freiform-Doku für Editor und Agenten (`purpose`, `instructions`, `fieldHints`, `workflows`, dazu `examples`, `hints`); wird u. a. über MCP/REST `_meta` ausgespielt | `agent: { purpose: "Seiteninhalte", workflows: ["Seite anlegen → übersetzen"] }` |
| `meta.defaultSort` | Start-Sortierung (`field` + `order`: `ASC`, `DESC` oder `MANUALLY` — manuell heißt Drag-Reihenfolge in der Liste) | `defaultSort: { field: name, order: MANUALLY }` |
## Indizes und Suche ## Indizes und Suche
@@ -163,4 +178,4 @@ Lege neue Felder zuerst in einer Test-Sammlung an, nicht in `content`.
`content` hat Pagebuilder-Blöcke und drei Such-Indizes — Fehler dort tun mehr weh. `content` hat Pagebuilder-Blöcke und drei Such-Indizes — Fehler dort tun mehr weh.
</Aside> </Aside>
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 4c55f82 2026-10-12 (tibi-types). Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 4c55f82 2026-10-17 (tibi-types); Meta-Ergänzungen verifiziert am 1897bb5 2026-10-17 (tibi-docs), am 2b091d8 2026-10-17 (tibi-admin-nova: NavigationTreeField), am 4c55f82 2026-10-17 (tibi-types: collection-meta.schema.json)
+38 -4
View File
@@ -15,8 +15,15 @@ Container: Seiten-Routen, Daten-Lookups, Blöcke, SSR-Bundle, Cache.
- App.svelte - App.svelte
- ssr.ts - ssr.ts
- blocks/ - blocks/
- frontend/spa.html
- api/templates/spa.html
- api/collections/ssr.yml
- api/hooks/ - api/hooks/
- lib/app.server.js - lib/app.server.js
- lib/utils.js
- ssr/get_read.js
- ssr/post_bind.js
- clear_cache.js
</FileTree> </FileTree>
@@ -61,10 +68,10 @@ getCachedEntries("navigation", {
| `elements.page:content` | Navigations-Elemente bekommen den Zielpfad (`_lookup.page.path`) | | `elements.page:content` | Navigations-Elemente bekommen den Zielpfad (`_lookup.page.path`) |
| Kommentar-Aggregat | `commentCount` ohne zweite Abfrage | | Kommentar-Aggregat | `commentCount` ohne zweite Abfrage |
`sort: "sort"` sortiert nach dem Feld `sort` (manuelle Reihenfolge, aufsteigend) — der Parametername und der Feldname heißen hier zufällig gleich.
<Aside type="caution"> <Aside type="caution">
Öffentlich sichtbar ist nur `active: true` — das gilt auch für `navigation` und `medialib`, obwohl deren Schema gar kein `active`-Feld kennt. Öffentlich sichtbar ist nur `active: true` — das gilt auch für `navigation` und `medialib` (kanonische Erklärung unter [Rechte und Tokens](/projekt/rechte-tokens/)).
Ohne das Flag: Header/Footer leer (`count 0`), Bild-Lookups unaufgelöst (nur IDs).
Details unter [Rechte und Tokens](/projekt/rechte-tokens/).
</Aside> </Aside>
## Blöcke ## Blöcke
@@ -119,6 +126,33 @@ frontend/src/ssr.ts ── esbuild (esbuild.config.server.js) ──▶ api/hook
Beide Builds schreiben `buildInfo` — der Server-Build überschreibt sie nie, sonst löst `checkBuildVersion` einen Schein-Reload aus. Beide Builds schreiben `buildInfo` — der Server-Build überschreibt sie nie, sonst löst `checkBuildVersion` einen Schein-Reload aus.
Nach jedem Frontend-Build: `yarn build:server` erneut laufen lassen und deployen (siehe [Veröffentlichen](/projekt/deploy/)). Nach jedem Frontend-Build: `yarn build:server` erneut laufen lassen und deployen (siehe [Veröffentlichen](/projekt/deploy/)).
## Vorlage und Cache-Sammlung
Quelldatei der Vorlage ist `frontend/spa.html`. `ci-modify-config.sh` ersetzt `__TIMESTAMP__` durch einen Build-Stempel und kopiert die Datei nach `api/templates/spa.html`.
`get_read.js` liest sie pro Anfrage und füllt die Marker.
| Marker | Inhalt |
|---|---|
| `<!--HEAD-->` | Head aus dem SSR-Render |
| `<!--HTML-->` | Gerendertes App-HTML |
| `<!--PRELOAD-->` | Preload-Hinweise (`scripts/preload-meta.sh`) |
| `__TIMESTAMP__` | Build-Stempel für `/dist`-Assets (Cache-Buster, schon beim Kopieren ersetzt) |
| `<!--SSR.ERROR-->` | Render-Fehler als Kommentar (leer im Normalfall) |
| `<!--SSR.COMMENT-->` | Diagnose (`url`, `deps`) als Kommentar |
| `<html lang>` | Sprache aus dem URL-Präfix (`de`/`en`, sonst `de`) |
Die Sammlung `ssr` (`api/collections/ssr.yml`) hält einen Eintrag je URL:
| Feld | Rolle |
|---|---|
| `path` | URL ohne Query, eindeutig (Unique-Index — parallele Writes gewinnt genau einer) |
| `content` | Fertiges HTML aus der Vorlage |
| `validUntil` | Ablaufdatum — der Hook setzt es derzeit auf `null`, Einträge leben bis zur Invalidierung |
| `dependencies` | `col:<id>` (Detail) oder `col:*` (Liste), indexiert |
| `_testdata` | Test-Marker, im Editor verborgen |
Schreiben und Löschen steuert `clear_cache.js` als `return`-Hook (`post`, `put`, `delete` u. a. in `content`, `navigation`, `medialib`, `comment`, `tag`) über `utils.clearSSRCache()`; `POST /api/ssr?clear=1` wischt alles (`post_bind.js`).
## Cache und Invalidierung ## Cache und Invalidierung
`api/hooks/ssr/get_read.js` rendert jede anonyme Anfrage — außer Tracking (`x-ssr-skip`) und Admins: `api/hooks/ssr/get_read.js` rendert jede anonyme Anfrage — außer Tracking (`x-ssr-skip`) und Admins:
@@ -175,4 +209,4 @@ curl -s "http://localhost:8080/de/?noCache=1" | head -c 300
| 500 `ssr is only a dummy collection` | `POST` an `ssr` ohne `?clear=1` | Query-Parameter ergänzen | | 500 `ssr is only a dummy collection` | `POST` an `ssr` ohne `?clear=1` | Query-Parameter ergänzen |
| PAT reicht, anonym fehlt Lookup | `lookup permission denied` — Ziel-Sammlung ohne Leserecht | [Rechte und Tokens](/projekt/rechte-tokens/) | | PAT reicht, anonym fehlt Lookup | `lookup permission denied` — Ziel-Sammlung ohne Leserecht | [Rechte und Tokens](/projekt/rechte-tokens/) |
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter). Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter)
+36 -7
View File
@@ -3,10 +3,10 @@ title: 'Suche'
description: 'Textindex, search-Blöcke, Hybrid-Suche, Neuaufbau des Index aus Projektsicht.' description: 'Textindex, search-Blöcke, Hybrid-Suche, Neuaufbau des Index aus Projektsicht.'
--- ---
import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components'; import { Aside, Steps } from '@astrojs/starlight/components';
Die Suche läuft über `search`-Blöcke in der Sammlungs-YAML plus Abfrage per `q` und `qName`. Die Suche läuft über `search`-Blöcke in der Sammlungs-YAML plus Abfrage per `q` und `qName`.
Suche-Begriffe: Volltextsuche, Hybrid-Suche, Suchindex neu aufbauen. Suchbegriffe: Volltextsuche, Hybrid-Suche, Suchindex neu aufbauen.
## Index anlegen ## Index anlegen
@@ -74,30 +74,59 @@ score(doc) += weight / (k + rank) # k = 60, weights: semantic 1.5, fuzzy 1.0
- Weitere Strategien (`score_rrf`, `score_sum`, `rank_score`, `hybrid`), Normierung (`minmax`, `zscore`), Gewichte (`scoreWeight`) und Quellen-Config (`sourceConfig`) existieren im Code **und** sind als `rrf`-Block in der Projekt-YAML einstellbar — aus Projektsicht reicht `combined` mit den Starter-Gewichten. - Weitere Strategien (`score_rrf`, `score_sum`, `rank_score`, `hybrid`), Normierung (`minmax`, `zscore`), Gewichte (`scoreWeight`) und Quellen-Config (`sourceConfig`) existieren im Code **und** sind als `rrf`-Block in der Projekt-YAML einstellbar — aus Projektsicht reicht `combined` mit den Starter-Gewichten.
- Alte Score-Beispiele aus Erzähl-Docs nicht übernehmen — nur diese Formel gilt. - Alte Score-Beispiele aus Erzähl-Docs nicht übernehmen — nur diese Formel gilt.
## Neuaufbau ## Suchmodi im Überblick
Alle Modi aus `collection.go:314` — `fields` gilt für `regex` und `ngram`, `vector.provider` muss auf einen Server-Provider zeigen (`collection.go:377`):
| Modus | Einsatzfall | Braucht Index/Aufbau |
|---|---|---|
| `text` | MongoDB-Volltext, eine Textstelle pro Sammlung | Ja — Mongo-Textindex (`hasTextIndex`; nur **ein** Textindex je Sammlung) |
| `regex` | Einfache Teilübereinstimmung (`default`-Block in `tag`, `navigation`, `comment`, `medialib`) | Nein — läuft direkt auf den Feldern, kein Aufbau, keine Queue |
| `eval` | Eigene JS-Bewertung pro Treffer (`eval`-Ausdruck) | Nein — Ausdruck läuft zur Abfragezeit, optional `score.expr` |
| `filter` | Feste MongoDB-Filtervorlage mit `$subst` | Nein — Filter zur Abfragezeit, optional `score.expr` |
| `ngram` | Fehlertolerante Teilwortsuche (Starter-`fuzzy`) | Ja — `_search`-Anreicherung mit `autoRegenerate`, Queue bis zum Worker-Lauf |
| `vector` | Semantische Suche (Starter-`semantic`, Provider `bge_m3`) | Ja — Embeddings über Embedding-Provider, Queue bis zum Worker-Lauf |
| `combined` | Hybrid aus mehreren Blöcken (Starter-`combined` = RRF aus `semantic` + `fuzzy`) | Je beteiligtem Block (siehe oben); Fusion selbst braucht keinen eigenen Index |
<Aside type="note">
`qName` wählt den Block, `q` liefert den Suchtext. Ohne `qName` greift der erste Block der Sammlung. `text`, `eval` und `filter` kommen im Starter nicht vor — dort zählen `regex` (kein Aufbau), `ngram`/`vector` (Aufbau nötig) und `combined` (Fusion).
</Aside>
## Index aufbauen und prüfen
<Steps> <Steps>
1. Bearbeite Einträge normal — `fuzzy` und `semantic` haben `autoRegenerate: true` und bauen sich selbst nach. 1. Bearbeite Einträge normal — `fuzzy` und `semantic` haben `autoRegenerate: true` und bauen sich selbst nach.
2. Beobachte im tibi-admin den Hinweis `SearchIndexBuildNotice`: Er erscheint im Projekt-Header (Summe offener `_queue`-Einträge aller `ngram`-/`vector`-Sammlungen) und im Sammlungs-Header der betroffenen Sammlung. 2. Beobachte im tibi-admin den Hinweis `SearchIndexBuildNotice`: Er erscheint im Projekt-Header (Summe offener `_queue`-Einträge aller `ngram`-/`vector`-Sammlungen) und im Sammlungs-Header der betroffenen Sammlung.
3. Warte, bis der Hinweis nach dem nächsten Polling von selbst verschwindet (`_queue` = 0). Der `regex`-Modus (`default`) hat keine Queue und keinen Hinweis. 3. Warte, bis der Hinweis nach dem nächsten Polling von selbst verschwindet (`_queue` = 0). Der `regex`-Modus (`default`) hat keine Queue und keinen Hinweis.
4. Bleibt die Queue stehen oder hast du die Config geändert: `POST /_/:project/_/admin/regenerate-search` aufrufen — optional mit Body `{ collection, modes, names, ids, filter, force }` (`admin_regenerate_search.go:17-25`). Mit `?async=1` nur einreihen und sofort antworten (`{"message": "queued", …}`), ohne `filter` auch synchron möglich. Ungültige IDs ohne einzige gültige Hex-ID brechen mit `400 no valid document IDs in request` ab.
5. Status prüfen: `GET /_/:project/_/admin/search-worker/status` zeigt pro Provider Aktiv-Flag, Queue-Tiefe, laufenden Job und Fortschrittszähler (`admin_search_status.go:10-15`). Auth jeweils: Scope `project.regenerate-search` plus Token-Flag `AllowRegenerateSearch`.
</Steps> </Steps>
<Aside type="note" title="Regenerate auslösen"> <Aside type="note" title="Neuaufbau aus Sicht des Projekts">
Den Neuaufbau stößt du im tibi-admin über den Regenerate-Flow an (Test-Plan `41-search-index-status`). Aus Projektsicht musst du nichts einrichten — lass `autoRegenerate: true` in der YAML stehen, dann pflegen sich `fuzzy`- und `semantic`-Indizes selbst. Der Regenerate-Flow im tibi-admin (Test-Plan `41-search-index-status`) nutzt genau den Endpunkt aus Schritt 4.
Aus Projektsicht musst du nichts einrichten — nur `autoRegenerate: true` in der YAML stehen lassen.
</Aside> </Aside>
## Was der Vektor-Pfad zusätzlich braucht
- **Provider-Name:** `vector.provider` (z. B. `bge_m3`) muss mit einem Servernamen aus der Embedding-Registry übereinstimmen (`collection.go:377`) — sonst löst die Config nicht auf. Unbekannte Provider-Typen brechen ab, defekte einzelne Provider werden übersprungen (`registry.go:58-70`).
- **Gesundheit prüfen:** `GET /_/:project/_/admin/embedding/status` zeigt Gruppen-/Mitglieder-Gesundheit (an diesem Scope sind Fehlertexte redigiert, nur Zustände und Zähler; Volldetail nur am Server-Endpunkt `GET /api/v1/admin/embedding/status`, `admin_embedding_status.go:49-72`).
- **Feinheiten (optional):** `documentPrefix`/`queryPrefix`, `overflow: truncate` (Standard) oder `chunk` mit `chunkSize` (Standard 512) und `chunkOverlap` (Standard 50) — `collection.go:376-383`.
- **Debug:** Mit Header `X-Search-Debug: true` antwortet die Suche mit `X-Search-Config`, `X-Search-Timing` und `X-Search-Sources` (Treffer-/Score-Spannen je Quelle, `search_debug.go:145-159`).
## Fehlerbilder ## Fehlerbilder
| Symptom | Ursache | Lösung | | Symptom | Ursache | Lösung |
|---|---|---| |---|---|---|
| Leere Scores / keine Treffer trotz Daten | Index fehlt oder `_queue` noch > 0 | Hinweis im Admin abwarten, dann erneut suchen | | Leere Scores / keine Treffer trotz Daten | Index fehlt oder `_queue` noch > 0 | Hinweis im Admin abwarten, dann erneut suchen |
| `combined` findet nichts, `fuzzy` schon | Vektor-Anteil ohne Embeddings | `semantic`-Felder und Provider (`bge_m3`) prüfen | | `combined` findet nichts, `fuzzy` schon | Vektor-Anteil ohne Embeddings | `semantic`-Felder und Provider (`bge_m3`) prüfen |
| `400 no valid document IDs in request` | Regenerate mit nur ungültigen `ids` | Hex-IDs prüfen oder `ids` weglassen (Vollaufbau) |
| `404 collection "…" not found` | Regenerate mit falschem `collection`-Namen | Namen gegen `api/collections/*.yml` prüfen |
| Worker-Queue steht | Provider defekt oder überlastet | `search-worker/status` und `embedding/status` lesen, dann Regenerate mit `force: true` |
| Suche über `tag`/`navigation` unscharf | `default` ist `regex`, kein Fuzzy | Erwartung anpassen oder `content`-Indexe nutzen | | Suche über `tag`/`navigation` unscharf | `default` ist `regex`, kein Fuzzy | Erwartung anpassen oder `content`-Indexe nutzen |
| `?facet=` an der Suche wirkungslos | Facetten sind eigene Route (`…/_facet`) | [Lesen und suchen](/projekt/abfragen-lesen/) beachten | | `?facet=` an der Suche wirkungslos | Facetten sind eigene Route (`…/_facet`) | [Lesen und suchen](/projekt/abfragen-lesen/) beachten |
Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Sammlungen anlegen](/projekt/sammlungen-anlegen/), [Felder und Widgets](/projekt/felder-widgets/). Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Sammlungen anlegen](/projekt/sammlungen-anlegen/), [Felder und Widgets](/projekt/felder-widgets/).
Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter), am 68b42785 2026-10-15 (tibi-server, rankfusion), am 2b091d89 2026-10-13 (tibi-admin-nova, Test-Plan 41-search-index-status) Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 68b4278 2026-10-15 (tibi-server, rankfusion), am 2b091d8 2026-10-13 (tibi-admin-nova, Test-Plan 41-search-index-status)
+83
View File
@@ -0,0 +1,83 @@
---
title: 'Tests'
description: 'Playwright-Suite im Starter: Projekte, Befehle, Mock-Betrieb und eigene Tests.'
---
import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
Der Starter bringt eine Playwright-Suite mit: E2E im Browser, API-Tests, Admin-Smoke-Tests und visuelle Regression.
Basis ist `playwright.config.ts`; `tests/global-setup.ts` legt deterministische Seed-Daten an, `tests/global-teardown.ts` räumt sie wieder auf.
## Projekte und Ordner
| Projekt | Ordner | Wofür |
|---|---|---|
| `chromium` | `tests/e2e/` | Desktop-E2E gegen die Website |
| `api` | `tests/api/` | API-Tests mit den Fixtures `api`/`adminApi` |
| `admin` | `tests/e2e-admin/` | Admin-Smoke-Tests, eigene Basis-URL `TEST_ADMIN_BASE_URL` |
| `mobile-iphonese`, `mobile-ipad` | `tests/e2e-mobile/` | Mobile-E2E (iPhone SE, iPad Mini) |
| `visual-desktop`, `visual-iphonese`, `visual-ipad` | `tests/e2e-visual/` | Screenshots gegen Baselines in `__screenshots__/` |
Rahmen aus `playwright.config.ts`: Die Suite startet keinen eigenen Web-Server (kein `webServer`-Schlüssel) — sie läuft gegen die erreichbare Basis-URL.
`TEST_BASE_URL` kommt aus der Env-Variable `CODING_URL`, sonst aus `.env`, sonst `http://localhost:3000`; `TEST_ADMIN_BASE_URL` folgt derselben Kette aus `CODING_TIBIADMIN_URL`.
HTML-Report ohne Auto-Open, Trace an, `domcontentloaded` als Standard (BrowserSync hält `load` sonst offen), Wiederholungen und Worker-Zahl hängen von `CI` ab.
Anfragen mit `Playwright` im User-Agent umgehen die Basic-Auth (siehe Kommentar in `playwright.config.ts`).
Seed-Daten leben in `tests/api/helpers/seed-data.ts`, die Routen-Konstanten in `tests/fixtures/test-constants.ts` (`SEEDED_TEST_CONTENT`: Home, Pagebuilder-Vorschau, Kontakt, inaktiv — je mit `translationKey` und Pfad).
Das Setup prüft `/` und `/api/content` und sät die Datensätze; `_testdata` markiert Seed-Einträge.
## Starten
```bash title="Befehle aus package.json und Makefile"
yarn test # alles (playwright test)
yarn test:e2e # nur tests/e2e (Projekt chromium)
yarn test:api # nur tests/api (Projekt api)
yarn test:visual # visuelle Regression (drei visual-Projekte)
yarn test:visual:update # Baselines neu schreiben und committen
yarn test:report # HTML-Report öffnen (playwright show-report)
npx playwright test tests/e2e/home.spec.ts # einzelne Datei
npx playwright test -g "Titelwort" # einzelner Test per Titel
make test test-e2e test-api test-visual # gleiche Ziele via Makefile
```
Voraussetzung: Der Stack läuft und liefert unter der Basis-URL Website plus `/api/...` aus.
Artefakte: `playwright-report/` (HTML-Report) und `test-results/` (Traces).
## Mock-Betrieb
Ohne Backend arbeiten: `MOCK=1` schaltet den API-Interceptor ein.
```bash title="Frontend ohne Server starten"
yarn start:mock # MOCK=1 node scripts/esbuild-wrapper.js start
```
Wirkung (`frontend/src/lib/mock.ts`, `frontend/src/lib/api.ts`, `esbuild.config.js`):
- Mit `MOCK=1` steht `__MOCK__` auf `true`; `api()` beantwortet Lesezugriffe aus `frontend/mocking/*.json` (Sammlungen `content`, `medialib`, `navigation`, `comment`).
- Unterstützt Filter (auch `$and`/`$or`, `$in`, `$regex`), Sortierung, `limit`/`offset`, Projection, Einzelabruf per ID, Kommentar-Zähler und Lookups.
- Ohne `MOCK=1` ersetzt ein esbuild-Plugin das Mock-Modul durch einen No-Op — Mock-Daten landen nicht im Produktions-Bundle.
Grenzen:
- Nur Lesen: Schreibaufrufe speichern nichts (`_body` bleibt ungenutzt); Sammlungen ohne Mock-Daten antworten mit 404.
- `tags.json` liegt in `frontend/mocking/`, ist aber nicht im Mock-Register — Tag-Abfragen fallen auf 404.
- Der Mock greift nur in `api()`; direkte `fetch`-Aufrufe, Server-Hooks und SSR laufen unverändert.
- Im Compose-Stack ist `MOCK` Standard `0`, in `.env` als Kommentar hinterlegt.
## Eigene Tests ergänzen
<Steps>
1. E2E-Spec in `tests/e2e/` anlegen und die Fixtures aus `../fixtures` nutzen (`waitForSpaReady`, `navigateToRoute`, `clickSpaLink`).
2. Selektoren per Rolle formulieren (`getByRole`), wie `tests/e2e/home.spec.ts`; Navigation nutzt immer `domcontentloaded` (die Fixture überschreibt `goto`/`reload` wegen BrowserSync).
3. API-Spec in `tests/api/` schreiben mit den Fixtures `api`/`adminApi`; Seed-Routen stehen in `SEEDED_TEST_CONTENT`, neue Seed-Daten gehören in `tests/api/helpers/seed-data.ts` plus `_testdata`-Flag.
4. Visuellen Test in `tests/e2e-visual/` mit `prepareForScreenshot` und `expectScreenshot` aufbauen; dynamische Bereiche per `getDynamicMasks` ausblenden und Baselines committen.
</Steps>
<Aside type="caution">
Jede Browser-Fixture hängt `tests/fixtures/console-monitor.ts` an: `pageerror`, `console.error` und fehlgeschlagene Requests lassen den Test scheitern.
Fällt ein neuer Test damit auf, zuerst klären, ob der Test oder der Code falsch liegt.
</Aside>
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter, package.json, playwright.config.ts, tests/, frontend/src/lib/mock.ts)
+1 -1
View File
@@ -51,4 +51,4 @@ Ohne `legacy.httpCodes: false` in der Projekt-Config gelten die alten Codes: Kon
409/500 Konflikt ──▶ eindeutiges Feld ändern ──▶ erneut senden 409/500 Konflikt ──▶ eindeutiges Feld ändern ──▶ erneut senden
``` ```
Verifiziert am 68b42785 2026-10-15 (tibi-server) Verifiziert am 68b4278 2026-10-15 (tibi-server)
+9 -19
View File
@@ -3,28 +3,18 @@ title: 'Hook-Kontexte (Code-Stand)'
description: 'Verbindliche Hook-Kontext-Liste aus internal/hook: alle Pakete und wofür sie gut sind.' description: 'Verbindliche Hook-Kontext-Liste aus internal/hook: alle Pakete und wofür sie gut sind.'
--- ---
import { Aside, Card, CardGrid } from '@astrojs/starlight/components'; import { Aside, CardGrid, LinkCard } from '@astrojs/starlight/components';
In Hooks rufst du Helfer über Pakete auf, z. B. `db.find(...)` oder `http.fetch(...)`. Diese Liste stammt aus `internal/hook/context_*.go` im tibi-server (jeweils `RegisterPackageFunctions`). Sie ist verbindlich — nicht die Paket-Zahl aus älteren Erzähl-Docs. In Hooks rufst du Helfer über Pakete auf, z. B. `db.find(...)` oder `http.fetch(...)`. Diese Liste stammt aus `internal/hook/context_*.go` im tibi-server (jeweils `RegisterPackageFunctions`). Sie ist verbindlich — nicht die Paket-Zahl aus älteren Erzähl-Docs.
Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Referenz-Kapitel. Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Referenz-Kapitel.
<CardGrid stagger> <CardGrid stagger>
<Card title="Daten"> <LinkCard title="Daten" href="/reference/hooks-daten/" description="db, cache, request, response, fs." />
db, cache, request, response, fs. [Lesen](/reference/hooks-daten/). <LinkCard title="Netze und Nachrichten" href="/reference/hooks-netze/" description="http, smtp, channel, ratelimit." />
</Card> <LinkCard title="Sicherheit und Identität" href="/reference/hooks-sicherheit/" description="jwt, user, bcrypt, crypto, cookie, exec, config." />
<Card title="Netze und Nachrichten"> <LinkCard title="Templates und Medien" href="/reference/hooks-medien/" description="tpl, image, pdf." />
http, smtp, channel, ratelimit. [Lesen](/reference/hooks-netze/). <LinkCard title="Formate und Diagnose" href="/reference/hooks-meta/" description="base64, json, xml, charset, debug." />
</Card>
<Card title="Sicherheit und Identität">
jwt, user, bcrypt, crypto, cookie, exec, config. [Lesen](/reference/hooks-sicherheit/).
</Card>
<Card title="Templates und Medien">
tpl, image, pdf. [Lesen](/reference/hooks-medien/).
</Card>
<Card title="Formate und Diagnose">
base64, json, xml, charset, debug. [Lesen](/reference/hooks-meta/).
</Card>
</CardGrid> </CardGrid>
## Tiefenstufen: was „abgedeckt" heißt ## Tiefenstufen: was „abgedeckt" heißt
@@ -49,7 +39,7 @@ Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Refere
| `image` | Bildfilter anwenden | `image.filter` | [Hook-Referenz: Templates und Medien](/reference/hooks-medien/) | | `image` | Bildfilter anwenden | `image.filter` | [Hook-Referenz: Templates und Medien](/reference/hooks-medien/) |
| `pdf` | PDF erzeugen | `pdf.fromHTML`, `pdf.cpu` | [Hook-Referenz: Templates und Medien](/reference/hooks-medien/) | | `pdf` | PDF erzeugen | `pdf.fromHTML`, `pdf.cpu` | [Hook-Referenz: Templates und Medien](/reference/hooks-medien/) |
| `cache` | Hook-lokaler Zwischenspeicher | `cache.set`, `cache.get`, `cache.has`, `cache.delete`, `cache.clear`, `cache.count` | [Hook-Referenz: Daten](/reference/hooks-daten/) | | `cache` | Hook-lokaler Zwischenspeicher | `cache.set`, `cache.get`, `cache.has`, `cache.delete`, `cache.clear`, `cache.count` | [Hook-Referenz: Daten](/reference/hooks-daten/) |
| `crypto` | Hashes bilden | `crypto.sha256`, `crypto.md5` (plus weitere) | [Hook-Referenz: Sicherheit und Identität](/reference/hooks-sicherheit/) | | `crypto` | Hashes bilden | `crypto.sha256`, `crypto.md5` | [Hook-Referenz: Sicherheit und Identität](/reference/hooks-sicherheit/) |
| `base64` | Kodieren und Dekodieren | `base64.encode`, `base64.decode` | [Hook-Referenz: Formate und Diagnose](/reference/hooks-meta/) | | `base64` | Kodieren und Dekodieren | `base64.encode`, `base64.decode` | [Hook-Referenz: Formate und Diagnose](/reference/hooks-meta/) |
| `bcrypt` | Passwörter hashen und prüfen | `bcrypt.hash`, `bcrypt.check` | [Hook-Referenz: Sicherheit und Identität](/reference/hooks-sicherheit/) | | `bcrypt` | Passwörter hashen und prüfen | `bcrypt.hash`, `bcrypt.check` | [Hook-Referenz: Sicherheit und Identität](/reference/hooks-sicherheit/) |
| `channel` | Nachrichten senden und abonnieren | `channel.send`, `channel.subscribe` | [Hook-Referenz: Netze](/reference/hooks-netze/) | | `channel` | Nachrichten senden und abonnieren | `channel.send`, `channel.subscribe` | [Hook-Referenz: Netze](/reference/hooks-netze/) |
@@ -62,7 +52,7 @@ Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Refere
## Fehlende oder veraltete Einträge melden ## Fehlende oder veraltete Einträge melden
Die Hülle nannte nur: db, http, tpl, jwt, bcrypt, cache, channel, crypto, fs, image, pdf, smtp, exec, config, request, response, user, xml, base64. Im Code gibt es **zusätzlich**: Neben den oben tabellierten Paketen gibt es im Code zusätzlich:
- `charset`, `cookie`, `json`, `debug`, `ratelimit` - `charset`, `cookie`, `json`, `debug`, `ratelimit`
@@ -72,4 +62,4 @@ Wenn du eine Doku-Stelle findest, die eine kleinere Paket-Zahl nennt, ist sie ve
Paketname = Dateiname ohne `context_` und ohne `.go`. Die Funktionen stehen jeweils im `RegisterPackageFunctions`-Block am Dateianfang. Paketname = Dateiname ohne `context_` und ohne `.go`. Die Funktionen stehen jeweils im `RegisterPackageFunctions`-Block am Dateianfang.
</Aside> </Aside>
Verifiziert am 68b42785 2026-10-15 (tibi-server) Verifiziert am 68b4278 2026-10-15 (tibi-server)
+5 -1
View File
@@ -115,6 +115,10 @@ Leeres Ergebnis ist kein Fehler: `db.find` liefert dann ein leeres Array.
Quelle: `context_cache.go`, registriert per `RegisterPackageFunctions("cache", ...)` mit `set`, `get`, `has`, `delete`, `clear`, `count`. Der Cache ist projektweit (project-scoped), nicht pro Request. Quelle: `context_cache.go`, registriert per `RegisterPackageFunctions("cache", ...)` mit `set`, `get`, `has`, `delete`, `clear`, `count`. Der Cache ist projektweit (project-scoped), nicht pro Request.
<Aside type="note" title="Defaults und Grenzen">
Der Cache ist ein In-Memory-Speicher mit TTL und LRU-Verdrängung (`cache.go:38`): Standard `MaxEntries: 10000`, `DefaultTTL: 0` (= kein Ablauf), `CleanupInterval: 60s` (`cache.go:29-36`). Ohne `ttlMs` in `cache.set` lebt ein Eintrag also ewig — bis er verdrängt oder per `delete`/`clear` entfernt wird. Konfigurierbar pro Server-Config `hook.cache`: `maxEntries` / `defaultTTL` (`server.go:178-181`) bzw. per Env `HOOK_CACHE_MAX_ENTRIES` / `HOOK_CACHE_DEFAULT_TTL` (`server.go:690-697`).
</Aside>
### cache.set ### cache.set
**Signatur:** `cache.set(key: string, value: any, ttlMs?: number): void` — aus `// JS signature: cache.set(key: string, value: any, ttlMs?: number): void` (`context_cache.go:23`). **Signatur:** `cache.set(key: string, value: any, ttlMs?: number): void` — aus `// JS signature: cache.set(key: string, value: any, ttlMs?: number): void` (`context_cache.go:23`).
@@ -410,4 +414,4 @@ Quelle: `context_fs.go`, registriert per `RegisterPackageFunctions("fs", ...)` m
Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster. Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster.
Verifiziert am 68b42785 2026-10-16 (tibi-server) Verifiziert am 68b4278 2026-10-16 (tibi-server)
+1 -1
View File
@@ -81,4 +81,4 @@ Quelle: `context_pdf.go`, registriert per `RegisterPackageFunctions("pdf", ...)`
Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster. Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster.
Verifiziert am 68b42785 2026-10-16 (tibi-server) Verifiziert am 68b4278 2026-10-16 (tibi-server)
+1 -1
View File
@@ -170,4 +170,4 @@ Quelle: `context_debug.go`, registriert per `RegisterPackageFunctions("debug", .
Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hook-Referenz: Daten](/reference/hooks-daten/) für db, cache, request, response und fs. Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hook-Referenz: Daten](/reference/hooks-daten/) für db, cache, request, response und fs.
Verifiziert am 68b42785 2026-10-16 (tibi-server) Verifiziert am 68b4278 2026-10-16 (tibi-server)
+1 -1
View File
@@ -188,4 +188,4 @@ Quelle: `context_ratelimit.go`, registriert per `RegisterPackageFunctions("ratel
Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster. Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster.
Verifiziert am 68b42785 2026-10-16 (tibi-server) Verifiziert am 68b4278 2026-10-16 (tibi-server)
@@ -206,4 +206,4 @@ Quelle: `context_config.go`, registriert per `RegisterPackageFunctions("config",
Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster. Weiter: [Hook-Kontexte](/reference/hook-kontexte/) als Paket-Index, [Hooks schreiben](/projekt/hooks-schreiben/) für Lifecycle und Starter-Muster.
Verifiziert am 68b42785 2026-10-16 (tibi-server) Verifiziert am 68b4278 2026-10-16 (tibi-server)
+14 -1
View File
@@ -1,5 +1,18 @@
--- ---
title: 'OpenAPI (generiert)' title: 'OpenAPI (generiert)'
description: 'Projektspezifische OpenAPI-Spec vom laufenden tibi-server: Endpunkt, Abruf per curl und Sync.'
--- ---
> Stand: Platzhalter · Quelle: `TIBI_API_URL/openapi.json` — nicht von Hand editieren. Erzeugen mit `npm run sync:openapi`. > Stand: Platzhalter · Quelle: `GET /api/v1/_openapi/:project` — nicht von Hand editieren. Erzeugen mit `npm run sync:openapi` (braucht `TIBI_NAMESPACE`).
Die Spec erzeugt der Server frisch aus Config und Collections deines Projekts: `GET /api/v1/_openapi/:project`. Es gibt keine statische `/openapi.json` — jede Spec gehört zu genau einem Projekt (Namespace).
```bash title="Spec eines Projekts abrufen"
curl http://localhost:8080/api/v1/_openapi/meinprojekt
```
`npm run sync:openapi` ruft diesen Endpunkt für `$TIBI_NAMESPACE` auf (Basis `TIBI_API_URL`, Default `http://localhost:8080/api/v1`) und schreibt das Ergebnis in diese Seite.
Läuft der Admin hinter seinem Proxy, erreichst du dieselbe Spec auch über `$CODING_TIBIADMIN_URL/api/_openapi/<namespace>` — der Proxy schreibt `/api` auf `/api/v1` um.
Verifiziert am 68b4278 2026-10-17 (tibi-server)
+65
View File
@@ -0,0 +1,65 @@
---
title: 'System-Routen'
description: 'User-, Projekt-, Org-, Team- und Token-Verwaltung plus Meta-Routen: kein Collection-CRUD.'
---
import { Aside } from '@astrojs/starlight/components';
Neben dem Collection-CRUD (`/api/v1/_/:project/:collection`) verwaltet der Server System-Objekte direkt unter `/api/v1/`.
Jedes dieser fünf Modelle hat dieselben fünf Routen; dazu kommen Meta- und Scope-Routen.
## Routen-Tabelle
| Methode + Pfad | Zweck | Rechte |
|---|---|---|
| `GET /api/v1/user`, `POST /api/v1/user`, `GET /api/v1/user/:id`, `PUT /api/v1/user/:id`, `DELETE /api/v1/user/:id` | Nutzer anlegen, lesen, ändern, löschen (Passwort nie in Antworten) | Nutzer-Scope (`user.read` …) bzw. Admin-Token; Org-/Team-Zugehörigkeit schränkt ein |
| `GET /api/v1/project`, `POST /api/v1/project`, `GET /api/v1/project/:id`, `PUT /api/v1/project/:id`, `DELETE /api/v1/project/:id` | Projekte registrieren und konfigurieren | Projekt-Scope (`project.read` …); Fremd-Orgs sind unsichtbar |
| `GET /api/v1/org`, `POST /api/v1/org`, `GET /api/v1/org/:id`, `PUT /api/v1/org/:id`, `DELETE /api/v1/org/:id` | Mandanten (Orgs) verwalten | Org-Scope (`org.read` …); nur eigene Org |
| `GET /api/v1/team`, `POST /api/v1/team`, `GET /api/v1/team/:id`, `PUT /api/v1/team/:id`, `DELETE /api/v1/team/:id` | Gruppen in einer Org verwalten | Team-Scope (`team.read` …) |
| `GET /api/v1/token`, `POST /api/v1/token`, `GET /api/v1/token/:id`, `PUT /api/v1/token/:id`, `DELETE /api/v1/token/:id` | Eigene Personal-Access-Tokens (PAT) verwalten | Login-Sitzung nötig — ein PAT darf **nicht** seine eigene Verwaltung aufrufen |
| `GET /api/v1/token/_scopes` | Katalog aller Scopes, die du aktuell vergeben darfst (gleiche Quelle wie die Anlage-Prüfung) | Nur Nutzer-Login (`X-Auth-Token`/Session); PAT → `403` |
| `GET /api/v1/_meta` | Feld-Metadaten **aller** System-Modelle als `{models: {user: […], …}}` | Wie die Modell-Routen (System-Ebene) |
| `GET /api/v1/_meta/<modell>` | Feld-Metadaten eines Modells (`user`, `project`, `org`, `team`, `token`) | Wie oben |
Die fünf CRUD-Routen je Modell entstehen in `internal/dbapi/register.go` (`GET /:id`, `GET`, `POST`, `PUT /:id`, `DELETE /:id`); die Meta-Routen legt `AddMetaRoute` an.
`POST /api/v1/login` (JWT holen) und `POST /api/v1/_/:project/_/admin/…` (Reload, Suche, Backup) gehören ebenfalls zur System-Ebene — siehe [Rechte und Tokens](/projekt/rechte-tokens/) und [Betrieb](/server/betrieb/).
```text title="Projekt lesen (Nutzer-Login)"
GET /api/v1/project
X-Auth-Token: <jwt>
```
```text title="Token mit engem Scope anlegen"
POST /api/v1/token
X-Auth-Token: <jwt>
{"name": "lese-job", "scopes": ["namespace.meinprojekt.content.read"]}
# Antwort enthält einmalig: {"token": "tibi_pat_<id>_<secret>", …}
# Danach nur noch: userId, name, scopes, expiresAt, lastUsedAt — nie das Secret.
```
```text title="Vergebbare Scopes abfragen"
GET /api/v1/token/_scopes
X-Auth-Token: <jwt>
# ["audit.read", "llm.chat", "namespace.meinprojekt", "namespace.meinprojekt.content.read", "project.read", "user.read", …]
```
<Aside type="caution">
Das Klartext-Secret (`token`-Feld, Format `tibi_pat_<id>_<secret>`) steht nur in der Anlage-Antwort.
Gespeichert wird nur ein SHA-256-Hash (`tokenHash`); Anzeige-Prefix (`tokenPrefix`) und `lastUsedAt` dienen der Wiedererkennung.
`expiresAt` leer heißt: kein Ablauf.
</Aside>
## `_meta`: Was steckt drin
Jeder Eintrag beschreibt ein Struct-Feld — erzeugt aus den Go-Tags (`getModelMeta`, `getStructMeta`):
| Schlüssel | Inhalt | Beispiel (`token`-Modell) |
|---|---|---|
| `name` | JSON-Feldname | `userId`, `name`, `scopes`, `expiresAt`, `lastUsedAt` (`tokenHash` ist `-`, `token` nur lesend) |
| `type` / `kind` | Typname / Art | `string`, `slice` |
| `validator` | Validierungsregeln, falls gesetzt | `["required", "min=1", "max=100"]` bei `name` |
| `sliceOf` / `structOf` | Elementtyp bei Listen bzw. Unterfelder bei Objekten | `scopes`: `sliceOf: string` |
Nützlich für Generatoren und Agents: Statt Raten fragst du `GET /api/v1/_meta/token` und weißt, welche Felder `POST /api/v1/token` akzeptiert — etwa dass `scopes` eine String-Liste ist.
Verifiziert am 68b4278 2026-10-17 (tibi-server)
@@ -0,0 +1,386 @@
---
title: 'Schema-Properties (generiert)'
description: 'Alle Properties aller 16 Config-Schemas als Tabelle — generiert, nicht von Hand editieren.'
---
Diese Seite listet die Properties aller 16 Config-Schemas aus `tibi-types/schemas/config/`.
> Generiert, nicht von Hand editieren — neu erzeugen mit `npm run gen:schemas`.
Typnamen folgen JSON-Schema (`string`, `integer`, `number`, `boolean`, `object`, `Liste von …`);
`$ref`-Verweise nennen den Baustein und dessen Typ, `oneOf`-Alternativen sind mit „oder“ verbunden.
## action
*Tibi action config* — `action.schema.json`
Pflicht: `name`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `name` | string | ja | — | — |
| `meta` | `actionMeta` (action) — object | nein | — | — |
| `permissions` | `actionPermissions` (action) — object | nein | — | — |
| `hooks` | string oder `hooks.schema.json` — object | nein | — | — |
| `audit` | object | nein | — | — |
| `cors` | object | nein | — | — |
### Bausteine in action
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `actionField` | `name`, `type`, `index`, `subFields`, `validator`, `readonly`, `hidden`, `strictFields`, `meta` | `name`, `type` |
| `actionMappedField` | `source`, `name`, `type`, `subFields`, `meta` | `type` |
| `actionFieldSection` | `title`, `description`, `fields` | — |
| `actionExecutionMeta` | `kind`, `cancellable` | — |
| `actionOutputProgressMapping` | `indexField`, `countField`, `statusField`, `messageField`, `fields` | — |
| `actionOutputMappingSection` | `fields` | — |
| `actionOutputMapping` | `summary`, `details`, `progress` | — |
| `actionOutputMeta` | `title`, `mapping` | — |
| `actionMethodMeta` | `query`, `data`, `execution`, `output` | — |
| `actionMeta` | `label`, `muiIcon`, `description`, `group`, `hide`, `methods` | — |
| `actionPermissionSet` | `methods` | `methods` |
| `actionPermissions` | `public`, `user` | `public`, `user` |
## asset
*Tibi asset config* — `asset.schema.json`
Pflicht: `name`, `path`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `name` | string | ja | — | — |
| `path` | string | ja | — | — |
## collection-meta
*Tibi collection meta config* — `collection-meta.schema.json`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `label` | `i18nText` (defs) — string oder object | nein | — | — |
| `muiIcon` | string | nein | — | — |
| `group` | string | nein | — | — |
| `exports` | Liste von object | nein | — | Custom scripted exports (tibi-admin-nova ExportDialog). The script loads data itself via db.find/loadAll. |
| `designGuide` | `i18nText` (defs) — string oder object | nein | — | — |
| `imageUrl` | string oder `evalString` (defs) — object | nein | — | — |
| `hide` | boolean | nein | — | — |
| `singleton` | object | nein | — | — |
| `subNavigation` | Liste von object | nein | — | — |
| `preview` | `previewConfig` (defs) — `fieldPath` (defs) oder `evalHtml` (defs) oder `previewSlots` (defs) | nein | — | — |
| `i18n` | boolean, eine von: `false` oder object | nein | — | — |
| `defaultSort` | object | nein | — | — |
| `viewHint` | string, eine von: `table`, `cards` oder object | nein | — | — |
| `pagebuilder` | object | nein | — | — |
| `sidebar` | Liste von object | nein | — | — |
| `sections` | Liste von object | nein | — | — |
| `openapi` | `openapi.schema.json` — object | nein | — | — |
| `query` | object | nein | — | Additional query parameters hooks may read (ctx.request().query(name)). Documented for agents (REST _meta/OpenAPI, MCP collection_describe + extra argument) and validated on MCP calls. Credential-like keys (permission token names, auth headers) and core query keys are never allowed. |
| `agent` | object | nein | — | Editor- und Agenten-Dokumentation (Zweck, Workflows, Gotchas). Freiform-Doku, wird u.a. ueber MCP/REST _meta ausgespielt. |
## collection
*Tibi collection config* — `collection.schema.json`
Pflicht: `name`, `permissions`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `name` | string | ja | — | — |
| `defaultLanguage` | string | nein | — | — |
| `uploadPath` | string | nein | — | — |
| `upload` | `uploadConfig` (collection) — object | nein | — | — |
| `permissions` | string oder `permissions.schema.json` — object | ja | — | — |
| `projections` | string oder `projections.schema.json` — object | nein | — | — |
| `hooks` | string oder `hooks.schema.json` — object | nein | — | — |
| `meta` | string oder `collection-meta.schema.json` — object | nein | — | — |
| `imageFilter` | string oder `image-filter.schema.json` — object | nein | — | — |
| `fields` | string oder `field-list.schema.json` — Liste von string oder `field.schema.json` | nein | — | — |
| `indexes` | string oder Liste von (string oder `indexConfig` (collection) — object) | nein | — | — |
| `audit` | object | nein | — | — |
| `bulk` | object | nein | — | — |
| `cors` | object | nein | — | — |
| `queryLimits` | object | nein | — | — |
| `readonlyFields` | Liste von `fieldPath` (defs) — string | nein | — | — |
| `hiddenFields` | Liste von `fieldPath` (defs) — string | nein | — | — |
| `strictFields` | boolean | nein | — | — |
| `search` | Liste von `searchConfig` (collection) — object | nein | — | — |
### Bausteine in collection
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `uploadConfig` | `maxBodySize`, `allowedMimeTypes` | — |
| `indexConfig` | `name`, `key`, `unique`, `dropDups`, `background`, `sparse`, `defaultLanguage`, `languageOverride` | `key` |
| `searchConfig` | `name`, `mode`, `fields`, `meta`, `eval`, `filter`, `ngram`, `regex`, `score`, `vector`, `rrf`, `async`, `autoRegenerate` | `name`, `mode` |
## defs
*Shared definitions for Tibi config schemas* — `defs.schema.json`
Dieses Schema definiert nur **`$ref`-Bausteine** (kein eigenes Config-Ziel):
| Baustein | Typ | Pflicht | Beschreibung |
| --- | --- | --- | --- |
| `i18nText` | string oder object | — | — |
| `fieldPath` | string | — | — |
| `evalString` | object | Pflicht: `eval` | — |
| `evalHtml` | object | Pflicht: `eval` | — |
| `boolOrEval` | boolean oder `evalString` (defs) | — | — |
| `boolOrUiCondition` | boolean oder string oder `evalString` (defs) | — | — |
| `uiCondition` | string oder `evalString` (defs) | — | — |
| `sortField` | `fieldPath` (defs) oder object | — | — |
| `pagebuilderViewport` | integer oder string, eine von: `desktop`, `tablet`, `phone` | — | — |
| `badgeVariant` | string, eine von: `success`, `error`, `warning`, `info`, `neutral` | — | — |
| `choiceItem` | object | — | — |
| `choices` | string oder Liste von `choiceItem` (defs) oder object | — | — |
| `previewTableColumn` | `fieldPath` (defs) oder object | — | — |
| `previewSlots` | object | — | — |
| `previewConfig` | `fieldPath` (defs) oder `evalHtml` (defs) oder `previewSlots` (defs) | — | — |
| `foreignRender` | `fieldPath` (defs) oder `evalHtml` (defs) oder object oder `previewSlots` (defs) | — | — |
| `methodPermissionGet` | boolean oder object | — | — |
| `methodPermissionWrite` | boolean oder object | — | — |
## field-list
*Tibi field list config* — `field-list.schema.json`
Dieses Schema beschreibt eine **Liste** — keine Objekt-Properties, sondern Elemente:
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `[]` (Elemente) | string oder `field.schema.json` — `field` | — | — | Jedes Element ist entweder ein Feldname (`string`) oder ein Feld-Objekt nach `field.schema.json`. |
## field-meta
*Tibi field meta config* — `field-meta.schema.json`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `label` | `i18nText` (defs) — string oder object | nein | — | — |
| `widget` | string, eine von: `text`, `textarea`, `number`, `checkbox`, `switch`, `select`, `selectArray`, `chipArray`, `date`, `datetime`, `file`, `image`, `json`, `richtext`, `html`, `foreignKey`, `foreignKeyChipArray`, `foreignFile`, `foreignMedia`, `pagebuilder`, `pageBuilder`, `templateProps`, `containerLessObject`, `containerLessObjectArray`, `color` | nein | — | — |
| `i18n` | fest: `false` | nein | — | — |
| `helperText` | `i18nText` (defs) — string oder object | nein | — | — |
| `hideLabel` | boolean | nein | — | — |
| `hide` | boolean | nein | — | — |
| `protected` | `boolOrUiCondition` (defs) — boolean oder string oder `evalString` (defs) | nein | — | — |
| `position` | string | nein | — | — |
| `section` | `i18nText` (defs) — string oder object | nein | — | — |
| `containerProps` | object | nein | — | — |
| `defaultValue` | string oder number oder boolean oder null oder array oder object | nein | — | — |
| `dependsOn` | `uiCondition` (defs) — string oder `evalString` (defs) | nein | — | — |
| `inputProps` | object | nein | — | — |
| `choices` | `choices` (defs) — string oder Liste von `choiceItem` (defs) oder object | nein | — | — |
| `foreign` | object | nein | — | — |
| `downscale` | object | nein | — | — |
| `imageEditor` | boolean oder object | nein | — | — |
| `drillDown` | boolean | nein | — | — |
| `pageSize` | integer | nein | — | — |
| `preview` | `previewConfig` (defs) — `fieldPath` (defs) oder `evalHtml` (defs) oder `previewSlots` (defs) | nein | — | — |
| `addElementLabel` | `i18nText` (defs) — string oder object | nein | — | — |
| `filter` | boolean oder object | nein | — | — |
| `pagebuilder` | object | nein | — | — |
| `schemaRef` | object | nein | — | — |
| `openapi` | `fieldOpenapi` (openapi) — object | nein | — | — |
## field
*Tibi field config* — `field.schema.json`
Bildet den Baustein `field` dieses Schemas ab.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `name` | string | ja | — | — |
| `type` | string, eine von: `string`, `string[]`, `number`, `number[]`, `boolean`, `object`, `object[]`, `file`, `file[]`, `date`, `any` | ja | — | — |
| `index` | Liste von string, eine von: `single`, `unique`, `text`, `sparse`, `background` | nein | — | — |
| `subFields` | string oder Liste von (string oder `field` — object) | nein | — | — |
| `validator` | object | nein | — | — |
| `readonly` | `boolOrEval` (defs) — boolean oder `evalString` (defs) | nein | — | — |
| `hidden` | `boolOrEval` (defs) — boolean oder `evalString` (defs) | nein | — | — |
| `strictFields` | boolean | nein | — | — |
| `meta` | `field-meta.schema.json` — object | nein | — | — |
### Bausteine in field
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `field` | `name`, `type`, `index`, `subFields`, `validator`, `readonly`, `hidden`, `strictFields`, `meta` | `name`, `type` |
## hooks
*Tibi hooks config* — `hooks.schema.json`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `get` | `getHooks` (hooks) — object | nein | — | — |
| `post` | `postHooks` (hooks) — object | nein | — | — |
| `put` | `putHooks` (hooks) — object | nein | — | — |
| `delete` | `deleteHooks` (hooks) — object | nein | — | — |
| `audit` | `auditHooks` (hooks) — object | nein | — | — |
### Bausteine in hooks
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `hook` | `type`, `file`, `timeout` | `type`, `file` |
| `getHooks` | `handle`, `read`, `return`, `file` | — |
| `postHooks` | `bind`, `validate`, `handle`, `create`, `return`, `bulkCreate`, `bulkReturn` | — |
| `putHooks` | `bind`, `validate`, `update`, `return`, `bulkUpdate`, `bulkReturn` | — |
| `deleteHooks` | `delete`, `return`, `bulkDelete`, `bulkReturn` | — |
| `auditHooks` | `return` | — |
## image-filter
*Tibi image filter config* — `image-filter.schema.json`
Dieses Schema nutzt **freie Schlüssel** (Pattern):
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `<name>` (Muster `^[a-zA-Z0-9_-]+$`) | Liste von object | — | — | Freier Schlüssel, Wert siehe Untertabelle. |
### Wertefelder zu `^[a-zA-Z0-9_-]+$`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `width` | number | nein | — | — |
| `height` | number | nein | — | — |
| `fit` | boolean | nein | — | — |
| `fill` | boolean | nein | — | — |
| `resampling` | string, eine von: `nearestNeighbor`, `hermite`, `linear`, `catmullRom`, `lanczos`, `box`, `mitchellNetravili`, `bSpline`, `gaussian`, `bartlett`, `hann`, `hamming`, `blackman`, `welch`, `cosine` | nein | — | — |
| `anchor` | string, eine von: `center`, `topLeft`, `top`, `topRight`, `left`, `right`, `bottomLeft`, `bottom`, `bottomRight` | nein | — | — |
| `brightness` | number | nein | — | — |
| `saturation` | number | nein | — | — |
| `contrast` | number | nein | — | — |
| `gamma` | number | nein | — | — |
| `blur` | number | nein | — | — |
| `sharpen` | number | nein | — | — |
| `invert` | boolean | nein | — | — |
| `grayscale` | boolean | nein | — | — |
| `quality` | number | nein | — | — |
| `lossless` | boolean | nein | — | — |
| `skipLargerDimension` | boolean | nein | — | — |
| `skipLargerFilesize` | boolean | nein | — | — |
| `outputType` | string, eine von: `jpg`, `jpeg`, `png`, `webp` | nein | — | — |
## job
*Tibi job config* — `job.schema.json`
Pflicht: `type`, `file`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `cron` | string | nein | — | — |
| `type` | fest: `javascript` | ja | — | — |
| `file` | string | ja | — | — |
| `timeout` | integer | nein | — | — |
| `meta` | object | nein | — | — |
## openapi
*Tibi OpenAPI collection config* — `openapi.schema.json`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `disabled` | boolean | nein | — | Exclude this entire collection from the generated OpenAPI spec |
| `get` | `operationObject` — object | nein | — | OpenAPI operation overrides for the list endpoint (GET /{collection}) |
| `getOne` | `operationObject` — object | nein | — | OpenAPI operation overrides for the single-entry endpoint (GET /{collection}/{id}) |
| `post` | `operationObject` — object | nein | — | OpenAPI operation overrides for the create endpoint (POST /{collection}) |
| `put` | `operationObject` — object | nein | — | OpenAPI operation overrides for the update endpoint (PUT /{collection}/{id}) |
| `delete` | `operationObject` — object | nein | — | OpenAPI operation overrides for the delete endpoint (DELETE /{collection}/{id}) |
### Bausteine in openapi
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `codeSample` | `lang`, `label`, `source` | `lang`, `source` |
| `responseObject` | `description`, `x-summary` | — |
| `operationObject` | `disabled`, `tags`, `summary`, `description`, `x-codeSamples`, `parameters`, `responses`, `security` | — |
| `fieldOpenapi` | `disabled`, `description`, `example`, `format`, `enum`, `deprecated` | — |
## permissions
*Tibi permissions config* — `permissions.schema.json`
Pflicht: `public`, `user`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `public` | `permissionSet` (permissions) — object | ja | — | — |
| `user` | `permissionSet` (permissions) — object | ja | — | — |
### Bausteine in permissions
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `permissionSet` | `methods`, `filter`, `validProjections`, `readonlyFields`, `hiddenFields` | `methods` |
## project
*Tibi project config* — `project.schema.json`
Pflicht: `namespace`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `namespace` | string | ja | — | — |
| `meta` | object | nein | — | — |
| `upload` | `uploadConfig` (project) — object | nein | — | — |
| `collections` | Liste von (string oder `collection.schema.json` — object) | nein | — | — |
| `jobs` | Liste von (string oder `job.schema.json` — object) | nein | — | — |
| `actions` | Liste von (string oder `action.schema.json` — object) | nein | — | — |
| `assets` | Liste von (string oder `asset.schema.json` — object) | nein | — | — |
| `cors` | object | nein | — | — |
| `admin` | object | nein | — | — |
| `hook` | object | nein | — | — |
| `audit` | object | nein | — | — |
| `strictFields` | boolean | nein | — | — |
| `snapshotPath` | string | nein | — | Base directory for snapshot data, relative to configDir. Default: '{configDir}/snapshots'. |
| `snapshots` | Liste von (string oder `snapshot.schema.json` — object) | nein | — | List of snapshot profiles for automated point-in-time backups. |
| `legacy` | object | nein | — | — |
### Bausteine in project
| Baustein | Felder | Pflicht |
| --- | --- | --- |
| `uploadConfig` | `maxBodySize`, `allowedMimeTypes` | — |
## projections
*Tibi projections config* — `projections.schema.json`
Dieses Schema nutzt **freie Schlüssel** (Pattern):
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `<name>` (Muster `^[a-zA-Z0-9_]+$`) | object | — | — | Freier Schlüssel, Wert siehe Untertabelle. |
### Wertefelder zu `^[a-zA-Z0-9_]+$`
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `select` | object oder null | ja | — | — |
## snapshot
*Tibi snapshot config* — `snapshot.schema.json`
Pflicht: `name`.
| Property | Typ | Pflicht | Vorgabe/Default | Beschreibung |
| --- | --- | --- | --- | --- |
| `name` | string | ja | — | Unique identifier for this snapshot profile (e.g. 'content-navigation') |
| `description` | string | nein | — | Optional human-readable explanation of this snapshot profile |
| `collections` | Liste von (string oder object) | nein | — | Collection filter: empty = all, include-mode = only listed, exclude-mode = all except listed. Mixed include+exclude is invalid. |
| `includeMedia` | boolean | nein | `true` | Whether to include media files (file/file[] fields) via hardlinks. Default: true. |
| `collectionDedup` | boolean | nein | `false` | Enable SHA256-based deduplication of collection JSON exports. Identical exports share one canonical copy via hardlinks in .store/collections/. Default: false. |
| `mode` | string, eine von: `json`, `db`, `both` | nein | — | Storage strategy for collection data. 'json' = BSON Extended JSON files on disk (current, implemented), 'db' = MongoDB $out copy (future), 'both' = both (future). Default: 'json'. |
| `retention` | object | nein | — | Auto-pruning rules for old snapshot entries. Omit for unlimited retention. |
| `schedule` | string | nein | — | Cron expression for automatic execution (e.g. '0 3 * * *' = daily 3am UTC, '@every 24h'). Empty = manual trigger only. |
| `permissions` | Liste von string | nein | — | Bare permission names for team-based access (Phase 2). Empty = admin-only in Phase 1. |
| `enabled` | boolean | nein | `false` | Whether this snapshot profile is active. Disabled profiles are skipped by the scheduler but can still be triggered manually. |
Generiert aus `tibi-types 4c55f82` (Stand 2026-10-12) — Änderungen an den Schemas dort erfordern `npm run gen:schemas`.
+14 -1
View File
@@ -30,9 +30,22 @@ Das Verzeichnis enthält 16 Dateien (nicht 15, wie ältere Docs behaupten):
| `openapi.schema.json` | OpenAPI-Metadaten je Collection | keine | | `openapi.schema.json` | OpenAPI-Metadaten je Collection | keine |
| `defs.schema.json` | nur `$ref`-Bausteine, kein eigenes Ziel | — | | `defs.schema.json` | nur `$ref`-Bausteine, kein eigenes Ziel | — |
## Vollständige Property-Matrix
Jede Property jedes Schemas — Typ, Pflicht, Vorgabe und Beschreibung — steht in der [Schema-Properties-Referenz](/reference/schema-properties/).
Diese Seite hier bleibt der Einstieg: welches Schema wofür gilt und wie du die Dateien verdrahtest.
Die Property-Tabellen dort erzeugt `npm run gen:schemas` direkt aus den JSON-Schemas; nichts davon ist von Hand abgeschrieben.
## Dateinamen und Auflösung
- Dein Projekt liegt unter `api/`: `api/config.yml` (Schema `project.schema.json`) plus `api/collections/*.yml` (je Datei `collection.schema.json`).
- Viele Stellen akzeptieren **Dateipfad oder Inline-Objekt**: In der Typ-Spalte der [Property-Matrix](/reference/schema-properties/) erkennst du das an `string oder …` — etwa bei `collections`, `fields`, `permissions`, `hooks` oder `meta`.
Ein `string` verweist auf eine weitere Datei (z. B. eine Collection- oder Hook-Datei), ein Objekt steht direkt an Ort und Stelle.
- Die Reihenfolge der Auflösung ist dabei immer gleich: Datei lesen, `$schema`-Form prüfen, dann Referenzen (`string`-Pfade, `$ref`-Bausteine aus `defs.schema.json`) auflösen.
## Lesart ## Lesart
<Tabs> <Tabs syncKey="schema-lesart">
<TabItem label="Kopfzeile"> <TabItem label="Kopfzeile">
```yaml title="api/collections/content.yml" ```yaml title="api/collections/content.yml"
@@ -8,6 +8,7 @@ import { Aside } from '@astrojs/starlight/components';
Du musst die Maus nicht anfassen: Fast alles im **tibi-admin** erreichst du per Taste oder über die Befehlspalette. Du musst die Maus nicht anfassen: Fast alles im **tibi-admin** erreichst du per Taste oder über die Befehlspalette.
`?` zeigt die Kürzel-Übersicht direkt im Admin. Alle Bezeichnungen unten stehen wörtlich so in der Oberfläche (`de.json`). `?` zeigt die Kürzel-Übersicht direkt im Admin. Alle Bezeichnungen unten stehen wörtlich so in der Oberfläche (`de.json`).
Suchbegriffe: Shortcuts, Hotkeys, Command Palette, Schnellsuche, Brotkrumen. Suchbegriffe: Shortcuts, Hotkeys, Command Palette, Schnellsuche, Brotkrumen.
Die Anwender-Kurzfassung für den Alltag steht unter [Tastenkürzel](/anwender/tastenkombinationen/).
## Regeln zuerst ## Regeln zuerst
@@ -79,4 +80,4 @@ Der Browser-Zurück-Button gehört dazu: Nach dem Eintritt ins Projekt stellt
Hat der Eintrag ungespeicherte Änderungen, blockiert erst ein Schutz-Dialog den Weggang — erst deine Bestätigung („Verwerfen“) setzt die Navigation fort. Hat der Eintrag ungespeicherte Änderungen, blockiert erst ein Schutz-Dialog den Weggang — erst deine Bestätigung („Verwerfen“) setzt die Navigation fort.
In verschachtelten Seiten-Panels (Drawern) zeigt eine zweite, kompakte Pfadzeile den Ebenen-Pfad („Navigationspfad“); auf schmalen Bildschirmen klappt sie mittlere Ebenen zu „…“ zusammen, Antippen zeigt den vollen Pfad („Vollständigen Pfad anzeigen“). In verschachtelten Seiten-Panels (Drawern) zeigt eine zweite, kompakte Pfadzeile den Ebenen-Pfad („Navigationspfad“); auf schmalen Bildschirmen klappt sie mittlere Ebenen zu „…“ zusammen, Antippen zeigt den vollen Pfad („Vollständigen Pfad anzeigen“).
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova: shortcuts.ts, KeyboardShortcuts-, CommandPalette-, Breadcrumb-, DrawerBreadcrumb-Komponenten, de.json, Test-Plan 02). Verifiziert am 2b091d8 2026-10-13 (tibi-admin-nova: shortcuts.ts, KeyboardShortcuts-, CommandPalette-, Breadcrumb-, DrawerBreadcrumb-Komponenten, de.json, Test-Plan 02)
+4 -4
View File
@@ -5,7 +5,7 @@ description: 'Welcher Kopf wofür: Token, X-Auth-Token, X-Admin-Token, Bearer.'
import { Aside, Tabs, TabItem } from '@astrojs/starlight/components'; import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';
Jeder Request braucht den passenden Kopf im passenden Header. Falscher Kopf heißt fast immer `permission denied` oder `project not found`. Jeder Request braucht den passenden Kopf im passenden Header. Falscher Kopf führt häufig zu `permission denied` oder `project not found`.
## Tabelle: Vier Köpfe ## Tabelle: Vier Köpfe
@@ -16,10 +16,10 @@ Jeder Request braucht den passenden Kopf im passenden Header. Falscher Kopf hei
| `X-Admin-Token` | Server-Admin-Token | System-APIs wie Projekt-CRUD, Reload | Server-Config des laufenden tibi-servers | | `X-Admin-Token` | Server-Admin-Token | System-APIs wie Projekt-CRUD, Reload | Server-Config des laufenden tibi-servers |
| `Authorization: Bearer …` | Gleicher Wert wie `X-Admin-Token` | Alias für Server-Admin-Token; Deploy-Skripte nutzen ihn am Reload-Endpunkt | Server-Config bzw. `api/config.yml.env` | | `Authorization: Bearer …` | Gleicher Wert wie `X-Admin-Token` | Alias für Server-Admin-Token; Deploy-Skripte nutzen ihn am Reload-Endpunkt | Server-Config bzw. `api/config.yml.env` |
<Tabs> <Tabs syncKey="token-anwendung">
<TabItem label="Collection schreiben"> <TabItem label="Collection schreiben">
```sh title="Dokument anlegen" ```sh title="Dokument anlegen" {2}
curl -s -X POST "$URL/api/content" \ curl -s -X POST "$URL/api/content" \
-H "Token: $ADMIN_TOKEN" \ -H "Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
@@ -64,4 +64,4 @@ Direkt am Server heißt die Route `/api/v1/login` (`setup.go`: Gruppe `/api/v1`
Ein funktionierender Collection-Token beweist keinen Projekt-Admin-Zugriff. Prüfe jeden Kopf an seinem eigenen Endpunkt. Ein funktionierender Collection-Token beweist keinen Projekt-Admin-Zugriff. Prüfe jeden Kopf an seinem eigenen Endpunkt.
</Aside> </Aside>
Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter) und 68b42785 2026-10-15 (tibi-server) Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter) und 68b4278 2026-10-15 (tibi-server)
+21 -3
View File
@@ -3,7 +3,7 @@ title: 'Betrieb und Beobachtung'
description: 'Logs, Metriken, Suche neu aufbauen, Jobs und Audit aus Betreiber-Sicht.' description: 'Logs, Metriken, Suche neu aufbauen, Jobs und Audit aus Betreiber-Sicht.'
--- ---
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components'; import { Aside, Badge, Steps } from '@astrojs/starlight/components';
Du betreibst den **tibi-server** als Container. Diese Seite zeigt Startprofile, Gesundheit, Suche, Jobs und typische Störungen. Details zur Datei stehen unter [Server-Konfiguration](/server/konfiguration/). Du betreibst den **tibi-server** als Container. Diese Seite zeigt Startprofile, Gesundheit, Suche, Jobs und typische Störungen. Details zur Datei stehen unter [Server-Konfiguration](/server/konfiguration/).
@@ -18,7 +18,7 @@ Der Starter (`docker-compose-local.yml`) kennt zwei Profile:
| `tibi` | `tibiserver` (Image `tibi-server:nova`) | Normalbetrieb: fertiges Image, Projekt unter `./` als `/data` gemountet | | `tibi` | `tibiserver` (Image `tibi-server:nova`) | Normalbetrieb: fertiges Image, Projekt unter `./` als `/data` gemountet |
| `tibi-dev` | `tibiserver-dev` (Build aus `tibi-server/Dockerfile.air`) | Entwicklung am Server: Live-Reload, Server-Code gemountet | | `tibi-dev` | `tibiserver-dev` (Build aus `tibi-server/Dockerfile.air`) | Entwicklung am Server: Live-Reload, Server-Code gemountet |
Dazu laufen `mongo` (MongoDB 7) und `maildev` (Mails auf `maildev:25`, Web-UI zum Lesen). Dazu laufen `mongo` (MongoDB 8) und `maildev` (Mails auf `maildev:25`, Web-UI zum Lesen).
```bash title="Starten und Logs ansehen" ```bash title="Starten und Logs ansehen"
docker compose --profile tibi up -d docker compose --profile tibi up -d
@@ -73,6 +73,23 @@ Jobs sind geplante Skripte je Projekt (Cron in der Projekt-`config.yml`, gleiche
- Sie laufen unter der Projekt-UID (suexec-Isolation, siehe [Sicherheit](/server/sicherheit/)). - Sie laufen unter der Projekt-UID (suexec-Isolation, siehe [Sicherheit](/server/sicherheit/)).
- Schlägt ein Job fehl, steht die Ursache im Server-Log; Quotas und Nutzung landen in der LLM-Nutzungsablage. - Schlägt ein Job fehl, steht die Ursache im Server-Log; Quotas und Nutzung landen in der LLM-Nutzungsablage.
## Datei-Cleanup
Löschst du ein Dokument mit Datei-Feldern, räumt der Server dessen Upload-Verzeichnis (`<uploadPfad>/<docId>/`) unter der Projekt-UID weg — kein separater Job, sondern Teil des Löschens:
| Auslöser | Wann genau | Bei Fehler |
|---|---|---|
| `DELETE …/<collection>/<id>` | Nach erfolgreichem DB-Löschen, nur bei Sammlungen mit Datei-Feldern | Dokument ist weg; Antwort trägt `warning: "Document deleted, but file cleanup partially failed"`, Details im Server-Log (`delete cleanup failed for <ns>/<col>/<id>`) |
| Bulk-Delete | Pro Dokument wie oben | Gleich, pro Dokument |
| `PUT`, wenn alle Datei-Referenzen entfernt wurden | Nur wenn danach kein `file.src` mehr im Dokument steht | `400` mit der Fehlerursache — das Dokument-Update schlägt fehl |
| Hook-Funktionen `db.delete` / `db.deleteMany` | Wie Einzel-Delete | Nur Log (`db.delete cleanup failed …`) |
<Aside type="note" title="Kein Schalter, kein Kehr-Job">
Es gibt **keinen** Config-Schalter und **keinen** periodischen Waisen-Lauf: Was beim Löschen nicht wegkommt (z. B. Server-Absturz dazwischen), bleibt liegen.
Der Pfad-Schutz (`SafeRemoveAll`) löscht nur innerhalb des Upload-Verzeichnisses mit Mindest-Tiefe — außerhalb liegende Pfade werden verweigert statt gelöscht.
Waisen findest du per Dateisuche im Upload-Pfad (`media/` bzw. `uploadPath` der Sammlung).
</Aside>
## Störungen ## Störungen
| Bild | Ursache (häufig) | Maßnahme | | Bild | Ursache (häufig) | Maßnahme |
@@ -82,8 +99,9 @@ Jobs sind geplante Skripte je Projekt (Cron in der Projekt-`config.yml`, gleiche
| Login wird immer langsamer | Ratelimit-Backoff nach Fehlversuchen | Warten (Retry-After), Zugangsdaten prüfen | | Login wird immer langsamer | Ratelimit-Backoff nach Fehlversuchen | Warten (Retry-After), Zugangsdaten prüfen |
| Suche findet nichts Neues | Worker hängt oder Index alt | Worker-Status abfragen, `regenerate-search` | | Suche findet nichts Neues | Worker hängt oder Index alt | Worker-Status abfragen, `regenerate-search` |
| 413 bei Upload | Body-Limit erreicht (Server → Projekt → Collection) | `API_UPLOAD_MAX_BODY_SIZE` bzw. Collection-Limit erhöhen | | 413 bei Upload | Body-Limit erreicht (Server → Projekt → Collection) | `API_UPLOAD_MAX_BODY_SIZE` bzw. Collection-Limit erhöhen |
| `warning: Document deleted, but file cleanup …` | Upload-Verzeichnis ließ sich nicht entfernen (Rechte, Pfad) | Log-Zeile `delete cleanup failed …` prüfen, Rest per Dateisuche im Upload-Pfad entfernen |
| Leere Seite trotz Daten | CORS blockt den Browser | Origins in der Collection-Config prüfen | | Leere Seite trotz Daten | CORS blockt den Browser | Origins in der Collection-Config prüfen |
Begriffe wie Collection, Hook oder Namespace klärt [Begriffe](/einstieg/begriffe/). Zum Einrichten siehe [Schnellstart](/einstieg/schnellstart/). Begriffe wie Collection, Hook oder Namespace klärt [Begriffe](/einstieg/begriffe/). Zum Einrichten siehe [Schnellstart](/einstieg/schnellstart/).
Verifiziert am `68b42785` 2026-10-15 (tibi-server); Compose-Profile am `e3f92ebf` 2026-10-14 (tibi-svelte-starter). Verifiziert am 68b4278 2026-10-15 (tibi-server); Compose-Profile am e3f92eb 2026-10-14 (tibi-svelte-starter). Datei-Cleanup aus `internal/filecleanup/cleanup.go` und Aufrufern (`collection_delete.go`, `collection_bulk_delete.go`, `collection_put.go`, `hook/context_db.go`)
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more