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
No files matched your search
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 48 KiB |
@@ -3,7 +3,7 @@ title: 'Aktionen'
|
||||
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/).
|
||||
|
||||
@@ -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`) |
|
||||
| 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>
|
||||
|
||||

|
||||
|
||||
## Parameter
|
||||
|
||||
@@ -47,13 +68,13 @@ Das Formular (`ActionMethodScaffold.svelte`) trennt zwei Blöcke:
|
||||
## Ausgabe und Fortschritt
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Kurfassung und Details">
|
||||
<TabItem label="Zusammenfassung und Details">
|
||||
|
||||
`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.
|
||||
- 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 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).
|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: 'Ansichten und Listen'
|
||||
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/).
|
||||
|
||||
@@ -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 |
|
||||
| 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/. -->
|
||||

|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: 'Benutzer, Teams, Tokens'
|
||||
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/).
|
||||
|
||||
@@ -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 |
|
||||
|
||||
- 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
|
||||
|
||||
@@ -78,6 +78,32 @@ Persönliche Token (PATs) pflegst du auf zwei Wegen: im eigenen **Profil** (`Tok
|
||||
</TabItem>
|
||||
</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)
|
||||
|
||||
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 |
|
||||
| `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. -->
|
||||

|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: 'Admin anpassen'
|
||||
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/).
|
||||
|
||||
@@ -11,26 +11,15 @@ Du passt das **tibi-admin** (Verwaltungs-Backend, Release Nova) an dein Projekt
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
## 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):
|
||||
|
||||
| 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`).
|
||||
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`).
|
||||
|
||||
## 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>
|
||||
|
||||
<!-- Screenshot-Platzhalter (T-SHOTS): angepasste Listenansicht mit Spaltenwahl und eigenem Export-Reiter. -->
|
||||

|
||||
|
||||
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.
|
||||
@@ -3,7 +3,7 @@ title: 'Eintrags-Editor'
|
||||
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/).
|
||||
|
||||
@@ -26,21 +26,7 @@ Du öffnest einen Eintrag aus einer [Liste](/admin/ansichten-listen/) und bearbe
|
||||
|
||||
## 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`).
|
||||
|
||||
| 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 |
|
||||
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.
|
||||
|
||||
<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.
|
||||
@@ -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).
|
||||
|
||||
## 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
|
||||
|
||||
| Kürzel | Wirkung |
|
||||
@@ -88,7 +89,7 @@ Sprachwechsel-Tastenkürzel: `Strg + L` (zyklisch durch die Inhaltssprachen).
|
||||
| `?` | Tastaturhilfe öffnen |
|
||||
|
||||
<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>
|
||||
|
||||
## 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 |
|
||||
| Ä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/. -->
|
||||

|
||||
|
||||
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -3,7 +3,7 @@ title: '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 Tabellenkalkulation, 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:
|
||||
- name: bestellreport
|
||||
label: { de: "Bestellreport", en: "Order report" }
|
||||
file: "_/assets/exports/reports.mjs"
|
||||
file: "_/exports/reports.mjs"
|
||||
filename: "bestellreport-{date}.xlsx"
|
||||
```
|
||||
|
||||
## 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 |
|
||||
|---|---|
|
||||
@@ -56,19 +79,23 @@ Projekt-Entwickler legen eigene Exporte als ES-Modul unter `meta.exports[].file`
|
||||
| `text` | Text wie CSV oder HTML |
|
||||
| `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)"
|
||||
// reports.mjs — stark vereinfacht
|
||||
```js title="reports.mjs — lauffähiges Minimalbeispiel (CSV)"
|
||||
// _/exports/reports.mjs
|
||||
export default async function (ctx) {
|
||||
const rows = await ctx.db.loadAll({ filter: ctx.filter });
|
||||
ctx.progress(1, 1, "Fertig");
|
||||
const wb = ctx.xlsx.utils.json_to_sheet(rows);
|
||||
return { workbook: { SheetNames: ["Report"], Sheets: { Report: wb } }, filename: "bestellreport-{date}.xlsx", contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" };
|
||||
const rows = await ctx.db.loadAll(
|
||||
"orders",
|
||||
{ select: ["orderNo", "total"], filter: ctx.view.filter, sort: [`-${ctx.view.sortField || "orderNo"}`] },
|
||||
(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
|
||||
|
||||
@@ -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 |
|
||||
| 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/. -->
|
||||

|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: 'KI-Funktionen'
|
||||
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.
|
||||
|
||||
@@ -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) |
|
||||
| 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`).
|
||||
|
||||
@@ -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 |
|
||||
| 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`).
|
||||
- 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“**) |
|
||||
| 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“**) |
|
||||
| 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“**) |
|
||||
@@ -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 |
|
||||
| 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. -->
|
||||

|
||||
|
||||
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)
|
||||
@@ -33,4 +33,4 @@ import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova).
|
||||
Verifiziert am 2b091d8 2026-10-13 (tibi-admin-nova)
|
||||
@@ -3,7 +3,7 @@ title: 'Medien im Admin'
|
||||
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/).
|
||||
|
||||
@@ -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 |
|
||||
| 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/. -->
|
||||

|
||||
|
||||
Verifiziert am 2b091d89 2026-10-13 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -3,7 +3,7 @@ title: 'Admin einrichten'
|
||||
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`).
|
||||
|
||||
@@ -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/).
|
||||
- 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
|
||||
|
||||
```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>
|
||||
|
||||
<!-- Screenshot-Platzhalter (T-SHOTS): frisch gestarteter Admin mit Projektliste nach Login. -->
|
||||

|
||||
|
||||
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)
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
@@ -70,4 +70,4 @@ Ungespeicherte Eingaben gehen beim automatischen Abmelden verloren. Speichere de
|
||||
|
||||
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**.
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
@@ -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/).
|
||||
|
||||
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/).
|
||||
|
||||
Geprüft an 2b091d89 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -3,7 +3,7 @@ title: '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.
|
||||
|
||||
@@ -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).
|
||||
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**).
|
||||
4. Klicke auf **Exportieren**.
|
||||
5. Warte, bis der Fortschritt abgeschlossen ist.
|
||||
6. Öffne die Datei auf deinem Rechner.
|
||||
3. Klicke auf **Exportieren**.
|
||||
4. Warte, bis der Fortschritt abgeschlossen ist.
|
||||
5. Ö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>
|
||||
|
||||
<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 type="caution">
|
||||
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.
|
||||
</Aside>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 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/).
|
||||
|
||||
Geprüft an 2b091d89 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -5,22 +5,11 @@ description: 'Häufige Fragen und was du dann tust.'
|
||||
|
||||
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
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
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/).
|
||||
|
||||
<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.
|
||||
@@ -28,49 +17,19 @@ Lautet die Meldung „Keine Berechtigung für diese Aktion", fehlt dir das Recht
|
||||
|
||||
## Eintrag wird nicht gefunden
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
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/).
|
||||
|
||||
## Speichern ist blockiert
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
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/).
|
||||
|
||||
<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>
|
||||
|
||||
## Bild fehlt
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
Ö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/).
|
||||
|
||||
## 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.
|
||||
</Aside>
|
||||
|
||||
Geprüft an 2b091d89 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -3,7 +3,7 @@ title: 'KI nutzen'
|
||||
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.
|
||||
|
||||
@@ -57,6 +57,9 @@ Geht dir der Zusammenhang verloren, beginne über **Neuer Chat** von vorn und st
|
||||
|
||||
## Sprechen statt tippen
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Eingabe">
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Ausgabe">
|
||||
|
||||
<Steps>
|
||||
|
||||
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>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Aside type="note">
|
||||
Für die Spracheingabe benötigt der Browser Zugriff auf dein Mikrofon. Verweigert er ihn, erlaube ihn in den Browser-Einstellungen.
|
||||
</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/).
|
||||
|
||||
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.
|
||||
@@ -95,9 +95,9 @@ Bei vielen Einträgen erscheinen unten Seitenzahlen.
|
||||
</Steps>
|
||||
|
||||
<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>
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
@@ -74,9 +74,9 @@ Ohne **OK** geht die Anpassung verloren. Klicke zuerst auf **OK** und speichere
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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/).
|
||||
|
||||
Geprüft an 2b091d89 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -5,7 +5,7 @@ description: 'Start, Navigation und Übersicht im tibi-admin.'
|
||||
|
||||
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
|
||||
|
||||
@@ -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/).
|
||||
|
||||
## 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>
|
||||
|
||||
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.
|
||||
3. Wähle einen Vorschlag, um dorthin zu springen.
|
||||
|
||||
</Steps>
|
||||
|
||||
<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>
|
||||
|
||||

|
||||

|
||||
|
||||
## Ausgangspunkt wiederfinden
|
||||
|
||||
@@ -83,6 +83,6 @@ Die Schnellsuche springt zu Zielen wie Sammlungen und Projekten. Inhalte suchst
|
||||
|
||||
</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)
|
||||
@@ -3,7 +3,7 @@ title: 'Seiten bauen'
|
||||
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.
|
||||
|
||||
@@ -88,17 +88,37 @@ Das Block-Label mit Farbpunkt zeigt Typ und Nummer des Blocks, beispielsweise He
|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Tablet">
|
||||
|
||||
Simulierte Breite 768 px — prüfe, ob Text lesbar bleibt und Bilder nicht abgeschnitten sind.
|
||||
|
||||

|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Mobil">
|
||||
|
||||
Simulierte Breite 375 px — engste Ansicht, hier fallen Umbruch- und Schnittfehler zuerst auf.
|
||||
|
||||

|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Steps>
|
||||
|
||||
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).
|
||||
3. Ziehe bei Bedarf den Slider für eine freie Breite (320–1920 px).
|
||||
2. Klicke unter der Vorschau auf eines der drei Geräte-Symbole.
|
||||
3. Ziehe bei Bedarf den Slider für eine freie Breite.
|
||||
4. Prüfe, ob Text lesbar bleibt und Bilder nicht abgeschnitten sind.
|
||||
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.
|
||||
|
||||
Geprüft an 2b091d89 (tibi-admin-nova)
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova)
|
||||
@@ -64,4 +64,6 @@ Du spielst nichts selbst zurück. Eine Wiederherstellung (**Wiederherstellung st
|
||||
Stoße keine Wiederherstellung ohne Auftrag an.
|
||||
</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**.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
@@ -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. |
|
||||
| **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. |
|
||||
|
||||
## 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)
|
||||
@@ -3,7 +3,7 @@ title: Neues Projekt aufsetzen
|
||||
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.
|
||||
|
||||
@@ -11,16 +11,14 @@ Verifiziert gegen: Starter-`README.md`, Skill `tibi-project-setup/SKILL.md`, `Ma
|
||||
|
||||
## Setup-Checkliste
|
||||
|
||||
```text title="Setup-Checkliste"
|
||||
[ ] Geklont, `origin` zeigt aufs Projekt-Repo (`template` heißt das Starter-Remote)
|
||||
[ ] `__PROJECT_NAME__` (kebab-case) und `__TIBI_NAMESPACE__` (snake_case) ersetzt
|
||||
[ ] `__ORG__` / `__PROJECT__` ersetzt, kein `__*__` mehr per `rg` findbar
|
||||
[ ] `.env`-URLs gesetzt (`CODING_URL`, `CODING_TIBIADMIN_URL`, `STAGING_URL`, `LIVE_URL`)
|
||||
[ ] `api/config.yml.env` mit frischem `ADMIN_TOKEN` + `ADMIN_ASSET_VERSION`
|
||||
[ ] `App.svelte` hat `<svelte:head>` mit `<title>`
|
||||
[ ] `yarn install`, `make docker-up`, Website + Admin + API antworten
|
||||
[ ] `yarn validate`, `yarn build`, `yarn build:server` fehlerfrei
|
||||
```
|
||||
- [ ] Geklont, `origin` zeigt aufs Projekt-Repo (`template` heißt das Starter-Remote)
|
||||
- [ ] `__PROJECT_NAME__` (kebab-case) und `__TIBI_NAMESPACE__` (snake_case) ersetzt
|
||||
- [ ] `__ORG__` / `__PROJECT__` ersetzt, kein `__*__` mehr per `rg` findbar
|
||||
- [ ] `.env`-URLs gesetzt (`CODING_URL`, `CODING_TIBIADMIN_URL`, `STAGING_URL`, `LIVE_URL`)
|
||||
- [ ] `api/config.yml.env` mit frischem `ADMIN_TOKEN` + `ADMIN_ASSET_VERSION`
|
||||
- [ ] `App.svelte` hat `<svelte:head>` mit `<title>`
|
||||
- [ ] `yarn install`, `make docker-up`, Website + Admin + API antworten
|
||||
- [ ] `yarn validate`, `yarn build`, `yarn build:server` fehlerfrei
|
||||
|
||||
## 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`)
|
||||
|
||||
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" \
|
||||
-H "Content-Type: application/json" \
|
||||
-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 "X-Auth-Token: $jwt" \
|
||||
-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"
|
||||
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"
|
||||
```
|
||||
|
||||
:::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.
|
||||
:::
|
||||
</Aside>
|
||||
|
||||
## 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.
|
||||
|
||||
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server)
|
||||
@@ -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.
|
||||
|
||||
:::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.
|
||||
:::
|
||||
</Aside>
|
||||
|
||||
## 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` |
|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: Was ist Tibi?
|
||||
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:
|
||||
|
||||
@@ -27,10 +27,8 @@ Backend verwalten, Website bauen: als Projekt-Entwickler pflegst du Inhalte im A
|
||||
## Wo geht es weiter?
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Schnellstart">
|
||||
Starter klonen, starten, prüfen. [Loslegen](/einstieg/schnellstart/).
|
||||
</Card>
|
||||
<Card title="Begriffe">
|
||||
Die wichtigsten Tibi-Begriffe im Glossar. [Nachschlagen](/einstieg/begriffe/).
|
||||
</Card>
|
||||
<LinkCard title="Schnellstart" href="/einstieg/schnellstart/" description="Starter klonen, starten, prüfen." />
|
||||
<LinkCard title="Begriffe" href="/einstieg/begriffe/" description="Die wichtigsten Tibi-Begriffe im Glossar." />
|
||||
</CardGrid>
|
||||
|
||||
Verifiziert am 2b091d8 2026-10-17 (tibi-admin-nova), am 68b4278 2026-10-17 (tibi-server)
|
||||
@@ -13,33 +13,17 @@ hero:
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
|
||||
|
||||
## Wo finde ich was?
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Was ist Tibi?">
|
||||
Backend plus eigenständige Projekt-Websites im Überblick. [Lesen](/einstieg/was-ist-tibi/).
|
||||
</Card>
|
||||
<Card title="Schnellstart">
|
||||
Starter klonen, starten, prüfen — in 6 Schritten. [Starten](/einstieg/schnellstart/).
|
||||
</Card>
|
||||
<Card title="Neues Projekt">
|
||||
Vom Klon zum registrierten Projekt inkl. Checkliste. [Aufsetzen](/einstieg/neues-projekt/).
|
||||
</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>
|
||||
<LinkCard title="Was ist Tibi?" href="/einstieg/was-ist-tibi/" description="Backend plus eigenständige Projekt-Websites im Überblick." />
|
||||
<LinkCard title="Schnellstart" href="/einstieg/schnellstart/" description="Starter klonen, starten, prüfen — in 6 Schritten." />
|
||||
<LinkCard title="Neues Projekt" href="/einstieg/neues-projekt/" description="Vom Klon zum registrierten Projekt inkl. Checkliste." />
|
||||
<LinkCard title="Projekt bauen" href="/projekt/sammlungen-anlegen/" description="Sammlungen, Felder, Hooks, Suche, SSR, Pagebuilder, LLM, MCP, Deploy." />
|
||||
<LinkCard title="Server betreiben" href="/server/uebersicht/" description="Konfiguration, Betrieb, Sicherheit, Schnittstellen." />
|
||||
<LinkCard title="Admin bedienen" href="/admin/konzept/" description="Ansichten, Editor, Medien, Exporte, Aktionen, Rechte, KI." />
|
||||
<LinkCard title="Anwender-Anleitung" href="/anwender/anmelden/" description="Anmelden bis Sichern — für Nicht-Profis mit Bildern." />
|
||||
<LinkCard title="Referenz" href="/reference/schemas/" description="Schemas, Hook-Kontexte, Token-Köpfe, Fehlerbilder, OpenAPI." />
|
||||
</CardGrid>
|
||||
@@ -31,7 +31,7 @@ Einzelner Eintrag per ID.
|
||||
</Tabs>
|
||||
|
||||
<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>
|
||||
|
||||
## 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).
|
||||
|
||||
<Aside type="caution">
|
||||
Ein `lookup` löst nur auf, wenn der Zieldatensatz für dich lesbar ist.
|
||||
Fehlt dort `active:true`, bleibt `_lookup` öffentlich leer — Bilder und Navigation verschwinden.
|
||||
Details: [Rechte und Tokens](/projekt/rechte-tokens/).
|
||||
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/)).
|
||||
</Aside>
|
||||
|
||||
## 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`.
|
||||
|
||||
## 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
|
||||
|
||||
`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"
|
||||
# kombinierte Suche in content (RRF aus semantisch + fuzzy)
|
||||
@@ -121,13 +150,6 @@ GET …/content?q=kontakt&qName=combined
|
||||
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.
|
||||
|
||||
## 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 |
|
||||
| `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 |
|
||||
| Leere Liste trotz Daten | `filter_public`-Hook filtert `active:true` + Zeitraum | `active:true` setzen, `publication`-Fenster prüfen |
|
||||
| `_lookup` leer | Zieldatensatz nicht lesbar (meist `active:true` fehlt) | Zieldatensatz prüfen, nicht die Abfrage |
|
||||
| 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, 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)
|
||||
@@ -47,15 +47,7 @@ Die Sammlung `ssr` (Cache) ist bewusst ausgenommen (`enabled: false`).
|
||||
|
||||
## 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).
|
||||
|
||||
**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.
|
||||
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/).
|
||||
|
||||
## Snapshots
|
||||
|
||||
@@ -92,8 +84,59 @@ snapshots:
|
||||
| `retention.maxCount` | Älteste fliegen automatisch raus |
|
||||
| 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">
|
||||
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>
|
||||
|
||||
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 |
|
||||
| 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.
|
||||
|
||||
### 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)
|
||||
|
||||
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 |
|
||||
| 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) |
|
||||
|
||||
<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 |
|
||||
| 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 |
|
||||
| `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/)) |
|
||||
|
||||
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)
|
||||
@@ -7,7 +7,7 @@ import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
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.
|
||||
Suche-Begriffe: Datei hochladen, Bild anzeigen, Bildfilter.
|
||||
Suchbegriffe: Datei hochladen, Bild anzeigen, Bildfilter.
|
||||
|
||||
## 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.
|
||||
|
||||
<Aside type="caution" title="active:true nicht vergessen">
|
||||
`filter_public.js` erzwingt anonym `active:true` — `medialib.yml` deklariert aber kein `active`-Feld.
|
||||
Ohne gespeichertes `active:true` löst **kein** öffentlicher Lookup auf (Vorfall 2026-10-05).
|
||||
Speichere `active:true` auf jedem Medialib-Dokument.
|
||||
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.
|
||||
</Aside>
|
||||
|
||||
## 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 |
|
||||
| Richtext-Bild verschwindet öffentlich | Gleicher Grund | Gleicher Fix |
|
||||
| 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/).
|
||||
|
||||
Verifiziert am e3f92ebf 2026-10-14 (tibi-svelte-starter)
|
||||
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter)
|
||||
@@ -12,7 +12,8 @@ Suchbegriffe: Deploy, Staging, Live, Produktion, Reload, Freischaltung.
|
||||
## URL-Bild
|
||||
|
||||
```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
|
||||
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`.
|
||||
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>
|
||||
</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
|
||||
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `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/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 |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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)
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
</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
|
||||
|
||||
@@ -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.
|
||||
</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)
|
||||
@@ -3,11 +3,11 @@ title: 'Hooks schreiben'
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
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>
|
||||
|
||||
@@ -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.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
|
||||
|
||||
@@ -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/).
|
||||
|
||||
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)
|
||||
@@ -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 |
|
||||
| `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)
|
||||
@@ -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.
|
||||
|
||||
<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.
|
||||
</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.
|
||||
|
||||
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)
|
||||
@@ -3,7 +3,7 @@ title: 'MCP nutzen'
|
||||
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.
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `documents_list` | Einträge listen (Filter, Sort, Limit wie [Lesen und suchen](/projekt/abfragen-lesen/)) | `namespace.<ns>.<col>.get` |
|
||||
| `document_get` | Ein Eintrag per ID | `.get` |
|
||||
| `documents_count` | Nur Anzahl | `.get` |
|
||||
| `documents_facet` | Werte je Feld zählen | `.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 | `.read` |
|
||||
| `documents_count` | Nur Anzahl | `.read` |
|
||||
| `documents_facet` | Werte je Feld zählen | `.read` |
|
||||
| `queue_status` | Suchindex-Warteschlange | Projekt-Leserecht |
|
||||
| `document_create` | Eintrag anlegen | `.post` |
|
||||
| `document_update` | Eintrag ändern | `.put` |
|
||||
| `document_create` | Eintrag anlegen | `.create` |
|
||||
| `document_update` | Eintrag ändern | `.update` |
|
||||
| `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 |
|
||||
| `projects_list` / `project_get` | Projekte auflisten / ein Projekt | `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`.
|
||||
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`).
|
||||
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:
|
||||
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.
|
||||
|
||||
| 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` |
|
||||
| `asset_read` | `GET /_/:project/_/assets/:name/*path` |
|
||||
|
||||
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.
|
||||
## Werkzeug-Referenz (alle 36)
|
||||
|
||||
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`)
|
||||
|
||||
@@ -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 |
|
||||
| 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)
|
||||
@@ -6,7 +6,7 @@ description: 'Navigations-Collection, Sprachfelder, translationKey, Pfad-Regeln.
|
||||
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.
|
||||
Suche-Begriffe: Navigation anlegen, Sprache umschalten, translationKey.
|
||||
Suchbegriffe: Navigation anlegen, Sprache umschalten, translationKey.
|
||||
|
||||
## Navigations-Dokumente
|
||||
|
||||
@@ -41,9 +41,7 @@ fields:
|
||||
</Steps>
|
||||
|
||||
<Aside type="caution" title="active:true auch hier">
|
||||
`filter_public.js` filtert anonym `active:true` — `navigation.yml` deklariert kein solches Feld.
|
||||
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.
|
||||
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.
|
||||
</Aside>
|
||||
|
||||
## Sprachmodell: lang + translationKey
|
||||
@@ -63,7 +61,7 @@ meta:
|
||||
|---|---|
|
||||
| `lang` | Sprachkürzel, z. B. `de`, `en` |
|
||||
| `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`) |
|
||||
| `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)
|
||||
|
||||
| Sammlung | Ohne `active:true` öffentlich … |
|
||||
|---|---|
|
||||
| `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 |
|
||||
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.
|
||||
|
||||
## 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/).
|
||||
|
||||
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)
|
||||
@@ -53,8 +53,23 @@ export { registry as blockRegistry }
|
||||
| `css` | Array mit Stylesheet-URLs, wird vor dem Mount vorgeladen |
|
||||
| Fehlt der Typ | `render()` gibt `false` zurück → Blueprint-Karte statt Vorschau |
|
||||
|
||||
Laden ist lazy und gecacht (`loadBlockRegistry`).
|
||||
Schlägt der Import fehl, zeigt der Editor den Blueprint-Modus — kein Fehler, nur keine Vorschau.
|
||||
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 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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
## 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
|
||||
|
||||
### Editor-Overlay (`PagebuilderOverlay.svelte`)
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
| 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 |
|
||||
| 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)
|
||||
@@ -141,6 +141,56 @@ Mitglieder von Team A1 kommen in Projekt A, aber nicht in Projekt B.
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
| 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 |
|
||||
| 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)
|
||||
@@ -70,7 +70,7 @@ fields:
|
||||
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).
|
||||
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.
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `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` |
|
||||
| `imageUrl` | Vorschaubild für Karten | Unsplash-URL in `content.yml` |
|
||||
| `preview` | Listenvorschau: `label`, `secondary`, `tertiary`, `image` | `content.yml`: `label: name`, `secondary: path`, `tertiary: lang` |
|
||||
| `viewHint` | Darstellungsart im Admin | siehe unten |
|
||||
| `sidebar` | Eigene Seitenleiste mit Gruppen | `content.yml`: `publishing`, `seo` |
|
||||
| `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`.
|
||||
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.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
|
||||
|
||||
@@ -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.
|
||||
</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)
|
||||
@@ -15,8 +15,15 @@ Container: Seiten-Routen, Daten-Lookups, Blöcke, SSR-Bundle, Cache.
|
||||
- App.svelte
|
||||
- ssr.ts
|
||||
- blocks/
|
||||
- frontend/spa.html
|
||||
- api/templates/spa.html
|
||||
- api/collections/ssr.yml
|
||||
- api/hooks/
|
||||
- lib/app.server.js
|
||||
- lib/utils.js
|
||||
- ssr/get_read.js
|
||||
- ssr/post_bind.js
|
||||
- clear_cache.js
|
||||
|
||||
</FileTree>
|
||||
|
||||
@@ -61,10 +68,10 @@ getCachedEntries("navigation", {
|
||||
| `elements.page:content` | Navigations-Elemente bekommen den Zielpfad (`_lookup.page.path`) |
|
||||
| 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">
|
||||
Öffentlich sichtbar ist nur `active: true` — das gilt auch für `navigation` und `medialib`, obwohl deren Schema gar kein `active`-Feld kennt.
|
||||
Ohne das Flag: Header/Footer leer (`count 0`), Bild-Lookups unaufgelöst (nur IDs).
|
||||
Details unter [Rechte und Tokens](/projekt/rechte-tokens/).
|
||||
Öffentlich sichtbar ist nur `active: true` — das gilt auch für `navigation` und `medialib` (kanonische Erklärung unter [Rechte und Tokens](/projekt/rechte-tokens/)).
|
||||
</Aside>
|
||||
|
||||
## 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.
|
||||
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
|
||||
|
||||
`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 |
|
||||
| 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)
|
||||
@@ -3,10 +3,10 @@ title: 'Suche'
|
||||
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`.
|
||||
Suche-Begriffe: Volltextsuche, Hybrid-Suche, Suchindex neu aufbauen.
|
||||
Suchbegriffe: Volltextsuche, Hybrid-Suche, Suchindex neu aufbauen.
|
||||
|
||||
## 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.
|
||||
- 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>
|
||||
|
||||
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.
|
||||
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>
|
||||
|
||||
<Aside type="note" title="Regenerate auslösen">
|
||||
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 — nur `autoRegenerate: true` in der YAML stehen lassen.
|
||||
<Aside type="note" title="Neuaufbau aus Sicht des Projekts">
|
||||
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.
|
||||
</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
|
||||
|
||||
| Symptom | Ursache | Lösung |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `?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/).
|
||||
|
||||
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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
```
|
||||
|
||||
Verifiziert am 68b42785 2026-10-15 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server)
|
||||
@@ -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.'
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Referenz-Kapitel.
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Daten">
|
||||
db, cache, request, response, fs. [Lesen](/reference/hooks-daten/).
|
||||
</Card>
|
||||
<Card title="Netze und Nachrichten">
|
||||
http, smtp, channel, ratelimit. [Lesen](/reference/hooks-netze/).
|
||||
</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>
|
||||
<LinkCard title="Daten" href="/reference/hooks-daten/" description="db, cache, request, response, fs." />
|
||||
<LinkCard title="Netze und Nachrichten" href="/reference/hooks-netze/" description="http, smtp, channel, ratelimit." />
|
||||
<LinkCard title="Sicherheit und Identität" href="/reference/hooks-sicherheit/" description="jwt, user, bcrypt, crypto, cookie, exec, config." />
|
||||
<LinkCard title="Templates und Medien" href="/reference/hooks-medien/" description="tpl, image, pdf." />
|
||||
<LinkCard title="Formate und Diagnose" href="/reference/hooks-meta/" description="base64, json, xml, charset, debug." />
|
||||
</CardGrid>
|
||||
|
||||
## 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/) |
|
||||
| `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/) |
|
||||
| `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/) |
|
||||
| `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/) |
|
||||
@@ -62,7 +52,7 @@ Die Detail-Doku wächst kapitelweise: Jedes Paket verlinkt unten auf sein Refere
|
||||
|
||||
## 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`
|
||||
|
||||
@@ -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.
|
||||
</Aside>
|
||||
|
||||
Verifiziert am 68b42785 2026-10-15 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server)
|
||||
@@ -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.
|
||||
|
||||
<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
|
||||
|
||||
**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.
|
||||
|
||||
Verifiziert am 68b42785 2026-10-16 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-16 (tibi-server)
|
||||
@@ -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.
|
||||
|
||||
Verifiziert am 68b42785 2026-10-16 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-16 (tibi-server)
|
||||
@@ -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.
|
||||
|
||||
Verifiziert am 68b42785 2026-10-16 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-16 (tibi-server)
|
||||
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
Verifiziert am 68b42785 2026-10-16 (tibi-server)
|
||||
Verifiziert am 68b4278 2026-10-16 (tibi-server)
|
||||
@@ -1,5 +1,18 @@
|
||||
---
|
||||
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)
|
||||
@@ -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`.
|
||||
@@ -30,9 +30,22 @@ Das Verzeichnis enthält 16 Dateien (nicht 15, wie ältere Docs behaupten):
|
||||
| `openapi.schema.json` | OpenAPI-Metadaten je Collection | keine |
|
||||
| `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
|
||||
|
||||
<Tabs>
|
||||
<Tabs syncKey="schema-lesart">
|
||||
<TabItem label="Kopfzeile">
|
||||
|
||||
```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.
|
||||
`?` 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.
|
||||
Die Anwender-Kurzfassung für den Alltag steht unter [Tastenkürzel](/anwender/tastenkombinationen/).
|
||||
|
||||
## 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.
|
||||
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)
|
||||
@@ -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';
|
||||
|
||||
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
|
||||
|
||||
@@ -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 |
|
||||
| `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">
|
||||
|
||||
```sh title="Dokument anlegen"
|
||||
```sh title="Dokument anlegen" {2}
|
||||
curl -s -X POST "$URL/api/content" \
|
||||
-H "Token: $ADMIN_TOKEN" \
|
||||
-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.
|
||||
</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)
|
||||
@@ -3,7 +3,7 @@ title: 'Betrieb und Beobachtung'
|
||||
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/).
|
||||
|
||||
@@ -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-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"
|
||||
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/)).
|
||||
- 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
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
| `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 |
|
||||
|
||||
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`)
|
||||
@@ -3,7 +3,7 @@ title: 'Server-Konfiguration'
|
||||
description: 'config.yml und Env-Overrides des tibi-servers selbst (Betreiber-Sicht).'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
import { Aside, Steps, TabItem, Tabs } from '@astrojs/starlight/components';
|
||||
|
||||
Du konfigurierst den **tibi-server** über eine `config.yml` plus Umgebungsvariablen. Env schlägt Datei. Die Wahrheit steht in `internal/config/server.go` (Struct-Tags).
|
||||
|
||||
@@ -18,7 +18,7 @@ config.yml (Datei) < Umgebungsvariable (gewinnt immer)
|
||||
|
||||
- Fehlt die Datei, startet der Server mit Notwerten: Port `8080`, Prefix `tibi`, Stacktraces an.
|
||||
- `API_PORT` oder `PORT` sind Pflicht: Port `<= 0` beendet den Start per Panic.
|
||||
- Namen mit Bindestrich werden in Env zu Unterstrich + Großschrift: `bge-m3` → `EMBEDDING_BGE-M3_*`.
|
||||
- Namen mit Bindestrich werden in Env zu Unterstrich + Großschrift: `bge-m3` → `EMBEDDING_BGE_M3_*`.
|
||||
- Listen in Env sind Komma-getrennt: `LLM_PROVIDERS=openai,lokal`.
|
||||
|
||||
## Die Bereiche im Überblick
|
||||
@@ -26,7 +26,7 @@ config.yml (Datei) < Umgebungsvariable (gewinnt immer)
|
||||
| Bereich (`config.yml`) | Wichtigste Schlüssel | Env-Override |
|
||||
|---|---|---|
|
||||
| `db` | `dial`, `prefix`, `logging` | `DB_DIAL` (Fallback `MGO_DIAL`), `DB_PREFIX`, `DB_LOGGING` |
|
||||
| `api` | `port`, `jwtSecret`, `upload.maxBodySize`, Token-Lifetimes | `API_PORT`/`PORT`, `API_JWTSECRET`, `API_UPLOAD_MAX_BODY_SIZE`, `API_ACCESS_TOKEN_LIFETIME`, `API_REFRESH_TOKEN_LIFETIME`, `API_SECURE_COOKIES` |
|
||||
| `api` | `port`, `jwtSecret`, `upload.maxBodySize`, `upload.allowedMimeTypes` (nur Datei), Token-Lifetimes | `API_PORT`/`PORT`, `API_JWTSECRET`, `API_UPLOAD_MAX_BODY_SIZE`, `API_ACCESS_TOKEN_LIFETIME`, `API_REFRESH_TOKEN_LIFETIME`, `API_SECURE_COOKIES` |
|
||||
| `mail` | `host`, `username`, `password` | `MAIL_HOST` |
|
||||
| `security` | `allowAbsolutePaths`, `allowUpperPaths` | `SECURITY_ALLOWABSOLUTEPATHS`, `SECURITY_ALLOWUPPERPATHS` |
|
||||
| `response.error` | `stack`, `message` | `RESPONSE_ERROR_STACK`, `RESPONSE_ERROR_MESSAGE` |
|
||||
@@ -44,10 +44,10 @@ Das README-Beispiel ist unvollständig und nennt <del>Go 1.23</del> <ins>go 1.25
|
||||
|
||||
## Datenbank, API, Mail
|
||||
|
||||
<Tabs>
|
||||
<Tabs syncKey="konfig-quelle">
|
||||
<TabItem label="Minimal-Beispiel">
|
||||
|
||||
```yaml title="config.yml"
|
||||
```yaml title="config.yml" {6}
|
||||
db:
|
||||
dial: mongodb://mongo
|
||||
prefix: meinprojekt
|
||||
@@ -97,15 +97,63 @@ LLM_OPENAI_MODELS=gpt-4o,gpt-4o-mini
|
||||
|
||||
```bash title="Embedding-Provider anmelden (Starter-Beispiel)"
|
||||
EMBEDDING_PROVIDERS=bge-m3
|
||||
EMBEDDING_BGE-M3_TYPE=native
|
||||
EMBEDDING_BGE-M3_MODELPATH=/models/bge-m3
|
||||
EMBEDDING_BGE-M3_DIMENSIONS=1024
|
||||
EMBEDDING_BGE-M3_DOCUMENTPREFIX="search_document: "
|
||||
EMBEDDING_BGE-M3_QUERYPREFIX="search_query: "
|
||||
EMBEDDING_BGE_M3_TYPE=native
|
||||
EMBEDDING_BGE_M3_MODELPATH=/models/bge-m3
|
||||
EMBEDDING_BGE_M3_DIMENSIONS=1024
|
||||
EMBEDDING_BGE_M3_DOCUMENTPREFIX="search_document: "
|
||||
EMBEDDING_BGE_M3_QUERYPREFIX="search_query: "
|
||||
```
|
||||
|
||||
Gruppen gehen über `EMBEDDING_GROUPS=main` plus `EMBEDDING_GROUP_MAIN_MEMBERS=...`, `..._POLICY=...`, `..._TIMEOUT_TOTAL=...`.
|
||||
|
||||
### Upload-Allowlist und Embedding-Gruppen im Detail
|
||||
|
||||
`upload.allowedMimeTypes` begrenzt, welche MIME-Typen der Server bei Datei-Feldern annimmt.
|
||||
Kaskade (erste nicht-leere Liste gewinnt): Collection (`upload.allowedMimeTypes`) → Projekt (`upload.allowedMimeTypes`) → Server (`api.upload.allowedMimeTypes`).
|
||||
Leer heißt: alles erlaubt. **Es gibt dafür kein Env-Override** — nur `api.upload.maxBodySize` hat eins (`API_UPLOAD_MAX_BODY_SIZE`).
|
||||
|
||||
```yaml title="config.yml — nur Bilder und PDFs hochladen lassen"
|
||||
api:
|
||||
upload:
|
||||
maxBodySize: 50MB
|
||||
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"]
|
||||
```
|
||||
|
||||
Eine **Embedding-Gruppe** fasst mehrere Provider zu einer Failover-Einheit zusammen (Suche: `onSingleQuery`, Index-Schreiben: `onBulkWrite`, Prüfen: `health`).
|
||||
Alle Gruppen-Schlüssel hängen an `EMBEDDING_GROUP_<NAME>_` — `<NAME>` groß, Bindestrich zu Unterstrich (`main` → `EMBEDDING_GROUP_MAIN_…`).
|
||||
|
||||
| Env (Suffix an `EMBEDDING_GROUP_<NAME>_`) | Wirkung | Gültige Werte |
|
||||
|---|---|---|
|
||||
| `MEMBERS` | Komma-getrennte Provider-Namen der Gruppe | z. B. `bge-m3,openai-embed` |
|
||||
| `DESCRIPTION` | Freitext | beliebig |
|
||||
| `POLICY` | Strategie für einzelne Suchanfragen | `prefer-in-order`, `fastest-wins`, `round-robin`, `lowest-latency`, `weighted` |
|
||||
| `PREFERRED_ORDER` | Bevorzugte Reihenfolge | Komma-Liste von Provider-Namen |
|
||||
| `TIMEOUT_PER_ATTEMPT` / `TIMEOUT_TOTAL` | Timeout je Versuch / gesamt (Gesamt `>=` Versuch, sonst Fehler) | Go-Duration (`120s`, `30s`) |
|
||||
| `AUTO_FAILBACK` | Nach Ausfall automatisch zurück | `true`/`false` |
|
||||
| `RACE_DELAY` / `RACE_MAX_FANOUT` | Verzögerung / max. parallele Teilnehmer beim Rennen | nur mit `POLICY=fastest-wins` sinnvoll |
|
||||
| `WEIGHTS` | Relative Gewichte je Mitglied | nur mit `POLICY=weighted` sinnvoll |
|
||||
| `LATENCY_WINDOW` | Fenster für Latenz-Tracking | Zahl (`100`) |
|
||||
| `RETRY_ATTEMPTS` / `RETRY_BACKOFF` / `RETRY_BACKOFF_MAX` | Wiederholungen und Abstände | Zahl / Go-Duration |
|
||||
| `HEDGE_AFTER_SLOW` | Hedge-Anfrage nach dieser Dauer | Go-Duration |
|
||||
| `DISTRIBUTION` | Verteilung beim Bulk-Schreiben | `round-robin`, `lowest-latency`, `weighted` |
|
||||
| `MAX_CONCURRENT_TOTAL` / `MAX_CONCURRENT_PER_ENDPOINT` | Parallele Bulk-Schreibvorgänge | Zahl |
|
||||
| `BULK_TIMEOUT_PER_ATTEMPT` / `BULK_TIMEOUT_TOTAL` | Timeouts beim Bulk-Schreiben | Go-Duration |
|
||||
| `CHECK_INTERVAL` / `CHECK_TIMEOUT` | Healthcheck-Abstand / -Timeout | Go-Duration |
|
||||
| `UNHEALTHY_AFTER_FAILURES` / `HEALTHY_AFTER_SUCCESSES` | Fehlschläge bis ungesund / Erfolge bis gesund | Zahl |
|
||||
| `COOLDOWN_AFTER_UNHEALTHY` | Pause nach Ausfall | Go-Duration |
|
||||
|
||||
```bash title="Gruppe main per Env anmelden"
|
||||
EMBEDDING_GROUPS=main
|
||||
EMBEDDING_GROUP_MAIN_MEMBERS=bge-m3,openai-embed
|
||||
EMBEDDING_GROUP_MAIN_POLICY=prefer-in-order
|
||||
EMBEDDING_GROUP_MAIN_TIMEOUT_TOTAL=30s
|
||||
```
|
||||
|
||||
Provider-Env zum Vergleich (Provider `<NAME>` → `EMBEDDING_<NAME>_…`, ebenfalls Bindestrich zu Unterstrich):
|
||||
`APIKEY`, `TYPE`, `MODEL`, `MODELPATH`, `DIMENSIONS`, `BASEURL`, `DOCUMENTPREFIX`, `QUERYPREFIX`, `REVISION`, `TIMEOUTPERATTEMPT`, `SLOWTHRESHOLD`, `SPARSE` —
|
||||
z. B. `EMBEDDING_BGE_M3_MODELPATH`, `EMBEDDING_BGE_M3_DIMENSIONS`.
|
||||
Unbekannte `POLICY`/`DISTRIBUTION` oder ungültige Durations lassen die Gruppe mit Fehler im Log scheitern; unpassende `WEIGHTS`/`RACE_*`-Kombis geben nur eine Warnung und werden ignoriert.
|
||||
|
||||
## Admin-Tokens
|
||||
|
||||
Server-Admin-Tokens stehen unter `api.adminTokens`. Jedes Token hat `token`, `label` und `permissions`:
|
||||
@@ -137,4 +185,4 @@ api:
|
||||
|
||||
Weiter: [Betrieb und Beobachtung](/server/betrieb/) zeigt Profile und Metriken. [Sicherheit](/server/sicherheit/) erklärt Token-Arten und Ratelimit.
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server); Starter-Compose am `e3f92ebf` 2026-10-14 (tibi-svelte-starter); Go-Version aus `go.mod`.
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server); Starter-Compose am e3f92eb 2026-10-14 (tibi-svelte-starter); Go-Version aus `go.mod`. Upload-Kaskade aus `internal/controller/collection.go` (resolveAllowedMimeTypes), Gruppen-Env aus `internal/config/server.go` und `internal/config/embedding_group.go`.
|
||||
@@ -3,7 +3,7 @@ title: 'Schnittstellen im Überblick'
|
||||
description: 'REST, SSE, MCP, OpenAPI: was wofür gilt und wo die Referenz liegt.'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
import { TabItem, Tabs } from '@astrojs/starlight/components';
|
||||
|
||||
Der **tibi-server** spricht vier Sprachen: REST, SSE-Streams, MCP und OpenAPI. Alle hängen unter `/api/v1` (außer `/metrics`). Quelle: `internal/controller/setup.go`.
|
||||
|
||||
@@ -18,7 +18,7 @@ Der **tibi-server** spricht vier Sprachen: REST, SSE-Streams, MCP und OpenAPI. A
|
||||
|
||||
## REST: System gegen Collection
|
||||
|
||||
<Tabs>
|
||||
<Tabs syncKey="api-sicht">
|
||||
<TabItem label="System-Endpunkte">
|
||||
|
||||
| Methode | Pfad | Wofür |
|
||||
@@ -59,7 +59,7 @@ Trick für lange Filter: `POST` mit Header `X-HTTP-Method-Override: GET` — der
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Alle Antworten sind gzip-komprimiert, Body-Limits kaskadieren (Server → Projekt → Collection). Erstellen meldet `201`, Löschen `204` — außer das Projekt will Legacy-Codes.
|
||||
Antworten sind gzip-komprimiert — außer `.pdf`, `.mp4`, `.jpg`, `.jpeg`, `.png` und dem Pfad `/metrics` —, Body-Limits kaskadieren (Server → Projekt → Collection). Erstellen meldet `201`, Löschen `204` — außer das Projekt will Legacy-Codes.
|
||||
|
||||
## SSE (Echtzeit und Fortschritt)
|
||||
|
||||
@@ -80,7 +80,7 @@ Alle Antworten sind gzip-komprimiert, Body-Limits kaskadieren (Server → Projek
|
||||
| Ergebnis-Deckel | `mcp.maxResultItems` (Standard 100) |
|
||||
| Admin-Werkzeuge | nur mit `mcp.allowAdminActions: true`, weiter pro Scope begrenzt |
|
||||
|
||||
```bash title="MCP-Tools auflisten (describe)"
|
||||
```bash title="MCP-Tools auflisten (describe)" {2}
|
||||
curl -X POST http://localhost:8080/api/v1/mcp \
|
||||
-H "X-Auth-Token: tibi_pat_..." \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -101,4 +101,4 @@ curl -X POST http://localhost:8080/api/v1/mcp \
|
||||
| Wie frage ich Daten ab? | Projekt-Seiten ab [Begriffe](/einstieg/begriffe/) |
|
||||
| Was ist Tibi überhaupt? | [Was ist Tibi?](/einstieg/was-ist-tibi/) |
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server).
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server)
|
||||
@@ -3,7 +3,7 @@ title: 'Sicherheit'
|
||||
description: 'Tokens, CORS-Kaskade, Ratelimit, Pfad-Regeln, Isolation als Betreiber-Überblick.'
|
||||
---
|
||||
|
||||
import { Aside, Tabs, TabItem, Steps, Card, CardGrid, Badge, FileTree } from '@astrojs/starlight/components';
|
||||
import { Aside, Badge, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
Du sicherst den **tibi-server** auf vier Ebenen: Tokens, CORS, Ratelimit und Dateipfade plus Prozess-Isolation. Quelle ist der Code (`internal/config`, `internal/ratelimit`, `internal/suexec`, `internal/controller/setup.go`).
|
||||
|
||||
@@ -20,7 +20,7 @@ Du sicherst den **tibi-server** auf vier Ebenen: Tokens, CORS, Ratelimit und Dat
|
||||
|
||||
Gültige Domänen: `user`, `project` (`read/create/update/delete`, `reload`, `backup`, `regenerate-search`, `snapshot`), `team`, `org`, `audit.read`, `llm.chat`, `namespace.<ns>.<collection>.<aktion>`.
|
||||
|
||||
```text title="Scope-Abgleich (Beispiel)"
|
||||
```text title="Scope-Abgleich (Beispiel)" {4}
|
||||
Anfrage-Recht: namespace.shop.artikel.read
|
||||
Token-Scope: namespace.shop → passt (Kurzform)
|
||||
Token-Scope: namespace.*.artikel.read → passt (Wildcard)
|
||||
@@ -79,4 +79,4 @@ Der Starter setzt beide Pfad-Schalter lokal auf `"true"`. In Produktion lässt d
|
||||
|
||||
Config-Schlüssel stehen unter [Server-Konfiguration](/server/konfiguration/). Laufende Kontrolle unter [Betrieb und Beobachtung](/server/betrieb/).
|
||||
|
||||
Verifiziert am `68b42785` 2026-10-15 (tibi-server).
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server)
|
||||
@@ -3,38 +3,30 @@ title: Server-Übersicht
|
||||
description: Wie der Tibi-Server aufgebaut ist und wo welche Referenzseite hilft.
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
import { Aside, Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
||||
|
||||
> **Handseite.** Wird vom Sync nie überschrieben (`keep`). Stand wird am Code verifiziert, nicht aus alten Docs übernommen.
|
||||
|
||||
**Tibi-server** ist das Go-Backend (MongoDB, Gin, goja-Hooks, SSE, LLM, Jobs, Audit). Details stehen in den synchronisierten Referenzseiten daneben — diese Seite ordnet nur.
|
||||
|
||||
:::note title="Projekt-Entwickler nutzen, nicht weiterentwickeln"
|
||||
<Aside type="note" title="Projekt-Entwickler nutzen, nicht weiterentwickeln">
|
||||
Als Projekt-Entwickler entwickelst du den Server nicht weiter. Du **nutzt** ihn: `api/config.yml` plus `api/collections/*.yml` und `api/hooks/*.js` in deinem geklonten Starter. Go-Code fasst du nicht an.
|
||||
:::
|
||||
</Aside>
|
||||
|
||||
## Wo finde ich was?
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Server-Konfiguration">
|
||||
`config.yml`, Env-Overrides, Admin-Tokens (am Code verifiziert, inkl. Go-Version und Legacy-Default). [Lesen](/server/konfiguration/).
|
||||
</Card>
|
||||
<Card title="Betrieb und Beobachtung">
|
||||
Profile, Version/Metriken, Reload, Jobs, Störungen. [Lesen](/server/betrieb/).
|
||||
</Card>
|
||||
<Card title="Sicherheit">
|
||||
Token-Arten, CORS-Kaskade, Login-Ratelimit (429), Pfad-Schalter, suexec. [Lesen](/server/sicherheit/).
|
||||
</Card>
|
||||
<Card title="Schnittstellen">
|
||||
REST (System vs. Collection), SSE, MCP (`/api/v1/mcp` fest), OpenAPI-Generator. [Lesen](/server/schnittstellen/).
|
||||
</Card>
|
||||
<LinkCard title="Server-Konfiguration" href="/server/konfiguration/" description="config.yml, Env-Overrides, Admin-Tokens — am Code verifiziert." />
|
||||
<LinkCard title="Betrieb und Beobachtung" href="/server/betrieb/" description="Profile, Version/Metriken, Reload, Jobs, Störungen." />
|
||||
<LinkCard title="Sicherheit" href="/server/sicherheit/" description="Token-Arten, CORS-Kaskade, Login-Ratelimit (429), Pfad-Schalter, suexec." />
|
||||
<LinkCard title="Schnittstellen" href="/server/schnittstellen/" description="REST (System vs. Collection), SSE, MCP, OpenAPI-Generator." />
|
||||
<Card title="Projekt-Sicht">
|
||||
[Sammlungen](/projekt/sammlungen-anlegen/), [Hooks](/projekt/hooks-schreiben/), [Lesen und suchen](/projekt/abfragen-lesen/), [Rechte und Tokens](/projekt/rechte-tokens/).
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
Verifiziert am 68b42785 2026-10-15 (tibi-server).
|
||||
|
||||
## Nächster Schritt
|
||||
|
||||
- Neues Projekt? Siehe [Neues Projekt aufsetzen](/einstieg/neues-projekt/).
|
||||
|
||||
Verifiziert am 68b4278 2026-10-15 (tibi-server)
|
||||