Skip to content

feat(create-a-container): notification queue + inbound webhook (#434) - #442

Draft
runleveldev wants to merge 3 commits into
mieweb:mainfrom
runleveldev:feat/434-notification-queue
Draft

feat(create-a-container): notification queue + inbound webhook (#434)#442
runleveldev wants to merge 3 commits into
mieweb:mainfrom
runleveldev:feat/434-notification-queue

Conversation

@runleveldev

Copy link
Copy Markdown
Collaborator

Closes #434. Part of #431 (memory-exhaustion thrash); the integration target for the lxc-oomd packaged hook script.

What

A decoupled notification mechanism in the create-a-container manager: node-side tools POST a structured event; the web app surfaces it per owner.

Deliverables

  • Inbound webhook POST /api/v1/notifications — admin-API-key authenticated (rejects unauthenticated 401 / non-admin 403). Mirrors the agent check-in auth precedent.
  • PersistenceNotifications Sequelize model + migration (source, severity, node, ctid, owner, action, message, evidence JSON, eventAt, ack fields; owner/ack + createdAt indexes). Migrate up/down verified against SQLite.
  • Layered resource (resources/notifications/, per docs/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/ack and POST /:id/ack — owner-scoped acknowledge (idempotent; 404 on foreign). all/ack registered before :id/ack.
  • UI surface — a notification bell replacing the unused header search button, with an unread badge and an ack-capable dropdown (severity, related node/CT, relative time, per-item ack + "mark all as read"). Polls every 30s.
  • Contract docs — OpenAPI paths + Notification schema, and docs/notification-webhook.md documenting 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 as eventAt; ctid accepts int/string; when owner is omitted it is resolved best-effort from node+ctid via the Containers table.

Acceptance criteria

  • Webhook accepts and persists the payload; rejects unauthenticated calls.
  • Events appear in the UI with owner/container/severity and can be acked.
  • Contract documented so the lxc-oomd hook script can target it.
  • Email sink deferred — the issue lists it as optional; deferred per maintainer direction. Easy to add later as a post-commit utils/email.js call.

Tests

24 new (18 API + 6 service): auth (401/403), persistence, coercions (ts, ctid), owner resolution + null fallback, owner-scoped listing, ack flows. client tsc + vite build pass.

Notes

  • Owner-only visibility: events with an unresolved owner persist but surface in no bell — documented as a known limitation in the webhook doc.
  • One pre-existing, unrelated test failure (middlewares/__tests__/mcp-proxy.test.js) and a pre-existing multi-suite SQLite teardown crash exist on main, independent of this change.

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.
Copilot AI review requested due to automatic review settings August 3, 2026 17:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 notifications resource (router/controller/service/repository/serializer/validator) with ingest, list, and acknowledge endpoints.
  • Adds Notification persistence (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
runleveldev marked this pull request as draft August 3, 2026 18:51
@runleveldev
runleveldev marked this pull request as draft August 3, 2026 18:51
@runleveldev
runleveldev marked this pull request as draft August 3, 2026 18:51
@runleveldev
runleveldev marked this pull request as draft August 3, 2026 18:51
@runleveldev
runleveldev marked this pull request as draft August 3, 2026 18:51
@runleveldev
runleveldev marked this pull request as draft August 3, 2026 18:51
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.

Notification queue + inbound webhook in create-a-container

2 participants