feat(notifications): persistent read cursor + delivery history

notification_cursors table stores one read-cursor per user per tenant;
GET /v1/activity returns unreadCount + readAt, POST /v1/activity/read
persists the cursor. Reminder dispatches now append reminder.sent /
reminder.failed system events so delivery history reaches the feed.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 16:55:21 +02:00
1 parent 76e45d4178
commit 2464aa45fa
14 files changed
+332 -8

No files matched your search

@@ -35,6 +35,26 @@ export interface paths {
patch?: never;
trace?: never;
};
"/v1/activity/read": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Mark all activity events as read
* @description Persists the caller's read cursor — activity events at or before the returned timestamp count as read for this user on subsequent loads.
*/
post: operations["markActivityRead"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/v1/admin/stats": {
parameters: {
query?: never;
@@ -1161,6 +1181,13 @@ export interface components {
};
ActivityFeedResponse: {
events: components["schemas"]["ActivityEvent"][];
/**
* Format: date-time
* @description Caller's read cursor; absent until the first mark-read
*/
readAt?: string;
/** @description Events newer than the caller's persisted read cursor */
unreadCount: number;
};
AuthResponse: {
accessToken: string;
@@ -1832,6 +1859,36 @@ export interface operations {
};
};
};
markActivityRead: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Read cursor persisted */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
/** Format: date-time */
readAt: string;
};
};
};
/** @description Tenant membership not found */
404: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
getAdminStats: {
parameters: {
query?: never;