chore: release v0.4.0
This commit is contained in:
+33
-16
@@ -1,28 +1,45 @@
|
||||
# v0.3.2 (2026-04-22)
|
||||
# v0.4.0 (2026-04-23)
|
||||
|
||||
Scheduler now honors the app-level timezone. Before this, a cron expression
|
||||
like `0 9 * * *` was firing at 09:00 in the server's host-local tz — not
|
||||
at 09:00 in the timezone the admin configured under Settings — because
|
||||
`CronTrigger.from_crontab` was constructed without a tz. Same fix extends
|
||||
to scheduler-provider template rendering so `{{ current_date }}` / `{{ current_time }}`
|
||||
match the configured tz, and scheduled firings now show up in the dashboard
|
||||
event feed with context.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Cron triggers honor app timezone** — all tracker and action cron triggers are now built with the configured app tz; `CronTrigger` freezes its tz at construction, so the `PUT /settings` endpoint rebuilds existing cron jobs when the timezone changes. Scheduled messages that were silently firing at host-local time will fire at the intended time after upgrade. ([1024085](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/1024085))
|
||||
- **Scheduler template context renders in the app tz** — `current_date`, `current_time`, `current_datetime`, `current_weekday` in scheduler-provider templates are now formatted in the configured timezone instead of UTC/host-local. Custom templates that built date strings in the wrong tz now render correctly. ([1024085](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/1024085))
|
||||
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.
|
||||
|
||||
## Features
|
||||
|
||||
- **New `timezone` template variable** — scheduler-provider templates can reference `{{ timezone }}` to display the active IANA tz alongside a date/time. Added across the context builder, variable catalog, sample context, and runtime validator (per the project's 6-file sync rule for template vars). ([1024085](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/1024085))
|
||||
- **`scheduled_message` events surface in the dashboard feed** — `EventLog` entries for scheduled firings now carry `schedule_type`, `cron_expression` / `interval_seconds`, `timezone`, and `fire_count`; the dashboard renders them with a dedicated label, icon, and colour so operators can see at a glance when scheduled messages actually fired. ([1024085](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/1024085))
|
||||
- **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))
|
||||
- **Production-readiness hardening across security, async, DB, and ops** ([920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b)):
|
||||
- *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.
|
||||
- *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).
|
||||
- *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.
|
||||
- *Scheduler:* `AsyncIOScheduler` job defaults set (`coalesce`, `misfire_grace_time=300`, `max_instances=1`).
|
||||
- *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))
|
||||
|
||||
## 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.
|
||||
- Docker healthchecks now probe `/api/ready` (separate from `/api/health`); update any external monitors accordingly.
|
||||
- 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 backend pytest, frontend `svelte-check` + build, and a non-push image build. Release workflow is gated on tests, publishes an immutable `sha-<commit>` image tag, and adds a Trivy scan ([920920b](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/920920b))
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>All Commits</summary>
|
||||
|
||||
- [1024085](https://git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge/commit/1024085) — fix(scheduler): honor app timezone for cron triggers and log scheduled events *(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>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "notify-bridge-frontend",
|
||||
"private": true,
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "notify-bridge-core"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
description = "Core library for Notify Bridge — service provider abstractions, models, notifications, and templates"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "notify-bridge-server"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
description = "Standalone Notify Bridge server — FastAPI REST API with SQLite database"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
|
||||
Reference in New Issue
Block a user