feat(create-a-container): notification queue + inbound webhook (#434) - #442
Draft
runleveldev wants to merge 3 commits into
Draft
feat(create-a-container): notification queue + inbound webhook (#434)#442runleveldev wants to merge 3 commits into
runleveldev wants to merge 3 commits into
Conversation
Adds a decoupled notification mechanism so node-side tools (e.g. lxc-oomd)
can report events to the manager without hard-coupling detection to email.
- Notifications model + migration (source, severity, node, ctid, owner,
action, message, evidence JSON, eventAt, ack fields).
- resources/notifications/ (layered per the MVC manifesto):
- POST /api/v1/notifications: inbound webhook, admin-API-key auth. Coerces
ts->eventAt and ctid->string; resolves owner from node+ctid when omitted.
- GET /api/v1/notifications: owner-scoped, unacked-first + newest-first.
- POST /notifications/all/ack and /:id/ack: owner-scoped acknowledge.
- UI: notification bell (replaces the unused header search button) with an
unread badge and an ack-capable dropdown; polls every 30s.
- OpenAPI paths/schema + docs/notification-webhook.md contract for the
lxc-oomd hook script.
- Tests: 24 (18 API + 6 service).
Part of mieweb#431. Closes mieweb#434.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a decoupled notifications mechanism to the create-a-container manager, allowing node-side tools to POST structured events via an inbound webhook that are then persisted and surfaced per-owner in the web UI.
Changes:
- Introduces a new
notificationsresource (router/controller/service/repository/serializer/validator) with ingest, list, and acknowledge endpoints. - Adds
Notificationpersistence (Sequelize model + migration) and documents the webhook contract + OpenAPI paths/schema. - Updates the client to display an owner-scoped notification bell with unread badge and ack actions.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| create-a-container/routers/api/v1/index.js | Registers the notifications router under /api/v1/notifications. |
| create-a-container/resources/notifications/validator.js | Defines Zod schemas for ingest payload, list query, and id param coercion. |
| create-a-container/resources/notifications/service.js | Implements ingest (owner resolution + ts→Date) and ack/list service logic. |
| create-a-container/resources/notifications/serializer.js | Serializes Notification rows to API JSON shape. |
| create-a-container/resources/notifications/router.js | Wires routes + auth (admin ingest; owner-scoped list/ack). |
| create-a-container/resources/notifications/repository.js | Adds DB access helpers for list/create/ack and owner resolution. |
| create-a-container/resources/notifications/controller.js | Implements HTTP handlers for create/list/ack endpoints. |
| create-a-container/resources/notifications/tests/service.test.js | Unit tests for ingest coercions and ack-all behavior. |
| create-a-container/resources/notifications/tests/notifications.api.test.js | Integration tests for auth, persistence, listing, and ack flows. |
| create-a-container/openapi.v1.yaml | Documents Notification schema and notification endpoints. |
| create-a-container/models/notification.js | Defines the Notification Sequelize model and indexes. |
| create-a-container/migrations/20260731120000-create-notifications.js | Adds Notifications table + indexes via migration. |
| create-a-container/docs/notification-webhook.md | Documents webhook contract and curl example for node-side hook scripts. |
| create-a-container/client/src/lib/types.ts | Adds client types for notifications (avoids DOM Notification clash). |
| create-a-container/client/src/lib/queries.ts | Adds notification list/ack query helpers and query keys. |
| create-a-container/client/src/app/NotificationsBell.tsx | Implements the polling bell dropdown UI with per-item and bulk ack. |
| create-a-container/client/src/app/Header.tsx | Replaces unused header search action with the NotificationsBell. |
| order: [ | ||
| // acknowledgedAt IS NULL sorts before non-null in both Postgres and | ||
| // SQLite once expressed as a boolean (false < true). | ||
| [require('sequelize').literal('"acknowledgedAt" IS NOT NULL'), 'ASC'], |
Comment on lines
+49
to
+53
| function NotificationRow({ | ||
| n, | ||
| onAck, | ||
| acking, | ||
| }: { |
…ive time Replace the hand-rolled relative-time helper in the notification bell with the existing formatLastSeen formatter from @mieweb/ui (the project's UI/util library), rather than maintaining bespoke date math.
… the bell Replace the hand-rolled row/list/empty/loading markup in the notification bell with the shared @mieweb/ui NotificationCenter component, kept inside the Dropdown for the trigger + unread badge. Adapts AppNotification to the component's Notification shape (severity -> type/priority, a source/node/CT title) and wires onMarkRead/onMarkAllRead to the ack endpoints. Verified end-to-end in a browser: badge count, mark-one, and mark-all-read all update correctly.
runleveldev
marked this pull request as draft
August 3, 2026 18:51
runleveldev
marked this pull request as draft
August 3, 2026 18:51
runleveldev
marked this pull request as draft
August 3, 2026 18:51
runleveldev
marked this pull request as draft
August 3, 2026 18:51
runleveldev
marked this pull request as draft
August 3, 2026 18:51
runleveldev
marked this pull request as draft
August 3, 2026 18:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #434. Part of #431 (memory-exhaustion thrash); the integration target for the
lxc-oomdpackaged hook script.What
A decoupled notification mechanism in the
create-a-containermanager: node-side tools POST a structured event; the web app surfaces it per owner.Deliverables
POST /api/v1/notifications— admin-API-key authenticated (rejects unauthenticated401/ non-admin403). Mirrors the agent check-in auth precedent.NotificationsSequelize model + migration (source,severity,node,ctid,owner,action,message,evidenceJSON,eventAt, ack fields; owner/ack + createdAt indexes). Migrate up/down verified against SQLite.resources/notifications/, perdocs/mvc-manifesto.md): validator/repository/service/serializer/controller/router.GET /api/v1/notifications— owner-scoped, unacked-first + newest-first (badge derives from the response).POST /api/v1/notifications/all/ackandPOST /:id/ack— owner-scoped acknowledge (idempotent;404on foreign).all/ackregistered before:id/ack.Notificationschema, anddocs/notification-webhook.mddocumenting the payload contract + curl example for the hook script.Payload contract
{ "source": "lxc-oomd", "severity": "warning|critical", "node": "opensource-phxdc-pve1", "ctid": 392, "owner": "mbachelder", "action": "freeze|kill|bump|quarantine|detect", "message": "CT 392 frozen: memory PSI full avg10=83 for 45s", "evidence": { "psiFullAvg10": 83.6 }, "ts": 1771234560 }ts(epoch seconds) is recorded aseventAt;ctidaccepts int/string; whenowneris omitted it is resolved best-effort fromnode+ctidvia the Containers table.Acceptance criteria
utils/email.jscall.Tests
24 new (18 API + 6 service): auth (401/403), persistence, coercions (
ts,ctid), owner resolution + null fallback, owner-scoped listing, ack flows.clienttsc+vite buildpass.Notes
middlewares/__tests__/mcp-proxy.test.js) and a pre-existing multi-suite SQLite teardown crash exist onmain, independent of this change.