@rpcbase/server 0.594.0 → 0.595.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/convertHeifToWebp-BT1n72DQ.js +148 -0
- package/dist/convertHeifToWebp-BT1n72DQ.js.map +1 -0
- package/dist/email-Co1GNjlT.js +7204 -0
- package/dist/email-Co1GNjlT.js.map +1 -0
- package/dist/handler-BqlcQ9sE.js +245 -0
- package/dist/handler-BqlcQ9sE.js.map +1 -0
- package/dist/handler-C92_gJkX.js +743 -0
- package/dist/handler-C92_gJkX.js.map +1 -0
- package/dist/handler-CapJTGzc.js +795 -0
- package/dist/handler-CapJTGzc.js.map +1 -0
- package/dist/handler-Fk-5fOSm.js +119 -0
- package/dist/handler-Fk-5fOSm.js.map +1 -0
- package/dist/htmlTemplate.d.ts +16 -0
- package/dist/htmlTemplate.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5140 -5286
- package/dist/index.js.map +1 -1
- package/dist/notifications.js +273 -382
- package/dist/notifications.js.map +1 -1
- package/dist/postProcessors-iffHvje4.js +89 -0
- package/dist/postProcessors-iffHvje4.js.map +1 -0
- package/dist/queryWindow-DP8zD0lb.js +555 -0
- package/dist/queryWindow-DP8zD0lb.js.map +1 -0
- package/dist/render_resend-B1SSQ4f7.js +7 -0
- package/dist/render_resend-B1SSQ4f7.js.map +1 -0
- package/dist/rts/index.js +990 -1207
- package/dist/rts/index.js.map +1 -1
- package/dist/schemas-B2fUvgYo.js +4151 -0
- package/dist/schemas-B2fUvgYo.js.map +1 -0
- package/dist/shared-DfrVDSp0.js +79 -0
- package/dist/shared-DfrVDSp0.js.map +1 -0
- package/dist/ssrMiddleware.d.ts +7 -3
- package/dist/ssrMiddleware.d.ts.map +1 -1
- package/dist/uploads/worker.js +139 -168
- package/dist/uploads/worker.js.map +1 -1
- package/dist/uploads.js +12 -22
- package/dist/uploads.js.map +1 -1
- package/package.json +1 -1
- package/dist/convertHeifToWebp-C-DGXZ2k.js +0 -169
- package/dist/convertHeifToWebp-C-DGXZ2k.js.map +0 -1
- package/dist/email-BCf24GmK.js +0 -8071
- package/dist/email-BCf24GmK.js.map +0 -1
- package/dist/handler-BqoKvylN.js +0 -147
- package/dist/handler-BqoKvylN.js.map +0 -1
- package/dist/handler-CUOJ51-w.js +0 -903
- package/dist/handler-CUOJ51-w.js.map +0 -1
- package/dist/handler-D-XdgeG_.js +0 -986
- package/dist/handler-D-XdgeG_.js.map +0 -1
- package/dist/handler-F0gFTzvh.js +0 -275
- package/dist/handler-F0gFTzvh.js.map +0 -1
- package/dist/postProcessors-D27fGZP0.js +0 -107
- package/dist/postProcessors-D27fGZP0.js.map +0 -1
- package/dist/queryWindow-Cdr7K-S1.js +0 -714
- package/dist/queryWindow-Cdr7K-S1.js.map +0 -1
- package/dist/render_resend_false-MiC__Smr.js +0 -6
- package/dist/render_resend_false-MiC__Smr.js.map +0 -1
- package/dist/schemas-Drf83ni9.js +0 -4517
- package/dist/schemas-Drf83ni9.js.map +0 -1
- package/dist/shared-CJrm9Wjp.js +0 -104
- package/dist/shared-CJrm9Wjp.js.map +0 -1
package/dist/handler-CUOJ51-w.js
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
import { models } from "@rpcbase/db";
|
|
2
|
-
import { buildAbilityFromSession, getAccessibleByQuery } from "@rpcbase/db/acl";
|
|
3
|
-
import { createNotification, sendNotificationsDigestForUser } from "./notifications.js";
|
|
4
|
-
import { o as object, b as boolean, n as number, a as array, s as string, r as record, _ as _enum, u as unknown } from "./schemas-Drf83ni9.js";
|
|
5
|
-
const getSessionUser = (ctx) => {
|
|
6
|
-
const rawSessionUser = ctx.req.session?.user;
|
|
7
|
-
const userId = typeof rawSessionUser?.id === "string" ? rawSessionUser.id.trim() : "";
|
|
8
|
-
const tenantId = typeof rawSessionUser?.currentTenantId === "string" ? rawSessionUser.currentTenantId.trim() : "";
|
|
9
|
-
if (!userId) {
|
|
10
|
-
ctx.res.status(401);
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
if (!tenantId) {
|
|
14
|
-
ctx.res.status(400);
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
userId,
|
|
19
|
-
tenantId
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
const ListRoute = "/api/rb/notifications";
|
|
23
|
-
const CreateRoute = "/api/rb/notifications/create";
|
|
24
|
-
const MarkReadRoute = "/api/rb/notifications/:notificationId/read";
|
|
25
|
-
const MarkReadByUrlRoute = "/api/rb/notifications/mark-read-by-url";
|
|
26
|
-
const ArchiveRoute = "/api/rb/notifications/:notificationId/archive";
|
|
27
|
-
const MarkAllReadRoute = "/api/rb/notifications/mark-all-read";
|
|
28
|
-
const SettingsRoute = "/api/rb/notifications/settings";
|
|
29
|
-
const DigestRunRoute = "/api/rb/notifications/digest/run";
|
|
30
|
-
const listRequestSchema = object({
|
|
31
|
-
includeArchived: boolean().optional(),
|
|
32
|
-
unreadOnly: boolean().optional(),
|
|
33
|
-
limit: number().int().min(1).max(200).optional(),
|
|
34
|
-
markSeen: boolean().optional()
|
|
35
|
-
});
|
|
36
|
-
const notificationDataSchema = record(string(), string());
|
|
37
|
-
const notificationPlatformPayloadSchema = object({
|
|
38
|
-
webpush: record(string(), unknown()).optional(),
|
|
39
|
-
fcmOptions: record(string(), unknown()).optional(),
|
|
40
|
-
android: record(string(), unknown()).optional(),
|
|
41
|
-
apns: record(string(), unknown()).optional()
|
|
42
|
-
}).passthrough();
|
|
43
|
-
const createRequestStringSchema = string().trim();
|
|
44
|
-
const notificationUrlModeSchema = _enum(["navigate", "merge_current_search"]);
|
|
45
|
-
const notificationContentFieldsSchema = object({
|
|
46
|
-
title: string().min(1),
|
|
47
|
-
body: string().optional(),
|
|
48
|
-
image: string().optional()
|
|
49
|
-
});
|
|
50
|
-
const createRequestSchema = object({
|
|
51
|
-
topic: createRequestStringSchema.min(1).optional(),
|
|
52
|
-
tag: createRequestStringSchema.min(1).optional(),
|
|
53
|
-
title: createRequestStringSchema.min(1),
|
|
54
|
-
body: createRequestStringSchema.optional(),
|
|
55
|
-
image: createRequestStringSchema.optional(),
|
|
56
|
-
url: createRequestStringSchema.min(1).optional(),
|
|
57
|
-
urlMode: notificationUrlModeSchema.optional(),
|
|
58
|
-
metadata: record(string(), unknown()).optional(),
|
|
59
|
-
data: notificationDataSchema.optional(),
|
|
60
|
-
platform: notificationPlatformPayloadSchema.optional()
|
|
61
|
-
});
|
|
62
|
-
const normalizeNotificationActionUrl = (url, origin) => {
|
|
63
|
-
const raw = url.trim();
|
|
64
|
-
if (!raw) return null;
|
|
65
|
-
try {
|
|
66
|
-
const base = new URL(origin);
|
|
67
|
-
const parsed = new URL(raw, base);
|
|
68
|
-
if (parsed.origin !== base.origin) return null;
|
|
69
|
-
return `${parsed.pathname}${parsed.search}`;
|
|
70
|
-
} catch {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const normalizeNotificationActionLink = (url, origin, urlMode = "navigate") => {
|
|
75
|
-
const raw = url.trim();
|
|
76
|
-
if (!raw) return null;
|
|
77
|
-
try {
|
|
78
|
-
const base = new URL(origin);
|
|
79
|
-
const parsed = new URL(raw, base);
|
|
80
|
-
if (parsed.origin !== base.origin) return null;
|
|
81
|
-
if (urlMode === "navigate") return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
82
|
-
return `${parsed.search}${parsed.hash}` || null;
|
|
83
|
-
} catch {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
const notificationSchema = object({
|
|
88
|
-
id: string(),
|
|
89
|
-
topic: string().optional(),
|
|
90
|
-
tag: string().optional(),
|
|
91
|
-
deliveryId: string().optional(),
|
|
92
|
-
title: notificationContentFieldsSchema.shape.title,
|
|
93
|
-
body: notificationContentFieldsSchema.shape.body,
|
|
94
|
-
image: notificationContentFieldsSchema.shape.image,
|
|
95
|
-
data: notificationDataSchema.optional(),
|
|
96
|
-
platform: notificationPlatformPayloadSchema.optional(),
|
|
97
|
-
createdAt: string(),
|
|
98
|
-
seenAt: string().optional(),
|
|
99
|
-
readAt: string().optional(),
|
|
100
|
-
archivedAt: string().optional()
|
|
101
|
-
});
|
|
102
|
-
const listResponseSchema = object({
|
|
103
|
-
ok: boolean(),
|
|
104
|
-
error: string().optional(),
|
|
105
|
-
notifications: array(notificationSchema).optional(),
|
|
106
|
-
unreadCount: number().int().min(0).optional(),
|
|
107
|
-
unseenCount: number().int().min(0).optional()
|
|
108
|
-
});
|
|
109
|
-
const createResponseSchema = object({
|
|
110
|
-
ok: boolean(),
|
|
111
|
-
error: string().optional(),
|
|
112
|
-
id: string().optional()
|
|
113
|
-
});
|
|
114
|
-
object({
|
|
115
|
-
ok: boolean(),
|
|
116
|
-
error: string().optional()
|
|
117
|
-
});
|
|
118
|
-
const markReadByUrlRequestSchema = object({
|
|
119
|
-
url: string().trim().min(1)
|
|
120
|
-
});
|
|
121
|
-
const markReadByUrlResponseSchema = object({
|
|
122
|
-
ok: boolean(),
|
|
123
|
-
error: string().optional(),
|
|
124
|
-
matchedCount: number().int().min(0).optional(),
|
|
125
|
-
modifiedCount: number().int().min(0).optional()
|
|
126
|
-
});
|
|
127
|
-
object({
|
|
128
|
-
ok: boolean(),
|
|
129
|
-
error: string().optional()
|
|
130
|
-
});
|
|
131
|
-
object({
|
|
132
|
-
ok: boolean(),
|
|
133
|
-
error: string().optional()
|
|
134
|
-
});
|
|
135
|
-
const digestFrequencySchema = _enum(["off", "daily", "weekly"]);
|
|
136
|
-
const topicPreferenceSchema = object({
|
|
137
|
-
topic: string(),
|
|
138
|
-
inApp: boolean(),
|
|
139
|
-
emailDigest: boolean(),
|
|
140
|
-
push: boolean()
|
|
141
|
-
});
|
|
142
|
-
const settingsSchema = object({
|
|
143
|
-
digestFrequency: digestFrequencySchema,
|
|
144
|
-
topicPreferences: array(topicPreferenceSchema),
|
|
145
|
-
lastDigestSentAt: string().optional()
|
|
146
|
-
});
|
|
147
|
-
const settingsResponseSchema = object({
|
|
148
|
-
ok: boolean(),
|
|
149
|
-
error: string().optional(),
|
|
150
|
-
settings: settingsSchema.optional()
|
|
151
|
-
});
|
|
152
|
-
const updateSettingsRequestSchema = object({
|
|
153
|
-
digestFrequency: digestFrequencySchema.optional(),
|
|
154
|
-
topicPreferences: array(topicPreferenceSchema).optional()
|
|
155
|
-
});
|
|
156
|
-
const updateSettingsResponseSchema = object({
|
|
157
|
-
ok: boolean(),
|
|
158
|
-
error: string().optional(),
|
|
159
|
-
settings: settingsSchema.optional()
|
|
160
|
-
});
|
|
161
|
-
const digestRunRequestSchema = object({
|
|
162
|
-
force: boolean().optional()
|
|
163
|
-
});
|
|
164
|
-
object({
|
|
165
|
-
ok: boolean(),
|
|
166
|
-
error: string().optional(),
|
|
167
|
-
sent: boolean().optional(),
|
|
168
|
-
skippedReason: string().optional()
|
|
169
|
-
});
|
|
170
|
-
const toIso = (value) => value instanceof Date ? value.toISOString() : void 0;
|
|
171
|
-
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
172
|
-
const toStringMap = (value) => {
|
|
173
|
-
if (!isRecord(value)) return void 0;
|
|
174
|
-
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
175
|
-
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
176
|
-
};
|
|
177
|
-
const getForwardedHeaderValue = (value) => {
|
|
178
|
-
const raw = Array.isArray(value) ? value[0] : value;
|
|
179
|
-
if (typeof raw !== "string") return void 0;
|
|
180
|
-
return raw.split(",")[0]?.trim() || void 0;
|
|
181
|
-
};
|
|
182
|
-
const getRequestOrigin = (ctx) => {
|
|
183
|
-
const protocol = getForwardedHeaderValue(ctx.req.headers["x-forwarded-proto"]) ?? ctx.req.protocol;
|
|
184
|
-
const host = getForwardedHeaderValue(ctx.req.headers["x-forwarded-host"]) ?? ctx.req.get("host");
|
|
185
|
-
return protocol && host ? `${protocol}://${host}` : "";
|
|
186
|
-
};
|
|
187
|
-
const getNotificationActionLinks = (notification) => {
|
|
188
|
-
const links = [];
|
|
189
|
-
if (typeof notification.data?.link === "string") {
|
|
190
|
-
links.push({
|
|
191
|
-
link: notification.data.link,
|
|
192
|
-
urlMode: notification.data.linkMode === "merge_current_search" ? "merge_current_search" : "navigate"
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
const platformWebpush = notification.platform?.webpush;
|
|
196
|
-
if (platformWebpush && typeof platformWebpush === "object" && !Array.isArray(platformWebpush)) {
|
|
197
|
-
const fcmOptions = platformWebpush.fcmOptions;
|
|
198
|
-
if (fcmOptions && typeof fcmOptions === "object" && !Array.isArray(fcmOptions)) {
|
|
199
|
-
const link = fcmOptions.link;
|
|
200
|
-
if (typeof link === "string") {
|
|
201
|
-
links.push({
|
|
202
|
-
link,
|
|
203
|
-
urlMode: "navigate"
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return links;
|
|
209
|
-
};
|
|
210
|
-
const actionSearchParamsMatchTargetUrl = (actionUrl, targetUrl, origin) => {
|
|
211
|
-
try {
|
|
212
|
-
const base = new URL(origin);
|
|
213
|
-
const action = new URL(actionUrl, base);
|
|
214
|
-
const target = new URL(targetUrl, base);
|
|
215
|
-
for (const [key, value] of action.searchParams) {
|
|
216
|
-
if (target.searchParams.get(key) !== value) return false;
|
|
217
|
-
}
|
|
218
|
-
return Array.from(action.searchParams.keys()).length > 0;
|
|
219
|
-
} catch {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
const notificationActionLinkMatchesUrl = (action, targetUrl, origin) => {
|
|
224
|
-
if (action.urlMode === "merge_current_search") {
|
|
225
|
-
return actionSearchParamsMatchTargetUrl(action.link, targetUrl, origin);
|
|
226
|
-
}
|
|
227
|
-
return normalizeNotificationActionUrl(action.link, origin) === targetUrl;
|
|
228
|
-
};
|
|
229
|
-
const buildDisabledTopics = (settings, key) => {
|
|
230
|
-
const raw = settings?.topicPreferences;
|
|
231
|
-
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
232
|
-
return raw.map((pref) => {
|
|
233
|
-
if (!pref || typeof pref !== "object") return null;
|
|
234
|
-
const topic = typeof pref.topic === "string" ? pref.topic.trim() : "";
|
|
235
|
-
if (!topic) return null;
|
|
236
|
-
const enabled = pref[key] === true;
|
|
237
|
-
return enabled ? null : topic;
|
|
238
|
-
}).filter((topic) => Boolean(topic));
|
|
239
|
-
};
|
|
240
|
-
const listNotifications = async (payload, ctx) => {
|
|
241
|
-
const session = getSessionUser(ctx);
|
|
242
|
-
if (!session) {
|
|
243
|
-
return {
|
|
244
|
-
ok: false,
|
|
245
|
-
error: "unauthorized"
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const ability = buildAbilityFromSession({
|
|
249
|
-
tenantId: session.tenantId,
|
|
250
|
-
session: ctx.req.session
|
|
251
|
-
});
|
|
252
|
-
if (!ability.can("read", "RBNotification")) {
|
|
253
|
-
ctx.res.status(403);
|
|
254
|
-
return {
|
|
255
|
-
ok: false,
|
|
256
|
-
error: "forbidden"
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
const parsed = listRequestSchema.safeParse(payload);
|
|
260
|
-
if (!parsed.success) {
|
|
261
|
-
ctx.res.status(400);
|
|
262
|
-
return {
|
|
263
|
-
ok: false,
|
|
264
|
-
error: "invalid_payload"
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
const {
|
|
268
|
-
userId
|
|
269
|
-
} = session;
|
|
270
|
-
const includeArchived = parsed.data.includeArchived === true;
|
|
271
|
-
const unreadOnly = parsed.data.unreadOnly === true;
|
|
272
|
-
const limit = parsed.data.limit ?? 50;
|
|
273
|
-
const markSeen = parsed.data.markSeen === true;
|
|
274
|
-
const SettingsModel = await models.get("RBNotificationSettings", {
|
|
275
|
-
req: ctx.req,
|
|
276
|
-
ability
|
|
277
|
-
});
|
|
278
|
-
const settings = await SettingsModel.findOne({
|
|
279
|
-
userId
|
|
280
|
-
}).lean();
|
|
281
|
-
const disabledTopics = buildDisabledTopics(settings, "inApp");
|
|
282
|
-
const NotificationModel = await models.get("RBNotification", {
|
|
283
|
-
req: ctx.req,
|
|
284
|
-
ability
|
|
285
|
-
});
|
|
286
|
-
const queryFilters = [{
|
|
287
|
-
userId
|
|
288
|
-
}, getAccessibleByQuery(ability, "read", "RBNotification")];
|
|
289
|
-
if (!includeArchived) queryFilters.push({
|
|
290
|
-
archivedAt: {
|
|
291
|
-
$exists: false
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
if (unreadOnly) queryFilters.push({
|
|
295
|
-
readAt: {
|
|
296
|
-
$exists: false
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
if (disabledTopics.length > 0) queryFilters.push({
|
|
300
|
-
topic: {
|
|
301
|
-
$nin: disabledTopics
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
const query = {
|
|
305
|
-
$and: queryFilters
|
|
306
|
-
};
|
|
307
|
-
const notifications = await NotificationModel.find(query).sort({
|
|
308
|
-
createdAt: -1
|
|
309
|
-
}).limit(limit).lean();
|
|
310
|
-
const unseenQueryFilters = [{
|
|
311
|
-
userId
|
|
312
|
-
}, {
|
|
313
|
-
archivedAt: {
|
|
314
|
-
$exists: false
|
|
315
|
-
}
|
|
316
|
-
}, {
|
|
317
|
-
seenAt: {
|
|
318
|
-
$exists: false
|
|
319
|
-
}
|
|
320
|
-
}, getAccessibleByQuery(ability, "read", "RBNotification")];
|
|
321
|
-
if (disabledTopics.length > 0) unseenQueryFilters.push({
|
|
322
|
-
topic: {
|
|
323
|
-
$nin: disabledTopics
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
const unseenQuery = {
|
|
327
|
-
$and: unseenQueryFilters
|
|
328
|
-
};
|
|
329
|
-
const unreadQueryFilters = [{
|
|
330
|
-
userId
|
|
331
|
-
}, {
|
|
332
|
-
archivedAt: {
|
|
333
|
-
$exists: false
|
|
334
|
-
}
|
|
335
|
-
}, {
|
|
336
|
-
readAt: {
|
|
337
|
-
$exists: false
|
|
338
|
-
}
|
|
339
|
-
}, getAccessibleByQuery(ability, "read", "RBNotification")];
|
|
340
|
-
if (disabledTopics.length > 0) unreadQueryFilters.push({
|
|
341
|
-
topic: {
|
|
342
|
-
$nin: disabledTopics
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
const unreadQuery = {
|
|
346
|
-
$and: unreadQueryFilters
|
|
347
|
-
};
|
|
348
|
-
const [unreadCount, unseenCount] = await Promise.all([NotificationModel.countDocuments(unreadQuery), NotificationModel.countDocuments(unseenQuery)]);
|
|
349
|
-
const now = markSeen ? /* @__PURE__ */ new Date() : null;
|
|
350
|
-
if (now && unseenCount > 0) {
|
|
351
|
-
await NotificationModel.updateMany(unseenQuery, {
|
|
352
|
-
$set: {
|
|
353
|
-
seenAt: now
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
return listResponseSchema.parse({
|
|
358
|
-
ok: true,
|
|
359
|
-
notifications: notifications.map((n) => ({
|
|
360
|
-
id: String(n._id),
|
|
361
|
-
topic: typeof n.topic === "string" ? n.topic : void 0,
|
|
362
|
-
tag: typeof n.tag === "string" ? n.tag : void 0,
|
|
363
|
-
deliveryId: typeof n.deliveryId === "string" ? n.deliveryId : void 0,
|
|
364
|
-
title: typeof n.title === "string" ? n.title : "",
|
|
365
|
-
body: typeof n.body === "string" ? n.body : void 0,
|
|
366
|
-
image: typeof n.image === "string" ? n.image : void 0,
|
|
367
|
-
data: toStringMap(n.data),
|
|
368
|
-
platform: isRecord(n.platform) ? n.platform : void 0,
|
|
369
|
-
createdAt: toIso(n.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
370
|
-
seenAt: toIso(n.seenAt) ?? (now && !n.archivedAt && !n.seenAt ? now.toISOString() : void 0),
|
|
371
|
-
readAt: toIso(n.readAt),
|
|
372
|
-
archivedAt: toIso(n.archivedAt)
|
|
373
|
-
})),
|
|
374
|
-
unreadCount,
|
|
375
|
-
unseenCount: now ? 0 : unseenCount
|
|
376
|
-
});
|
|
377
|
-
};
|
|
378
|
-
const createNotificationForCurrentUser = async (payload, ctx) => {
|
|
379
|
-
const session = getSessionUser(ctx);
|
|
380
|
-
if (!session) {
|
|
381
|
-
return {
|
|
382
|
-
ok: false,
|
|
383
|
-
error: "unauthorized"
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
const ability = buildAbilityFromSession({
|
|
387
|
-
tenantId: session.tenantId,
|
|
388
|
-
session: ctx.req.session
|
|
389
|
-
});
|
|
390
|
-
if (!ability.can("create", "RBNotification")) {
|
|
391
|
-
ctx.res.status(403);
|
|
392
|
-
return {
|
|
393
|
-
ok: false,
|
|
394
|
-
error: "forbidden"
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
const parsed = createRequestSchema.safeParse(payload);
|
|
398
|
-
if (!parsed.success) {
|
|
399
|
-
ctx.res.status(400);
|
|
400
|
-
return {
|
|
401
|
-
ok: false,
|
|
402
|
-
error: "invalid_payload"
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
const origin = getRequestOrigin(ctx);
|
|
406
|
-
const actionLink = parsed.data.url ? normalizeNotificationActionLink(parsed.data.url, origin, parsed.data.urlMode) ?? void 0 : void 0;
|
|
407
|
-
if (parsed.data.url && !actionLink) {
|
|
408
|
-
ctx.res.status(400);
|
|
409
|
-
return {
|
|
410
|
-
ok: false,
|
|
411
|
-
error: "invalid_payload"
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
const created = await createNotification(ctx, {
|
|
415
|
-
userId: session.userId,
|
|
416
|
-
topic: parsed.data.topic,
|
|
417
|
-
tag: parsed.data.tag,
|
|
418
|
-
title: parsed.data.title,
|
|
419
|
-
body: parsed.data.body,
|
|
420
|
-
image: parsed.data.image,
|
|
421
|
-
url: actionLink,
|
|
422
|
-
urlMode: parsed.data.urlMode,
|
|
423
|
-
metadata: parsed.data.metadata,
|
|
424
|
-
data: parsed.data.data,
|
|
425
|
-
platform: parsed.data.platform
|
|
426
|
-
}, ability);
|
|
427
|
-
return createResponseSchema.parse({
|
|
428
|
-
ok: true,
|
|
429
|
-
id: created.id
|
|
430
|
-
});
|
|
431
|
-
};
|
|
432
|
-
const markRead = async (_payload, ctx) => {
|
|
433
|
-
const session = getSessionUser(ctx);
|
|
434
|
-
if (!session) {
|
|
435
|
-
return {
|
|
436
|
-
ok: false,
|
|
437
|
-
error: "unauthorized"
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
const ability = buildAbilityFromSession({
|
|
441
|
-
tenantId: session.tenantId,
|
|
442
|
-
session: ctx.req.session
|
|
443
|
-
});
|
|
444
|
-
if (!ability.can("update", "RBNotification")) {
|
|
445
|
-
ctx.res.status(403);
|
|
446
|
-
return {
|
|
447
|
-
ok: false,
|
|
448
|
-
error: "forbidden"
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
const notificationId = typeof ctx.req.params.notificationId === "string" ? ctx.req.params.notificationId.trim() : "";
|
|
452
|
-
if (!notificationId) {
|
|
453
|
-
ctx.res.status(400);
|
|
454
|
-
return {
|
|
455
|
-
ok: false,
|
|
456
|
-
error: "missing_notification_id"
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
const NotificationModel = await models.get("RBNotification", {
|
|
460
|
-
req: ctx.req,
|
|
461
|
-
ability
|
|
462
|
-
});
|
|
463
|
-
const now = /* @__PURE__ */ new Date();
|
|
464
|
-
try {
|
|
465
|
-
await NotificationModel.updateOne({
|
|
466
|
-
$and: [{
|
|
467
|
-
_id: notificationId
|
|
468
|
-
}, {
|
|
469
|
-
archivedAt: {
|
|
470
|
-
$exists: false
|
|
471
|
-
}
|
|
472
|
-
}, getAccessibleByQuery(ability, "update", "RBNotification")]
|
|
473
|
-
}, {
|
|
474
|
-
$set: {
|
|
475
|
-
readAt: now,
|
|
476
|
-
seenAt: now
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
|
-
} catch {
|
|
480
|
-
ctx.res.status(400);
|
|
481
|
-
return {
|
|
482
|
-
ok: false,
|
|
483
|
-
error: "invalid_notification_id"
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
return {
|
|
487
|
-
ok: true
|
|
488
|
-
};
|
|
489
|
-
};
|
|
490
|
-
const markReadByUrl = async (payload, ctx) => {
|
|
491
|
-
const session = getSessionUser(ctx);
|
|
492
|
-
if (!session) {
|
|
493
|
-
return {
|
|
494
|
-
ok: false,
|
|
495
|
-
error: "unauthorized"
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
const ability = buildAbilityFromSession({
|
|
499
|
-
tenantId: session.tenantId,
|
|
500
|
-
session: ctx.req.session
|
|
501
|
-
});
|
|
502
|
-
if (!ability.can("update", "RBNotification")) {
|
|
503
|
-
ctx.res.status(403);
|
|
504
|
-
return {
|
|
505
|
-
ok: false,
|
|
506
|
-
error: "forbidden"
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
const parsed = markReadByUrlRequestSchema.safeParse(payload);
|
|
510
|
-
if (!parsed.success) {
|
|
511
|
-
ctx.res.status(400);
|
|
512
|
-
return {
|
|
513
|
-
ok: false,
|
|
514
|
-
error: "invalid_payload"
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const origin = getRequestOrigin(ctx);
|
|
518
|
-
const targetUrl = normalizeNotificationActionUrl(parsed.data.url, origin);
|
|
519
|
-
if (!targetUrl) {
|
|
520
|
-
ctx.res.status(400);
|
|
521
|
-
return {
|
|
522
|
-
ok: false,
|
|
523
|
-
error: "invalid_payload"
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
const NotificationModel = await models.get("RBNotification", {
|
|
527
|
-
req: ctx.req,
|
|
528
|
-
ability
|
|
529
|
-
});
|
|
530
|
-
const query = {
|
|
531
|
-
$and: [{
|
|
532
|
-
userId: session.userId
|
|
533
|
-
}, {
|
|
534
|
-
archivedAt: {
|
|
535
|
-
$exists: false
|
|
536
|
-
}
|
|
537
|
-
}, {
|
|
538
|
-
readAt: {
|
|
539
|
-
$exists: false
|
|
540
|
-
}
|
|
541
|
-
}, {
|
|
542
|
-
$or: [{
|
|
543
|
-
"data.link": {
|
|
544
|
-
$type: "string"
|
|
545
|
-
}
|
|
546
|
-
}, {
|
|
547
|
-
"platform.webpush.fcmOptions.link": {
|
|
548
|
-
$type: "string"
|
|
549
|
-
}
|
|
550
|
-
}]
|
|
551
|
-
}, getAccessibleByQuery(ability, "update", "RBNotification")]
|
|
552
|
-
};
|
|
553
|
-
const candidates = await NotificationModel.find(query).select({
|
|
554
|
-
_id: 1,
|
|
555
|
-
data: 1,
|
|
556
|
-
platform: 1
|
|
557
|
-
}).lean();
|
|
558
|
-
const matchingIds = candidates.filter((notification) => getNotificationActionLinks(notification).some((action) => notificationActionLinkMatchesUrl(action, targetUrl, origin))).map((notification) => notification._id);
|
|
559
|
-
if (matchingIds.length === 0) {
|
|
560
|
-
return markReadByUrlResponseSchema.parse({
|
|
561
|
-
ok: true,
|
|
562
|
-
matchedCount: 0,
|
|
563
|
-
modifiedCount: 0
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
const now = /* @__PURE__ */ new Date();
|
|
567
|
-
const result = await NotificationModel.updateMany({
|
|
568
|
-
$and: [{
|
|
569
|
-
_id: {
|
|
570
|
-
$in: matchingIds
|
|
571
|
-
}
|
|
572
|
-
}, {
|
|
573
|
-
userId: session.userId
|
|
574
|
-
}, {
|
|
575
|
-
archivedAt: {
|
|
576
|
-
$exists: false
|
|
577
|
-
}
|
|
578
|
-
}, {
|
|
579
|
-
readAt: {
|
|
580
|
-
$exists: false
|
|
581
|
-
}
|
|
582
|
-
}, getAccessibleByQuery(ability, "update", "RBNotification")]
|
|
583
|
-
}, {
|
|
584
|
-
$set: {
|
|
585
|
-
readAt: now,
|
|
586
|
-
seenAt: now
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
const modifiedCount = typeof result.modifiedCount === "number" ? result.modifiedCount : matchingIds.length;
|
|
590
|
-
return markReadByUrlResponseSchema.parse({
|
|
591
|
-
ok: true,
|
|
592
|
-
matchedCount: matchingIds.length,
|
|
593
|
-
modifiedCount
|
|
594
|
-
});
|
|
595
|
-
};
|
|
596
|
-
const markAllRead = async (_payload, ctx) => {
|
|
597
|
-
const session = getSessionUser(ctx);
|
|
598
|
-
if (!session) {
|
|
599
|
-
return {
|
|
600
|
-
ok: false,
|
|
601
|
-
error: "unauthorized"
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
const ability = buildAbilityFromSession({
|
|
605
|
-
tenantId: session.tenantId,
|
|
606
|
-
session: ctx.req.session
|
|
607
|
-
});
|
|
608
|
-
if (!ability.can("update", "RBNotification")) {
|
|
609
|
-
ctx.res.status(403);
|
|
610
|
-
return {
|
|
611
|
-
ok: false,
|
|
612
|
-
error: "forbidden"
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
const SettingsModel = await models.get("RBNotificationSettings", {
|
|
616
|
-
req: ctx.req,
|
|
617
|
-
ability
|
|
618
|
-
});
|
|
619
|
-
const settings = await SettingsModel.findOne({
|
|
620
|
-
userId: session.userId
|
|
621
|
-
}).lean();
|
|
622
|
-
const disabledTopics = buildDisabledTopics(settings, "inApp");
|
|
623
|
-
const NotificationModel = await models.get("RBNotification", {
|
|
624
|
-
req: ctx.req,
|
|
625
|
-
ability
|
|
626
|
-
});
|
|
627
|
-
const queryFilters = [{
|
|
628
|
-
userId: session.userId
|
|
629
|
-
}, {
|
|
630
|
-
archivedAt: {
|
|
631
|
-
$exists: false
|
|
632
|
-
}
|
|
633
|
-
}, {
|
|
634
|
-
readAt: {
|
|
635
|
-
$exists: false
|
|
636
|
-
}
|
|
637
|
-
}, getAccessibleByQuery(ability, "update", "RBNotification")];
|
|
638
|
-
if (disabledTopics.length > 0) queryFilters.push({
|
|
639
|
-
topic: {
|
|
640
|
-
$nin: disabledTopics
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
const query = {
|
|
644
|
-
$and: queryFilters
|
|
645
|
-
};
|
|
646
|
-
const now = /* @__PURE__ */ new Date();
|
|
647
|
-
await NotificationModel.updateMany(query, {
|
|
648
|
-
$set: {
|
|
649
|
-
readAt: now,
|
|
650
|
-
seenAt: now
|
|
651
|
-
}
|
|
652
|
-
});
|
|
653
|
-
return {
|
|
654
|
-
ok: true
|
|
655
|
-
};
|
|
656
|
-
};
|
|
657
|
-
const archiveNotification = async (_payload, ctx) => {
|
|
658
|
-
const session = getSessionUser(ctx);
|
|
659
|
-
if (!session) {
|
|
660
|
-
return {
|
|
661
|
-
ok: false,
|
|
662
|
-
error: "unauthorized"
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
const ability = buildAbilityFromSession({
|
|
666
|
-
tenantId: session.tenantId,
|
|
667
|
-
session: ctx.req.session
|
|
668
|
-
});
|
|
669
|
-
if (!ability.can("update", "RBNotification")) {
|
|
670
|
-
ctx.res.status(403);
|
|
671
|
-
return {
|
|
672
|
-
ok: false,
|
|
673
|
-
error: "forbidden"
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
const notificationId = typeof ctx.req.params.notificationId === "string" ? ctx.req.params.notificationId.trim() : "";
|
|
677
|
-
if (!notificationId) {
|
|
678
|
-
ctx.res.status(400);
|
|
679
|
-
return {
|
|
680
|
-
ok: false,
|
|
681
|
-
error: "missing_notification_id"
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
const NotificationModel = await models.get("RBNotification", {
|
|
685
|
-
req: ctx.req,
|
|
686
|
-
ability
|
|
687
|
-
});
|
|
688
|
-
try {
|
|
689
|
-
await NotificationModel.updateOne({
|
|
690
|
-
$and: [{
|
|
691
|
-
_id: notificationId
|
|
692
|
-
}, {
|
|
693
|
-
archivedAt: {
|
|
694
|
-
$exists: false
|
|
695
|
-
}
|
|
696
|
-
}, getAccessibleByQuery(ability, "update", "RBNotification")]
|
|
697
|
-
}, {
|
|
698
|
-
$set: {
|
|
699
|
-
archivedAt: /* @__PURE__ */ new Date(),
|
|
700
|
-
drawerActive: false
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
} catch {
|
|
704
|
-
ctx.res.status(400);
|
|
705
|
-
return {
|
|
706
|
-
ok: false,
|
|
707
|
-
error: "invalid_notification_id"
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
return {
|
|
711
|
-
ok: true
|
|
712
|
-
};
|
|
713
|
-
};
|
|
714
|
-
const getSettings = async (_payload, ctx) => {
|
|
715
|
-
const session = getSessionUser(ctx);
|
|
716
|
-
if (!session) {
|
|
717
|
-
return {
|
|
718
|
-
ok: false,
|
|
719
|
-
error: "unauthorized"
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
const ability = buildAbilityFromSession({
|
|
723
|
-
tenantId: session.tenantId,
|
|
724
|
-
session: ctx.req.session
|
|
725
|
-
});
|
|
726
|
-
if (!ability.can("read", "RBNotificationSettings")) {
|
|
727
|
-
ctx.res.status(403);
|
|
728
|
-
return {
|
|
729
|
-
ok: false,
|
|
730
|
-
error: "forbidden"
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
const SettingsModel = await models.get("RBNotificationSettings", {
|
|
734
|
-
req: ctx.req,
|
|
735
|
-
ability
|
|
736
|
-
});
|
|
737
|
-
const settings = await SettingsModel.findOne({
|
|
738
|
-
$and: [{
|
|
739
|
-
userId: session.userId
|
|
740
|
-
}, getAccessibleByQuery(ability, "read", "RBNotificationSettings")]
|
|
741
|
-
}).lean();
|
|
742
|
-
const digestFrequencyRaw = typeof settings?.digestFrequency === "string" ? settings.digestFrequency : "weekly";
|
|
743
|
-
const digestFrequency = digestFrequencyRaw === "off" || digestFrequencyRaw === "daily" || digestFrequencyRaw === "weekly" ? digestFrequencyRaw : "weekly";
|
|
744
|
-
const topicPreferences = Array.isArray(settings?.topicPreferences) ? settings.topicPreferences.map((pref) => ({
|
|
745
|
-
topic: typeof pref.topic === "string" ? pref.topic : "",
|
|
746
|
-
inApp: pref.inApp === true,
|
|
747
|
-
emailDigest: pref.emailDigest === true,
|
|
748
|
-
push: pref.push === true
|
|
749
|
-
})).filter((pref) => pref.topic.length > 0) : [];
|
|
750
|
-
return settingsResponseSchema.parse({
|
|
751
|
-
ok: true,
|
|
752
|
-
settings: {
|
|
753
|
-
digestFrequency,
|
|
754
|
-
topicPreferences,
|
|
755
|
-
lastDigestSentAt: toIso(settings?.lastDigestSentAt)
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
};
|
|
759
|
-
const updateSettings = async (payload, ctx) => {
|
|
760
|
-
const session = getSessionUser(ctx);
|
|
761
|
-
if (!session) {
|
|
762
|
-
return {
|
|
763
|
-
ok: false,
|
|
764
|
-
error: "unauthorized"
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
const ability = buildAbilityFromSession({
|
|
768
|
-
tenantId: session.tenantId,
|
|
769
|
-
session: ctx.req.session
|
|
770
|
-
});
|
|
771
|
-
if (!ability.can("update", "RBNotificationSettings")) {
|
|
772
|
-
ctx.res.status(403);
|
|
773
|
-
return {
|
|
774
|
-
ok: false,
|
|
775
|
-
error: "forbidden"
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
const parsed = updateSettingsRequestSchema.safeParse(payload);
|
|
779
|
-
if (!parsed.success) {
|
|
780
|
-
ctx.res.status(400);
|
|
781
|
-
return {
|
|
782
|
-
ok: false,
|
|
783
|
-
error: "invalid_payload"
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
const SettingsModel = await models.get("RBNotificationSettings", {
|
|
787
|
-
req: ctx.req,
|
|
788
|
-
ability
|
|
789
|
-
});
|
|
790
|
-
const nextValues = {};
|
|
791
|
-
if (parsed.data.digestFrequency) {
|
|
792
|
-
nextValues.digestFrequency = parsed.data.digestFrequency;
|
|
793
|
-
}
|
|
794
|
-
if (parsed.data.topicPreferences) {
|
|
795
|
-
const seen = /* @__PURE__ */ new Set();
|
|
796
|
-
const next = parsed.data.topicPreferences.map((pref) => ({
|
|
797
|
-
topic: pref.topic.trim(),
|
|
798
|
-
inApp: pref.inApp,
|
|
799
|
-
emailDigest: pref.emailDigest,
|
|
800
|
-
push: pref.push
|
|
801
|
-
})).filter((pref) => pref.topic.length > 0).filter((pref) => {
|
|
802
|
-
if (seen.has(pref.topic)) return false;
|
|
803
|
-
seen.add(pref.topic);
|
|
804
|
-
return true;
|
|
805
|
-
});
|
|
806
|
-
nextValues.topicPreferences = next;
|
|
807
|
-
}
|
|
808
|
-
const ops = {
|
|
809
|
-
$setOnInsert: {
|
|
810
|
-
userId: session.userId
|
|
811
|
-
}
|
|
812
|
-
};
|
|
813
|
-
if (Object.keys(nextValues).length > 0) {
|
|
814
|
-
ops.$set = nextValues;
|
|
815
|
-
}
|
|
816
|
-
const settings = await SettingsModel.findOneAndUpdate({
|
|
817
|
-
$and: [{
|
|
818
|
-
userId: session.userId
|
|
819
|
-
}, getAccessibleByQuery(ability, "update", "RBNotificationSettings")]
|
|
820
|
-
}, ops, {
|
|
821
|
-
upsert: true,
|
|
822
|
-
returnDocument: "after",
|
|
823
|
-
setDefaultsOnInsert: true
|
|
824
|
-
}).lean();
|
|
825
|
-
const digestFrequencyRaw = typeof settings?.digestFrequency === "string" ? settings.digestFrequency : "weekly";
|
|
826
|
-
const digestFrequency = digestFrequencyRaw === "off" || digestFrequencyRaw === "daily" || digestFrequencyRaw === "weekly" ? digestFrequencyRaw : "weekly";
|
|
827
|
-
const topicPreferences = Array.isArray(settings?.topicPreferences) ? settings.topicPreferences.map((pref) => ({
|
|
828
|
-
topic: typeof pref.topic === "string" ? pref.topic : "",
|
|
829
|
-
inApp: pref.inApp === true,
|
|
830
|
-
emailDigest: pref.emailDigest === true,
|
|
831
|
-
push: pref.push === true
|
|
832
|
-
})).filter((pref) => pref.topic.length > 0) : [];
|
|
833
|
-
return updateSettingsResponseSchema.parse({
|
|
834
|
-
ok: true,
|
|
835
|
-
settings: {
|
|
836
|
-
digestFrequency,
|
|
837
|
-
topicPreferences,
|
|
838
|
-
lastDigestSentAt: toIso(settings?.lastDigestSentAt)
|
|
839
|
-
}
|
|
840
|
-
});
|
|
841
|
-
};
|
|
842
|
-
const runDigest = async (payload, ctx) => {
|
|
843
|
-
const session = getSessionUser(ctx);
|
|
844
|
-
if (!session) {
|
|
845
|
-
return {
|
|
846
|
-
ok: false,
|
|
847
|
-
error: "unauthorized"
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
const ability = buildAbilityFromSession({
|
|
851
|
-
tenantId: session.tenantId,
|
|
852
|
-
session: ctx.req.session
|
|
853
|
-
});
|
|
854
|
-
if (!ability.can("read", "RBNotification")) {
|
|
855
|
-
ctx.res.status(403);
|
|
856
|
-
return {
|
|
857
|
-
ok: false,
|
|
858
|
-
error: "forbidden"
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
const parsed = digestRunRequestSchema.safeParse(payload);
|
|
862
|
-
if (!parsed.success) {
|
|
863
|
-
ctx.res.status(400);
|
|
864
|
-
return {
|
|
865
|
-
ok: false,
|
|
866
|
-
error: "invalid_payload"
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
const result = await sendNotificationsDigestForUser(ctx, {
|
|
870
|
-
userId: session.userId,
|
|
871
|
-
ability,
|
|
872
|
-
force: parsed.data.force === true
|
|
873
|
-
});
|
|
874
|
-
if (!result.ok) {
|
|
875
|
-
ctx.res.status(500);
|
|
876
|
-
return {
|
|
877
|
-
ok: false,
|
|
878
|
-
error: result.error
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
return {
|
|
882
|
-
ok: true,
|
|
883
|
-
sent: result.sent,
|
|
884
|
-
...result.skippedReason ? {
|
|
885
|
-
skippedReason: result.skippedReason
|
|
886
|
-
} : {}
|
|
887
|
-
};
|
|
888
|
-
};
|
|
889
|
-
const handler = (api) => {
|
|
890
|
-
api.post(ListRoute, listNotifications);
|
|
891
|
-
api.post(CreateRoute, createNotificationForCurrentUser);
|
|
892
|
-
api.post(MarkReadRoute, markRead);
|
|
893
|
-
api.post(MarkReadByUrlRoute, markReadByUrl);
|
|
894
|
-
api.post(MarkAllReadRoute, markAllRead);
|
|
895
|
-
api.post(ArchiveRoute, archiveNotification);
|
|
896
|
-
api.get(SettingsRoute, getSettings);
|
|
897
|
-
api.put(SettingsRoute, updateSettings);
|
|
898
|
-
api.post(DigestRunRoute, runDigest);
|
|
899
|
-
};
|
|
900
|
-
export {
|
|
901
|
-
handler as default
|
|
902
|
-
};
|
|
903
|
-
//# sourceMappingURL=handler-CUOJ51-w.js.map
|