Changeset 0.28.2 (#183)

This commit is contained in:
2026-03-13 12:19:55 +00:00
parent 9b9e2eb756
commit f94243fbf3
13 changed files with 1070 additions and 21 deletions

View File

@@ -3,6 +3,9 @@
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
```
@@ -13,32 +16,102 @@ 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 | 1100, 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|memory.extracted|system.announcement|...",
"type": "role.fallback",
"title": "Role Fallback Triggered",
"body": "Primary model unavailable, using fallback",
"metadata": {},
"read": false,
"created_at": "..."
"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 → { "preferences": [...] }
PUT /notifications/preferences/:type ← { "in_app": true, "email": false }
DELETE /notifications/preferences/:type → reset to default
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.
### 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 |
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.
### Retention
Background cleanup prunes notifications older than 90 days (default,
configurable via `WithRetention`). Runs daily after a 5-minute
startup delay.
---