Skip to content

complete scheduled email digest reports (#154)#278

Merged
Polliog merged 6 commits into
developfrom
feat/digest-completion-154
Jul 15, 2026
Merged

complete scheduled email digest reports (#154)#278
Polliog merged 6 commits into
developfrom
feat/digest-completion-154

Conversation

@Polliog

@Polliog Polliog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Finishes the digest feature whose foundation landed in #209 and shipped disabled since 1.0.0-beta. Closes #154.

What changed

Scheduling redesign. Per-org cron jobs registered at worker boot are replaced by a single static hourly digest-dispatch cron that sweeps enabled digest_configs, matches delivery_hour/delivery_day_of_week against the current UTC hour and enqueues digest-generation jobs. graphile-worker cron items are fixed once the runner starts, so per-org registration could never pick up config changes at runtime; the sweep keeps config CRUD live on both queue backends. Hour-keyed dedup jobKeys plus a last_sent_at double-fire guard (migration 053).

Report sections. The generator grows from log volume only to five sections, each with a quiet empty state:

  • log volume with period-over-period trend (unchanged)
  • top 5 services by error+critical count with delta vs the previous period, engine-agnostic via reservoir.topValues
  • new error groups first seen in the period (error_groups.first_seen)
  • security summary: windowed detection totals and top triggered Sigma rules from raw detection_events (avoids continuous-aggregate staleness on the most recent day), plus open/investigating incident count
  • monitor uptime aggregated from monitor_uptime_daily, omitted for orgs without monitors

HTML email. generateDigestEmail joins the shared email-templates.ts component library (inline CSS, escaped user-controlled strings, en-US number formatting) with the full report duplicated in the plaintext fallback. The base template footer gains an unsubscribeUrl variant so digest recipients, who may have no account, get a one-click unsubscribe instead of the channel-settings link.

Management API. Session-auth CRUD under /api/v1/digests: config upsert/delete, recipient add/remove/resubscribe (resubscribe rotates the token so old email links cannot flip the state back). Membership for reads, owner/admin for writes, audit-logged as digest.*, recipients capped by a new digests.max_recipients capability (default unlimited) with the canonical 4-case tests.

Public unsubscribe. POST /api/v1/digests/unsubscribe: the 32-byte random token is the credential, idempotent, returns a masked email. Consumed by the new frontend /unsubscribe page linked from the email footer.

Frontend. New "Email Digests" settings page (schedule card with UTC delivery time, frequency and day-of-week; recipient management with subscribe state badges), nav entry under Notifications.

Worker. The 1.0.0-beta disable is removed; the dispatch cron registers at boot, so digests go out once this merges.

Testing

  • Full backend suite green: 4735 tests / 270 files
  • New tests: dispatch due-check + enqueue, generator section units, integration test seeding error groups / detections / incidents / monitor results end to end, digest email template suite, routes CRUD + isolation + public unsubscribe, capability 4-case
  • check:tenant-scoping green (new sweep/token sites carry tenant-scope-ok markers), tsc --noEmit clean, svelte-check has no new errors, frontend unit suite green

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.37570% with 50 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/backend/src/modules/digests/routes.ts 80.60% 45 Missing ⚠️
packages/backend/src/modules/digests/index.ts 0.00% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Polliog
Polliog merged commit 8478d3b into develop Jul 15, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant