Files
nas-tools/PLAN.md
Gan, Jimmy 14965c7e03
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m49s
feat: add cc-connect dashboard health integration
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>
2026-03-02 23:24:03 +08:00

231 lines
10 KiB
Markdown

# 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: `httpx` async 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 ✅
1. Add `oci-amd-monitor.py` to 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
2. Run script to grab AMD instance
3. Harden new VPS: SSH key-only, firewall (only 22/80/443), fail2ban, unattended-upgrades
4. Install Tailscale + Caddy on new VPS
5. Configure Caddy with HTTP basic auth immediately (placeholder until Phase 7 upgrades to 2FA)
6. Update Cloudflare DNS: `nas.jimmygan.com` and `music.jimmygan.com` → new VPS IP (set to "DNS only", let Caddy handle TLS)
7. Clean up stale DNS: `moldcost` → dead server1 (152.53.226.207) — remove or update
### Phase 2 — Dashboard MVP: Docker management ✅
8. Create directory structure, Dockerfile, docker-compose.yml
9. Add docker-socket-proxy (Tecnativa) to limit Docker API access
10. Backend: main.py + config.py + docker router (list, start/stop, logs)
11. Frontend: Svelte app with sidebar + Docker container view
12. Terminal access control: backend middleware blocks `/ws/terminal` for non-Tailscale IPs (100.x.x.x)
13. File browser: read-write (mount `/volume1` as rw), with path traversal protection
14. Add Docker log rotation to all compose files (`max-size: 10m`, `max-file: 3`)
15. Build image on Mac, push to Gitea container registry
16. Deploy on NAS, port 4000
17. Configure Caddy on new VPS: `nas.jimmygan.com` → NAS :4000 via Tailscale (behind basic auth from Phase 1)
### Phase 3 — Gitea + Files + Terminal ✅
18. Add gitea router (repos, commits, trigger workflows) — needs Gitea API token
19. Add files router (browse /volume1, download, upload, delete) with path traversal protection
20. Add terminal router (WebSocket pty bridge + xterm.js) — blocked for non-Tailscale IPs
21. Dashboard overview page with links to all services
### Phase 4 — Navidrome (Music) ✅
22. Deploy Navidrome Docker stack on port 4533 (with log rotation)
23. Point at `/volume1/music`
24. Link from dashboard sidebar
25. Configure Caddy: `music.jimmygan.com` → NAS :4533 via Tailscale
### Phase 5 — Immich (Photos) ✅
26. Deploy Immich Docker stack on port 2283 (disable ML/face recognition, with log rotation)
27. Point at `/volume1/photo` + `/volume1/homes/zjgump/Photos`
28. Link from dashboard sidebar (Tailscale-only)
### Phase 6 — OpenClaw (AI Assistant) ✅
29. Deploy OpenClaw Docker stack on port 3100 (with log rotation)
30. Configure Claude API as LLM provider
31. Configure messaging channels (Telegram, etc.)
32. Link from dashboard sidebar (Tailscale-only)
### Phase 7 — Authentication & Security ✅
33. ~~Upgrade dashboard auth from Caddy basic auth → session-based login + TOTP 2FA~~ — Done (password + TOTP 2FA + passkey/WebAuthn login)
34. ~~Rate limiting for brute force protection on Caddy~~ — Done (fail2ban + failed login Telegram alerts)
### Phase 8 — CI/CD for Dashboard ✅
35. ~~Add Gitea workflow to nas-tools: on push to main, auto-deploy~~ — Done (Gitea Actions runs `docker compose up -d` on push)
36. ~~Tag images with git SHA + `latest`~~ — Done
37. ~~Auto-deploy on NAS~~ — Done
38. Rollback: `docker compose pull <previous-tag> && docker compose up -d`
### Phase 9 — Backup & Recovery ✅
33. ~~Automated daily backup script~~ — Done (Gitea DB, Immich DB, Gitea dump, docker configs + Vaultwarden, Paperless, n8n, Audiobookshelf, Calibre-web)
34. ~~Backups stored to `/volume1/backups`~~ — Done (7-day retention)
35. ~~Telegram failure alerts~~ — Done (sources creds from nas-dashboard .env)
36. Periodic restore test to verify backups work
### Phase 10 — Monitoring & Alerts ✅
36. ~~Add system stats to dashboard overview: CPU, RAM, disk usage~~ — Done
37. ~~Container health checks in docker-compose files~~ — Done
38. ~~Notification on failure (Telegram bot)~~ — Done (container monitor + failed login alerts)
### Phase 11 — Polish ✅
39. ~~Light/dark theme toggle~~ — Done
40. ~~Add Jellyfin link to dashboard sidebar (port 8096)~~ — Done (replaced Emby)
### Phase 12 — Additional Services ✅
41. ~~Deploy Audiobookshelf on port 13378~~ — Done, in dashboard sidebar
42. ~~Deploy Stirling PDF on port 8030~~ — Done, in dashboard sidebar
43. ~~Deploy Vaultwarden on port 8222~~ — Done, in dashboard sidebar
44. ~~Deploy Watchtower for auto-updates~~ — Done (internal, no sidebar link needed)
45. ~~Deploy Speedtest on port 8080~~ — Done, in dashboard sidebar
### Phase 14 — Hybrid AI Gateway (LiteLLM + cc-connect) 🚧
49. ~~Add `litellm/` service scaffold (`docker-compose.yml`, `config/litellm.yaml`, `.env.example`) with localhost-only host binding and env-driven provider keys~~ — Done
50. ~~Add dashboard backend LiteLLM operational probe (`/api/litellm/health`) and wire into protected routes~~ — Done
51. ~~Add `cc-connect/` templates (`README.md`, `config.example.toml`, optional compose) for mobile bridge rollout~~ — Done
52. ~~Deploy LiteLLM image to NAS using server2 pull + stream load workflow to avoid slow GHCR pulls on NAS~~ — Done
53. ~~Network fix: attach LiteLLM to `nas-dashboard_internal` and set dashboard-dev `LITELLM_URL=http://litellm:4005`~~ — Done
54. ~~Update backend health probe to support auth-required mode and configured health auth key~~ — Done
55. ~~Add LiteLLM dashboard UI page with sidebar link and health status card~~ — Done
56. ~~Add cc-connect dashboard UI page, sidebar link, and backend health/status endpoint~~ — Done
### Phase 13 — Off-site Backup
46. Add cloud backup (OneDrive or Google Drive) for critical data
47. Encrypt backups before upload
48. 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.com` A 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
1. Dashboard at `http://100.78.131.124:4000` — Docker page shows containers, start/stop works
2. Gitea page lists repos and commits
3. Files page browses /volume1
4. Terminal opens a working shell session
5. Navidrome at `:4533` — plays music from `/volume1/music`
6. Immich at `:2283` — browses photos
7. OpenClaw at `:3100` — responds via Claude API
8. `https://nas.jimmygan.com` — dashboard accessible from internet with login + 2FA
9. `https://music.jimmygan.com` — Navidrome accessible from internet
10. Backups run daily, restore tested
11. Dashboard shows system stats, alerts on disk/container issues