feat(notifications): producers — backups, agent health, scans, upgrades (#49)

* feat(filters): dynamic filter framework — typed schema, URL state, FilterBar

Port of dflow's filter.utils pattern, adapted to Containr:

- lib/dynamic-filter.ts: FilterConfig schema (search/select/multiselect/
  boolean/date-range/status), FilterEngine with client-side apply,
  derived options with counts, active-filter tracking, removal.
- lib/use-dynamic-filter.ts: filter state lives in ?f.<key> search
  params — filtered views are shareable links. useFilterState exposes
  URL state before data loads (server-backed filters); useDynamicFilter
  adds the engine over fetched data.
- shared/components/FilterBar.tsx: renders a schema as compact controls
  + active-filter chips, matching design tokens.
- Applied: audit logs (server-backed — state in URL, params to API),
  builds (server-backed + client date-range), activity (severity to the
  server query, category/search on the fetched page).
- 8 unit tests for the engine.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(filters): erasableSyntaxOnly compat + BuildStatus cast

- Constructor parameter properties are banned under erasableSyntaxOnly —
  declare fields explicitly.
- listBuilds status param needs the BuildStatus type, not raw string.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat(notifications): producers round-out — backups, agent health, scans, upgrades

Four new emitters behind insertUserNotification:

- Backups: createBackupProcess notifies the database owner on
  completion (with size) and on failure (docker-down, snapshot error).
- Agent health: StartAgentHealthSweep marks agents offline after 3 min
  without heartbeat and back online on recovery — admins notified on
  each transition.
- Security scans: Scanner.OnScanComplete callback notifies the project
  owner with critical/high/total counts + score; scans with findings
  get a distinct title.
- Upgrades: daily check against the latest GitHub release; admins
  notified once per new tag (deduped via app_settings). Version stamped
  into the image via Dockerfile ARG + ldflags on tag builds; "dev"
  disables the check.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomáš DvořákandDevin authored and GitHub committed 2026-10-09 12:12:21 +02:00
1 parent d4a4cde1fd
commit 7547375cc2
8 files changed
+289 -3

No files matched your search

+11 -1
View File
@@ -54,6 +54,9 @@ type ScanSummary struct {
// Scanner handles security scanning operations
type Scanner struct {
db *database.DB
// OnScanComplete fires after a scan finishes (goroutine context) —
// wired by the api layer for notifications. Optional.
OnScanComplete func(scan *SecurityScan)
}
// NewScanner creates a new security scanner
@@ -115,8 +118,11 @@ func (s *Scanner) performScan(scan *SecurityScan) {
// Update scan with results
completedAt := time.Now()
scan.CompletedAt = &completedAt
scan.Summary = summary
scan.Status = "completed"
_, err := s.db.Exec(`
UPDATE security_scans
UPDATE security_scans
SET status = $1, completed_at = $2, summary = $3
WHERE id = $4
`, "completed", completedAt, summaryToJSON(summary), scan.ID)
@@ -137,6 +143,10 @@ func (s *Scanner) performScan(scan *SecurityScan) {
log.Printf("Failed to store vulnerability %s: %v", vuln.ID, err)
}
}
if s.OnScanComplete != nil {
s.OnScanComplete(scan)
}
}
// scanDependencies scans for known vulnerable dependencies