Skip to main content

Module notifications

Module notifications 

Source
Expand description

notifications.* method types — paginated history + ack + push for incoming notifications.

The notification lifecycle (SPEC §2.12.8 emergency example):

  1. Operator publishes via backend HTTP API → backend writes to NATS NOTIFICATIONS JetStream.
  2. Agent consumes the stream, fans out to connected clients via notifications.new push.
  3. User clicks “確認” → client sends notifications.ack → agent writes notifications_read KV (keyed by {pc_id}.{user_sid}.{notification_id}) AND publishes events.notifications.acked.{pc_id}.{user_sid}.{notification_id} so the SPA can show per-user confirmation status.
  4. Past notifications stay queryable via notifications.list — that’s the recovery path when the agent missed a push during a network blip.

Structs§

AudiencePc
One targeted PC’s confirmation state, for the detail page’s “who hasn’t confirmed” roster (④). Resolved by expanding the notification’s fan-out subjects (all / group.X / pc.Y) to the fleet’s PCs and joining against the recorded acks.
EditNotificationRequest
Operator-facing request body for PATCH /api/notifications/{id} — edit an already-sent notification’s content (fix a typo, shorten/extend the expiry, change priority / require_ack / toast) without re-sending it.
Notification
Notification body — used both for NotificationsListResult entries and the NotificationNewParams push.
NotificationAckEntry
One recipient’s confirmation record for a notification.
NotificationAckStatus
Response of GET /api/notifications/{id}/ack_status — every (pc_id, user_sid, acked_at) tuple recorded for the notification, powering the SPA’s “who confirmed when” view.
NotificationAcked
Body of the events.notifications.acked.{pc_id}.{user_sid}.{notif_id} event the agent publishes when a user acks a notification. The backend’s notification-acks projector reads these fields from the JSON body (not by parsing the subject) so an id / SID containing a . can’t desync the projected row from its subject tokens.
NotificationAmend
A post-send amendment to an already-fanned-out notification, broadcast fleet-wide on the ephemeral crate::subject::NOTIFICATIONS_AMEND_SUBJECT channel so every connected client showing the notification can react in real time. Carries only the notification id plus the operation — a client applies it only if it currently holds that id (an id it never received is a no-op), so the single broadcast needs no audience routing.
NotificationAmendedParams
Params of the notifications.amended push (Agent → Client) — the flattened NotificationAmend ({ "id", "kind": "recall" }).
NotificationDetail
Response of GET /api/notifications/{id} — one sent notification’s full content (so the SPA can show “what was sent”, including the body the history table truncates away) paired with its per-recipient confirmation list. Powers the deep-linkable /notifications/{id} detail page, which an operator opens in a new tab from the history list (Ctrl/⌘ click), mirroring the Activity → result-detail deep link.
NotificationNewParams
Push payload for notifications.new. The full notification body inline — no second round-trip needed.
NotificationTarget
The audience a notification was addressed to (the target: of the publish), reconstructed from its fan-out subjects (notifications.{all|group.X|pc.Y}). Distinct from the resolved per-PC AudiencePc roster: this is the operator’s intent (“sent to the it-admins group + PC minipc”), not the expanded PC list.
NotificationUnacked
Body of the events.notifications.unacked.{pc_id}.{user_sid}.{notif_id} event the agent publishes when a user retracts a confirmation. Mirror of NotificationAcked; the projector reads these body fields (not the subject) and, in the same stream-ordered consumer, appends a kind = 'unacked' row to notification_ack_events and stamps notification_acks.unacked_at so the SPA roster flips the recipient from confirmed back to “未確認” while the audit log keeps the original ack.
NotificationsAckParams
notifications.ack params — mark this notification read for the caller’s user (SID derived from the OS at connect time, NOT from the payload). SPEC §2.12.4 forbids ack-ing other users’ notifications even on a shared PC — the agent rejects with Unauthorized if the notification’s audience doesn’t include the caller.
NotificationsAckResult
notifications.ack response — confirms the agent persisted the ack and published the events.notifications.acked.> event.
NotificationsListParams
notifications.list params — paginated history of notifications this user has received (per-user, scoped via OS SID).
NotificationsListResult
notifications.list response.
NotificationsSubscribeParams
NotificationsSubscribeResult
NotificationsUnackParams
notifications.unack params — retract this user’s prior ack (the read↔unread toggle): the user clicked “確認” by mistake and wants the notification back as unread. Same SID-from-the-OS / audience guard as NotificationsAckParams; a user may only unack their own confirmation.
NotificationsUnackResult
notifications.unack response — confirms the agent deleted the notifications_read KV entry and published events.notifications.unacked.>. Carries the instant the revoke was recorded (the agent’s wall clock), so the operator’s audit view can show “confirmed at X, retracted at Y”.
NotificationsUnsubscribeParams
PublishNotificationRequest
Operator-facing request body for POST /api/notifications (and the equivalent notifications/*.yaml manifest, SPEC §2.4.1). The backend mints the Notification::id (when id is omitted) and Notification::issued_at, resolves target into the notifications.{all|group.X|pc.Y} fan-out subjects, and publishes one Notification per resolved subject into the NOTIFICATIONS stream.
PublishNotificationResponse
Response of POST /api/notifications — the minted/echoed id plus the subjects the notification fanned out to, so the operator UI can confirm the resolved audience.

Enums§

NotificationAmendOp
The operation an NotificationAmend applies. Tagged on kind so future data-carrying variants (e.g. Update { notification }) stay wire-compatible.
NotificationPriority
Severity ladder. Drives the SPA color, toast/dialog choice, and whether the Client App grabs window focus on push arrival. #[non_exhaustive] so a future SPEC can add severities (e.g. Critical above Emergency) without a wire bump.
NotificationsFilter
History-list filter selector.