diff --git a/README.md b/README.md index 46deac1..58c22cb 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,12 @@ below for where things stand against the original 22 criteria. Set via `AUTH_MODE` (`.env` / `docker-compose.yml` environment): `disabled`, `proxy`, or `oidc` — see [docs/phase1/02-threat-model.md](docs/phase1/02-threat-model.md). `disabled` refuses to start when `NODE_ENV=production` (it grants every -request implicit admin access — dev/test only). `oidc` is implemented -against the `openid-client` v5 API but hasn't been run against a real IdP -yet; test it once Pocket ID (or similar) is configured. +request implicit admin access — dev/test only). `oidc` is confirmed working +end-to-end against a real Pocket ID instance (Authorization Code + PKCE via +`openid-client` v5). Once `AUTH_MODE=oidc` is set and the server restarted, +the actual provider details (issuer URL, client ID/secret, redirect URL, +scopes) are editable any time from **Admin → Auth** — no further env file +edits or restarts needed for those. All mutating requests on the web listener (`POST`/`PUT`/`PATCH`/`DELETE` under `/api/`) require a `X-Requested-With: notizarr` header — this is the @@ -150,6 +153,12 @@ push subscriptions, the VAPID keypair, the session secret — lives in Postgres (see [docs/phase1/06-docker-unraid-design.md](docs/phase1/06-docker-unraid-design.md#backup--restore)), so backing up the database is a complete backup. +**Automatic (no setup needed):** the app backs itself up once a day and +keeps the last 14, no Unraid User Scripts cron entry required. Trigger one +manually or download an existing one from **Admin → Backups**. + +**Manual / scripted**, e.g. for an off-box copy: + ```bash cd /mnt/tank/appdata/notizarr ./scripts/backup.sh @@ -158,24 +167,78 @@ cd /mnt/tank/appdata/notizarr Writes a timestamped, gzipped `pg_dump` plus a copy of the compose file (env vars, including the DB password and OIDC client secret, are inline there rather than in a separate `.env` — keep `backups/` off shared or -public storage) into `backups/`, keeping the last 14 of each. Restore -with: +public storage) into `backups/`, keeping the last 14 of each — the same +directory and naming the in-app job uses, so both coexist. Restore with: ```bash ./scripts/restore.sh backups/notizarr-db-.sql.gz ``` This is destructive (replaces all current data) and asks for confirmation. -Consider scheduling `backup.sh` via an Unraid User Scripts cron entry for -regular backups. -## Migrating from ntfy / integrating other services +## Home Assistant -- [docs/migration-ntfy.md](docs/migration-ntfy.md) — endpoint mapping, - before/after examples, what's intentionally not replicated -- [docs/home-assistant.md](docs/home-assistant.md) — `rest_command` example - replacing `ntfy.publish`, plus Seerr, Uptime Kuma, and generic webhook - configuration +Add a `rest_command` pointing at the LAN ingestion listener (`:8081`, never +the public URL): + +```yaml +# configuration.yaml +rest_command: + notizarr: + url: "http://:8081/api/v1/notifications" + method: POST + headers: + Authorization: !secret notizarr_token + Content-Type: "application/json" + payload: > + { + "topic": "{{ topic }}", + "title": "{{ title }}", + "message": "{{ message }}", + "source": "{{ source | default('home-assistant') }}", + "priority": "{{ priority | default('default') }}", + "image_url": "{{ image_url | default('') }}" + } +``` + +```yaml +# secrets.yaml — the value is the FULL header including "Bearer ", since a +# YAML !secret must be a key's entire value, not embedded in a larger string +notizarr_token: "Bearer nzr_your_token_here" +``` + +Then call it from any automation: + +```yaml +action: rest_command.notizarr +data: + topic: homeassistant + title: "🏠 Home" + message: "Office window is open, don't forget to close it" +``` + +Get the token from **Admin → API Tokens → Create token**. Two extras worth +knowing: + +- **`priority: min`** stores and shows the notification as usual but skips + the push — useful for automations that should log something without + buzzing anyone's phone, without needing a separate "quiet" topic. +- **Quiet hours** (Admin → System) and **per-topic snooze** (the alarm-clock + icon on any topic page) both suppress push independently of what any + individual automation sends, if you'd rather configure "don't wake me + up" once instead of per-automation. + +Full field reference, dedup keys, and examples for Seerr/Uptime +Kuma/generic webhooks: [docs/home-assistant.md](docs/home-assistant.md). + +## Migrating from ntfy + +[docs/migration-ntfy.md](docs/migration-ntfy.md) — endpoint mapping, +before/after examples, what's intentionally not replicated. Short version: +point existing `ntfy.sh/` publishers at +`http://:8081/` with an `Authorization: Bearer nzr_...` +header instead — headers, JSON body, and query-param publishing all still +work the same way. ## Troubleshooting @@ -208,6 +271,17 @@ regular backups. non-admin user was created first (e.g. during testing), promote them manually via Settings → Admin → Users using an existing admin account, or directly in Postgres if none exists yet. +- **A third-party app's built-in "Ntfy" notification type won't send + ("test failed", 404).** Sonarr/Radarr/Prowlarr-style apps typically + append each topic in their own "Topics" field to the Server URL + (`{Server URL}/{topic}`), so the topic must **not** already be in the + Server URL — just the bare host+port. Overseerr/Jellyseerr-style apps do + the opposite: they expect the topic already in the Server URL and don't + append anything, so putting a topic in a separate "Topic" field there is + redundant but harmless. When in doubt, `curl` the ingestion listener + directly with the same topic path the app would use — Notizarr always + expects exactly one path segment (`POST /:topic`), and comparing that + against what actually gets sent explains almost every case of this. ## MVP acceptance status