go-wiki/REPOMAP.md
2026-07-14 20:46:19 +02:00

18 KiB

Repomap

Übersicht aller Dateien und Ordner im Projekt.

go-wiki/
├── main.go                   ← Einstiegspunkt. Lädt .env, prüft Pflicht-Secrets
│                               (SESSION_SECRET, ADMIN_TOKEN), lädt teams.yaml und
│                               baut die TeamRegistry auf, richtet alle Routen ein
│                               und verwaltet den Graceful Shutdown (wartet auf
│                               laufende Requests bevor der Server sich bei
│                               CTRL+C / docker stop beendet).
│
├── go.mod                    ← Go-Moduldefinition (Name, Go-Version, direkte Dependencies).
│                               Entspricht in etwa einer package.json in Node.
│
├── go.sum                    ← Automatisch generierte Checksummen aller Dependencies.
│                               Nicht manuell bearbeiten.
│
├── Dockerfile                ← Multi-Stage Build: Stage 1 kompiliert das Binary mit dem
│                               Go-Compiler, Stage 2 packt nur das fertige Binary in ein
│                               minimales Alpine-Linux-Image (mit git, für den Webhook).
│
├── deploy.sh                 ← Shell-Skript zum Hochladen einer lokalen Markdown-Datei
│                               auf den Wiki-Server via REST-API (curl + Team-Key oder
│                               Admin-Token).
│
├── .gitignore                ← Schließt Binary, data/-Ordner, .env, teams.yaml und
│                               .DS_Store aus Git aus.
│
├── .dockerignore              ← Schließt data/, Binary und .git aus dem Docker-Build-
│                               Kontext aus — macht den Build schneller.
│
├── .env.example               ← Vorlage für die .env-Datei. Zeigt alle verfügbaren
│                               Umgebungsvariablen mit Erklärungen (u.a. SESSION_SECRET
│                               und ADMIN_TOKEN — beides Pflichtfelder ohne unsicheren
│                               Default). Kopieren nach .env und mit echten Werten befüllen.
│
├── teams.yaml.example         ← Vorlage für die Team-Konfiguration. Jedes Team bekommt
│                               eine ID (a-z, 0-9, -, _), einen Anzeigenamen und einen
│                               Zugriffskey. Kopieren nach teams.yaml.
│
├── golang-wiki-spec.md        ← Ursprüngliche Projektspezifikation (Planungsdokument).
│
├── README.md                  ← Dokumentation: Server starten, Teams anlegen, Seiten
│                               anlegen, Deployment, MCP-Integration, API-Übersicht.
│
├── REPOMAP.md                 ← Diese Datei.
│
├── handler/
│   ├── registry.go            ← TeamRegistry: zentrale Stelle die weiß welche Teams es
│   │                           gibt, wo ihre Markdown-Dateien liegen (data/<id>/content)
│   │                           und welcher Zugriffskey zu welchem Team gehört. Löst Keys
│   │                           timing-sicher auf (subtle.ConstantTimeCompare) und
│   │                           unterscheidet Team-Key vom globalen Admin-Token.
│   │                           Unterstützt Hot-Reload (Reload()) für teams.yaml-Änderungen
│   │                           ohne Serverneustart. Stellt außerdem die Request-Context-
│   │                           Helfer bereit (WithTeam/TeamFromContext/IsAdminFromContext).
│   │
│   ├── teams_config.go         ← Lädt und validiert teams.yaml (LoadTeamsConfig) und
│   │                           überwacht die Datei per mtime-Polling auf Änderungen
│   │                           (StartTeamsConfigWatcher) — lädt neue/geänderte/entfernte
│   │                           Teams automatisch in die Registry.
│   │
│   ├── cache.go                ← In-Memory-Cache für Wiki-Seiten, ein PageCache pro Team.
│   │                           Beim Serverstart werden alle .md-Dateien eines Teams
│   │                           einmal geladen und im Arbeitsspeicher gehalten — Metadaten
│   │                           (Titel, Tags, Hidden) sind danach sofort verfügbar ohne
│   │                           Dateisystem-Zugriff. Thread-sicher via sync.RWMutex.
│   │                           Hält außerdem den BM25-Suchindex des Teams aktuell.
│   │                           Wird nach jedem Upload/Delete sofort aktualisiert.
│   │
│   ├── search.go               ← In-Memory-Volltextindex mit BM25-Ranking (invertierter
│   │                           Index: Begriff → Dokumente). Titel-Treffer werden 3x,
│   │                           Tag-Treffer 2x höher gewichtet als Body-Treffer. Genutzt
│   │                           von /search (Web) und dem search_pages-MCP-Tool.
│   │
│   ├── session.go              ← Signierte Web-Session fürs Team-Login. SignSession/
│   │                           VerifySession erzeugen bzw. prüfen einen HMAC-SHA256-
│   │                           signierten, ablaufenden Cookie-Wert ("team:<id>", "admin"
│   │                           oder "admin:<id>") — der Server signiert nur eine
│   │                           Behauptung, nie den Zugriffskey selbst.
│   │
│   ├── auth.go                 ← Team-Login fürs Web: Formular (GET/POST /login), Logout
│   │                           (/logout) und Team-Umschaltung für Admins (/switch-team).
│   │                           Nutzt den LoginRateLimiter (ratelimit.go) gegen Brute-Force
│   │                           und schreibt Login-Versuche ins Audit-Log. clientIP()
│   │                           liest bewusst nur r.RemoteAddr, keinen X-Forwarded-For-
│   │                           Header (sonst wäre das Rate-Limiting ohne bekannte,
│   │                           vertrauenswürdige Proxy-Kette leicht auszuhebeln).
│   │
│   ├── ratelimit.go             ← LoginRateLimiter: Fixed-Window-Zähler pro Client-IP
│   │                           (5 Fehlversuche/Minute). Wird sowohl vom Team-Login
│   │                           (auth.go) als auch vom Seiten-Passwort (wiki.go,
│   │                           HandlePasswordSubmit) genutzt, damit keiner der beiden
│   │                           Auth-Wege unbegrenzt durchprobiert werden kann.
│   │
│   ├── wiki.go                 ← Liefert Wiki-Seiten des aktiven Teams aus (GET /:pfad,
│   │                           Team kommt aus dem Request-Context). Liest Markdown-
│   │                           Datei, prüft Seiten-Passwortschutz via Cookie
│   │                           (konstantzeitiger Vergleich + Rate-Limiting), baut die
│   │                           Sidebar-Navigation rekursiv aus der Ordnerstruktur und
│   │                           rendert das HTML-Template. Verarbeitet auch
│   │                           POST /auth/:pfad (Passwort-Formular) und GET /search
│   │                           (BM25-Trefferliste im normalen Wiki-Layout).
│   │
│   ├── api.go                  ← REST-API für das Deployment. POST /api/upload lädt eine
│   │                           Markdown-Datei hoch, DELETE /api/delete löscht eine Datei.
│   │                           Team-Key wirkt nur aufs eigene Team; der globale Admin-Key
│   │                           muss das Zielteam explizit per Parameter "team" angeben.
│   │                           Prüft Pfade gegen Path-Traversal ("..") und aktualisiert
│   │                           nach jeder Änderung den PageCache sowie das Audit-Log.
│   │
│   ├── webhook.go              ← Git-Webhook-Handler (POST /api/webhook, nur aktiv wenn
│   │                           WEBHOOK_SECRET gesetzt ist). Erkennt den Anbieter anhand
│   │                           der Request-Headers und prüft die Signatur: GitHub
│   │                           (X-Hub-Signature-256, HMAC-SHA256), GitLab
│   │                           (X-Gitlab-Token, direkter Vergleich), Bitbucket
│   │                           (X-Hub-Signature, HMAC-SHA256). Nach erfolgreicher Prüfung:
│   │                           git pull im gemeinsamen data-Wurzelverzeichnis, danach
│   │                           Cache aller Teams komplett neu aufbauen (RebuildAll).
│   │
│   ├── mcp.go                  ← MCP-Server (Model Context Protocol) über Streamable HTTP
│   │                           (POST /mcp, ein MCP-Server pro Team, lazy gebaut). Ermöglicht
│   │                           KI-Tools (Claude Desktop, Cline, Opencode) direkten Zugriff
│   │                           auf das Wiki des aufgelösten Teams.
│   │                           Resources (Daten):
│   │                             wiki://pages          — alle Seiten als JSON
│   │                             wiki://page/{+path}   — eine Seite als rohes Markdown
│   │                           Tools (Aktionen):
│   │                             search_pages   — BM25-Volltextsuche mit Snippet
│   │                             upload_page    — Seite erstellen/überschreiben
│   │                             delete_page    — Seite löschen
│   │                             rebuild_index  — generiert _index.md (Karpathy-Wiki-Index)
│   │                           Admin-Zugriff braucht den Header "X-Wiki-Team". Alle
│   │                           lesenden Operationen nutzen den PageCache.
│   │
│   ├── audit.go                ← AuditLogger: schreibt sicherheits-/nachvollziehbarkeits-
│   │                           relevante Ereignisse (Upload, Delete, Login-Versuche,
│   │                           Webhook-Sync) als JSON Lines in data/audit.log — eine
│   │                           gemeinsame Datei für alle Teams. Tail(n) liest die letzten
│   │                           n Ereignisse für die Admin-Ansicht, neueste zuerst.
│   │
│   └── audit_page.go           ← Admin-Ansicht des Audit-Logs (GET /admin/audit). Nur mit
│                               dem globalen Admin-Zugang erreichbar, zeigt die letzten
│                               200 Ereignisse als Tabelle.
│
├── middleware/
│   ├── session.go              ← RequireTeamSession: schützt die Web-Ansicht ("/",
│   │                           "/search", "/auth/", "/switch-team", "/admin/audit").
│   │                           Prüft den Session-Cookie (handler.VerifySession) und legt
│   │                           das aufgelöste Team bzw. den Admin-Status im Request-
│   │                           Context ab. Ohne gültige Session: Redirect zu /login.
│   │
│   └── auth.go                 ← RequireTeamOrAdminKey: schützt /api/upload, /api/delete
│                               und /mcp. Liest den Bearer-Token aus dem Authorization-
│                               Header und löst ihn timing-sicher gegen die TeamRegistry
│                               auf (handler.TeamRegistry.Resolve) — legt das Team bzw.
│                               den Admin-Status im Request-Context ab.
│
├── render/
│   └── markdown.go             ← Kernlogik: Liest eine .md-Datei, trennt den YAML-
│                               Frontmatter-Block (title, password, tags, hidden) vom
│                               Markdown-Body und wandelt den Body mit goldmark (Default-
│                               Konfiguration, kein html.WithUnsafe — eingebettetes
│                               Roh-HTML wird sicher escaped) in HTML um. Gibt ein
│                               Page-Struct zurück, inkl. RawBody für die Volltextsuche.
│
├── templates/
│   ├── page.html               ← HTML-Template für eine Wiki-Seite. Zweispaltiges Layout
│   │                           mit Sidebar (rekursive Navigation, Team-Name, "Team
│   │                           wechseln"-Link für Admins) und Hauptinhalt bzw.
│   │                           BM25-Suchergebnissen. Kein CSS inline — lädt
│   │                           /static/style.css und /static/sidebar.js.
│   │
│   ├── password.html            ← HTML-Template für das Seiten-Passwort-Formular. Wird
│   │                           angezeigt wenn eine Seite passwortgeschützt ist und kein
│   │                           gültiger Auth-Cookie vorhanden ist.
│   │
│   ├── login.html               ← HTML-Template für den Team-Login (GET/POST /login) —
│   │                           fragt den Zugriffskey des Teams oder den Admin-Token ab.
│   │
│   ├── switch-team.html         ← HTML-Template für die Team-Auswahl des Admin-Zugangs
│   │                           (GET/POST /switch-team) — listet alle Teams aus der
│   │                           Registry als Buttons.
│   │
│   └── audit.html               ← HTML-Template für die Audit-Log-Ansicht
│                               (GET /admin/audit) — Tabelle mit Zeit, Team, Akteur,
│                               Aktion, Pfad, Quelle und Details je Ereignis.
│
├── static/
│   ├── style.css                ← Gesamtes CSS des Wikis an einem Ort: Layout, Sidebar,
│   │                           Navigation, Markdown-Styling, Login-/Passwort-Formulare,
│   │                           Audit-Tabelle. Wird unter /static/ ausgeliefert.
│   │
│   └── sidebar.js               ← Steuert das Ein-/Ausklappen der Sidebar, getrennt für
│                               Desktop (Zustand in localStorage gemerkt) und Mobile
│                               (Overlay, startet nach jedem Seitenaufruf zu).
│
└── data/                       ← Wurzelverzeichnis aller Teams (DATA_ROOT). Nicht im
    ├── audit.log                Git-Repository — wird als Docker-Volume eingehängt.
    ├── <team-id>/
    │   └── content/            ← Markdown-Dateien dieses Teams, z.B. data/alpha/content/.
    │       └── index.md          Wird beim Serverstart automatisch angelegt, pro Team
    │                             per deploy.sh, REST-API, MCP oder Git-Webhook befüllt.
    └── audit.log               ← JSON-Lines-Protokoll aller Uploads/Deletes/Logins/
                                  Webhook-Syncs (teamübergreifend, eine Datei für alle).

Datenfluss: Browser ruft eine Seite auf

Browser GET /projekte/notizen
    → middleware/session.go (RequireTeamSession)   Session-Cookie prüfen, Team auflösen
    → handler/wiki.go (ServeHTTP)
        → render/markdown.go (LoadPage)            Datei lesen, Frontmatter + HTML
        → Seiten-Passwortprüfung via Cookie (konstantzeitig, rate-limited)
        → buildNavForDir() (rekursiv)               Ordnerstruktur → Baumnavigation
        → templates/page.html                       Template rendern
    → HTML an Browser

Datenfluss: Team-Login und Team-Umschaltung

Browser POST /login { key }
    → handler/auth.go (HandleLogin)
        → handler/ratelimit.go (LoginRateLimiter)   IP-Sperre nach 5 Fehlversuchen/Minute
        → handler/registry.go (TeamRegistry.Resolve) Key → Team oder Admin, timing-sicher
        → handler/audit.go (Log)                     login_success / login_failure
        → handler/session.go (SignSession)           signierten Cookie setzen
    → Redirect zu "/" (Team) oder "/switch-team" (Admin)

Admin POST /switch-team { team }
    → handler/auth.go (HandleSwitchTeam)             nur mit Admin-Session erreichbar
        → handler/session.go (SignSession)           Cookie "admin:<team-id>" setzen
    → Redirect zu "/"

Datenfluss: KI-Tool liest/schreibt Seiten via MCP

Claude Desktop/Cline/Opencode  →  POST /mcp (Streamable HTTP, JSON-RPC pro Request)
                                →  middleware/auth.go (RequireTeamOrAdminKey)
                                    Bearer-Token → Team (oder Admin + Header X-Wiki-Team)
                                →  handler/mcp.go (Team-Server, lazy gebaut)

                   Resource: wiki://pages
                       → handler/cache.go (All())        aus dem Cache, kein Disk-Zugriff
                       → JSON-Liste aller Seiten des Teams zurück

                   Resource: wiki://page/projekte/notizen
                       → handler/cache.go (URLPathToFilePath)
                       → os.ReadFile()                   Markdown direkt von Disk
                       → Frontmatter entfernt, Titel als H1 vorangestellt

                   Tool: search_pages { query: "Docker" }
                       → handler/search.go (BM25 Search) invertierter Index, kein Disk-Zugriff
                       → Treffer mit Snippet zurück

                   Tool: upload_page / delete_page
                       → Datei auf Disk schreiben/löschen
                       → handler/cache.go (Set/Delete)   Cache + Suchindex aktualisieren
                       → buildWikiIndex()                 _index.md automatisch neu generieren
                       → handler/audit.go (Log)           Audit-Eintrag schreiben

Datenfluss: Git-Webhook (automatische Synchronisation)

Nutzer git push
    → GitHub / GitLab / Bitbucket
    → POST /api/webhook
        → handler/webhook.go
            → Anbieter erkennen (Header-Analyse)
            → Signatur prüfen (HMAC-SHA256 oder Token-Vergleich, konstantzeitig)
            → git pull                       im data-Wurzelverzeichnis (alle Teams)
            → handler/registry.go (RebuildAll) Cache + Suchindex jedes Teams neu aufbauen
            → handler/audit.go (Log)           webhook_sync protokollieren
    → Seiten aller Teams sofort live ohne Neustart

Datenfluss: Datei deployen (REST oder MCP)

deploy.sh / MCP upload_page
    → POST /api/upload  oder  Tool: upload_page
    → middleware/auth.go (Token prüfen, Team auflösen)
    → Pfad gegen Path-Traversal prüfen ("..")
    → Datei auf Disk schreiben
    → render/markdown.go (LoadPage)          neue Seite laden
    → handler/cache.go (Set())               Cache + Suchindex sofort aktualisieren
    → handler/audit.go (Log)                 Audit-Eintrag schreiben
    → ab sofort in Navigation, Suche und MCP sichtbar