14965c7e03
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m49s
Add a protected backend health endpoint and a new dashboard page/sidebar entry so cc-connect operational status is visible in the UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10 KiB
10 KiB
NAS Dashboard — Implementation Plan
Context
A modern, self-hosted web dashboard on your Synology DS224+ NAS (Tailscale-only) that serves as a single entry point to manage repos, Docker containers, files, terminal sessions, and media — plus an AI assistant. Replaces the need to SSH into the NAS for routine tasks. Public access via a dedicated Caddy reverse proxy VPS.
Architecture
Public Internet → nas.jimmygan.com / music.jimmygan.com
│
└── New AMD VPS (Caddy, HTTPS, Tailscale)
│
└── Tailscale → NAS (100.78.131.124)
├── :4000 NAS Dashboard (FastAPI + Svelte)
│ ├── /api/docker/* → Docker socket proxy (limited API)
│ ├── /api/gitea/* → Gitea REST API
│ ├── /api/files/* → /volume1 filesystem
│ ├── /ws/terminal → WebSocket pty (Tailscale-only)
│ └── /* → Svelte SPA
├── :4533 Navidrome (music)
├── :2283 Immich (photos, Tailscale-only)
├── :3300 Gitea (Tailscale-only)
├── :8096 Jellyfin (video, Tailscale-only)
├── :3100 OpenClaw (AI assistant, Tailscale-only)
├── :8080 Speedtest (Tailscale-only)
├── :13378 Audiobookshelf (audiobooks, Tailscale-only)
├── :8030 Stirling PDF (PDF tools, Tailscale-only)
└── :8222 Vaultwarden (passwords, Tailscale-only)
Existing VPS (158.101.140.85): xray VLESS+REALITY on :443 — untouched
Tech Stack (Dashboard)
- Backend: Python FastAPI + uvicorn
- Frontend: Svelte + Tailwind CSS (light theme default, with dark/light toggle)
- Terminal: xterm.js + WebSocket pty bridge
- Docker mgmt: via docker-socket-proxy (Tecnativa) for safety
- Gitea:
httpxasync client - Build: Multi-stage Dockerfile (node → python-slim), built on Mac, pushed to Gitea container registry
- LLM: Claude API (key from ~/.zshrc)
File Structure
nas-tools/
PLAN.md
backup.sh # Daily backup cron script
notify.sh # Telegram notification utility
dashboard/
docker-compose.yml
Dockerfile
.env.example
backend/
main.py
config.py
auth.py
requirements.txt
routers/
auth.py
docker.py
gitea.py
files.py
terminal.py
system.py
frontend/
package.json
vite.config.js
svelte.config.js
src/
App.svelte
lib/
api.js
terminal.js
routes/
Dashboard.svelte
Docker.svelte
Gitea.svelte
Files.svelte
Terminal.svelte
components/
Sidebar.svelte
ContainerCard.svelte
LogViewer.svelte
navidrome/
docker-compose.yml
immich/
docker-compose.yml
openclaw/
docker-compose.yml
Implementation Phases
Phase 1 — Provision new AMD VPS ✅
- Add
oci-amd-monitor.pyto cloud-monitor repo:- Shape:
VM.Standard.E2.1.Micro(1 OCPU, 1GB RAM, AMD free tier) - Display name:
free-amd-caddy - AMD-compatible image (Ubuntu x86_64)
- Same compartment, availability domain, subnet as existing VPS
- Shape:
- Run script to grab AMD instance
- Harden new VPS: SSH key-only, firewall (only 22/80/443), fail2ban, unattended-upgrades
- Install Tailscale + Caddy on new VPS
- Configure Caddy with HTTP basic auth immediately (placeholder until Phase 7 upgrades to 2FA)
- Update Cloudflare DNS:
nas.jimmygan.comandmusic.jimmygan.com→ new VPS IP (set to "DNS only", let Caddy handle TLS) - Clean up stale DNS:
moldcost→ dead server1 (152.53.226.207) — remove or update
Phase 2 — Dashboard MVP: Docker management ✅
- Create directory structure, Dockerfile, docker-compose.yml
- Add docker-socket-proxy (Tecnativa) to limit Docker API access
- Backend: main.py + config.py + docker router (list, start/stop, logs)
- Frontend: Svelte app with sidebar + Docker container view
- Terminal access control: backend middleware blocks
/ws/terminalfor non-Tailscale IPs (100.x.x.x) - File browser: read-write (mount
/volume1as rw), with path traversal protection - Add Docker log rotation to all compose files (
max-size: 10m,max-file: 3) - Build image on Mac, push to Gitea container registry
- Deploy on NAS, port 4000
- Configure Caddy on new VPS:
nas.jimmygan.com→ NAS :4000 via Tailscale (behind basic auth from Phase 1)
Phase 3 — Gitea + Files + Terminal ✅
- Add gitea router (repos, commits, trigger workflows) — needs Gitea API token
- Add files router (browse /volume1, download, upload, delete) with path traversal protection
- Add terminal router (WebSocket pty bridge + xterm.js) — blocked for non-Tailscale IPs
- Dashboard overview page with links to all services
Phase 4 — Navidrome (Music) ✅
- Deploy Navidrome Docker stack on port 4533 (with log rotation)
- Point at
/volume1/music - Link from dashboard sidebar
- Configure Caddy:
music.jimmygan.com→ NAS :4533 via Tailscale
Phase 5 — Immich (Photos) ✅
- Deploy Immich Docker stack on port 2283 (disable ML/face recognition, with log rotation)
- Point at
/volume1/photo+/volume1/homes/zjgump/Photos - Link from dashboard sidebar (Tailscale-only)
Phase 6 — OpenClaw (AI Assistant) ✅
- Deploy OpenClaw Docker stack on port 3100 (with log rotation)
- Configure Claude API as LLM provider
- Configure messaging channels (Telegram, etc.)
- Link from dashboard sidebar (Tailscale-only)
Phase 7 — Authentication & Security ✅
Upgrade dashboard auth from Caddy basic auth → session-based login + TOTP 2FA— Done (password + TOTP 2FA + passkey/WebAuthn login)Rate limiting for brute force protection on Caddy— Done (fail2ban + failed login Telegram alerts)
Phase 8 — CI/CD for Dashboard ✅
Add Gitea workflow to nas-tools: on push to main, auto-deploy— Done (Gitea Actions runsdocker compose up -don push)Tag images with git SHA +— DonelatestAuto-deploy on NAS— Done- Rollback:
docker compose pull <previous-tag> && docker compose up -d
Phase 9 — Backup & Recovery ✅
Automated daily backup script— Done (Gitea DB, Immich DB, Gitea dump, docker configs + Vaultwarden, Paperless, n8n, Audiobookshelf, Calibre-web)Backups stored to— Done (7-day retention)/volume1/backupsTelegram failure alerts— Done (sources creds from nas-dashboard .env)- Periodic restore test to verify backups work
Phase 10 — Monitoring & Alerts ✅
Add system stats to dashboard overview: CPU, RAM, disk usage— DoneContainer health checks in docker-compose files— DoneNotification on failure (Telegram bot)— Done (container monitor + failed login alerts)
Phase 11 — Polish ✅
Light/dark theme toggle— DoneAdd Jellyfin link to dashboard sidebar (port 8096)— Done (replaced Emby)
Phase 12 — Additional Services ✅
Deploy Audiobookshelf on port 13378— Done, in dashboard sidebarDeploy Stirling PDF on port 8030— Done, in dashboard sidebarDeploy Vaultwarden on port 8222— Done, in dashboard sidebarDeploy Watchtower for auto-updates— Done (internal, no sidebar link needed)Deploy Speedtest on port 8080— Done, in dashboard sidebar
Phase 14 — Hybrid AI Gateway (LiteLLM + cc-connect) 🚧
Add— Donelitellm/service scaffold (docker-compose.yml,config/litellm.yaml,.env.example) with localhost-only host binding and env-driven provider keysAdd dashboard backend LiteLLM operational probe (— Done/api/litellm/health) and wire into protected routesAdd— Donecc-connect/templates (README.md,config.example.toml, optional compose) for mobile bridge rolloutDeploy LiteLLM image to NAS using server2 pull + stream load workflow to avoid slow GHCR pulls on NAS— DoneNetwork fix: attach LiteLLM to— Donenas-dashboard_internaland set dashboard-devLITELLM_URL=http://litellm:4005Update backend health probe to support auth-required mode and configured health auth key— DoneAdd LiteLLM dashboard UI page with sidebar link and health status card— DoneAdd cc-connect dashboard UI page, sidebar link, and backend health/status endpoint— Done
Phase 13 — Off-site Backup
- Add cloud backup (OneDrive or Google Drive) for critical data
- Encrypt backups before upload
- Retention policy for cloud copies
Media Paths
- Music:
/volume1/music(70GB, FLAC/WAV/DSD/ALAC) - Photos:
/volume1/photo(186GB) +/volume1/homes/zjgump/Photos - Video:
/volume1/video(759GB) - Emby: replaced by Jellyfin (port 8096)
VPS Strategy
- Existing VPS (158.101.140.85): xray VLESS+REALITY on :443 — untouched
- New AMD VPS: Caddy on :80/:443, Tailscale, reverse proxy to NAS
- Both in Tokyo region, Oracle free tier
DNS (Cloudflare)
nas.jimmygan.com→ new AMD VPS IP (DNS only, Caddy handles TLS)music.jimmygan.com→ new AMD VPS IP (DNS only)moldcost.jimmygan.com→ remove (dead server1)- Existing
jimmygan.comA record → keep as-is
Build Strategy
- Docker images built on Mac (faster than NAS Celeron)
- Push to Gitea container registry (
100.78.131.124:3300) - NAS pulls and runs images
- CI/CD automates this via Gitea Actions
Verification
- Dashboard at
http://100.78.131.124:4000— Docker page shows containers, start/stop works - Gitea page lists repos and commits
- Files page browses /volume1
- Terminal opens a working shell session
- Navidrome at
:4533— plays music from/volume1/music - Immich at
:2283— browses photos - OpenClaw at
:3100— responds via Claude API https://nas.jimmygan.com— dashboard accessible from internet with login + 2FAhttps://music.jimmygan.com— Navidrome accessible from internet- Backups run daily, restore tested
- Dashboard shows system stats, alerts on disk/container issues