Files
hermes-wiki-static/scripts/hermes-wiki-serve.sh
T
agent bb9bdf817f Add scripts/hermes-wiki-serve.sh wrapper + README update
Wiki-Serve-Skript war bisher in ~/.local/bin/, jetzt auch im Repo versioniert,
damit Updates via git pull verfügbar sind.

CHANGELOG korrigiert: server.py jetzt +12 Zeilen statt +7 (Bug-Fix für trailing
'/' ist mit drin).
2026-07-16 15:29:00 +00:00

346 lines
13 KiB
Bash
Executable File

#!/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 <<EOF
hermes-wiki-serve.sh — Hermes Wiki via obsidian-web-viewer + Tailscale
Dieses Skript wrapper't DanielCheer/obsidian-web-viewer (3D-Graph, File-Tree,
WikiLink-Navigation, Volltext-Suche, Catppuccin-Theme) und integriert Tailscale
serve für Tailnet-Zugriff.
Verwendung:
$0 start # Server starten (klont obv beim ersten Mal)
$0 stop # Server stoppen
$0 restart # stop + start
$0 status # Process-Status, Health-Check, Tailscale-URL
$0 logs # tail -f der Log-Files
$0 install # nur das Clonen/Update, kein Server-Start
$0 --help
Umgebungsvariablen:
HERMES_WIKI_PORT=8765
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
HERMES_WIKI_TS_PATH=/wiki URL-Pfad unter dem Tailscale
HERMES_WIKI_OWV_DIR=\$HOME/.local/share/hermes-wiki/owv
Dependencies (einmalig):
- python3 (stdlib)
- git
- pip install PyYAML (optional, für sauberes Frontmatter)
- tailscale (für Tailnet-Zugriff)
Zugriff:
- Lokal: http://127.0.0.1:8765/
- Tailnet: https://<hostname>.<tailnet>.ts.net/wiki/
- API: http://127.0.0.1:8765/api/vault/{tree,graph,search,file/<path>}
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