From 02dec087481bef19659215f340ef2ee8158e95a0 Mon Sep 17 00:00:00 2001 From: Housemann <40449280+Housemann@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:40:27 +0200 Subject: [PATCH] Initial commit: Media Server Dashboard Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 2 + README.md | 335 ++++++++++++++++++++++++++ docker-compose.yml | 25 ++ html/bk_index.htm | 349 +++++++++++++++++++++++++++ html/filebot-media-browser.svg | 18 ++ html/index.html | 421 +++++++++++++++++++++++++++++++++ html/redirect.html | 55 +++++ scripts/update-disk.sh | 34 +++ 8 files changed, 1239 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 html/bk_index.htm create mode 100644 html/filebot-media-browser.svg create mode 100644 html/index.html create mode 100644 html/redirect.html create mode 100644 scripts/update-disk.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e5c744 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +html/disk.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..13402b8 --- /dev/null +++ b/README.md @@ -0,0 +1,335 @@ +# Media Server Dashboard + +Statisches Homelab-Dashboard mit Live-Speicheranzeige für einen gemounteten Pfad (`/downloads`). Die Weboberfläche listet Media-Server-Dienste als Kacheln und zeigt den aktuellen Speicherplatz des Download-Laufwerks an. + +## Übersicht + +| Komponente | Aufgabe | +|---|---| +| **nginx** | Liefert die statische Website (`index.html`) und die Datei `disk.json` aus | +| **diskinfo** | Liest per `df` den Speicher des gemounteten Pfads und schreibt alle 30 Sekunden `disk.json` | +| **Browser** | Lädt `disk.json` per Javalescript und aktualisiert die Speicheranzeige | + +``` +┌─────────────┐ GET /disk.json ┌─────────────┐ +│ Browser │ ◄────────────────────── │ nginx │ +└─────────────┘ │ (Port 80) │ + └──────▲──────┘ + │ liest + /opt/nginx/html/disk.json + ▲ + │ schreibt alle 30s + ┌──────┴──────┐ + │ diskinfo │ + │ (alpine) │ + └──────┬──────┘ + │ df -h + /mnt/ssd → /downloads +``` + +## Projektstruktur + +``` +. +├── docker-compose.yml # Container-Definition (nginx + diskinfo) +├── html/ +│ ├── index.html # Dashboard (Apps + Speicheranzeige) +│ ├── disk.json # Wird vom diskinfo-Container geschrieben +│ ├── filebot-media-browser.svg +│ ├── redirect.html +│ └── bk_index.htm # Backup der alten index.html +└── scripts/ + └── update-disk.sh # Speicher auslesen + Endlosschleife +``` + +## Voraussetzungen + +- Docker und Docker Compose auf dem Host +- Gemounteter Speicherpfad auf dem Host (Standard: `/mnt/ssd`) +- Port 80 frei + +## Deployment + +### 1. Dateien auf den Server kopieren + +Auf dem Server liegt das Projekt typischerweise unter `/opt/nginx`: + +```bash +/opt/nginx/ +├── docker-compose.yml +├── html/ +└── scripts/ + └── update-disk.sh +``` + +Dateien aus diesem Repository dorthin kopieren, z. B. per `git clone`, `rsync` oder `scp`. + +Alternativ kann `update-disk.sh` direkt auf dem Server angelegt werden: + +```bash +cat > /opt/nginx/scripts/update-disk.sh << 'EOF' +#!/bin/sh + +MOUNT="/downloads" +OUTPUT="/html/disk.json" + +update_disk() { + LINE=$(df -h "$MOUNT" 2>/dev/null | awk 'NR==2') + if [ -z "$LINE" ]; then + echo "ERROR: df failed for $MOUNT" >&2 + return 1 + fi + + TOTAL=$(echo "$LINE" | awk '{print $2}') + USED=$(echo "$LINE" | awk '{print $3}') + AVAILABLE=$(echo "$LINE" | awk '{print $4}') + USE_PERCENT=$(echo "$LINE" | awk '{print $5}') + + cat > "$OUTPUT" < ... << 'EOF'` direkt auf dem Server anlegen, dann Container neu erstellen: + +```bash +docker compose up -d --force-recreate diskinfo +``` + +### `disk.json` bleibt bei 93 Bytes / alte Werte + +- Altes JSON-Format ohne `updated_at` → Skript auf dem Server ist veraltet +- Neues Format ist ca. 130+ Bytes groß + +### Speicheranzeige zeigt „Fehler“ + +```bash +# Mount im Container prüfen +docker compose exec diskinfo df -h /downloads + +# Schreibrechte prüfen +docker compose exec diskinfo sh -c 'touch /html/test && rm /html/test && echo OK' +``` + +### `diskinfo` zeigt `ERROR: df failed for /downloads` + +- Host-Pfad `/mnt/ssd` existiert nicht oder ist nicht gemountet +- Volume-Mapping in `docker-compose.yml` prüfen + +### Nginx liefert `disk.json`, Werte ändern sich aber nicht + +1. `docker compose ps diskinfo` → Status muss `Up` sein (nicht `Restarting`) +2. `docker compose logs diskinfo` → regelmäßige `disk.json updated`-Zeilen +3. `watch -n 5 cat /opt/nginx/html/disk.json` → `updated_at` muss sich ändern + +## Wartung + +```bash +# Container neu starten +docker compose restart + +# Nur diskinfo neu erstellen (nach Skript-Update) +docker compose up -d --force-recreate diskinfo + +# Logs anzeigen +docker compose logs -f + +# Container stoppen +docker compose down +``` + +## Bekannte Einschränkungen + +- Die Speicheranzeige bezieht sich auf das **Filesystem des Mounts**, nicht auf den Inhalt eines Unterordners +- nginx mountet `/downloads` nur lesend; Schreibzugriff erfolgt ausschließlich über `diskinfo` auf `/html` +- Service-Status (grüner Punkt) nutzt `fetch` mit `no-cors` und kann je nach Browser/Dienst ungenau sein +- Apple Touch Icons (`apple-touch-icon.png` etc.) sind nicht vorhanden → 404 in den nginx-Logs (harmlos) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9003781 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + nginx: + image: nginx:latest + container_name: nginx + restart: unless-stopped + ports: + - 80:80 + volumes: + - ./html:/usr/share/nginx/html:ro + - /mnt/ssd:/downloads:ro + environment: + - NGINX_HOST=example.com + - NGINX_PORT=80 + + diskinfo: + image: alpine:latest + container_name: diskinfo + restart: unless-stopped + volumes: + - /mnt/ssd:/downloads:ro + - ./html:/html + - ./scripts/update-disk.sh:/update-disk.sh:ro + command: ["/bin/sh", "/update-disk.sh"] + +networks: {} diff --git a/html/bk_index.htm b/html/bk_index.htm new file mode 100644 index 0000000..1912b3f --- /dev/null +++ b/html/bk_index.htm @@ -0,0 +1,349 @@ + + + + + + Media Server Dashboard + + + + + + +
+

Media Server

+

Homelab Dashboard

+
+ +
+
+ + + + diff --git a/html/filebot-media-browser.svg b/html/filebot-media-browser.svg new file mode 100644 index 0000000..439b18a --- /dev/null +++ b/html/filebot-media-browser.svg @@ -0,0 +1,18 @@ + + + + + + + + + FB + diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..77f10bd --- /dev/null +++ b/html/index.html @@ -0,0 +1,421 @@ + + + + + + Media Server Dashboard + + + + + + +
+

Media Server

+

Homelab Dashboard

+
+ +
+
+
Speicher /downloads
+
Lade...
+
+
+
+
+
+ Belegt: - + Frei: - + Gesamt: - +
+
+ +
+
+ + + + diff --git a/html/redirect.html b/html/redirect.html new file mode 100644 index 0000000..9093f5b --- /dev/null +++ b/html/redirect.html @@ -0,0 +1,55 @@ + + + + + VNC Viewer + + + +
+
+

Verbinde...

+
+ + + + diff --git a/scripts/update-disk.sh b/scripts/update-disk.sh new file mode 100644 index 0000000..a06f9b1 --- /dev/null +++ b/scripts/update-disk.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +MOUNT="/downloads" +OUTPUT="/html/disk.json" + +update_disk() { + LINE=$(df -h "$MOUNT" 2>/dev/null | awk 'NR==2') + if [ -z "$LINE" ]; then + echo "ERROR: df failed for $MOUNT" >&2 + return 1 + fi + + TOTAL=$(echo "$LINE" | awk '{print $2}') + USED=$(echo "$LINE" | awk '{print $3}') + AVAILABLE=$(echo "$LINE" | awk '{print $4}') + USE_PERCENT=$(echo "$LINE" | awk '{print $5}') + + cat > "$OUTPUT" <