d4f95b64d4
Manage now connects to existing Grafana/Prometheus/Alertmanager instances and never deploys its own stack. - docker-compose.yml / docker-compose.dev.yml: removed prometheus, loki, alloy, grafana, alertmanager, node-exporter services, the monitoring network, and observability named volumes; they now ship only backend + frontend. Dev frontend now joins the web network so the Vite dev proxy can reach the backend. - backend: alertmanager_url default is now empty; /api/monitoring/alerts and /alertmanager-status return graceful "not configured" responses when ALERTMANAGER_URL is unset. Added not-configured tests. - docker-compose.observability.yml: kept as the optional standalone example; header clarifies Manage does not deploy it. - Removed orphaned combined monitoring/prometheus/prometheus.yml (standalone stack uses prometheus.standalone.yml). - Docs (README, REQUIREMENTS decision log, monitoring-logging-design, observability-runbooks, context.md, MIGRATION_PLAN, frontend/README, CHANGELOG) updated to the connect-to-existing model. VITE_GRAFANA_URL / VITE_PROMETHEUS_URL remain as optional frontend deep-link overrides. .env.example still needs a manual update (safety policy blocks assistant edits): set ALERTMANAGER_URL empty/optional and move standalone-only vars out of the root file.
190 lines
8.2 KiB
Markdown
190 lines
8.2 KiB
Markdown
# 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
|
|
|
|
- Configurable dashboard with persisted widgets (Jellyfin activity, backups summary, Grafana deep-links, Prometheus metrics, SSH task output, static text) and shortcuts
|
|
- Thin-dashboard observability: Alertmanager alerts, Prometheus target health, machine status, and Grafana deep-links (no in-app charting)
|
|
- Per-machine settings for Jellyfin, Jellyseerr, SSH, and monitoring targets
|
|
- 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 safe remote job templates
|
|
- Addon pages for Grafana, Prometheus, and SSH tasks at `/addons/:addonId`
|
|
|
|
## 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>.
|
|
|
|
The production Compose file requires OIDC and Traefik variables; see [Configuration](#configuration) below. Copy `.env.example` to `.env`, fill in the required values, and export them in your shell before running `docker compose up`.
|
|
|
|
> **Observability is external.** Manage only ships its **backend** and **frontend**. It does **not** deploy Grafana, Prometheus, Loki, Alertmanager, Alloy, or Node Exporter. The backend exposes a `/metrics` endpoint and optional Alertmanager proxy endpoints so an *existing* observability deployment can scrape and consume them. For a ready-to-run example stack you can deploy alongside Manage, see [`docker-compose.observability.yml`](docker-compose.observability.yml) and [`docs/observability-runbooks.md`](docs/observability-runbooks.md).
|
|
|
|
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>. Dev compose disables OIDC by default (`AUTH_ENABLED=false`), so you can open it directly without an identity provider.
|
|
|
|
The backend media index and settings database (including monitoring machines, SSH keys, saved tasks, and dashboard widgets) are persisted in Docker volumes so rebuilds and container restarts do not reset state.
|
|
|
|
### 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=api.manage.example.com
|
|
export FRONTEND_APP_HOST=manage.example.com
|
|
export CERT_RESOLVER=letsencrypt
|
|
export VITE_OIDC_ISSUER=https://auth.example.com/application/o/manage/
|
|
export VITE_OIDC_CLIENT_ID=manage
|
|
export VITE_OIDC_REDIRECT_URI=https://manage.example.com/oidc/callback
|
|
export VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/
|
|
export VITE_GRAFANA_URL=https://grafana.manage.example.com
|
|
export VITE_PROMETHEUS_URL=https://prometheus.manage.example.com
|
|
export MANAGE_ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
|
|
|
|
docker compose up --build
|
|
```
|
|
|
|
Inline one-liner example:
|
|
|
|
```bash
|
|
BACKEND_APP_HOST=api.manage.example.com FRONTEND_APP_HOST=manage.example.com CERT_RESOLVER=letsencrypt VITE_OIDC_ISSUER=https://auth.example.com/application/o/manage/ VITE_OIDC_CLIENT_ID=manage VITE_OIDC_REDIRECT_URI=https://manage.example.com/oidc/callback VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/ VITE_GRAFANA_URL=https://grafana.manage.example.com VITE_PROMETHEUS_URL=https://prometheus.manage.example.com docker compose up --build
|
|
```
|
|
|
|
For local development, no SSH key is required unless you want to connect to remote SSH machines later:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.dev.yml 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.
|
|
#
|
|
# Remote SSH machines can store their private key and optional passphrase directly in Settings,
|
|
# so no SSH key mount is required for normal use.
|
|
|
|
# Authentik / OIDC
|
|
AUTH_ENABLED=true
|
|
OIDC_ISSUER_URL=https://auth.example.com/application/o/manage/
|
|
OIDC_AUDIENCE=manage
|
|
OIDC_JWKS_URL=
|
|
OIDC_CLOCK_SKEW_SECONDS=30
|
|
|
|
# Frontend OIDC settings
|
|
VITE_OIDC_ENABLED=true
|
|
VITE_OIDC_ISSUER=https://auth.example.com/application/o/manage/
|
|
VITE_OIDC_CLIENT_ID=manage
|
|
VITE_OIDC_SCOPE=openid profile email
|
|
VITE_OIDC_REDIRECT_URI=https://manage.example.com/oidc/callback
|
|
VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/
|
|
|
|
# Grafana / Prometheus public URLs for frontend deep-links (service adapters read URLs from service records)
|
|
VITE_GRAFANA_URL=https://grafana.manage.example.com
|
|
VITE_PROMETHEUS_URL=https://prometheus.manage.example.com
|
|
|
|
# Required: master key encrypting service secrets (API keys/tokens) at rest.
|
|
# Generate one with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
MANAGE_ENCRYPTION_KEY=replace-with-a-fernet-key
|
|
```
|
|
|
|
## Remote server requirements
|
|
|
|
The remote server needs:
|
|
|
|
- `/bin/sh` (POSIX shell)
|
|
- `python3`, `ffprobe`, `find`, `stat`, `df`, `awk` for file inspection and job templates
|
|
- SSH access with a key configured in the app's Settings tab
|
|
|
|
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`.
|
|
- 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. They deploy **only** the backend and frontend; Manage never deploys its own observability stack (see `docker-compose.observability.yml` for an optional standalone example).
|
|
- The configurable dashboard stores widget instances in the backend SQLite settings database. New installs seed default Jellyfin activity and Backups widgets automatically.
|
|
- Grafana and Prometheus widget adapters resolve URLs from service records configured in the app; `VITE_GRAFANA_URL` / `VITE_PROMETHEUS_URL` are only used for frontend deep-links. No credentials are stored in widget config; service API keys are encrypted at rest with `MANAGE_ENCRYPTION_KEY`.
|
|
- `ALERTMANAGER_URL` is optional. When unset, the Alertmanager proxy endpoints return graceful "not configured" responses instead of erroring.
|