18 KiB
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