|
|
@@ -1,69 +1,28 @@
|
|
|
|
# v0.4.0 (2026-04-23)
|
|
|
|
# v0.5.0 (2026-04-24)
|
|
|
|
|
|
|
|
|
|
|
|
A production-readiness release focused on hardening the service for real-world deployment: end-to-end structured logging with runtime controls, a broad security and runtime review across the HTTP, auth, DB, and scheduler layers, and a new pre-migration database snapshot that makes upgrades recoverable with a single file restore. Release CI and the Docker image build were also reworked for speed and reliability.
|
|
|
|
A small but impactful release that finally makes the Immich scheduled / periodic / memory dispatch fire on its own. The slot was already visible in the tracker UI and the "Test" button worked — but no production scheduler was reading the config, so users only ever saw fires through manual tests. This release wires the missing cron jobs end-to-end.
|
|
|
|
|
|
|
|
|
|
|
|
## Features
|
|
|
|
## Features
|
|
|
|
|
|
|
|
|
|
|
|
- **Production-grade logging** with per-request correlation (`request_id` / `command` / `chat_id` / `bot_id` / `dispatch_id`), secret masking in both messages and tracebacks, JSON or text format, runtime log level + per-module overrides editable from the settings UI, and env-var boot overrides (`NOTIFY_BRIDGE_LOG_LEVEL` / `_FORMAT` / `_LEVELS`). Closes every silent drop in the Telegram send path — `/random` and media-group failures now log `WARN` / `ERROR` with full context instead of disappearing ([f50d465](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/f50d465))
|
|
|
|
- **Cron-fired Immich dispatch for scheduled / periodic / memory slots** — adds the missing production fan-out so `scheduled_enabled` / `scheduled_times` (and the periodic / memory counterparts) on `TrackingConfig` actually fire on their own, not only through "Test" ([309dec2](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/309dec2)):
|
|
|
|
- **Production-readiness hardening across security, async, DB, and ops** ([920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b)):
|
|
|
|
- New `services/scheduled_dispatch.py` reuses the test-path event builders, picks the slot template per kind (`scheduled_assets` / `periodic_assets` / `memory_assets`), and writes an `EventLog` row per fire so the dashboard reflects it.
|
|
|
|
- *Security:* async SSRF-safe DNS resolver; `allow_redirects=False` on all outbound clients; Matrix `homeserver_url` validation; rejection of `***`-masked secrets on provider / email-bot updates; bcrypt moved off the event loop; JWT `iss` / `aud` + leeway with strict claim rejection; setup TOCTOU closed inside a transaction; expanded rate limits; constant-time login; config rejects known dev secret keys and validates CORS / ports / token lifetimes; webhook bodies capped at 1 MiB; Discord 429 retries bounded; CSP + HSTS headers added.
|
|
|
|
- `services/scheduler.py` gains `_load_immich_dispatch_jobs`, which builds one `CronTrigger` per `(tracker, kind, HH:MM)` from each tracker's default `TrackingConfig`, all keyed off the app-level IANA timezone. `reschedule_immich_dispatch_jobs` rebuilds the job set on any relevant CRUD or timezone change.
|
|
|
|
- *Async / runtime:* SQLite engine tuned (WAL, `synchronous=NORMAL`, `foreign_keys=ON`, busy timeout, pool pre-ping); ordered lifespan shutdown; shared `aiohttp` session race-free; blocking storage / backup writes offloaded to threads; NUT client timeouts; Telegram poller switched from 3 s short-poll to 30 s + 25 s long-poll (~10x fewer API calls).
|
|
|
|
- Tracker / link / tracking-config CRUD endpoints now invalidate the schedule, so edits take effect immediately without a restart.
|
|
|
|
- *Database:* new performance-index migration covering every FK and hot-path composite; new `schema_version` table; `__system__` placeholder user (`id=0`) seeded to satisfy FKs; `list_notification_trackers` rewritten from `1+N+N*M` to batched loads; retention job extended to event / webhook / action-execution logs.
|
|
|
|
- Dispatch is skipped when scheduled / memory queries yield zero matching assets — prevents header-only "On this day:" spam when nothing qualifies.
|
|
|
|
- *Scheduler:* `AsyncIOScheduler` job defaults set (`coalesce`, `misfire_grace_time=300`, `max_instances=1`).
|
|
|
|
- EN / RU default `scheduled_assets` templates updated to surface that the delivery is a scheduled random selection.
|
|
|
|
- *Ops:* uvicorn runs with `proxy_headers` / `forwarded_allow_ips` / graceful shutdown timeout; access log suppressed outside debug; FastAPI version read from `importlib.metadata`; new `/api/ready` endpoint; docker-compose adds resource / PID limits, `read_only` + tmpfs, `cap_drop: ALL`, `no-new-privileges`, drops the `ALLOW_PRIVATE_URLS=1` default, and points healthcheck at `/api/ready`.
|
|
|
|
|
|
|
|
- *Frontend:* `/login` redirects already-authenticated users to `/` and shows a distinct "backend unreachable" banner (en / ru) when `/auth/needs-setup` fails.
|
|
|
|
|
|
|
|
- **Pre-migration SQLite snapshots** via `VACUUM INTO` at lifespan startup — takes a consistent, atomic copy of the DB before migrations run, so a botched upgrade is recoverable by restoring a single file. Safe under WAL; best-effort (failures log but never raise); configurable via `NOTIFY_BRIDGE_PRE_MIGRATE_SNAPSHOT_KEEP` (default 5; 0 disables). Snapshots land in `data_dir/backups/pre-migrate-<ts>.db` and the N oldest are pruned each boot ([7cbb02b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/7cbb02b))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Bug Fixes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Allow `unsafe-inline` scripts in CSP so SvelteKit's hydration bootstrap inline `<script>` runs in production — without it the frontend failed to hydrate under the hardened CSP introduced in this release ([8f0346e](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/8f0346e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Upgrade Notes
|
|
|
|
## Upgrade Notes
|
|
|
|
|
|
|
|
|
|
|
|
- `ALLOW_PRIVATE_URLS=1` is no longer set by default in `docker-compose.yml`. If your deployment targets private network URLs, set it explicitly.
|
|
|
|
- No config changes required. Existing `scheduled_enabled` / `scheduled_times` / `periodic_*` / `memory_*` settings on tracking configs will start firing automatically on the next startup.
|
|
|
|
- Docker healthchecks now probe `/api/ready` (separate from `/api/health`); update any external monitors accordingly.
|
|
|
|
- If you had been relying on the "Test" button as a workaround, you can stop doing that now.
|
|
|
|
- Config startup now rejects known dev secret keys — set real values (e.g. `JWT_SECRET`) before upgrading.
|
|
|
|
|
|
|
|
- Log format and level can now be changed at runtime from the settings UI; the `log_format` field still requires a restart to apply (a `WARN` is logged noting this).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Development / Internal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Tests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- New `packages/server/tests/` suite with 29 passing tests: config validation; JWT round-trip and `aud` / `alg=none` rejection; SSRF scheme and private-range enforcement (sync + async); Discord bounded retry; a lifespan-level `/api/health` + `/api/ready` smoke check. `services/test_dispatch.py` renamed to `manual_dispatch.py` so pytest no longer auto-collects production code ([920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### CI / Build
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- CI now runs on push / PR with frontend `svelte-check` + build, and a non-push image build. Release workflow is gated on tests and publishes an immutable `sha-<commit>` image tag ([920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b))
|
|
|
|
|
|
|
|
- Install editable packages inside a venv ([2bec253](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2bec253))
|
|
|
|
|
|
|
|
- Cache pip downloads and collapse install into a single pip call ([3b683ce](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/3b683ce))
|
|
|
|
|
|
|
|
- Drop backend pytest from Gitea CI — editable install is too slow on the hosted runner ([f904037](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/f904037))
|
|
|
|
|
|
|
|
- Skip `build.yml` on release commits to avoid redundant runs ([bbcdf1c](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/bbcdf1c))
|
|
|
|
|
|
|
|
- Drop Trivy scan from release (output was discarded and it never failed) ([19036a9](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/19036a9))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Performance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Split external Docker deps into a cacheable layer and swap pip for uv for faster image builds ([592e1b6](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/592e1b6))
|
|
|
|
|
|
|
|
- Install uv from PyPI instead of the ghcr.io distroless image to avoid slow GHCR pulls on CI ([a6a854a](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/a6a854a))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
<details>
|
|
|
|
<details>
|
|
|
|
<summary>All Commits</summary>
|
|
|
|
<summary>All Commits</summary>
|
|
|
|
|
|
|
|
|
|
|
|
| Hash | Message | Author |
|
|
|
|
| Hash | Message | Author |
|
|
|
|
| ---- | ------- | ------ |
|
|
|
|
|------------------------------------------------------------------------------------------|------------------------------------------------------------------|------------------|
|
|
|
|
| [8f0346e](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/8f0346e) | fix(csp): allow unsafe-inline scripts for SvelteKit hydration bootstrap | alexei.dolgolyov |
|
|
|
|
| [309dec2](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/309dec2) | feat(immich): wire cron-fired scheduled/periodic/memory dispatch | alexei.dolgolyov |
|
|
|
|
| [a6a854a](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/a6a854a) | perf(docker): install uv from PyPI instead of ghcr.io (avoid slow GHCR pulls) | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [19036a9](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/19036a9) | ci: drop trivy scan from release (never failed, output discarded) | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [592e1b6](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/592e1b6) | perf(docker): split external deps into a cacheable layer, swap pip for uv | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [bbcdf1c](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/bbcdf1c) | ci: skip build.yml on release commits | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [f904037](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/f904037) | ci: drop backend pytest stage (too slow on hosted runner) | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [3b683ce](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/3b683ce) | ci: cache pip downloads and collapse install into one pip call | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [2bec253](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/2bec253) | ci: install editable packages inside a venv | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [7cbb02b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/7cbb02b) | feat(db): pre-migration SQLite snapshots via VACUUM INTO | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b) | feat: production-readiness hardening across security, async, DB, ops | alexei.dolgolyov |
|
|
|
|
|
|
|
|
| [f50d465](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/f50d465) | feat(logging): production-grade logging with context vars, secret masking, and runtime level control | alexei.dolgolyov |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
</details>
|
|
|
|