Files
nas-tools/PLAN.md
T

206 lines
7.9 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 Emby (video, Tailscale-only)
└── :3100 OpenClaw (AI assistant, 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
34. Rate limiting for brute force protection on Caddy
### Phase 8 — CI/CD for Dashboard
35. Add Gitea workflow to nas-tools: on push to main, build Docker image, push to Gitea registry
36. Tag images with git SHA + `latest`; keep previous image for rollback
37. Auto-deploy on NAS: pull new image, restart container
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
36. Add system stats to dashboard overview: CPU, RAM, disk usage
37. Container health checks in docker-compose files
38. Notification on failure (Telegram bot or email)
### Phase 11 — Polish
39. Light/dark theme toggle, loading states, error handling
40. Add Emby link to dashboard sidebar (port 8096)
## Media Paths
- Music: `/volume1/music` (70GB, FLAC/WAV/DSD/ALAC)
- Photos: `/volume1/photo` (186GB) + `/volume1/homes/zjgump/Photos`
- Video: `/volume1/video` (759GB)
- Emby: installed but unconfigured (port 8096, needs setup wizard)
## 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