156 lines
4.1 KiB
Markdown
156 lines
4.1 KiB
Markdown
# Notifications
|
||
|
||
Real-time notification infrastructure with WebSocket delivery and
|
||
optional email transport.
|
||
|
||
**Auth:** All endpoints require authentication (JWT via `sb_token`
|
||
cookie or `Authorization: Bearer` header).
|
||
|
||
### CRUD
|
||
|
||
```
|
||
GET /notifications → paginated list
|
||
GET /notifications/unread-count → { "count": 5 }
|
||
PATCH /notifications/:id/read → mark one as read
|
||
POST /notifications/mark-all-read → mark all as read
|
||
DELETE /notifications/:id
|
||
```
|
||
|
||
**`GET /notifications`** — paginated list for the authenticated user.
|
||
|
||
Query parameters:
|
||
|
||
| Param | Type | Default | Description |
|
||
|-------|------|---------|-------------|
|
||
| `limit` | int | 20 | 1–100, clamped |
|
||
| `offset` | int | 0 | Pagination offset |
|
||
| `unread_only` | bool | false | Filter to unread only |
|
||
|
||
Response:
|
||
|
||
```json
|
||
{
|
||
"data": [ ... ],
|
||
"total": 42,
|
||
"limit": 20,
|
||
"offset": 0
|
||
}
|
||
```
|
||
|
||
Notification object:
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"user_id": "uuid",
|
||
"type": "role.fallback",
|
||
"title": "Role Fallback Triggered",
|
||
"body": "Primary model unavailable, using fallback",
|
||
"resource_type": "channel",
|
||
"resource_id": "uuid",
|
||
"is_read": false,
|
||
"created_at": "2025-01-15T12:00:00Z"
|
||
}
|
||
```
|
||
|
||
`resource_type` and `resource_id` are optional — used for
|
||
click-to-navigate. The resource may be deleted (no FK constraint).
|
||
|
||
### Preferences
|
||
|
||
Users control per-type delivery:
|
||
|
||
```
|
||
GET /notifications/preferences → { "data": [...] }
|
||
PUT /notifications/preferences/:type ← { "in_app": true, "email": false }
|
||
DELETE /notifications/preferences/:type → reset to default
|
||
```
|
||
|
||
**`GET /notifications/preferences`** returns all preferences set by
|
||
the user. Empty array if none are configured.
|
||
|
||
**`PUT /notifications/preferences/:type`** creates or updates a
|
||
preference. Both `in_app` and `email` fields are optional (merges
|
||
with existing values if set).
|
||
|
||
**`DELETE /notifications/preferences/:type`** removes the preference,
|
||
falling back to the next level in the resolution chain. Idempotent —
|
||
deleting a non-existent preference returns 200.
|
||
|
||
Resolution: specific type pref → user wildcard `*` pref → system
|
||
default (`in_app=true`, `email=false`).
|
||
|
||
Returns 503 if the preference store is not available (unmanaged mode).
|
||
|
||
### Admin
|
||
|
||
```
|
||
POST /admin/notifications/test-email → send test email to requesting admin
|
||
```
|
||
|
||
Requires admin role. Loads SMTP config from platform settings,
|
||
sends a test message to the admin's registered email address.
|
||
|
||
### Admin Broadcast (v0.28.6)
|
||
|
||
```
|
||
POST /admin/notifications/broadcast
|
||
```
|
||
|
||
**Auth:** Admin only.
|
||
|
||
Sends a `system.announcement` notification to all active users via
|
||
`NotifyMany`. Also emits a `system.broadcast` WebSocket event for
|
||
real-time visibility.
|
||
|
||
**Request body:**
|
||
|
||
```json
|
||
{
|
||
"title": "Scheduled Maintenance",
|
||
"message": "Servers will be down from 2-4 AM EST.",
|
||
"level": "warning"
|
||
}
|
||
```
|
||
|
||
| Field | Type | Required | Description |
|
||
|-------|------|----------|-------------|
|
||
| `title` | string | yes | Short summary shown in bell dropdown |
|
||
| `message` | string | yes | Detail text |
|
||
| `level` | string | no | `info` (default), `warning`, or `critical` |
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{ "message": "broadcast sent", "count": 42 }
|
||
```
|
||
|
||
`count` is the number of active users notified.
|
||
|
||
### WebSocket Events
|
||
|
||
| Event | Payload | Description |
|
||
|-------|---------|-------------|
|
||
| `notification.new` | Full notification object | New notification created |
|
||
| `notification.read` | `{"id": "uuid"}` | Single notification marked read |
|
||
| `notification.read` | `{"action": "mark_all_read"}` | All notifications marked read |
|
||
| `system.broadcast` | `{"title", "message", "level"}` | Admin announcement (v0.28.6) |
|
||
|
||
These are targeted via `Hub.SendToUser` — no room subscription
|
||
required. Used for badge sync across tabs.
|
||
|
||
### Notification Types
|
||
|
||
See [enums.md](enums.md#notification-types) for the canonical list.
|
||
Types are free-form strings (`domain.action` convention) — new types
|
||
don't require migration. Core types include `system.announcement`
|
||
(admin broadcast, v0.28.6).
|
||
|
||
### Retention
|
||
|
||
Background cleanup prunes notifications older than 90 days (default,
|
||
configurable via `WithRetention`). Runs daily after a 5-minute
|
||
startup delay.
|
||
|
||
---
|