# Manage Manage is a media and server operations tool with Jellyfin integration, SSH file inspection, server monitoring, and safe remote job templates. See `docs/REQUIREMENTS.md` for the living requirements, decisions, and planning history. See `docs/MIGRATION_PLAN.md` for the FastAPI + React architecture plan. Project policy/docs: - License: `LICENSE` (MIT) - Contributing guide: `CONTRIBUTING.md` ## Architecture The project consists of two subprojects: - **`backend/`** — FastAPI Python API (see `backend/README.md`) - **`frontend/`** — React + TypeScript SPA (see `frontend/README.md`) - **`archive/`** — Original Streamlit prototype (preserved for reference) ## Features - Dashboard with now-playing sessions, server monitoring overview, and per-library media counts - Server monitoring with CPU, IO wait, RAM, network, and disk I/O charts plus a sortable dashboard table covering all configured machines - Per-machine monitoring settings with local and remote targets managed in the UI, plus backend-collected recent action history per machine - SQLite-indexed media table with full-library sort/filter - Read-only Users tab with Jellyfin as the base source and optional Jellyseerr enrichment - Remote file browser with ffprobe preview and job execution - Jellyfin API integration for library metadata and user identity data - SSH-based file inspection and remote job templates ## Quick Start ### Docker Compose (recommended) Production-style deployment with the frontend serving the SPA and proxying `/api` to the backend. The compose files rely on environment-variable interpolation, so export the required values in your shell before running them (no `env_file` is needed): ```bash docker compose up --build ``` Open the app at http://localhost:8080. Local development with hot reload: ```bash docker compose -f docker-compose.dev.yml up --build ``` Frontend runs on http://localhost:5173 and the backend on http://localhost:8000. The backend media index is persisted in a Docker volume (`backend_cache`) so rebuilds and container restarts do not force a full re-index. Monitoring machine definitions and recent machine activity are stored in the backend so the UI can show one section per configured machine and preserve history across restarts. ### Manual backend/frontend development ```bash cd backend python -m venv .venv source .venv/bin/activate pip install -e '.[dev]' uvicorn media_library_viewer_api.main:app --reload --port 8000 ``` ```bash cd frontend npm install npm run dev ``` ## Configuration The Compose files use environment-variable interpolation. Export the required variables in your shell or pass them inline; a `.env` file is optional, not required. ### Compose examples Production-style example with shell exports: ```bash export BACKEND_APP_HOST=manage.example.com export FRONTEND_APP_HOST=manage.example.com export CERT_RESOLVER=letsencrypt export VITE_OIDC_ISSUER=https://authentik.example/application/o/manage/ export VITE_OIDC_CLIENT_ID=manage export VITE_OIDC_REDIRECT_URI=https://manage.example.com/ export VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/ # Optional: provide a single SSH private key file for remote machines configured later in Settings. # Compose mounts it as a Docker secret, so you do not need to mount your whole ~/.ssh directory. # If you prefer SSH agent forwarding, Paramiko will use SSH_AUTH_SOCK when you provide it. export SSH_KEY_HOST_PATH=$HOME/.ssh/id_ed25519 # The container-side secret path is fixed at /run/secrets/ssh_private_key. # In the app's SSH machine settings, use /run/secrets as the key directory and ssh_private_key as the key name. docker compose up --build ``` Inline one-liner example: ```bash BACKEND_APP_HOST=manage.example.com FRONTEND_APP_HOST=manage.example.com CERT_RESOLVER=letsencrypt VITE_OIDC_ISSUER=https://authentik.example/application/o/manage/ VITE_OIDC_CLIENT_ID=manage VITE_OIDC_REDIRECT_URI=https://manage.example.com/ VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/ SSH_KEY_HOST_PATH=$HOME/.ssh/id_ed25519 docker compose up --build ``` Example environment variables: ```bash # Optional backend logging level LOG_LEVEL=INFO # Optional SMTP settings for the Users -> message popup SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_USERNAME=your-smtp-username SMTP_PASSWORD=your-smtp-password SMTP_FROM_ADDRESS=no-reply@example.com SMTP_FROM_NAME=Manage SMTP_USE_TLS=true SMTP_USE_SSL=false SMTP_TIMEOUT=30 # Jellyfin, Jellyseerr, and SSH targets are now configured per machine in the app's Settings tab. # The backend seeds a local machine automatically, so no global Jellyfin or SSH env vars are required. # # If you still want to keep a fallback SSH key available for legacy/manual use, provide a single key file path: SSH_KEY_HOST_PATH=/absolute/path/to/id_ed25519 # The container-side secret path is fixed at /run/secrets/ssh_private_key. # Authentik / OIDC AUTH_ENABLED=true OIDC_ISSUER_URL=https://authentik.example/application/o/media-library-viewer/ OIDC_AUDIENCE=media-library-viewer OIDC_JWKS_URL= OIDC_CLOCK_SKEW_SECONDS=30 # Frontend OIDC settings VITE_OIDC_ENABLED=true VITE_OIDC_ISSUER=https://authentik.example/application/o/media-library-viewer/ VITE_OIDC_CLIENT_ID=media-library-viewer VITE_OIDC_SCOPE=openid profile email VITE_OIDC_REDIRECT_URI=http://localhost:8080/ VITE_OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:8080/ ``` ## Remote server requirements The remote server needs: - Linux `/proc` and `/sys/block` for monitoring - `/bin/sh` (POSIX shell) - `python3`, `ffprobe`, `find`, `stat`, `df`, `awk` The SSH client rejects unknown host keys. Connect manually once first: ```bash ssh user@host ``` ## Development ```bash # Backend cd backend && PYTHONPATH=src python -m py_compile src/media_library_viewer_api/main.py # Frontend cd frontend && npx tsc --noEmit && npm run build ``` ## Notes - Jellyfin server root URL required (not `/web`). The client strips trailing `/web` defensively. - SSH commands run through `/bin/sh -c` regardless of remote login shell. - Job templates are shell-quoted. Add new templates in `backend/src/media_library_viewer_api/jobs.py`. - Monitoring collector uses JSONL in `/tmp`, pruned to 7 days / 70k lines. - Root-level Docker Compose files are provided for production (`docker-compose.yml`) and local development (`docker-compose.dev.yml`), and both rely on Compose interpolation rather than `env_file` entries.