diff --git a/.env.example b/.env.example index 6818241..b97d67e 100644 --- a/.env.example +++ b/.env.example @@ -24,13 +24,23 @@ SMTP_TIMEOUT=30 SSH_HOST=media-server.example.com SSH_USERNAME=username SSH_PORT=22 -SSH_KEY_FILENAME=/home/username/.ssh/id_rsa +# In Docker Compose, mount your private key at ./secrets/ssh/id_ed25519 +# and set this path to /root/.ssh/id_ed25519 inside the backend container. +SSH_KEY_FILENAME=/root/.ssh/id_ed25519 +SSH_KEY_NAME=id_rsa # SSH_PASSWORD=optional-password-or-key-passphrase REMOTE_MEDIA_ROOT=/mnt/media # Optional fallback prefix when REMOTE_MEDIA_ROOT mapping is not enough. # Example: Jellyfin gives /media/... but SSH host requires /srv/media/... REMOTE_PATH_PREFIX= + +# For deployment with traefik +APP_NAME=management +APP_HOST=management.example.com +CERT_RESOLVER=lets-encrypt +APP_PORT=5173 + # Authentik / OIDC # Backend validates every API request with a Bearer JWT. AUTH_ENABLED=true diff --git a/.gitignore b/.gitignore index d000eae..9727411 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ env/ *.log tmp/ +# SSH secrets used by docker compose +secrets/ssh/ + # Frontend frontend/node_modules/ frontend/dist/ diff --git a/README.md b/README.md index e04384e..a424f11 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,9 @@ LOG_LEVEL=INFO SSH_HOST=media-server.example.com SSH_USERNAME=username SSH_PORT=22 -SSH_KEY_FILENAME=/home/username/.ssh/id_rsa +# In Docker Compose, mount your private key at ./secrets/ssh/id_ed25519 +# and your host key file at ./secrets/ssh/known_hosts. +SSH_KEY_FILENAME=/root/.ssh/id_ed25519 SSH_PASSWORD= REMOTE_MEDIA_ROOT=/srv/media diff --git a/backend/README.md b/backend/README.md index 0848273..7146302 100644 --- a/backend/README.md +++ b/backend/README.md @@ -71,7 +71,9 @@ OIDC_CLOCK_SKEW_SECONDS=30 SSH_HOST=media-server.example.com SSH_USERNAME=username SSH_PORT=22 -SSH_KEY_FILENAME=/home/username/.ssh/id_rsa +# In Docker Compose, mount your private key at ./secrets/ssh/id_ed25519 +# and your host key file at ./secrets/ssh/known_hosts. +SSH_KEY_FILENAME=/root/.ssh/id_ed25519 SSH_PASSWORD= REMOTE_MEDIA_ROOT=/srv/media diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 267efac..32d9622 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -9,10 +9,12 @@ services: - .env environment: AUTH_ENABLED: "false" + SSH_KEY_FILENAME: /root/.ssh/id_rsa ports: - "8000:8000" volumes: - ./backend:/app/backend + - ./secrets/ssh/:/root/.ssh/ restart: unless-stopped frontend: diff --git a/docker-compose.yml b/docker-compose.yml index 6687395..5036143 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,9 @@ services: - .env environment: AUTH_ENABLED: "true" + SSH_KEY_FILENAME: /root/.ssh/${SSH_KEY_NAME} + volumes: + - ./secrets/ssh:/root/.ssh:ro restart: unless-stopped expose: - "8000" @@ -40,6 +43,12 @@ services: depends_on: backend: condition: service_healthy + labels: + - "traefik.enable=true" + - "traefik.http.routers.${APP_NAME}.rule=Host(`${APP_HOST}`)" + - "traefik.http.routers.${APP_NAME}.entrypoints=websecure" + - "traefik.http.routers.${APP_NAME}.tls.certresolver=${CERT_RESOLVER}" + - "traefik.http.services.${APP_NAME}.loadbalancer.server.port=${APP_PORT}" ports: - "8080:80" restart: unless-stopped diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 185133b..d8e3e76 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -126,6 +126,7 @@ Phase 1: Jellyfin media index, SSH-based remote filesystem inspection, server mo - Provide a dashboard tab with a compact Jellyfin media library overview and server resource overview. - Support OIDC login in the frontend using an OIDC client library, with backend JWT validation for protected API requests. - Provide Docker Compose deployment files at the repository root for production and local development. +- Backend Docker deployment should mount a private SSH key and a known_hosts file into the container rather than baking them into the image. - Show Jellyfin media counts for movies, series, and series episodes on the dashboard. - Show dashboard session activity from Jellyfin, including both currently playing sessions and logged-in idle sessions. - Activity rows should include user, media title (or `(idle)`), playback state (`playing`/`paused`/`idle`), and whether transcoding is active. @@ -168,3 +169,4 @@ Phase 1: Jellyfin media index, SSH-based remote filesystem inspection, server mo - 2026-05-03: Confirmed the shared session table should keep the compact overall status summary line above the rows. - 2026-05-03: Updated the dashboard monitoring cards to show 10-minute averages with high/low subtext instead of only the latest sample. - 2026-05-03: Added OIDC/JWT auth support plus root-level Docker Compose deployment files for production and dev workflows. +- 2026-05-04: Backend Docker Compose now mounts `./secrets/ssh/id_ed25519` and `./secrets/ssh/known_hosts` into `/root/.ssh` so Paramiko can use a private key and strict host-key checking without baking secrets into the image.