Add settings UI, Discord notifications, and alert detail improvements

- Settings modal (gear icon) lets you configure all service URLs and API
  keys from the dashboard; values persist to data/settings.json with
  process.env as fallback so existing .env.local setups keep working
- Per-service Test button hits each service's status endpoint and reports
  the version on success
- Discord webhook support: structured embeds per alert category (requesters,
  approval age, episode progress, watch-rate stats) sent on new/reopened
  alerts only — already-open alerts don't re-notify
- Alert detail page restructured: prose descriptions replaced with labelled
  fields, episode progress bar for partial TV, watch-rate stat block,
  View in Radarr/Sonarr/Seerr action buttons, requester names link to
  Overseerr profiles, timestamps moved inline with status
- Tab state is pure client state (no ?tab= in URL); router.back() used
  on alert detail for clean browser history

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 14:57:07 -04:00
parent 2374bad7ba
commit 641a7fd096
20 changed files with 2191 additions and 302 deletions

View File

@@ -10,6 +10,8 @@ Built with Next.js 16, TypeScript, and Tailwind CSS.
- **Leaderboard** — per-user request count, total storage, average GB per request, and optional Tautulli watch stats (plays, watch hours), each ranked against the full userbase
- **Alerting** — automatic alerts for stalled downloads, neglected requesters, and abusive patterns, with open/close state, notes, and auto-resolve when conditions clear
- **Discord notifications** — posts a structured embed to a webhook whenever a new alert opens or a resolved one returns
- **Settings UI** — configure all service URLs and API keys from the dashboard; no need to touch `.env.local` after initial setup
- **SWR caching** — stats are cached server-side for 5 minutes and seeded from localStorage on the client, so the dashboard is instant on return visits
---
@@ -24,9 +26,21 @@ cd OverSnitch
npm install
```
### 2. Configure environment
### 2. Configure
Create `.env.local` in the project root:
**Option A — Settings UI (recommended)**
Start the app and click the gear icon in the top-right corner. Enter your service URLs and API keys, hit **Test** to verify each connection, then **Save**.
```bash
npm run dev # or: npm run build && npm start
```
Settings are written to `data/settings.json` (gitignored).
**Option B — Environment variables**
Create `.env.local` in the project root. Values here are used as fallbacks when `data/settings.json` doesn't exist or doesn't contain an override.
```env
# Required
@@ -43,22 +57,38 @@ SONARR_API=your_sonarr_api_key
TAUTULLI_URL=http://tautulli:8181
TAUTULLI_API=your_tautulli_api_key
# Optional — Discord webhook for new-alert notifications
DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
# Optional — if your services use self-signed certs
# NODE_TLS_REJECT_UNAUTHORIZED=0
```
### 3. Run
---
```bash
npm run dev # development
npm run build && npm start # production
```
## Discord Notifications
When configured, OverSnitch posts a structured embed to your Discord channel whenever an alert is newly opened or reopens after being resolved. Already-open alerts refreshing their data do not re-notify.
Each embed is formatted by category:
| Category | Fields shown |
|---|---|
| Not Downloaded | Requested by · Approved N ago · Status |
| Incomplete Download | Requested by · Approved N ago · Downloaded X/Y episodes |
| Pending Approval | Requested by · Waiting N days |
| Ghost Requester | User · description |
| Low Watch Rate | User · Watch rate · Plays · Requests |
Configure the webhook URL in the Settings UI or via `DISCORD_WEBHOOK` in `.env.local`. Use the **Test** button to send a sample embed before saving.
---
## Alerts
Alerts are generated on every stats refresh and persisted in `data/alerts.json` (gitignored). They have two states — **Open** and **Closed** — and can be manually closed with a per-category cooldown, or auto-resolved when the underlying condition clears.
Alerts are generated on every stats refresh and persisted in `data/alerts.db` (SQLite, gitignored). They have two states — **Open** and **Closed** — and can be manually closed with a per-category cooldown, or auto-resolved when the underlying condition clears.
The alert detail page shows structured metadata (requesters, age, episode progress bars, watch-rate stats), direct links to the item in Radarr/Sonarr, a link to the Overseerr/Jellyseerr media page, and a comment thread for notes.
### Content alerts