217 lines
8.7 KiB
Markdown
217 lines
8.7 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/
|
|
dashboard/
|
|
docker-compose.yml # Dashboard + docker-socket-proxy
|
|
Dockerfile
|
|
.env.example
|
|
backend/
|
|
main.py
|
|
config.py
|
|
requirements.txt
|
|
routers/
|
|
docker.py
|
|
gitea.py
|
|
files.py
|
|
terminal.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
|
|
|
|
cloud-monitor/
|
|
oci-amd-monitor.py # New: grab AMD free-tier instance
|
|
```
|
|
|
|
## 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 for:
|
|
- Gitea DB (pg_dump) + gitea-data
|
|
- Immich DB + upload data
|
|
- Navidrome config/DB
|
|
- OpenClaw data
|
|
- Dashboard .env and config
|
|
34. Backups stored to `/volume1/backups`
|
|
35. Periodic restore test to verify backups work
|
|
|
|
### Phase 10 — Monitoring & Alerts (partial)
|
|
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 or email)
|
|
|
|
### Phase 11 — Polish (partial)
|
|
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, add to dashboard sidebar
|
|
42. Deploy Stirling PDF on port 8030 — Done, add to dashboard sidebar
|
|
43. Deploy Vaultwarden on port 8222 — Done, add to dashboard sidebar
|
|
44. Deploy Watchtower for auto-updates — Done (internal, no sidebar link needed)
|
|
45. Deploy Speedtest on port 8080 — Done, already in dashboard sidebar
|
|
|
|
## 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
|