diff --git a/README.md b/README.md index e915ae6..9cd799f 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,25 @@ tailscale serve --bg --https=443 http://127.0.0.1:8765 # Access via https://..ts.net/ ``` +### One-command setup via wrapper script + +The `scripts/hermes-wiki-serve.sh` wrapper handles start/stop/logs/status, PID-tracking, log-files, and Tailscale integration: + +```bash +# Install: copy script to PATH +cp scripts/hermes-wiki-serve.sh ~/.local/bin/ +chmod +x ~/.local/bin/hermes-wiki-serve.sh + +# Use: +hermes-wiki-serve.sh start # starts server, sets up tailscale serve +hermes-wiki-serve.sh status # shows PID, Tailscale-URL, health +hermes-wiki-serve.sh logs # tail all logs +hermes-wiki-serve.sh stop # stops server (keeps tailscale running) +hermes-wiki-serve.sh restart # stop + start +``` + +The script defaults to using this repo at `~/repos/hermes-wiki-viewer/`. Override via `HERMES_WIKI_OWV_DIR`. + ## Customization `vault-custom.js` is Lukas' own code, organized into clear sections: diff --git a/scripts/hermes-wiki-serve.sh b/scripts/hermes-wiki-serve.sh new file mode 100755 index 0000000..422a495 --- /dev/null +++ b/scripts/hermes-wiki-serve.sh @@ -0,0 +1,346 @@ +#!/usr/bin/env bash +# hermes-wiki-serve.sh — Hermes Wiki via obsidian-web-viewer + Tailscale +# +# Was dieses Skript tut: +# - Klont DanielCheer/obsidian-web-viewer einmalig nach ~/.local/share/hermes-wiki/owv/ +# - Startet deren server.py (Python stdlib) auf 127.0.0.1:8765 (Loopback) +# - Aktiviert tailscale serve, der den Loopback-Port als https im Tailnet exponen lässt +# - Liefert File-Tree, 3D-Graph, Volltext-Suche, WikiLink-Navigation out-of-the-box +# +# Verwendung: +# hermes-wiki-serve.sh start # Server starten (klont obv beim ersten Mal) +# hermes-wiki-serve.sh stop # Server stoppen +# hermes-wiki-serve.sh restart # stop + start +# hermes-wiki-serve.sh status # Process-Status, Health-Check, Tailscale-URL +# hermes-wiki-serve.sh logs # tail -f der Log-Files +# hermes-wiki-serve.sh install # nur das Clonen/Update, kein Server-Start +# hermes-wiki-serve.sh --help +# +# Umgebungsvariablen: +# HERMES_WIKI_PORT=8765 Lokaler HTTP-Port (Loopback) +# HERMES_WIKI_DIR=/home/admin/my-karpathy-wiki +# HERMES_WIKI_LOG_DIR=$HOME/.local/share/hermes-wiki/log +# HERMES_WIKI_TS=auto auto|yes|no — Tailscale serve aktivieren +# HERMES_WIKI_TS_PATH= leer=Root-Prefix (default), /wiki=unter-Pfad +# HERMES_WIKI_OWV_DIR=$HOME/repos/hermes-wiki-viewer (Lukas-Fork default; upstream obv als Fallback) + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Defaults +# --------------------------------------------------------------------------- +readonly HERMES_WIKI_PORT="${HERMES_WIKI_PORT:-8765}" +readonly HERMES_WIKI_DIR="${HERMES_WIKI_DIR:-/home/admin/my-karpathy-wiki}" +readonly HERMES_WIKI_LOG_DIR="${HERMES_WIKI_LOG_DIR:-$HOME/.local/share/hermes-wiki/log}" +readonly HERMES_WIKI_TS="${HERMES_WIKI_TS:-auto}" +readonly HERMES_WIKI_TS_PATH="${HERMES_WIKI_TS_PATH:-}" # leer = Root-Prefix (obv nutzt relative URLs!) +readonly HERMES_WIKI_OWV_DIR="${HERMES_WIKI_OWV_DIR:-$HOME/repos/hermes-wiki-viewer}" +readonly HERMES_WIKI_REPO="${HERMES_WIKI_REPO:-}" # leer = lokales Lukas-Repo nutzen +readonly OWV_REPO_UPSTREAM="https://github.com/DanielCheer/obsidian-web-viewer.git" +readonly PID_FILE="/tmp/hermes-wiki-serve.pid" +readonly HEALTH_URL="http://127.0.0.1:${HERMES_WIKI_PORT}/api/vault/tree" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +log() { + printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" | tee -a "$HERMES_WIKI_LOG_DIR/server.log" >&2 +} + +die() { + log "ERROR: $*" + exit 1 +} + +ensure_dirs() { + mkdir -p "$HERMES_WIKI_LOG_DIR" || die "Kann Log-Dir nicht erstellen: $HERMES_WIKI_LOG_DIR" +} + +require_tools() { + command -v python3 >/dev/null || die "python3 nicht gefunden" + command -v git >/dev/null || die "git nicht gefunden" + [ -d "$HERMES_WIKI_DIR" ] || die "Wiki-Verzeichnis nicht gefunden: $HERMES_WIKI_DIR" +} + +detect_tailscale() { + [ "$HERMES_WIKI_TS" != "no" ] || return 1 + command -v tailscale >/dev/null || return 1 + tailscale status --json >/dev/null 2>&1 || return 1 + return 0 +} + +# --------------------------------------------------------------------------- +# Installation: Repo klonen oder updaten +# --------------------------------------------------------------------------- +install_repo() { + # Mode 1: Lokales Lukas-Repo (~/repos/hermes-wiki-viewer/) — default nach Fork + if [ -d "$HERMES_WIKI_OWV_DIR/.git" ] && [ -z "$HERMES_WIKI_REPO" ]; then + log "Nutze lokales Lukas-Repo: $HERMES_WIKI_OWV_DIR" + (cd "$HERMES_WIKI_OWV_DIR" && git pull --ff-only 2>>"$HERMES_WIKI_LOG_DIR/install.log") || \ + log "WARN: git pull fehlgeschlagen — nutze Working-Tree-Stand" + # Mode 2: Remote Lukas-Repo klonen (z.B. wenn HERMES_WIKI_REPO gesetzt) + elif [ -n "$HERMES_WIKI_REPO" ]; then + if [ -d "$HERMES_WIKI_OWV_DIR/.git" ]; then + log "Update Lukas-Fork in $HERMES_WIKI_OWV_DIR" + (cd "$HERMES_WIKI_OWV_DIR" && git pull --ff-only 2>>"$HERMES_WIKI_LOG_DIR/install.log") || \ + log "WARN: git pull fehlgeschlagen" + else + log "Klone Lukas-Fork $HERMES_WIKI_REPO nach $HERMES_WIKI_OWV_DIR" + git clone "$HERMES_WIKI_REPO" "$HERMES_WIKI_OWV_DIR" 2>>"$HERMES_WIKI_LOG_DIR/install.log" || \ + die "git clone fehlgeschlagen — Repo-URL korrekt?" + fi + # Mode 3: Fallback — upstream obv direkt (vor Lukas-Fork) + else + log "Kein Lukas-Repo gefunden, klone upstream obv nach $HERMES_WIKI_OWV_DIR" + log "(Für Lukas-Customizations: HERMES_WIKI_OWV_DIR=/path/to/hermes-wiki-viewer setzen)" + git clone "$OWV_REPO_UPSTREAM" "$HERMES_WIKI_OWV_DIR" 2>>"$HERMES_WIKI_LOG_DIR/install.log" || \ + die "git clone fehlgeschlagen" + fi + + # requirements.txt: nur PyYAML (optional). Stdlib reicht für unsere Zwecke. + if ! python3 -c "import yaml" 2>/dev/null; then + log "PyYAML nicht installiert (optional). Installiere..." + python3 -m pip install --user --quiet PyYAML 2>>"$HERMES_WIKI_LOG_DIR/install.log" || \ + log "WARN: PyYAML-Installation fehlgeschlagen — Frontmatter wird rudimentär behandelt" + fi + log "Wiki-Viewer bereit in $HERMES_WIKI_OWV_DIR" +} + +# --------------------------------------------------------------------------- +# Tailscale-Integration +# --------------------------------------------------------------------------- +start_tailscale() { + detect_tailscale || { + [ "$HERMES_WIKI_TS" = "no" ] || \ + log "Tailscale übersprungen (nicht verfügbar oder HERMES_WIKI_TS=$HERMES_WIKI_TS)" + return 0 + } + + if tailscale serve status --bg 2>/dev/null | grep -q "$HERMES_WIKI_PORT"; then + log "Tailscale serve bereits aktiv für Port $HERMES_WIKI_PORT" + return 0 + fi + + log "Starte tailscale serve auf Root-Prefix -> 127.0.0.1:$HERMES_WIKI_PORT" + local ts_path_arg=() + if [ -n "$HERMES_WIKI_TS_PATH" ]; then + ts_path_arg=(--set-path="$HERMES_WIKI_TS_PATH") + log "(Tailscale wird unter /$HERMES_WIKI_TS_PATH/ exposen — Achtung: obv nutzt relative URLs!)" + fi + if tailscale serve --bg --https=443 "${ts_path_arg[@]}" \ + "http://127.0.0.1:$HERMES_WIKI_PORT" 2>>"$HERMES_WIKI_LOG_DIR/tailscale.log"; then + local ts_url + ts_url=$(tailscale serve status --json 2>/dev/null | python3 -c " +import json, sys +try: + data = json.load(sys.stdin) + for entry in data.get('Web', {}).values(): + print(f'https://{entry[\"HTTPS\"]}{entry[\"Path\"]}/') + break +except: pass +" 2>/dev/null) + log "Tailscale serve aktiv: ${ts_url:-URL konnte nicht ermittelt werden}" + return 0 + else + log "WARN: tailscale serve fehlgeschlagen — siehe $HERMES_WIKI_LOG_DIR/tailscale.log" + return 0 + fi +} + +stop_tailscale() { + detect_tailscale || return 0 + if tailscale serve status --bg 2>/dev/null | grep -q "$HERMES_WIKI_PORT"; then + log "Stoppe tailscale serve" + tailscale serve --bg --https=443 --set-path="$HERMES_WIKI_TS_PATH" off \ + "http://127.0.0.1:$HERMES_WIKI_PORT" 2>>"$HERMES_WIKI_LOG_DIR/tailscale.log" || true + fi +} + +# --------------------------------------------------------------------------- +# Server-Lifecycle +# --------------------------------------------------------------------------- +is_running() { + [ -f "$PID_FILE" ] || return 1 + local pid + pid=$(cat "$PID_FILE" 2>/dev/null || echo "") + [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null +} + +start_server() { + ensure_dirs + require_tools + install_repo + + if is_running; then + log "Server läuft bereits (PID $(cat "$PID_FILE"))" + return 0 + fi + + # Port-Konflikt-Check + if ss -tln 2>/dev/null | grep -q ":${HERMES_WIKI_PORT} "; then + die "Port ${HERMES_WIKI_PORT} ist bereits belegt. HERMES_WIKI_PORT ändern oder Prozess beenden." + fi + + # Check, dass die Wiki-Page gerendert werden kann + local md_count + md_count=$(find "$HERMES_WIKI_DIR" -name "*.md" -not -path "*/raw/*" 2>/dev/null | wc -l) + log "Wiki-Dir: $HERMES_WIKI_DIR ($md_count .md-Dateien, ohne /raw/)" + + # Server starten — Loopback only, NICHT 0.0.0.0 + log "Starte obsidian-web-viewer server.py auf 127.0.0.1:$HERMES_WIKI_PORT" + nohup python3 "$HERMES_WIKI_OWV_DIR/server.py" \ + --vault "$HERMES_WIKI_DIR" \ + --host 127.0.0.1 \ + --port "$HERMES_WIKI_PORT" \ + > "$HERMES_WIKI_LOG_DIR/renderer.log" 2>&1 & + local pid=$! + echo "$pid" > "$PID_FILE" + + # Health-Check (das Tool hat keine /__health, aber /api/vault/tree ist immer da) + local i + for i in 1 2 3 4 5; do + sleep 1 + if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then + log "Server gestartet (PID $pid), Health-Check OK" + start_tailscale + return 0 + fi + done + + log "ERROR: Health-Check fehlgeschlagen nach 5s" + log "--- renderer.log ---" + tail -20 "$HERMES_WIKI_LOG_DIR/renderer.log" >&2 + rm -f "$PID_FILE" + return 1 +} + +stop_server() { + if is_running; then + local pid + pid=$(cat "$PID_FILE") + log "Stoppe Server (PID $pid)" + kill "$pid" 2>/dev/null || true + local i + for i in 1 2 3 4 5; do + kill -0 "$pid" 2>/dev/null || break + sleep 0.5 + done + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null || true + fi + rm -f "$PID_FILE" + fi + # Tailscale NICHT stoppen — soll laufen bleiben für Re-Start ohne Tailscale-Re-Init + # stop_tailscale + log "(Tailscale serve bleibt aktiv für Re-Start)" +} + +# --------------------------------------------------------------------------- +# Status / Help +# --------------------------------------------------------------------------- +show_status() { + echo "=== Hermes Wiki Server Status (via obsidian-web-viewer) ===" + echo "Wiki-Dir: $HERMES_WIKI_DIR" + echo "Viewer-Repo: $HERMES_WIKI_OWV_DIR" + echo "Port: $HERMES_WIKI_PORT (Loopback only)" + echo "Log-Dir: $HERMES_WIKI_LOG_DIR" + + if is_running; then + local pid + pid=$(cat "$PID_FILE") + echo "Status: RUNNING (PID $pid)" + ps -o pid,rss,etime,cmd -p "$pid" 2>/dev/null | tail -1 | \ + awk '{printf " Memory: %.1f MB, Uptime: %s\n", $2/1024, $3}' + + if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then + local md_count + md_count=$(curl -sf "$HEALTH_URL" | python3 -c " +import json, sys +try: + data = json.load(sys.stdin) + count = sum(1 + len(c.get('children',[])) for c in [data]) + # grobe zählung + print(len(json.dumps(data))) +except: print('?') +" 2>/dev/null || echo "?") + echo "Health: OK (Tree-API reachable, $md_count bytes)" + else + echo "Health: FAILED" + fi + + if detect_tailscale; then + if tailscale serve status --bg 2>&1 | grep -q "$HERMES_WIKI_PORT"; then + local ts_url + ts_url=$(tailscale serve status --json 2>/dev/null | python3 -c " +import json, sys +try: + data = json.load(sys.stdin) + for entry in data.get('Web', {}).values(): + print(f'https://{entry[\"HTTPS\"]}{entry[\"Path\"]}/') + break +except: pass +" 2>/dev/null) + echo "Tailscale: AKTIV — ${ts_url:-URL nicht extrahierbar}" + else + echo "Tailscale: nicht aktiv für Port $HERMES_WIKI_PORT (HERMES_WIKI_TS=$HERMES_WIKI_TS)" + fi + else + echo "Tailscale: nicht verfügbar oder deaktiviert" + fi + else + echo "Status: NOT RUNNING" + fi +} + +show_help() { + cat <..ts.net/wiki/ + - API: http://127.0.0.1:8765/api/vault/{tree,graph,search,file/} + +EOF +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +case "${1:-}" in + start) start_server ;; + stop) stop_server ;; + restart) stop_server; start_server ;; + status) show_status ;; + logs) tail -f "$HERMES_WIKI_LOG_DIR"/*.log 2>/dev/null || die "Keine Logs gefunden" ;; + install) ensure_dirs; require_tools; install_repo ;; + --help|-h|help|"") show_help ;; + *) die "Unbekanntes Kommando: $1 (--help für Hilfe)" ;; +esac \ No newline at end of file