@rpcbase/server 0.608.0 → 0.610.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/{fileReferences-Ca0tivFd.js → fileReferences-C6gDuRO3.js} +2 -2
- package/dist/{fileReferences-Ca0tivFd.js.map → fileReferences-C6gDuRO3.js.map} +1 -1
- package/dist/{handler-C63qGUhq.js → handler-B61RAoQK.js} +3 -3
- package/dist/{handler-C63qGUhq.js.map → handler-B61RAoQK.js.map} +1 -1
- package/dist/{handler-BgcIWWMc.js → handler-CnYlcu-N.js} +18 -8
- package/dist/handler-CnYlcu-N.js.map +1 -0
- package/dist/{handler-CqSlSCdd.js → handler-D9cQBJyz.js} +7 -7
- package/dist/handler-D9cQBJyz.js.map +1 -0
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/initServer.d.ts +19 -3
- package/dist/initServer.d.ts.map +1 -1
- package/dist/notifications/api/notifications/handler.d.ts.map +1 -1
- package/dist/notifications/digest.d.ts.map +1 -1
- package/dist/notifications.js +18 -15
- package/dist/notifications.js.map +1 -1
- package/dist/{queryWindow-DP8zD0lb.js → queryWindow-DNQxuY_M.js} +6 -5
- package/dist/queryWindow-DNQxuY_M.js.map +1 -0
- package/dist/rts/index.js +1 -1
- package/dist/rts/queryExecutor.d.ts.map +1 -1
- package/dist/{shared-Zt17vuAT.js → shared-BX-oLc9O.js} +4 -2
- package/dist/shared-BX-oLc9O.js.map +1 -0
- package/dist/uploads/api/file-uploads/handlers/initUpload.d.ts.map +1 -1
- package/dist/uploads/api/file-uploads/shared.d.ts +1 -2
- package/dist/uploads/api/file-uploads/shared.d.ts.map +1 -1
- package/dist/uploads/worker.js +1 -1
- package/dist/uploads.js +2 -2
- package/package.json +1 -1
- package/dist/handler-BgcIWWMc.js.map +0 -1
- package/dist/handler-CqSlSCdd.js.map +0 -1
- package/dist/queryWindow-DP8zD0lb.js.map +0 -1
- package/dist/shared-Zt17vuAT.js.map +0 -1
package/dist/initServer.d.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
import { Application } from 'express';
|
|
1
|
+
import { Application, RequestHandler } from 'express';
|
|
2
2
|
type ServerEnv = {
|
|
3
3
|
[key: string]: string | undefined;
|
|
4
4
|
};
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export type InitServerOptions = {
|
|
6
|
+
sessions?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type InitServerResult = {
|
|
9
|
+
sessionMiddleware: RequestHandler | undefined;
|
|
10
|
+
};
|
|
11
|
+
type InitServerWithSessionsOptions = InitServerOptions & {
|
|
12
|
+
sessions?: true;
|
|
13
|
+
};
|
|
14
|
+
type InitServerWithoutSessionsOptions = InitServerOptions & {
|
|
15
|
+
sessions: false;
|
|
16
|
+
};
|
|
17
|
+
export declare function initServer(app: Application, serverEnv: ServerEnv, options: InitServerWithoutSessionsOptions): Promise<{
|
|
18
|
+
sessionMiddleware: undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function initServer(app: Application, serverEnv: ServerEnv, options?: InitServerWithSessionsOptions): Promise<{
|
|
21
|
+
sessionMiddleware: RequestHandler;
|
|
7
22
|
}>;
|
|
23
|
+
export declare function initServer(app: Application, serverEnv: ServerEnv, options: InitServerOptions): Promise<InitServerResult>;
|
|
8
24
|
export {};
|
|
9
25
|
//# sourceMappingURL=initServer.d.ts.map
|
package/dist/initServer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA+
|
|
1
|
+
{"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AA+B1D,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAEtD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iBAAiB,EAAE,cAAc,GAAG,SAAS,CAAA;CAC9C,CAAA;AAED,KAAK,6BAA6B,GAAG,iBAAiB,GAAG;IACvD,QAAQ,CAAC,EAAE,IAAI,CAAA;CAChB,CAAA;AAED,KAAK,gCAAgC,GAAG,iBAAiB,GAAG;IAC1D,QAAQ,EAAE,KAAK,CAAA;CAChB,CAAA;AA0ED,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,gCAAgC,GACxC,OAAO,CAAC;IAAE,iBAAiB,EAAE,SAAS,CAAA;CAAE,CAAC,CAAA;AAC5C,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC;IAAE,iBAAiB,EAAE,cAAc,CAAA;CAAE,CAAC,CAAA;AACjD,wBAAgB,UAAU,CACxB,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/notifications/api/notifications/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAA6B,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/notifications/api/notifications/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAA6B,MAAM,cAAc,CAAA;yBA4nB7C,KAAK,GAAG;AAAxB,wBAUC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/notifications/digest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../../src/notifications/digest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AA6EjD,eAAO,MAAM,8BAA8B,GACzC,KAAK,GAAG,EACR,6BAIG;IACD,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,UAAU,CAAA;IACnB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,KACA,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA8F5F,CAAA"}
|
package/dist/notifications.js
CHANGED
|
@@ -2,7 +2,7 @@ import { i as sendEmail } from "./email-CK8NNDQY.js";
|
|
|
2
2
|
import { models } from "@rpcbase/db";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/notifications/routes.ts
|
|
5
|
-
var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-
|
|
5
|
+
var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-CnYlcu-N.js") }) }).reduce((acc, [path, mod]) => {
|
|
6
6
|
acc[path.replace("./api/", "@rpcbase/server/notifications/api/")] = mod;
|
|
7
7
|
return acc;
|
|
8
8
|
}, {});
|
|
@@ -163,6 +163,21 @@ var createNotificationsForUser = async (ctx, input, ability) => {
|
|
|
163
163
|
//#region src/notifications/digest.ts
|
|
164
164
|
var DAY_MS = 1440 * 60 * 1e3;
|
|
165
165
|
var WEEK_MS = 7 * DAY_MS;
|
|
166
|
+
var isDuplicateKeyError = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === 11e3);
|
|
167
|
+
var markDigestSent = async (SettingsModel, userId, lastDigestSentAt) => {
|
|
168
|
+
const update = { $set: { lastDigestSentAt } };
|
|
169
|
+
if ((await SettingsModel.updateOne({ userId }, update)).matchedCount > 0) return;
|
|
170
|
+
try {
|
|
171
|
+
await SettingsModel.create({
|
|
172
|
+
userId,
|
|
173
|
+
digestFrequency: "weekly",
|
|
174
|
+
lastDigestSentAt
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (!isDuplicateKeyError(error)) throw error;
|
|
178
|
+
await SettingsModel.updateOne({ userId }, update);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
166
181
|
var getDigestWindowMs = (frequency) => {
|
|
167
182
|
if (frequency === "daily") return DAY_MS;
|
|
168
183
|
if (frequency === "weekly") return WEEK_MS;
|
|
@@ -231,13 +246,7 @@ var sendNotificationsDigestForUser = async (ctx, { userId, ability, force = fals
|
|
|
231
246
|
if (disabledTopics.length > 0) query.topic = { $nin: disabledTopics };
|
|
232
247
|
const notifications = await NotificationModel.find(query).sort({ createdAt: -1 }).limit(50).lean();
|
|
233
248
|
if (!notifications.length) {
|
|
234
|
-
await SettingsModel
|
|
235
|
-
$set: { lastDigestSentAt: now },
|
|
236
|
-
$setOnInsert: {
|
|
237
|
-
userId,
|
|
238
|
-
digestFrequency: "weekly"
|
|
239
|
-
}
|
|
240
|
-
}, { upsert: true });
|
|
249
|
+
await markDigestSent(SettingsModel, userId, now);
|
|
241
250
|
return {
|
|
242
251
|
ok: true,
|
|
243
252
|
sent: false,
|
|
@@ -272,13 +281,7 @@ var sendNotificationsDigestForUser = async (ctx, { userId, ability, force = fals
|
|
|
272
281
|
error: error instanceof Error ? error.message : "Email delivery failed"
|
|
273
282
|
};
|
|
274
283
|
}
|
|
275
|
-
await SettingsModel
|
|
276
|
-
$set: { lastDigestSentAt: now },
|
|
277
|
-
$setOnInsert: {
|
|
278
|
-
userId,
|
|
279
|
-
digestFrequency: "weekly"
|
|
280
|
-
}
|
|
281
|
-
}, { upsert: true });
|
|
284
|
+
await markDigestSent(SettingsModel, userId, now);
|
|
282
285
|
return {
|
|
283
286
|
ok: true,
|
|
284
287
|
sent: emailResult.skipped !== true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","names":["routes","Object","entries","import","meta","glob","reduce","Record","acc","path","mod","replace","randomUUID","Ctx","models","IRBNotificationPlatformPayload","AppAbility","NotificationDataPayload","Record","NotificationMetadataPayload","NotificationPlatformPayload","NotificationUrlMode","CreateNotificationInput","userId","topic","tag","title","body","image","url","urlMode","metadata","data","platform","CreateNotificationForUserInput","Omit","CreateNotificationsForUserInput","notifications","normalizeRequiredString","value","name","normalized","trim","Error","normalizeOptionalString","undefined","normalizeUrlMode","isRecord","Boolean","Array","isArray","toNotificationDataString","trimmed","Number","isFinite","String","toString","Date","getTime","toISOString","normalizeNotificationActionLink","normalizedUrl","parsed","URL","search","hash","startsWith","buildNotificationData","input","Pick","link","key","Object","entries","assign","linkMode","keys","length","buildNotificationPlatform","webpush","fcmOptions","createNotificationDocument","deliveryId","drawerActive","createdAt","assertCreateNotificationsForUserInput","createNotification","ctx","ability","Promise","id","can","modelCtx","req","NotificationModel","NotificationDeliveryModel","all","getUnsafe","notification","delivery","create","doc","findOneAndReplace","archivedAt","$exists","new","upsert","sort","updateOne","_id","$set","notificationId","createNotificationsForUser","ids","document","push","Ctx","models","IRBNotification","IRBNotificationSettings","AppAbility","sendEmail","DAY_MS","WEEK_MS","DigestFrequency","NotificationDoc","_id","SettingsDoc","getDigestWindowMs","frequency","formatIso","value","Date","undefined","toISOString","getNotificationLink","notification","dataLink","data","link","trim","platform","Array","isArray","webpush","fcmOptions","buildPreferencesByTopic","settings","Record","emailDigest","record","raw","topicPreferences","pref","topic","sendNotificationsDigestForUser","ctx","userId","ability","force","Promise","ok","sent","skippedReason","error","modelCtx","req","SettingsModel","get","NotificationModel","findOne","lean","digestFrequencyRaw","digestFrequency","now","windowMs","lastSentAt","lastDigestSentAt","since","getTime","preferencesByTopic","disabledTopics","Object","entries","filter","map","query","archivedAt","$exists","readAt","createdAt","$gt","length","$nin","notifications","find","sort","limit","updateOne","$set","$setOnInsert","upsert","UserModel","getGlobal","user","findById","email","subject","rows","n","title","body","url","line","join","html","emailResult","to","missingTransport","Error","message","skipped"],"sources":["../src/notifications/routes.ts","../src/notifications/createNotification.ts","../src/notifications/digest.ts"],"sourcesContent":["export const routes = Object.entries({\n ...import.meta.glob(\"./api/**/handler.ts\"),\n}).reduce<Record<string, unknown>>((acc, [path, mod]) => {\n acc[path.replace(\"./api/\", \"@rpcbase/server/notifications/api/\")] = mod\n return acc\n}, {})\n\n","import { randomUUID } from \"node:crypto\"\n\nimport type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotificationPlatformPayload } from \"@rpcbase/db\"\nimport type { AppAbility } from \"@rpcbase/db/acl\"\n\n\nexport type NotificationDataPayload = Record<string, string>\nexport type NotificationMetadataPayload = Record<string, unknown>\nexport type NotificationPlatformPayload = IRBNotificationPlatformPayload\nexport type NotificationUrlMode = \"navigate\" | \"merge_current_search\"\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n tag?: string\n title: string\n body?: string\n image?: string\n url?: string\n urlMode?: NotificationUrlMode\n metadata?: NotificationMetadataPayload\n data?: NotificationDataPayload\n platform?: NotificationPlatformPayload\n}\n\nexport type CreateNotificationForUserInput = Omit<CreateNotificationInput, \"userId\">\n\nexport type CreateNotificationsForUserInput = {\n userId: string\n notifications: CreateNotificationForUserInput[]\n}\n\nconst normalizeRequiredString = (value: unknown, name: string): string => {\n const normalized = typeof value === \"string\" ? value.trim() : \"\"\n if (!normalized) {\n throw new Error(`${name} is required`)\n }\n return normalized\n}\n\nconst normalizeOptionalString = (value: string | undefined): string | undefined => {\n const normalized = typeof value === \"string\" ? value.trim() : \"\"\n return normalized || undefined\n}\n\nconst normalizeUrlMode = (value: unknown): NotificationUrlMode => {\n return value === \"merge_current_search\" ? \"merge_current_search\" : \"navigate\"\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value)\n\nconst toNotificationDataString = (value: unknown): string | undefined => {\n if (typeof value === \"string\") {\n const trimmed = value.trim()\n return trimmed || undefined\n }\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value)\n if (typeof value === \"boolean\") return String(value)\n if (typeof value === \"bigint\") return value.toString()\n if (value instanceof Date && Number.isFinite(value.getTime())) return value.toISOString()\n return undefined\n}\n\nconst normalizeNotificationActionLink = (url: string | undefined, urlMode: NotificationUrlMode): string | undefined => {\n const normalizedUrl = normalizeOptionalString(url)\n if (!normalizedUrl) return undefined\n if (urlMode === \"navigate\") return normalizedUrl\n\n try {\n const parsed = new URL(normalizedUrl, \"https://rpcbase.local/\")\n return `${parsed.search}${parsed.hash}` || undefined\n } catch {\n return normalizedUrl.startsWith(\"?\") ? normalizedUrl : undefined\n }\n}\n\nconst buildNotificationData = (\n input: Pick<CreateNotificationInput, \"data\" | \"metadata\"> & { link?: string; urlMode: NotificationUrlMode },\n): NotificationDataPayload | undefined => {\n const data: NotificationDataPayload = {}\n\n for (const [key, value] of Object.entries(input.metadata ?? {})) {\n const normalized = toNotificationDataString(value)\n if (normalized) {\n data[key] = normalized\n }\n }\n\n if (input.data) {\n Object.assign(data, input.data)\n }\n\n if (input.link) {\n data.link = input.link\n if (input.urlMode !== \"navigate\") {\n data.linkMode = input.urlMode\n }\n }\n\n return Object.keys(data).length > 0 ? data : undefined\n}\n\nconst buildNotificationPlatform = (\n platform: NotificationPlatformPayload | undefined,\n link: string | undefined,\n): NotificationPlatformPayload | undefined => {\n if (!link) return platform\n\n const webpush = isRecord(platform?.webpush) ? platform.webpush : {}\n const fcmOptions = isRecord(webpush.fcmOptions) ? webpush.fcmOptions : {}\n\n return {\n ...platform,\n webpush: {\n ...webpush,\n fcmOptions: {\n ...fcmOptions,\n link,\n },\n },\n }\n}\n\nconst createNotificationDocument = (input: CreateNotificationInput) => {\n const userId = normalizeRequiredString(input.userId, \"createNotification: userId\")\n const title = normalizeRequiredString(input.title, \"createNotification: title\")\n const topic = normalizeOptionalString(input.topic)\n const tag = normalizeOptionalString(input.tag)\n const body = normalizeOptionalString(input.body)\n const image = normalizeOptionalString(input.image)\n const urlMode = normalizeUrlMode(input.urlMode)\n const link = normalizeNotificationActionLink(input.url, urlMode)\n const data = buildNotificationData({ data: input.data, metadata: input.metadata, link, urlMode })\n const platform = buildNotificationPlatform(input.platform, link)\n\n return {\n userId,\n ...(topic ? { topic } : {}),\n ...(tag ? { tag } : {}),\n deliveryId: randomUUID(),\n title,\n ...(body ? { body } : {}),\n ...(image ? { image } : {}),\n ...(data ? { data } : {}),\n ...(platform ? { platform } : {}),\n drawerActive: true,\n createdAt: new Date(),\n }\n}\n\nconst assertCreateNotificationsForUserInput = (input: CreateNotificationsForUserInput): void => {\n if (typeof input.userId !== \"string\" || input.userId.trim().length === 0) {\n throw new Error(\"createNotificationsForUser: userId is required\")\n }\n if (!Array.isArray(input.notifications)) {\n throw new Error(\"createNotificationsForUser: notifications must be an array\")\n }\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n ability: AppAbility,\n): Promise<{ id: string }> => {\n if (!ability.can(\"create\", \"RBNotification\")) {\n throw new Error(\"createNotification: forbidden\")\n }\n\n const modelCtx = { req: ctx.req, ability }\n const [NotificationModel, NotificationDeliveryModel] = await Promise.all([\n models.getUnsafe(\"RBNotification\", modelCtx),\n models.getUnsafe(\"RBNotificationDelivery\", modelCtx),\n ])\n const notification = createNotificationDocument(input)\n const delivery = await NotificationDeliveryModel.create(notification)\n const doc = notification.tag\n ? await NotificationModel.findOneAndReplace(\n { userId: notification.userId, tag: notification.tag, archivedAt: { $exists: false } },\n notification,\n { new: true, upsert: true, sort: { drawerActive: -1, createdAt: -1 } },\n )\n : await NotificationModel.create(notification)\n\n if (!doc) {\n throw new Error(\"createNotification: failed to persist notification\")\n }\n\n await NotificationDeliveryModel.updateOne(\n { _id: delivery._id },\n { $set: { notificationId: doc._id.toString() } },\n )\n\n return { id: doc._id.toString() }\n}\n\nexport const createNotificationsForUser = async (\n ctx: Ctx,\n input: CreateNotificationsForUserInput,\n ability: AppAbility,\n): Promise<{ ids: string[] }> => {\n assertCreateNotificationsForUserInput(input)\n if (input.notifications.length === 0) {\n return { ids: [] }\n }\n\n if (!ability.can(\"create\", \"RBNotification\")) {\n throw new Error(\"createNotificationsForUser: forbidden\")\n }\n\n const modelCtx = { req: ctx.req, ability }\n const [NotificationModel, NotificationDeliveryModel] = await Promise.all([\n models.getUnsafe(\"RBNotification\", modelCtx),\n models.getUnsafe(\"RBNotificationDelivery\", modelCtx),\n ])\n const ids: string[] = []\n for (const notification of input.notifications) {\n const document = createNotificationDocument({\n ...notification,\n userId: input.userId,\n })\n const delivery = await NotificationDeliveryModel.create(document)\n const doc = document.tag\n ? await NotificationModel.findOneAndReplace(\n { userId: document.userId, tag: document.tag, archivedAt: { $exists: false } },\n document,\n { new: true, upsert: true, sort: { drawerActive: -1, createdAt: -1 } },\n )\n : await NotificationModel.create(document)\n\n if (!doc) {\n throw new Error(\"createNotificationsForUser: failed to persist notification\")\n }\n await NotificationDeliveryModel.updateOne(\n { _id: delivery._id },\n { $set: { notificationId: doc._id.toString() } },\n )\n ids.push(doc._id.toString())\n }\n\n return { ids }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\nimport type { AppAbility } from \"@rpcbase/db/acl\"\n\nimport { sendEmail } from \"../email\"\n\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst WEEK_MS = 7 * DAY_MS\n\ntype DigestFrequency = \"off\" | \"daily\" | \"weekly\"\n\ntype NotificationDoc = IRBNotification & { _id: unknown }\ntype SettingsDoc = IRBNotificationSettings & { _id: unknown }\n\nconst getDigestWindowMs = (frequency: DigestFrequency): number => {\n if (frequency === \"daily\") return DAY_MS\n if (frequency === \"weekly\") return WEEK_MS\n return 0\n}\n\nconst formatIso = (value: unknown): string | undefined => {\n if (!(value instanceof Date)) return undefined\n return value.toISOString()\n}\n\nconst getNotificationLink = (notification: NotificationDoc): string => {\n const dataLink = notification.data?.link?.trim()\n if (dataLink) return dataLink\n\n const platform = notification.platform\n if (!platform || typeof platform !== \"object\" || Array.isArray(platform)) return \"\"\n const webpush = (platform as { webpush?: unknown }).webpush\n if (!webpush || typeof webpush !== \"object\" || Array.isArray(webpush)) return \"\"\n const fcmOptions = (webpush as { fcmOptions?: unknown }).fcmOptions\n if (!fcmOptions || typeof fcmOptions !== \"object\" || Array.isArray(fcmOptions)) return \"\"\n const link = (fcmOptions as { link?: unknown }).link\n return typeof link === \"string\" ? link.trim() : \"\"\n}\n\nconst buildPreferencesByTopic = (settings: SettingsDoc | null): Record<string, { emailDigest: boolean }> => {\n const record: Record<string, { emailDigest: boolean }> = {}\n const raw = settings?.topicPreferences\n if (!Array.isArray(raw)) return record\n for (const pref of raw) {\n if (!pref || typeof pref !== \"object\") continue\n const topic = typeof (pref as { topic?: unknown }).topic === \"string\" ? (pref as { topic: string }).topic.trim() : \"\"\n if (!topic) continue\n const emailDigest = (pref as { emailDigest?: unknown }).emailDigest\n record[topic] = { emailDigest: emailDigest === true }\n }\n return record\n}\n\nexport const sendNotificationsDigestForUser = async (\n ctx: Ctx,\n {\n userId,\n ability,\n force = false,\n }: {\n userId: string\n ability: AppAbility\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const modelCtx = { req: ctx.req, ability }\n const SettingsModel = await models.get(\"RBNotificationSettings\", modelCtx)\n const NotificationModel = await models.get(\"RBNotification\", modelCtx)\n\n const settings = (await SettingsModel.findOne({ userId }).lean()) as SettingsDoc | null\n\n const digestFrequencyRaw = typeof settings?.digestFrequency === \"string\" ? settings.digestFrequency : \"weekly\"\n const digestFrequency: DigestFrequency =\n digestFrequencyRaw === \"daily\" || digestFrequencyRaw === \"weekly\" || digestFrequencyRaw === \"off\"\n ? digestFrequencyRaw\n : \"weekly\"\n\n if (digestFrequency === \"off\") {\n return { ok: true, sent: false, skippedReason: \"digest_off\" }\n }\n\n const now = new Date()\n const windowMs = getDigestWindowMs(digestFrequency)\n const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null\n const since = lastSentAt ?? new Date(now.getTime() - windowMs)\n\n if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {\n return { ok: true, sent: false, skippedReason: \"not_due\" }\n }\n\n const preferencesByTopic = buildPreferencesByTopic(settings)\n const disabledTopics = Object.entries(preferencesByTopic)\n .filter(([, pref]) => pref.emailDigest === false)\n .map(([topic]) => topic)\n\n const query: Record<string, unknown> = {\n userId,\n archivedAt: { $exists: false },\n readAt: { $exists: false },\n createdAt: { $gt: since },\n }\n\n if (disabledTopics.length > 0) {\n query.topic = { $nin: disabledTopics }\n }\n\n const notifications = (await NotificationModel.find(query)\n .sort({ createdAt: -1 })\n .limit(50)\n .lean()) as NotificationDoc[]\n\n if (!notifications.length) {\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n return { ok: true, sent: false, skippedReason: \"empty\" }\n }\n\n const UserModel = await models.getGlobal(\"RBUser\", ctx)\n const user = (await UserModel.findById(userId, { email: 1 }).lean()) as { email?: unknown } | null\n const email = typeof user?.email === \"string\" ? user.email.trim() : \"\"\n if (!email) {\n return { ok: true, sent: false, skippedReason: \"missing_email\" }\n }\n\n const subject = \"Notifications digest\"\n const rows = notifications\n .map((n) => {\n const title = typeof n.title === \"string\" ? n.title : \"\"\n const body = typeof n.body === \"string\" ? n.body : \"\"\n const url = getNotificationLink(n)\n const createdAt = formatIso(n.createdAt) ?? \"\"\n const line = url\n ? `<li><strong>${title}</strong><br>${body}<br><a href=\"${url}\">${url}</a><br><small>${createdAt}</small></li>`\n : `<li><strong>${title}</strong><br>${body}<br><small>${createdAt}</small></li>`\n return line\n })\n .join(\"\")\n\n const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`\n\n let emailResult\n try {\n emailResult = await sendEmail(\n { to: email, subject, html },\n { missingTransport: \"skip\" },\n )\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : \"Email delivery failed\",\n }\n }\n\n await SettingsModel.updateOne(\n { userId },\n { $set: { lastDigestSentAt: now }, $setOnInsert: { userId, digestFrequency: \"weekly\" } },\n { upsert: true },\n )\n\n return { ok: true, sent: emailResult.skipped !== true, ...(emailResult.skipped ? { skippedReason: \"email_skipped\" } : {}) }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notifications.js","names":["routes","Object","entries","import","meta","glob","reduce","Record","acc","path","mod","replace","randomUUID","Ctx","models","IRBNotificationPlatformPayload","AppAbility","NotificationDataPayload","Record","NotificationMetadataPayload","NotificationPlatformPayload","NotificationUrlMode","CreateNotificationInput","userId","topic","tag","title","body","image","url","urlMode","metadata","data","platform","CreateNotificationForUserInput","Omit","CreateNotificationsForUserInput","notifications","normalizeRequiredString","value","name","normalized","trim","Error","normalizeOptionalString","undefined","normalizeUrlMode","isRecord","Boolean","Array","isArray","toNotificationDataString","trimmed","Number","isFinite","String","toString","Date","getTime","toISOString","normalizeNotificationActionLink","normalizedUrl","parsed","URL","search","hash","startsWith","buildNotificationData","input","Pick","link","key","Object","entries","assign","linkMode","keys","length","buildNotificationPlatform","webpush","fcmOptions","createNotificationDocument","deliveryId","drawerActive","createdAt","assertCreateNotificationsForUserInput","createNotification","ctx","ability","Promise","id","can","modelCtx","req","NotificationModel","NotificationDeliveryModel","all","getUnsafe","notification","delivery","create","doc","findOneAndReplace","archivedAt","$exists","new","upsert","sort","updateOne","_id","$set","notificationId","createNotificationsForUser","ids","document","push","Ctx","models","IRBNotification","IRBNotificationSettings","AppAbility","Model","sendEmail","DAY_MS","WEEK_MS","DigestFrequency","NotificationDoc","_id","SettingsDoc","isDuplicateKeyError","error","Boolean","code","markDigestSent","SettingsModel","userId","lastDigestSentAt","Date","Promise","update","$set","result","updateOne","matchedCount","create","digestFrequency","getDigestWindowMs","frequency","formatIso","value","undefined","toISOString","getNotificationLink","notification","dataLink","data","link","trim","platform","Array","isArray","webpush","fcmOptions","buildPreferencesByTopic","settings","Record","emailDigest","record","raw","topicPreferences","pref","topic","sendNotificationsDigestForUser","ctx","ability","force","ok","sent","skippedReason","modelCtx","req","get","NotificationModel","findOne","lean","digestFrequencyRaw","now","windowMs","lastSentAt","since","getTime","preferencesByTopic","disabledTopics","Object","entries","filter","map","query","archivedAt","$exists","readAt","createdAt","$gt","length","$nin","notifications","find","sort","limit","UserModel","getGlobal","user","findById","email","subject","rows","n","title","body","url","line","join","html","emailResult","to","missingTransport","Error","message","skipped"],"sources":["../src/notifications/routes.ts","../src/notifications/createNotification.ts","../src/notifications/digest.ts"],"sourcesContent":["export const routes = Object.entries({\n ...import.meta.glob(\"./api/**/handler.ts\"),\n}).reduce<Record<string, unknown>>((acc, [path, mod]) => {\n acc[path.replace(\"./api/\", \"@rpcbase/server/notifications/api/\")] = mod\n return acc\n}, {})\n\n","import { randomUUID } from \"node:crypto\"\n\nimport type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotificationPlatformPayload } from \"@rpcbase/db\"\nimport type { AppAbility } from \"@rpcbase/db/acl\"\n\n\nexport type NotificationDataPayload = Record<string, string>\nexport type NotificationMetadataPayload = Record<string, unknown>\nexport type NotificationPlatformPayload = IRBNotificationPlatformPayload\nexport type NotificationUrlMode = \"navigate\" | \"merge_current_search\"\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n tag?: string\n title: string\n body?: string\n image?: string\n url?: string\n urlMode?: NotificationUrlMode\n metadata?: NotificationMetadataPayload\n data?: NotificationDataPayload\n platform?: NotificationPlatformPayload\n}\n\nexport type CreateNotificationForUserInput = Omit<CreateNotificationInput, \"userId\">\n\nexport type CreateNotificationsForUserInput = {\n userId: string\n notifications: CreateNotificationForUserInput[]\n}\n\nconst normalizeRequiredString = (value: unknown, name: string): string => {\n const normalized = typeof value === \"string\" ? value.trim() : \"\"\n if (!normalized) {\n throw new Error(`${name} is required`)\n }\n return normalized\n}\n\nconst normalizeOptionalString = (value: string | undefined): string | undefined => {\n const normalized = typeof value === \"string\" ? value.trim() : \"\"\n return normalized || undefined\n}\n\nconst normalizeUrlMode = (value: unknown): NotificationUrlMode => {\n return value === \"merge_current_search\" ? \"merge_current_search\" : \"navigate\"\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value)\n\nconst toNotificationDataString = (value: unknown): string | undefined => {\n if (typeof value === \"string\") {\n const trimmed = value.trim()\n return trimmed || undefined\n }\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value)\n if (typeof value === \"boolean\") return String(value)\n if (typeof value === \"bigint\") return value.toString()\n if (value instanceof Date && Number.isFinite(value.getTime())) return value.toISOString()\n return undefined\n}\n\nconst normalizeNotificationActionLink = (url: string | undefined, urlMode: NotificationUrlMode): string | undefined => {\n const normalizedUrl = normalizeOptionalString(url)\n if (!normalizedUrl) return undefined\n if (urlMode === \"navigate\") return normalizedUrl\n\n try {\n const parsed = new URL(normalizedUrl, \"https://rpcbase.local/\")\n return `${parsed.search}${parsed.hash}` || undefined\n } catch {\n return normalizedUrl.startsWith(\"?\") ? normalizedUrl : undefined\n }\n}\n\nconst buildNotificationData = (\n input: Pick<CreateNotificationInput, \"data\" | \"metadata\"> & { link?: string; urlMode: NotificationUrlMode },\n): NotificationDataPayload | undefined => {\n const data: NotificationDataPayload = {}\n\n for (const [key, value] of Object.entries(input.metadata ?? {})) {\n const normalized = toNotificationDataString(value)\n if (normalized) {\n data[key] = normalized\n }\n }\n\n if (input.data) {\n Object.assign(data, input.data)\n }\n\n if (input.link) {\n data.link = input.link\n if (input.urlMode !== \"navigate\") {\n data.linkMode = input.urlMode\n }\n }\n\n return Object.keys(data).length > 0 ? data : undefined\n}\n\nconst buildNotificationPlatform = (\n platform: NotificationPlatformPayload | undefined,\n link: string | undefined,\n): NotificationPlatformPayload | undefined => {\n if (!link) return platform\n\n const webpush = isRecord(platform?.webpush) ? platform.webpush : {}\n const fcmOptions = isRecord(webpush.fcmOptions) ? webpush.fcmOptions : {}\n\n return {\n ...platform,\n webpush: {\n ...webpush,\n fcmOptions: {\n ...fcmOptions,\n link,\n },\n },\n }\n}\n\nconst createNotificationDocument = (input: CreateNotificationInput) => {\n const userId = normalizeRequiredString(input.userId, \"createNotification: userId\")\n const title = normalizeRequiredString(input.title, \"createNotification: title\")\n const topic = normalizeOptionalString(input.topic)\n const tag = normalizeOptionalString(input.tag)\n const body = normalizeOptionalString(input.body)\n const image = normalizeOptionalString(input.image)\n const urlMode = normalizeUrlMode(input.urlMode)\n const link = normalizeNotificationActionLink(input.url, urlMode)\n const data = buildNotificationData({ data: input.data, metadata: input.metadata, link, urlMode })\n const platform = buildNotificationPlatform(input.platform, link)\n\n return {\n userId,\n ...(topic ? { topic } : {}),\n ...(tag ? { tag } : {}),\n deliveryId: randomUUID(),\n title,\n ...(body ? { body } : {}),\n ...(image ? { image } : {}),\n ...(data ? { data } : {}),\n ...(platform ? { platform } : {}),\n drawerActive: true,\n createdAt: new Date(),\n }\n}\n\nconst assertCreateNotificationsForUserInput = (input: CreateNotificationsForUserInput): void => {\n if (typeof input.userId !== \"string\" || input.userId.trim().length === 0) {\n throw new Error(\"createNotificationsForUser: userId is required\")\n }\n if (!Array.isArray(input.notifications)) {\n throw new Error(\"createNotificationsForUser: notifications must be an array\")\n }\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n ability: AppAbility,\n): Promise<{ id: string }> => {\n if (!ability.can(\"create\", \"RBNotification\")) {\n throw new Error(\"createNotification: forbidden\")\n }\n\n const modelCtx = { req: ctx.req, ability }\n const [NotificationModel, NotificationDeliveryModel] = await Promise.all([\n models.getUnsafe(\"RBNotification\", modelCtx),\n models.getUnsafe(\"RBNotificationDelivery\", modelCtx),\n ])\n const notification = createNotificationDocument(input)\n const delivery = await NotificationDeliveryModel.create(notification)\n const doc = notification.tag\n ? await NotificationModel.findOneAndReplace(\n { userId: notification.userId, tag: notification.tag, archivedAt: { $exists: false } },\n notification,\n { new: true, upsert: true, sort: { drawerActive: -1, createdAt: -1 } },\n )\n : await NotificationModel.create(notification)\n\n if (!doc) {\n throw new Error(\"createNotification: failed to persist notification\")\n }\n\n await NotificationDeliveryModel.updateOne(\n { _id: delivery._id },\n { $set: { notificationId: doc._id.toString() } },\n )\n\n return { id: doc._id.toString() }\n}\n\nexport const createNotificationsForUser = async (\n ctx: Ctx,\n input: CreateNotificationsForUserInput,\n ability: AppAbility,\n): Promise<{ ids: string[] }> => {\n assertCreateNotificationsForUserInput(input)\n if (input.notifications.length === 0) {\n return { ids: [] }\n }\n\n if (!ability.can(\"create\", \"RBNotification\")) {\n throw new Error(\"createNotificationsForUser: forbidden\")\n }\n\n const modelCtx = { req: ctx.req, ability }\n const [NotificationModel, NotificationDeliveryModel] = await Promise.all([\n models.getUnsafe(\"RBNotification\", modelCtx),\n models.getUnsafe(\"RBNotificationDelivery\", modelCtx),\n ])\n const ids: string[] = []\n for (const notification of input.notifications) {\n const document = createNotificationDocument({\n ...notification,\n userId: input.userId,\n })\n const delivery = await NotificationDeliveryModel.create(document)\n const doc = document.tag\n ? await NotificationModel.findOneAndReplace(\n { userId: document.userId, tag: document.tag, archivedAt: { $exists: false } },\n document,\n { new: true, upsert: true, sort: { drawerActive: -1, createdAt: -1 } },\n )\n : await NotificationModel.create(document)\n\n if (!doc) {\n throw new Error(\"createNotificationsForUser: failed to persist notification\")\n }\n await NotificationDeliveryModel.updateOne(\n { _id: delivery._id },\n { $set: { notificationId: doc._id.toString() } },\n )\n ids.push(doc._id.toString())\n }\n\n return { ids }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\nimport type { AppAbility } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport { sendEmail } from \"../email\"\n\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst WEEK_MS = 7 * DAY_MS\n\ntype DigestFrequency = \"off\" | \"daily\" | \"weekly\"\n\ntype NotificationDoc = IRBNotification & { _id: unknown }\ntype SettingsDoc = IRBNotificationSettings & { _id: unknown }\n\nconst isDuplicateKeyError = (error: unknown): boolean =>\n Boolean(error && typeof error === \"object\" && \"code\" in error && error.code === 11000)\n\nconst markDigestSent = async (\n SettingsModel: Model<IRBNotificationSettings>,\n userId: string,\n lastDigestSentAt: Date,\n): Promise<void> => {\n const update = { $set: { lastDigestSentAt } }\n const result = await SettingsModel.updateOne({ userId }, update)\n if (result.matchedCount > 0) return\n\n try {\n await SettingsModel.create({\n userId,\n digestFrequency: \"weekly\",\n lastDigestSentAt,\n })\n } catch (error) {\n if (!isDuplicateKeyError(error)) throw error\n await SettingsModel.updateOne({ userId }, update)\n }\n}\n\nconst getDigestWindowMs = (frequency: DigestFrequency): number => {\n if (frequency === \"daily\") return DAY_MS\n if (frequency === \"weekly\") return WEEK_MS\n return 0\n}\n\nconst formatIso = (value: unknown): string | undefined => {\n if (!(value instanceof Date)) return undefined\n return value.toISOString()\n}\n\nconst getNotificationLink = (notification: NotificationDoc): string => {\n const dataLink = notification.data?.link?.trim()\n if (dataLink) return dataLink\n\n const platform = notification.platform\n if (!platform || typeof platform !== \"object\" || Array.isArray(platform)) return \"\"\n const webpush = (platform as { webpush?: unknown }).webpush\n if (!webpush || typeof webpush !== \"object\" || Array.isArray(webpush)) return \"\"\n const fcmOptions = (webpush as { fcmOptions?: unknown }).fcmOptions\n if (!fcmOptions || typeof fcmOptions !== \"object\" || Array.isArray(fcmOptions)) return \"\"\n const link = (fcmOptions as { link?: unknown }).link\n return typeof link === \"string\" ? link.trim() : \"\"\n}\n\nconst buildPreferencesByTopic = (settings: SettingsDoc | null): Record<string, { emailDigest: boolean }> => {\n const record: Record<string, { emailDigest: boolean }> = {}\n const raw = settings?.topicPreferences\n if (!Array.isArray(raw)) return record\n for (const pref of raw) {\n if (!pref || typeof pref !== \"object\") continue\n const topic = typeof (pref as { topic?: unknown }).topic === \"string\" ? (pref as { topic: string }).topic.trim() : \"\"\n if (!topic) continue\n const emailDigest = (pref as { emailDigest?: unknown }).emailDigest\n record[topic] = { emailDigest: emailDigest === true }\n }\n return record\n}\n\nexport const sendNotificationsDigestForUser = async (\n ctx: Ctx,\n {\n userId,\n ability,\n force = false,\n }: {\n userId: string\n ability: AppAbility\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const modelCtx = { req: ctx.req, ability }\n const SettingsModel = await models.get(\n \"RBNotificationSettings\",\n modelCtx,\n ) as Model<IRBNotificationSettings>\n const NotificationModel = await models.get(\"RBNotification\", modelCtx)\n\n const settings = (await SettingsModel.findOne({ userId }).lean()) as SettingsDoc | null\n\n const digestFrequencyRaw = typeof settings?.digestFrequency === \"string\" ? settings.digestFrequency : \"weekly\"\n const digestFrequency: DigestFrequency =\n digestFrequencyRaw === \"daily\" || digestFrequencyRaw === \"weekly\" || digestFrequencyRaw === \"off\"\n ? digestFrequencyRaw\n : \"weekly\"\n\n if (digestFrequency === \"off\") {\n return { ok: true, sent: false, skippedReason: \"digest_off\" }\n }\n\n const now = new Date()\n const windowMs = getDigestWindowMs(digestFrequency)\n const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null\n const since = lastSentAt ?? new Date(now.getTime() - windowMs)\n\n if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {\n return { ok: true, sent: false, skippedReason: \"not_due\" }\n }\n\n const preferencesByTopic = buildPreferencesByTopic(settings)\n const disabledTopics = Object.entries(preferencesByTopic)\n .filter(([, pref]) => pref.emailDigest === false)\n .map(([topic]) => topic)\n\n const query: Record<string, unknown> = {\n userId,\n archivedAt: { $exists: false },\n readAt: { $exists: false },\n createdAt: { $gt: since },\n }\n\n if (disabledTopics.length > 0) {\n query.topic = { $nin: disabledTopics }\n }\n\n const notifications = (await NotificationModel.find(query)\n .sort({ createdAt: -1 })\n .limit(50)\n .lean()) as NotificationDoc[]\n\n if (!notifications.length) {\n await markDigestSent(SettingsModel, userId, now)\n return { ok: true, sent: false, skippedReason: \"empty\" }\n }\n\n const UserModel = await models.getGlobal(\"RBUser\", ctx)\n const user = (await UserModel.findById(userId, { email: 1 }).lean()) as { email?: unknown } | null\n const email = typeof user?.email === \"string\" ? user.email.trim() : \"\"\n if (!email) {\n return { ok: true, sent: false, skippedReason: \"missing_email\" }\n }\n\n const subject = \"Notifications digest\"\n const rows = notifications\n .map((n) => {\n const title = typeof n.title === \"string\" ? n.title : \"\"\n const body = typeof n.body === \"string\" ? n.body : \"\"\n const url = getNotificationLink(n)\n const createdAt = formatIso(n.createdAt) ?? \"\"\n const line = url\n ? `<li><strong>${title}</strong><br>${body}<br><a href=\"${url}\">${url}</a><br><small>${createdAt}</small></li>`\n : `<li><strong>${title}</strong><br>${body}<br><small>${createdAt}</small></li>`\n return line\n })\n .join(\"\")\n\n const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`\n\n let emailResult\n try {\n emailResult = await sendEmail(\n { to: email, subject, html },\n { missingTransport: \"skip\" },\n )\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : \"Email delivery failed\",\n }\n }\n\n await markDigestSent(SettingsModel, userId, now)\n\n return { ok: true, sent: emailResult.skipped !== true, ...(emailResult.skipped ? { skippedReason: \"email_skipped\" } : {}) }\n}\n"],"mappings":"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"}
|
|
@@ -209,6 +209,7 @@ var getTenantModel = async (tenantId, modelName, ability) => {
|
|
|
209
209
|
};
|
|
210
210
|
return models.get(modelName, ctx);
|
|
211
211
|
};
|
|
212
|
+
var getUnsafeTenantModel = async (tenantId, modelName) => models.getUnsafe(modelName, { tenantId });
|
|
212
213
|
var normalizeLimit = (limit) => {
|
|
213
214
|
if (typeof limit !== "number") return QUERY_MAX_LIMIT;
|
|
214
215
|
if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT;
|
|
@@ -497,7 +498,7 @@ var runRtsQuery = async ({ tenantId, ability, modelName, query, options, allowIn
|
|
|
497
498
|
includeCursors: includePaginationCursors
|
|
498
499
|
});
|
|
499
500
|
const totalCount = typeof paginatedResult.totalCount === "number" && Number.isFinite(paginatedResult.totalCount) && paginatedResult.totalCount >= 0 ? Math.floor(paginatedResult.totalCount) : void 0;
|
|
500
|
-
const approximateTotalCount = totalCount === void 0 ? await runApproximateRtsCount(model, finalQuery).catch(() => void 0) : void 0;
|
|
501
|
+
const approximateTotalCount = totalCount === void 0 ? await getUnsafeTenantModel(tenantId, modelName).then((estimatedCountModel) => runApproximateRtsCount(model, estimatedCountModel, finalQuery)).catch(() => void 0) : void 0;
|
|
501
502
|
const resolvedTotalCount = totalCount ?? approximateTotalCount;
|
|
502
503
|
return {
|
|
503
504
|
data: Array.isArray(paginatedResult.nodes) ? stripAddedProjectionPaths(paginatedResult.nodes, cursorProjection.addedPaths) : [],
|
|
@@ -520,11 +521,11 @@ var prepareRtsExecution = async ({ tenantId, ability, modelName, query, allowInt
|
|
|
520
521
|
finalQuery: { $and: [query, getAccessibleByQuery(ability, "read", modelName)] }
|
|
521
522
|
};
|
|
522
523
|
};
|
|
523
|
-
var runApproximateRtsCount = async (model, query) => {
|
|
524
|
+
var runApproximateRtsCount = async (model, estimatedCountModel, query) => {
|
|
524
525
|
const unsupportedOperator = findUnsupportedApproxCountOperator(query);
|
|
525
526
|
if (unsupportedOperator) throw new Error(`Approximate RTS count does not support ${unsupportedOperator} queries`);
|
|
526
527
|
const castedQuery = castApproxCountQuery(model, query);
|
|
527
|
-
const estimatedTotal = normalizeNonNegativeInteger(await
|
|
528
|
+
const estimatedTotal = normalizeNonNegativeInteger(await estimatedCountModel.estimatedDocumentCount());
|
|
528
529
|
if (estimatedTotal === 0) return 0;
|
|
529
530
|
const sampleSize = Math.min(getApproxCountSampleSize(), estimatedTotal);
|
|
530
531
|
const sampleMatches = normalizeNonNegativeInteger((await model.aggregate([
|
|
@@ -544,7 +545,7 @@ var runRtsCount = async ({ tenantId, ability, modelName, query, allowInternalMod
|
|
|
544
545
|
query,
|
|
545
546
|
allowInternalModels
|
|
546
547
|
});
|
|
547
|
-
return await runApproximateRtsCount(model, finalQuery);
|
|
548
|
+
return await runApproximateRtsCount(model, await getUnsafeTenantModel(tenantId, modelName), finalQuery);
|
|
548
549
|
};
|
|
549
550
|
//#endregion
|
|
550
551
|
//#region src/rts/queryWindow.ts
|
|
@@ -552,4 +553,4 @@ var RTS_QUERY_WINDOW_MAX_COUNT = 4096;
|
|
|
552
553
|
//#endregion
|
|
553
554
|
export { resolveRtsQueryDependencyModelNames as a, runRtsQuery as c, getDerivedKey as d, normalizeRtsQueryOptions as i, syncAuthenticatedSessionFromRequest as l, buildRtsAbilityFromRequest as n, resolveRtsRequestTenantId as o, isRtsRequestAuthorized as r, runRtsCount as s, RTS_QUERY_WINDOW_MAX_COUNT as t, syncAuthenticatedSessionMiddleware as u };
|
|
554
555
|
|
|
555
|
-
//# sourceMappingURL=queryWindow-
|
|
556
|
+
//# sourceMappingURL=queryWindow-DNQxuY_M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryWindow-DNQxuY_M.js","names":["assert","hkdfSync","getDerivedKey","masterKey","info","length","salt","Buffer","from","toString","createHmac","timingSafeEqual","AUTHENTICATED_USER_ID_HEADER","AUTHENTICATED_TENANT_ID_HEADER","PROXY_AUTH_TIMESTAMP_HEADER","PROXY_AUTH_SIGNATURE_HEADER","MAX_PROXY_AUTH_AGE_MS","HeadersLike","Record","normalizeString","value","normalized","trim","getHeaderValue","headers","name","Array","isArray","getProxySharedSecret","secret","process","env","RB_PROXY_SHARED_SECRET","timingSafeEqualText","left","right","leftBuffer","Buffer","from","rightBuffer","length","buildProxyAuthSignature","userId","tenantId","timestamp","update","digest","getTrustedProxyAuth","signature","parsedTimestamp","Number","isInteger","now","Date","Math","abs","expectedSignature","RequestHandler","models","LoadModelCtx","getTrustedProxyAuth","SessionUserLike","id","currentTenantId","signedInTenants","tenantRoles","isEntryGateAuthorized","RequestLike","headers","Record","session","user","LoadedUserDoc","tenants","normalizeString","value","normalized","trim","normalizeStringArray","Array","isArray","map","entry","String","filter","Boolean","normalizeRoles","normalizeTenantRoles","undefined","Map","entries","from","tenantId","roles","normalizedTenantId","const","length","Object","fromEntries","nextRoles","isSessionAuthorizedForTenant","sessionUser","includes","loadSessionUser","userId","Promise","ctx","req","User","getGlobal","findById","lean","syncAuthenticatedSessionFromRequest","proxyAuth","sessionUserId","baseUser","nextSessionUser","syncAuthenticatedSessionMiddleware","_res","next","then","Request","PaginationPageInfo","PaginationSpec","models","LoadModelCtx","buildAbilityFromSession","getAccessibleByQuery","AclSubjectType","AppAbility","Model","getDerivedKey","JsonObject","Record","SessionUser","id","currentTenantId","RtsPopulateObject","path","model","select","match","options","sort","limit","populate","RtsPopulateOption","Array","RtsQueryOptions","projection","pagination","RtsQueryResult","data","pageInfo","totalCount","PreparedRtsExecution","finalQuery","QUERY_MAX_LIMIT","INTERNAL_MODEL_NAMES","Set","DEFAULT_APPROX_COUNT_SAMPLE_SIZE","MAX_APPROX_COUNT_SAMPLE_SIZE","UNSUPPORTED_APPROX_COUNT_OPERATORS","paginationCursorSigningSecret","getPaginationCursorSigningSecret","masterKey","process","env","MASTER_KEY","trim","Error","normalizeTenantId","value","normalized","getTenantIdFromRequest","req","session","user","resolveRtsRequestTenantId","resolveRtsRequestUserId","isRtsRequestAuthorized","tenantId","sessionUser","buildRtsAbilityFromRequest","Promise","ability","userId","sessionUserId","getTenantModel","modelName","ctx","get","getUnsafeTenantModel","getUnsafe","normalizeLimit","Number","isFinite","Math","min","abs","normalizeNonNegativeInteger","floor","getApproxCountSampleSize","raw","RB_RTS_APPROX_COUNT_SAMPLE_SIZE","parsed","findUnsupportedApproxCountOperator","isArray","entry","unsupportedOperator","key","nestedValue","Object","entries","has","castApproxCountQuery","query","castedQuery","find","cast","normalizeString","normalizeObject","undefined","normalizePagination","prepareCursorProjection","includeCursors","addedPaths","inclusionProjection","some","nextProjection","sortFields","map","field","filter","cursorPaths","from","excludedPath","projectedPath","startsWith","push","isIncluded","_id","stripPath","target","parts","split","current","index","length","part","child","cloned","stripAddedProjectionPaths","nodes","node","toObject","call","projected","normalizePopulateSelect","normalizePopulateOptions","max","normalizePopulateObject","nestedPopulate","normalizeRtsPopulateOption","normalizeModelName","resolvePopulateRefModelName","explicitModelName","schema","schemaPath","directRef","ref","arrayRef","caster","virtualPath","virtualpath","virtualRef","mergePopulateMatchWithAcl","populateMatch","aclMatch","keys","$and","PreparedPopulateObject","PreparedPopulateOption","resolvePopulateSpecForModel","allowInternalModels","modelCache","dependencyModelNames","Map","getModelCached","targetModelName","cached","loaded","set","resolveOne","parentModel","refModelName","can","add","Exclude","nestedModel","normalizedEntry","resolved","all","filtered","normalizeRtsQueryOptions","resolveRtsQueryDependencyModelNames","runRtsQuery","paginationMaxLimit","includePaginationCursors","prepareRtsExecution","cursorProjection","paginatedQuery","paginatedResult","paginate","cursor","signingSecret","maxLimit","approximateTotalCount","then","estimatedCountModel","runApproximateRtsCount","catch","resolvedTotalCount","queryPromise","accessQuery","estimatedTotal","estimatedDocumentCount","sampleSize","sampleResult","aggregate","$sample","size","$match","$count","count","sampleMatches","estimatedMatches","round","runRtsCount","RTS_QUERY_WINDOW_MAX_COUNT"],"sources":["../src/getDerivedKey.ts","../src/proxyAuth.ts","../src/syncAuthenticatedSession.ts","../src/rts/queryExecutor.ts","../src/rts/queryWindow.ts"],"sourcesContent":["import assert from \"assert\"\nimport { hkdfSync } from \"crypto\"\n\n\nexport const getDerivedKey = (\n masterKey: string,\n info: string,\n length: number = 32, // Default to 256-bit keys\n salt: string = \"\",\n): string => {\n assert(masterKey?.length >= 32, \"MASTER_KEY must be 32 chars or longer.\")\n\n return Buffer.from(hkdfSync(\n \"sha256\",\n masterKey,\n Buffer.from(salt),\n Buffer.from(info),\n length,\n )).toString(\"hex\")\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\"\n\n\nexport const AUTHENTICATED_USER_ID_HEADER = \"rb-authenticated-user-id\"\nexport const AUTHENTICATED_TENANT_ID_HEADER = \"rb-authenticated-tenant-id\"\nexport const PROXY_AUTH_TIMESTAMP_HEADER = \"rb-proxy-auth-timestamp\"\nexport const PROXY_AUTH_SIGNATURE_HEADER = \"rb-proxy-auth-signature\"\n\nconst MAX_PROXY_AUTH_AGE_MS = 5 * 60 * 1000\n\ntype HeadersLike = Record<string, string | string[] | undefined>\n\nconst normalizeString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized || null\n}\n\nconst getHeaderValue = (headers: HeadersLike | undefined, name: string): string | null => {\n const value = headers?.[name]\n if (Array.isArray(value)) return normalizeString(value[0])\n return normalizeString(value)\n}\n\nconst getProxySharedSecret = (): string | null => {\n const secret = process.env.RB_PROXY_SHARED_SECRET?.trim()\n return secret || null\n}\n\nconst timingSafeEqualText = (left: string, right: string): boolean => {\n const leftBuffer = Buffer.from(left)\n const rightBuffer = Buffer.from(right)\n if (leftBuffer.length !== rightBuffer.length) return false\n return timingSafeEqual(leftBuffer, rightBuffer)\n}\n\nexport const buildProxyAuthSignature = ({\n userId,\n tenantId,\n timestamp,\n secret,\n}: {\n userId: string\n tenantId: string\n timestamp: string\n secret: string\n}): string => {\n return createHmac(\"sha256\", secret)\n .update(`${timestamp}:${userId}:${tenantId}`)\n .digest(\"hex\")\n}\n\nexport const getTrustedProxyAuth = (\n headers: HeadersLike | undefined,\n): { userId: string; tenantId: string } | null => {\n const userId = getHeaderValue(headers, AUTHENTICATED_USER_ID_HEADER)\n const tenantId = getHeaderValue(headers, AUTHENTICATED_TENANT_ID_HEADER)\n const timestamp = getHeaderValue(headers, PROXY_AUTH_TIMESTAMP_HEADER)\n const signature = getHeaderValue(headers, PROXY_AUTH_SIGNATURE_HEADER)\n if (!userId || !tenantId || !timestamp || !signature) return null\n\n const parsedTimestamp = Number(timestamp)\n if (!Number.isInteger(parsedTimestamp)) return null\n\n const now = Date.now()\n if (Math.abs(now - parsedTimestamp) > MAX_PROXY_AUTH_AGE_MS) return null\n\n const secret = getProxySharedSecret()\n if (!secret) return null\n\n const expectedSignature = buildProxyAuthSignature({\n userId,\n tenantId,\n timestamp,\n secret,\n })\n\n if (!timingSafeEqualText(signature, expectedSignature)) return null\n\n return { userId, tenantId }\n}\n","import type { RequestHandler } from \"express\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\n\nimport {\n getTrustedProxyAuth,\n} from \"./proxyAuth\"\n\n\ntype SessionUserLike = {\n id?: unknown\n currentTenantId?: unknown\n signedInTenants?: unknown\n tenantRoles?: unknown\n isEntryGateAuthorized?: unknown\n}\n\ntype RequestLike = {\n headers?: Record<string, string | string[] | undefined>\n session?: {\n user?: SessionUserLike\n }\n}\n\ntype LoadedUserDoc = {\n tenants?: unknown\n tenantRoles?: unknown\n}\n\nconst normalizeString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized || null\n}\n\nconst normalizeStringArray = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => normalizeString(String(entry)))\n .filter((entry): entry is string => Boolean(entry))\n}\n\nconst normalizeRoles = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => normalizeString(entry))\n .filter((entry): entry is string => Boolean(entry))\n}\n\nconst normalizeTenantRoles = (value: unknown): Record<string, string[]> | undefined => {\n if (!value || typeof value !== \"object\") return undefined\n\n if (value instanceof Map) {\n const entries = Array.from(value.entries())\n .map(([tenantId, roles]) => {\n const normalizedTenantId = normalizeString(String(tenantId))\n if (!normalizedTenantId) return null\n return [normalizedTenantId, normalizeRoles(roles)] as const\n })\n .filter((entry): entry is readonly [string, string[]] => Boolean(entry))\n return entries.length ? Object.fromEntries(entries) : undefined\n }\n\n const nextRoles = Object.entries(value as Record<string, unknown>)\n .map(([tenantId, roles]) => {\n const normalizedTenantId = normalizeString(tenantId)\n if (!normalizedTenantId) return null\n return [normalizedTenantId, normalizeRoles(roles)] as const\n })\n .filter((entry): entry is readonly [string, string[]] => Boolean(entry))\n\n return nextRoles.length ? Object.fromEntries(nextRoles) : undefined\n}\n\nconst isSessionAuthorizedForTenant = (sessionUser: SessionUserLike | undefined, tenantId: string): boolean => {\n if (!sessionUser) return false\n\n const signedInTenants = normalizeStringArray(sessionUser.signedInTenants)\n if (signedInTenants.length > 0) {\n return signedInTenants.includes(tenantId)\n }\n\n const currentTenantId = normalizeString(sessionUser.currentTenantId)\n return currentTenantId === tenantId\n}\n\nconst loadSessionUser = async (userId: string, tenantId: string): Promise<Record<string, unknown> | null> => {\n const ctx: LoadModelCtx = { req: { session: null } }\n const User = await models.getGlobal(\"RBUser\", ctx)\n const user = await User.findById(userId, { tenants: 1, tenantRoles: 1 }).lean() as LoadedUserDoc | null\n if (!user) return null\n\n const signedInTenants = normalizeStringArray(user.tenants)\n if (!signedInTenants.includes(tenantId)) return null\n\n const tenantRoles = normalizeTenantRoles(user.tenantRoles)\n\n return {\n id: userId,\n currentTenantId: tenantId,\n signedInTenants,\n isEntryGateAuthorized: true,\n ...(tenantRoles ? { tenantRoles } : {}),\n }\n}\n\nexport const syncAuthenticatedSessionFromRequest = async (req: RequestLike): Promise<void> => {\n const proxyAuth = getTrustedProxyAuth(req.headers)\n if (!proxyAuth) return\n\n const { userId, tenantId } = proxyAuth\n\n const session = req.session\n if (!session) return\n\n const sessionUser = session.user\n const sessionUserId = normalizeString(sessionUser?.id)\n\n if (sessionUserId === userId && isSessionAuthorizedForTenant(sessionUser, tenantId)) {\n const currentTenantId = normalizeString(sessionUser?.currentTenantId)\n if (currentTenantId === tenantId) return\n\n const baseUser = sessionUser && typeof sessionUser === \"object\" ? sessionUser as Record<string, unknown> : {}\n session.user = {\n ...baseUser,\n id: userId,\n currentTenantId: tenantId,\n isEntryGateAuthorized: true,\n }\n return\n }\n\n const nextSessionUser = await loadSessionUser(userId, tenantId)\n if (!nextSessionUser) {\n if (session.user) {\n delete session.user\n }\n return\n }\n\n session.user = nextSessionUser as SessionUserLike\n}\n\nexport const syncAuthenticatedSessionMiddleware: RequestHandler = (req, _res, next) => {\n void syncAuthenticatedSessionFromRequest(req).then(() => {\n next()\n }, next)\n}\n","import type { Request } from \"express\"\nimport type { PaginationPageInfo, PaginationSpec } from \"@rpcbase/api\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbilityFromSession, getAccessibleByQuery, type AclSubjectType, type AppAbility } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport { getDerivedKey } from \"../getDerivedKey\"\n\n\ntype JsonObject = Record<string, unknown>\n\ntype SessionUser = {\n id?: unknown\n currentTenantId?: unknown\n}\n\nexport type RtsPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: RtsPopulateOption\n}\n\nexport type RtsPopulateOption =\n | string\n | RtsPopulateObject\n | Array<string | RtsPopulateObject>\n\nexport type RtsQueryOptions = {\n projection?: JsonObject\n sort?: Record<string, 1 | -1>\n limit?: number\n populate?: RtsPopulateOption\n pagination?: PaginationSpec\n}\n\nexport type RtsQueryResult = {\n data: unknown[]\n pageInfo?: PaginationPageInfo\n totalCount?: number\n}\n\ntype PreparedRtsExecution = {\n model: Model<any>\n finalQuery: JsonObject\n}\n\nconst QUERY_MAX_LIMIT = 4096\nconst INTERNAL_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\nconst DEFAULT_APPROX_COUNT_SAMPLE_SIZE = 1000\nconst MAX_APPROX_COUNT_SAMPLE_SIZE = 10_000\nconst UNSUPPORTED_APPROX_COUNT_OPERATORS = new Set([\"$text\", \"$near\", \"$nearSphere\", \"$where\"])\nlet paginationCursorSigningSecret: string | null = null\n\nconst getPaginationCursorSigningSecret = (): string => {\n if (paginationCursorSigningSecret) return paginationCursorSigningSecret\n const masterKey = process.env.MASTER_KEY?.trim()\n if (!masterKey) {\n throw new Error(\"MASTER_KEY must be defined to derive pagination cursor signing secret\")\n }\n paginationCursorSigningSecret = getDerivedKey(masterKey, \"pagination_cursor_signing\")\n return paginationCursorSigningSecret\n}\n\nconst normalizeTenantId = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst getTenantIdFromRequest = (req: Request): string | null => {\n return normalizeTenantId((req.session?.user as SessionUser | undefined)?.currentTenantId)\n}\n\nexport const resolveRtsRequestTenantId = (req: Request): string | null => {\n return getTenantIdFromRequest(req)\n}\n\nexport const resolveRtsRequestUserId = (req: Request): string | null => {\n return normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n}\n\nexport const isRtsRequestAuthorized = (req: Request, tenantId: string): boolean => {\n const sessionUser = req.session?.user as SessionUser | undefined\n if (!sessionUser) return false\n\n const currentTenantId = normalizeTenantId(sessionUser.currentTenantId)\n if (!currentTenantId) return false\n return currentTenantId === tenantId\n}\n\nexport const buildRtsAbilityFromRequest = async (\n req: Request,\n tenantId: string,\n): Promise<{ ability: AppAbility; userId: string | null }> => {\n const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (!sessionUserId) {\n const currentTenantId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.currentTenantId)\n if (!currentTenantId || currentTenantId !== tenantId) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n return {\n ability: buildAbilityFromSession({ tenantId, session: req.session }),\n userId: null,\n }\n }\n\n const currentTenantId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.currentTenantId)\n if (!currentTenantId || currentTenantId !== tenantId) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n return {\n ability: buildAbilityFromSession({ tenantId, session: req.session }),\n userId: sessionUserId,\n }\n}\n\nconst getTenantModel = async (tenantId: string, modelName: string, ability: AppAbility): Promise<Model<any>> => {\n const ctx: LoadModelCtx = {\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n ability,\n }\n\n return models.get(modelName, ctx)\n}\n\nconst getUnsafeTenantModel = async (tenantId: string, modelName: string): Promise<Model<any>> =>\n models.getUnsafe(modelName, { tenantId })\n\nconst normalizeLimit = (limit?: number): number => {\n if (typeof limit !== \"number\") return QUERY_MAX_LIMIT\n if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT\n return Math.min(QUERY_MAX_LIMIT, Math.abs(limit))\n}\n\nconst normalizeNonNegativeInteger = (value: unknown): number => {\n if (typeof value !== \"number\") return 0\n if (!Number.isFinite(value) || value < 0) return 0\n return Math.floor(value)\n}\n\nconst getApproxCountSampleSize = (): number => {\n const raw = process.env.RB_RTS_APPROX_COUNT_SAMPLE_SIZE?.trim() ?? \"\"\n if (!raw) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE\n\n const parsed = Number(raw)\n if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE\n return Math.min(MAX_APPROX_COUNT_SAMPLE_SIZE, Math.floor(parsed))\n}\n\nconst findUnsupportedApproxCountOperator = (value: unknown): string | null => {\n if (!value || typeof value !== \"object\") return null\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n const unsupportedOperator = findUnsupportedApproxCountOperator(entry)\n if (unsupportedOperator) return unsupportedOperator\n }\n return null\n }\n\n for (const [key, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n if (UNSUPPORTED_APPROX_COUNT_OPERATORS.has(key)) {\n return key\n }\n\n const unsupportedOperator = findUnsupportedApproxCountOperator(nestedValue)\n if (unsupportedOperator) return unsupportedOperator\n }\n\n return null\n}\n\nconst castApproxCountQuery = (model: Model<any>, query: JsonObject): JsonObject => {\n const castedQuery = model.find(query).cast(model)\n if (!castedQuery || typeof castedQuery !== \"object\" || Array.isArray(castedQuery)) {\n return query\n }\n\n return castedQuery as JsonObject\n}\n\nconst normalizeString = (value: unknown): string => {\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\nconst normalizeObject = (value: unknown): JsonObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as JsonObject\n}\n\nconst normalizePagination = (value: unknown): PaginationSpec | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n return value as PaginationSpec\n}\n\nconst prepareCursorProjection = (\n projection: JsonObject | undefined,\n pagination: PaginationSpec,\n includeCursors: boolean,\n): { projection: JsonObject | undefined; addedPaths: string[] } => {\n if (!projection || !includeCursors) return { projection, addedPaths: [] }\n\n const inclusionProjection = Object.entries(projection).some(([, value]) => value === 1)\n const nextProjection = { ...projection }\n const addedPaths: string[] = []\n const sortFields = Array.isArray(pagination.sort)\n ? pagination.sort\n .map((entry) => entry?.field)\n .filter((field): field is string => typeof field === \"string\")\n : []\n const cursorPaths = Array.from(new Set([...sortFields, \"_id\"]))\n\n if (!inclusionProjection) {\n for (const path of cursorPaths) {\n const excludedPath = Object.entries(projection).find(([projectedPath, value]) =>\n value === 0 && (projectedPath === path || path.startsWith(`${projectedPath}.`)),\n )?.[0]\n if (!excludedPath) continue\n if (excludedPath !== path) {\n throw new Error(`Pagination projection cannot exclude cursor path: ${path}`)\n }\n delete nextProjection[path]\n addedPaths.push(path)\n }\n return { projection: nextProjection, addedPaths }\n }\n\n for (const path of cursorPaths) {\n const isIncluded = path === \"_id\"\n ? projection._id !== 0\n : Object.entries(projection).some(([projectedPath, value]) =>\n value === 1 && (projectedPath === path || path.startsWith(`${projectedPath}.`)),\n )\n if (isIncluded) continue\n nextProjection[path] = 1\n addedPaths.push(path)\n }\n\n return { projection: nextProjection, addedPaths }\n}\n\nconst stripPath = (target: JsonObject, path: string): void => {\n const parts = path.split(\".\")\n let current = target\n\n for (let index = 0; index < parts.length - 1; index += 1) {\n const part = parts[index]!\n const child = current[part]\n if (!child || typeof child !== \"object\" || Array.isArray(child)) return\n const cloned = { ...(child as JsonObject) }\n current[part] = cloned\n current = cloned\n }\n\n delete current[parts[parts.length - 1]!]\n}\n\nconst stripAddedProjectionPaths = (nodes: unknown[], addedPaths: string[]): unknown[] => {\n if (!addedPaths.length) return nodes\n\n return nodes.map((node) => {\n if (!node || typeof node !== \"object\" || Array.isArray(node)) return node\n const toObject = (node as { toObject?: unknown }).toObject\n const raw = typeof toObject === \"function\"\n ? toObject.call(node)\n : node\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return raw\n const projected = { ...(raw as JsonObject) }\n for (const path of addedPaths) stripPath(projected, path)\n return projected\n })\n}\n\nconst normalizePopulateSelect = (value: unknown): string | JsonObject | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n return normalizeObject(value)\n}\n\nconst normalizePopulateOptions = (value: unknown): RtsPopulateObject[\"options\"] | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as { sort?: unknown; limit?: unknown }\n const normalized: RtsPopulateObject[\"options\"] = {}\n\n if (raw.sort && typeof raw.sort === \"object\" && !Array.isArray(raw.sort)) {\n normalized.sort = raw.sort as Record<string, 1 | -1>\n }\n\n if (typeof raw.limit === \"number\" && Number.isFinite(raw.limit)) {\n normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)))\n }\n\n if (!normalized.sort && normalized.limit === undefined) return undefined\n return normalized\n}\n\nconst normalizePopulateObject = (value: unknown): RtsPopulateObject | undefined => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined\n const raw = value as Record<string, unknown>\n const path = normalizeString(raw.path)\n if (!path) return undefined\n\n const normalized: RtsPopulateObject = { path }\n\n const model = normalizeString(raw.model)\n if (model) normalized.model = model\n\n const select = normalizePopulateSelect(raw.select)\n if (select !== undefined) normalized.select = select\n\n const match = normalizeObject(raw.match)\n if (match) normalized.match = match\n\n const nestedPopulate = normalizeRtsPopulateOption(raw.populate)\n if (nestedPopulate !== undefined) normalized.populate = nestedPopulate\n\n const options = normalizePopulateOptions(raw.options)\n if (options) normalized.options = options\n\n return normalized\n}\n\nconst normalizeRtsPopulateOption = (value: unknown): RtsPopulateOption | undefined => {\n if (typeof value === \"string\") {\n const normalized = value.trim()\n return normalized || undefined\n }\n\n if (Array.isArray(value)) {\n const normalized = value\n .map((entry) => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n return path || null\n }\n return normalizePopulateObject(entry) ?? null\n })\n .filter((entry): entry is string | RtsPopulateObject => entry !== null)\n\n return normalized.length > 0 ? normalized : undefined\n }\n\n return normalizePopulateObject(value)\n}\n\nconst normalizeModelName = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized || null\n}\n\nconst resolvePopulateRefModelName = (\n model: Model<any>,\n path: string,\n explicitModelName: string | null,\n): string | null => {\n if (explicitModelName) return explicitModelName\n\n const schema = model.schema as any\n const schemaPath = typeof schema.path === \"function\" ? schema.path(path) : null\n const directRef = normalizeModelName(schemaPath?.options?.ref)\n if (directRef) return directRef\n\n const arrayRef = normalizeModelName(schemaPath?.caster?.options?.ref)\n if (arrayRef) return arrayRef\n\n const virtualPath = typeof schema.virtualpath === \"function\" ? schema.virtualpath(path) : null\n const virtualRef = normalizeModelName(virtualPath?.options?.ref)\n if (virtualRef) return virtualRef\n\n return null\n}\n\nconst mergePopulateMatchWithAcl = (\n populateMatch: JsonObject | undefined,\n aclMatch: JsonObject,\n): JsonObject => {\n if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch\n return { $and: [populateMatch, aclMatch] }\n}\n\ntype PreparedPopulateObject = {\n path: string\n model?: string\n select?: string | JsonObject\n match?: JsonObject\n options?: {\n sort?: Record<string, 1 | -1>\n limit?: number\n }\n populate?: PreparedPopulateOption\n}\n\ntype PreparedPopulateOption =\n | string\n | PreparedPopulateObject\n | Array<string | PreparedPopulateObject>\n\nconst resolvePopulateSpecForModel = async ({\n tenantId,\n model,\n ability,\n populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n}: {\n tenantId: string\n model: Model<any>\n ability: AppAbility\n populate: RtsPopulateOption | undefined\n allowInternalModels: boolean\n modelCache: Map<string, Model<any>>\n dependencyModelNames: Set<string>\n}): Promise<PreparedPopulateOption | undefined> => {\n if (!populate) return undefined\n\n const getModelCached = async (targetModelName: string): Promise<Model<any>> => {\n const cached = modelCache.get(targetModelName)\n if (cached) return cached\n const loaded = await getTenantModel(tenantId, targetModelName, ability)\n modelCache.set(targetModelName, loaded)\n return loaded\n }\n\n const resolveOne = async (\n entry: string | RtsPopulateObject,\n parentModel: Model<any>,\n ): Promise<string | PreparedPopulateObject | null> => {\n if (typeof entry === \"string\") {\n const path = entry.trim()\n if (!path) return null\n\n const refModelName = resolvePopulateRefModelName(parentModel, path, null)\n if (!refModelName) return path\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n )\n return {\n path,\n match: aclMatch as JsonObject,\n }\n }\n\n const path = entry.path.trim()\n if (!path) return null\n\n const explicitModelName = normalizeModelName(entry.model)\n const refModelName = resolvePopulateRefModelName(parentModel, path, explicitModelName)\n let nestedModel = parentModel\n\n const normalizedEntry: PreparedPopulateObject = {\n path,\n }\n\n if (entry.select !== undefined) normalizedEntry.select = entry.select\n if (entry.options !== undefined) normalizedEntry.options = entry.options\n if (explicitModelName) normalizedEntry.model = explicitModelName\n if (entry.match !== undefined) normalizedEntry.match = entry.match\n\n if (refModelName) {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {\n throw new Error(\"Model not allowed\")\n }\n if (!ability.can(\"read\", refModelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n dependencyModelNames.add(refModelName)\n nestedModel = await getModelCached(refModelName)\n\n const aclMatch = getAccessibleByQuery(\n ability,\n \"read\",\n refModelName as Exclude<AclSubjectType, \"all\">,\n ) as JsonObject\n normalizedEntry.match = mergePopulateMatchWithAcl(\n normalizedEntry.match,\n aclMatch,\n )\n } else if (entry.populate !== undefined) {\n throw new Error(\"Populate path must reference a model when nested populate is used\")\n }\n\n const nestedPopulate = await resolvePopulateSpecForModel({\n tenantId,\n model: nestedModel,\n ability,\n populate: entry.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n if (nestedPopulate !== undefined) normalizedEntry.populate = nestedPopulate\n\n return normalizedEntry\n }\n\n if (Array.isArray(populate)) {\n const resolved = await Promise.all(populate.map((entry) => resolveOne(entry, model)))\n const filtered = resolved.filter((entry): entry is string | PreparedPopulateObject => entry !== null)\n return filtered.length > 0 ? filtered : undefined\n }\n\n const resolved = await resolveOne(populate, model)\n return resolved ?? undefined\n}\n\nexport const normalizeRtsQueryOptions = (options: RtsQueryOptions | undefined): RtsQueryOptions => {\n if (!options || typeof options !== \"object\") return {}\n const normalized: RtsQueryOptions = {}\n\n if (options.projection && typeof options.projection === \"object\" && !Array.isArray(options.projection)) {\n normalized.projection = options.projection\n }\n\n if (options.sort && typeof options.sort === \"object\" && !Array.isArray(options.sort)) {\n normalized.sort = options.sort\n }\n\n normalized.limit = normalizeLimit(options.limit)\n normalized.populate = normalizeRtsPopulateOption(options.populate)\n normalized.pagination = normalizePagination(options.pagination)\n\n return normalized\n}\n\nexport const resolveRtsQueryDependencyModelNames = async ({\n tenantId,\n ability,\n modelName,\n options,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<string[]> => {\n const model = await getTenantModel(tenantId, modelName, ability)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const dependencyModelNames = new Set<string>()\n await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames,\n })\n\n return Array.from(dependencyModelNames)\n}\n\nexport const runRtsQuery = async ({\n tenantId,\n ability,\n modelName,\n query,\n options,\n allowInternalModels = false,\n paginationMaxLimit,\n includePaginationCursors = true,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: boolean\n paginationMaxLimit?: number\n includePaginationCursors?: boolean\n}): Promise<RtsQueryResult> => {\n const { model, finalQuery } = await prepareRtsExecution({\n tenantId,\n ability,\n modelName,\n query,\n allowInternalModels,\n })\n const projection = options.projection ?? undefined\n const sort = options.sort\n const limit = normalizeLimit(options.limit)\n const modelCache = new Map<string, Model<any>>()\n modelCache.set(modelName, model)\n\n const populate = await resolvePopulateSpecForModel({\n tenantId,\n model,\n ability,\n populate: options.populate,\n allowInternalModels,\n modelCache,\n dependencyModelNames: new Set<string>(),\n })\n\n if (options.pagination) {\n const cursorProjection = prepareCursorProjection(\n projection,\n options.pagination,\n includePaginationCursors,\n )\n const paginatedQuery = model.find(finalQuery, cursorProjection.projection)\n if (populate !== undefined) {\n paginatedQuery.populate(populate as any)\n }\n\n const paginatedResult = await paginatedQuery.paginate(options.pagination, {\n cursor: {\n signingSecret: getPaginationCursorSigningSecret(),\n ...(paginationMaxLimit !== undefined ? { maxLimit: paginationMaxLimit } : {}),\n },\n includeCursors: includePaginationCursors,\n })\n const totalCount = typeof paginatedResult.totalCount === \"number\"\n && Number.isFinite(paginatedResult.totalCount)\n && paginatedResult.totalCount >= 0\n ? Math.floor(paginatedResult.totalCount)\n : undefined\n const approximateTotalCount = totalCount === undefined\n ? await getUnsafeTenantModel(tenantId, modelName)\n .then((estimatedCountModel) => runApproximateRtsCount(\n model,\n estimatedCountModel,\n finalQuery,\n ))\n .catch(() => undefined)\n : undefined\n const resolvedTotalCount = totalCount ?? approximateTotalCount\n\n return {\n data: Array.isArray(paginatedResult.nodes)\n ? stripAddedProjectionPaths(paginatedResult.nodes, cursorProjection.addedPaths)\n : [],\n pageInfo: paginatedResult.pageInfo,\n ...(resolvedTotalCount !== undefined ? { totalCount: resolvedTotalCount } : {}),\n }\n }\n\n const queryPromise = model.find(finalQuery, projection)\n if (populate !== undefined) {\n queryPromise.populate(populate as any)\n }\n if (sort && Object.keys(sort).length) {\n queryPromise.sort(sort)\n }\n queryPromise.limit(limit)\n\n const data = await queryPromise\n return { data: Array.isArray(data) ? data : [] }\n}\n\nconst prepareRtsExecution = async ({\n tenantId,\n ability,\n modelName,\n query,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n allowInternalModels?: boolean\n}): Promise<PreparedRtsExecution> => {\n if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) {\n throw new Error(\"Model not allowed\")\n }\n\n if (!ability.can(\"read\", modelName as AclSubjectType)) {\n throw new Error(\"forbidden\")\n }\n\n const model = await getTenantModel(tenantId, modelName, ability)\n const accessQuery = getAccessibleByQuery(ability, \"read\", modelName as Exclude<AclSubjectType, \"all\">)\n const finalQuery: JsonObject = { $and: [query, accessQuery] }\n\n return { model, finalQuery }\n}\n\nconst runApproximateRtsCount = async (\n model: Model<any>,\n estimatedCountModel: Model<any>,\n query: JsonObject,\n): Promise<number> => {\n const unsupportedOperator = findUnsupportedApproxCountOperator(query)\n if (unsupportedOperator) {\n throw new Error(`Approximate RTS count does not support ${unsupportedOperator} queries`)\n }\n\n const castedQuery = castApproxCountQuery(model, query)\n const estimatedTotal = normalizeNonNegativeInteger(\n await estimatedCountModel.estimatedDocumentCount(),\n )\n if (estimatedTotal === 0) return 0\n\n const sampleSize = Math.min(getApproxCountSampleSize(), estimatedTotal)\n const sampleResult = await model.aggregate([\n { $sample: { size: sampleSize } },\n { $match: castedQuery },\n { $count: \"count\" },\n ]) as Array<{ count?: unknown }>\n const sampleMatches = normalizeNonNegativeInteger(sampleResult[0]?.count)\n\n if (sampleSize >= estimatedTotal) {\n return Math.min(sampleMatches, estimatedTotal)\n }\n\n const estimatedMatches = Math.round((estimatedTotal * sampleMatches) / sampleSize)\n return Math.max(0, Math.min(estimatedTotal, estimatedMatches))\n}\n\nexport const runRtsCount = async ({\n tenantId,\n ability,\n modelName,\n query,\n allowInternalModels = false,\n}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n allowInternalModels?: boolean\n}): Promise<number> => {\n const { model, finalQuery } = await prepareRtsExecution({\n tenantId,\n ability,\n modelName,\n query,\n allowInternalModels,\n })\n\n const estimatedCountModel = await getUnsafeTenantModel(tenantId, modelName)\n return await runApproximateRtsCount(model, estimatedCountModel, finalQuery)\n}\n","export const RTS_QUERY_WINDOW_MAX_COUNT = 4096\n"],"mappings":"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"}
|
package/dist/rts/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveRtsQueryDependencyModelNames, c as runRtsQuery, i as normalizeRtsQueryOptions, l as syncAuthenticatedSessionFromRequest, s as runRtsCount, t as RTS_QUERY_WINDOW_MAX_COUNT } from "../queryWindow-
|
|
1
|
+
import { a as resolveRtsQueryDependencyModelNames, c as runRtsQuery, i as normalizeRtsQueryOptions, l as syncAuthenticatedSessionFromRequest, s as runRtsCount, t as RTS_QUERY_WINDOW_MAX_COUNT } from "../queryWindow-DNQxuY_M.js";
|
|
2
2
|
import { models } from "@rpcbase/db";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { buildAbilityFromSession } from "@rpcbase/db/acl";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryExecutor.d.ts","sourceRoot":"","sources":["../../src/rts/queryExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAEtE,OAAO,EAAsE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAMrH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAOzC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC5B,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,iBAAiB,GACjB,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAA;AAErC,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,UAAU,CAAC,EAAE,cAAc,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAkCD,eAAO,MAAM,yBAAyB,GAAI,KAAK,OAAO,KAAG,MAAM,GAAG,IAEjE,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,KAAK,OAAO,KAAG,MAAM,GAAG,IAE/D,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,KAAK,OAAO,EAAE,UAAU,MAAM,KAAG,OAOvE,CAAA;AAED,eAAO,MAAM,0BAA0B,GACrC,KAAK,OAAO,EACZ,UAAU,MAAM,KACf,OAAO,CAAC;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAuBxD,CAAA;
|
|
1
|
+
{"version":3,"file":"queryExecutor.d.ts","sourceRoot":"","sources":["../../src/rts/queryExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { extname } from "node:path";
|
|
2
|
+
import { models } from "@rpcbase/db";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import { buildAbilityFromSession, getAccessibleByQuery } from "@rpcbase/db/acl";
|
|
4
5
|
//#region src/uploads/api/file-uploads/shared.ts
|
|
@@ -50,7 +51,8 @@ var toBufferPayload = (payload) => {
|
|
|
50
51
|
if (payload instanceof Uint8Array) return Buffer.from(payload);
|
|
51
52
|
return null;
|
|
52
53
|
};
|
|
53
|
-
var ensureUploadIndexes = async (
|
|
54
|
+
var ensureUploadIndexes = async (modelCtx) => {
|
|
55
|
+
const [UploadSession, UploadChunk] = await Promise.all([models.getUnsafe("RBUploadSession", modelCtx), models.getUnsafe("RBUploadChunk", modelCtx)]);
|
|
54
56
|
const dbName = String(UploadSession?.db?.name ?? "");
|
|
55
57
|
if (dbName && ensuredIndexDbNames.has(dbName)) return;
|
|
56
58
|
await Promise.all([UploadSession.createIndexes(), UploadChunk.createIndexes()]);
|
|
@@ -128,4 +130,4 @@ var getUploadSessionAccessQuery = (ability, action) => getAccessibleByQuery(abil
|
|
|
128
130
|
//#endregion
|
|
129
131
|
export { getUserId as _, ensureFilesystemUploadIndexes as a, resolveUploadAccessScope as b, getChunkSizeBytes as c, getModelCtx as d, getRawBodyLimitBytes as f, getUploadSessionAccessQuery as g, getUploadKeyHash as h, computeSha256Hex as i, getFileReferencesCollectionName as l, getTenantId as m, buildUploadDeduplication as n, ensureUploadIndexes as o, getSessionTtlMs as p, buildUploadsAbility as r, getBucketName as s, buildUploadAccessMetadata as t, getMaxClientUploadBytesPerSecond as u, normalizeSha256Hex as v, toBufferPayload as x, resolveStoredUploadAccessScope as y };
|
|
130
132
|
|
|
131
|
-
//# sourceMappingURL=shared-
|
|
133
|
+
//# sourceMappingURL=shared-BX-oLc9O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-BX-oLc9O.js","names":["createHash","timingSafeEqual","extname","Ctx","models","IRBUploadChunk","IRBUploadSession","LoadModelCtx","buildAbilityFromSession","getAccessibleByQuery","AppAbility","Db","Model","SessionUser","id","currentTenantId","UploadSessionDoc","UploadChunkDoc","Omit","data","Buffer","DEFAULT_CHUNK_SIZE_BYTES","MAX_CHUNK_SIZE_BYTES","DEFAULT_MAX_CLIENT_BYTES_PER_SECOND","DEFAULT_SESSION_TTL_S","ensuredIndexDbNames","Set","UploadAccessScope","UploadDeduplication","NonNullable","UPLOAD_DEDUPLICATION_PIPELINE_VERSION","parseOptionalPositiveInt","rawValue","normalized","trim","parsed","Number","isFinite","Math","floor","getChunkSizeBytes","configured","process","env","RB_UPLOAD_CHUNK_SIZE_BYTES","resolved","min","getMaxClientUploadBytesPerSecond","RB_UPLOAD_MAX_CLIENT_BYTES_PER_SECOND","getSessionTtlMs","ttlSeconds","RB_UPLOAD_SESSION_TTL_S","getRawBodyLimitBytes","chunkSizeBytes","getBucketName","RB_FILESYSTEM_BUCKET_NAME","getFileReferencesCollectionName","bucketName","getUserId","ctx","raw","req","session","user","getTenantId","rawSession","sessionTenantId","computeSha256Hex","update","digest","normalizeSha256Hex","value","toLowerCase","getModelCtx","_ctx","tenantId","ability","toBufferPayload","payload","isBuffer","Uint8Array","from","ensureUploadIndexes","modelCtx","Promise","UploadSession","UploadChunk","all","getUnsafe","dbName","String","db","name","has","createIndexes","add","ensureFilesystemUploadIndexes","collection","createIndex","unique","partialFilterExpression","$type","blobId","resolveUploadAccessScope","accessScope","isPublic","resolveStoredUploadAccessScope","metadata","Record","buildUploadDeduplication","namespace","hash","filename","mimeType","totalSize","userId","ownerKeyHash","ownerPartition","normalizedNamespace","normalizedHash","normalizedMimeType","normalizedExtension","key","JSON","stringify","algorithm","pipelineVersion","buildUploadAccessMetadata","Pick","normalizeUploadKey","getUploadKeyHash","uploadKey","get","buildUploadsAbility","uploadKeyHash","claims","undefined","getUploadSessionAccessQuery","action","timingSafeEqualHex","left","right","length","getOwnershipSelector"],"sources":["../src/uploads/api/file-uploads/shared.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from \"node:crypto\"\nimport { extname } from \"node:path\"\n\nimport { Ctx } from \"@rpcbase/api\"\nimport {\n models,\n type IRBUploadChunk,\n type IRBUploadSession,\n type LoadModelCtx,\n} from \"@rpcbase/db\"\nimport { buildAbilityFromSession, getAccessibleByQuery, type AppAbility } from \"@rpcbase/db/acl\"\nimport type { Db } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\n\nexport type SessionUser = {\n id?: string\n currentTenantId?: string\n}\n\nexport type UploadSessionDoc = IRBUploadSession\nexport type UploadChunkDoc = Omit<IRBUploadChunk, \"data\"> & { data: Buffer }\n\nconst DEFAULT_CHUNK_SIZE_BYTES = 5 * 1024 * 1024\nconst MAX_CHUNK_SIZE_BYTES = 15 * 1024 * 1024\n\nconst DEFAULT_MAX_CLIENT_BYTES_PER_SECOND = 10 * 1024 * 1024\n\nconst DEFAULT_SESSION_TTL_S = 60 * 60 * 24\n\nconst ensuredIndexDbNames = new Set<string>()\n\nexport type UploadAccessScope = \"owner\" | \"tenant\" | \"public\"\nexport type UploadDeduplication = NonNullable<IRBUploadSession[\"deduplication\"]>\n\nconst UPLOAD_DEDUPLICATION_PIPELINE_VERSION = 1\n\nconst parseOptionalPositiveInt = (rawValue: unknown): number | null => {\n if (typeof rawValue !== \"string\") return null\n const normalized = rawValue.trim()\n if (!normalized) return null\n const parsed = Number(normalized)\n if (!Number.isFinite(parsed) || parsed <= 0) return null\n return Math.floor(parsed)\n}\n\nexport const getChunkSizeBytes = (): number => {\n const configured = parseOptionalPositiveInt(process.env.RB_UPLOAD_CHUNK_SIZE_BYTES)\n const resolved = configured ?? DEFAULT_CHUNK_SIZE_BYTES\n return Math.min(MAX_CHUNK_SIZE_BYTES, resolved)\n}\n\nexport const getMaxClientUploadBytesPerSecond = (): number | null => {\n const configured = parseOptionalPositiveInt(process.env.RB_UPLOAD_MAX_CLIENT_BYTES_PER_SECOND)\n return configured ?? DEFAULT_MAX_CLIENT_BYTES_PER_SECOND\n}\n\nexport const getSessionTtlMs = (): number => {\n const ttlSeconds = parseOptionalPositiveInt(process.env.RB_UPLOAD_SESSION_TTL_S) ?? DEFAULT_SESSION_TTL_S\n return ttlSeconds * 1000\n}\n\nexport const getRawBodyLimitBytes = (chunkSizeBytes: number): number => chunkSizeBytes + 1024 * 1024\n\nexport const getBucketName = (): string => (process.env.RB_FILESYSTEM_BUCKET_NAME ?? \"\").trim() || \"fs\"\n\nexport const getFileReferencesCollectionName = (bucketName: string): string => `${bucketName}.references`\n\nexport const getUserId = (ctx: Ctx<SessionUser>): string | null => {\n const raw = ctx.req.session?.user?.id\n if (typeof raw !== \"string\") return null\n const normalized = raw.trim()\n return normalized ? normalized : null\n}\n\nexport const getTenantId = (ctx: Ctx<SessionUser>): string | null => {\n const rawSession = ctx.req.session?.user?.currentTenantId\n const sessionTenantId = typeof rawSession === \"string\" ? rawSession.trim() : \"\"\n return sessionTenantId || null\n}\n\nexport const computeSha256Hex = (data: Buffer): string => createHash(\"sha256\").update(data).digest(\"hex\")\n\nexport const normalizeSha256Hex = (value: string): string => value.trim().toLowerCase()\n\nexport const getModelCtx = (_ctx: Ctx<SessionUser>, tenantId: string, ability?: AppAbility): LoadModelCtx => ({\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n ability,\n})\n\nexport const toBufferPayload = (payload: unknown): Buffer | null => {\n if (Buffer.isBuffer(payload)) return payload\n if (payload instanceof Uint8Array) return Buffer.from(payload)\n return null\n}\n\nexport const ensureUploadIndexes = async (\n modelCtx: LoadModelCtx,\n): Promise<void> => {\n const [UploadSession, UploadChunk] = await Promise.all([\n models.getUnsafe(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.getUnsafe(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n const dbName = String((UploadSession as unknown as { db?: { name?: unknown } })?.db?.name ?? \"\")\n if (dbName && ensuredIndexDbNames.has(dbName)) return\n\n await Promise.all([\n UploadSession.createIndexes(),\n UploadChunk.createIndexes(),\n ])\n\n if (dbName) ensuredIndexDbNames.add(dbName)\n}\n\nexport const ensureFilesystemUploadIndexes = async (db: Db, bucketName: string): Promise<void> => {\n await Promise.all([\n db.collection(`${bucketName}.files`).createIndex(\n { \"metadata.deduplication.key\": 1 },\n {\n name: \"rb_upload_deduplication_key_unique\",\n unique: true,\n partialFilterExpression: { \"metadata.deduplication.key\": { $type: \"string\" } },\n },\n ),\n db.collection(getFileReferencesCollectionName(bucketName)).createIndex(\n { blobId: 1 },\n { name: \"rb_upload_file_reference_blob\" },\n ),\n ])\n}\n\nexport const resolveUploadAccessScope = ({\n accessScope,\n isPublic,\n}: {\n accessScope?: UploadAccessScope\n isPublic?: boolean\n}): UploadAccessScope => accessScope ?? (isPublic ? \"public\" : \"owner\")\n\nexport const resolveStoredUploadAccessScope = (metadata: Record<string, unknown>): UploadAccessScope => {\n const accessScope = metadata.accessScope\n if (accessScope === \"owner\" || accessScope === \"tenant\" || accessScope === \"public\") {\n return accessScope\n }\n return metadata.isPublic === true || String(metadata.isPublic).trim().toLowerCase() === \"true\" ? \"public\" : \"owner\"\n}\n\nexport const buildUploadDeduplication = ({\n namespace,\n hash,\n filename,\n mimeType,\n totalSize,\n accessScope,\n userId,\n ownerKeyHash,\n}: {\n namespace: string\n hash: string\n filename: string\n mimeType: string\n totalSize: number\n accessScope: UploadAccessScope\n userId?: string\n ownerKeyHash?: string\n}): UploadDeduplication | null => {\n const ownerPartition = accessScope === \"owner\"\n ? userId\n ? `user:${userId}`\n : ownerKeyHash\n ? `key:${ownerKeyHash}`\n : null\n : accessScope\n if (!ownerPartition) return null\n\n const normalizedNamespace = namespace.trim()\n const normalizedHash = normalizeSha256Hex(hash)\n const normalizedMimeType = mimeType.trim().toLowerCase()\n const normalizedExtension = extname(filename.trim()).toLowerCase()\n const key = computeSha256Hex(Buffer.from(JSON.stringify([\n UPLOAD_DEDUPLICATION_PIPELINE_VERSION,\n normalizedNamespace,\n normalizedHash,\n totalSize,\n normalizedMimeType,\n normalizedExtension,\n accessScope,\n ownerPartition,\n ])))\n\n return {\n namespace: normalizedNamespace,\n algorithm: \"sha256\",\n hash: normalizedHash,\n key,\n pipelineVersion: UPLOAD_DEDUPLICATION_PIPELINE_VERSION,\n }\n}\n\nexport const buildUploadAccessMetadata = (\n session: Pick<UploadSessionDoc, \"accessScope\" | \"isPublic\" | \"userId\" | \"ownerKeyHash\">,\n): Record<string, unknown> => {\n const accessScope = resolveUploadAccessScope({\n accessScope: session.accessScope,\n isPublic: session.isPublic,\n })\n\n return {\n accessScope,\n isPublic: accessScope === \"public\",\n ...(typeof session.ownerKeyHash === \"string\" ? { ownerKeyHash: session.ownerKeyHash } : {}),\n ...(typeof session.userId === \"string\" ? { userId: session.userId } : {}),\n }\n}\n\nconst normalizeUploadKey = (raw: unknown): string | null => {\n if (typeof raw !== \"string\") return null\n const normalized = raw.trim()\n return normalized ? normalized : null\n}\n\nexport const getUploadKeyHash = (ctx: Ctx<SessionUser>): string | null => {\n const uploadKey = normalizeUploadKey(ctx.req.get(\"X-Upload-Key\"))\n if (!uploadKey) return null\n return computeSha256Hex(Buffer.from(uploadKey))\n}\n\nexport const buildUploadsAbility = (ctx: Ctx<SessionUser>, tenantId: string): AppAbility => {\n const uploadKeyHash = getUploadKeyHash(ctx)\n const claims = uploadKeyHash ? { uploadKeyHash } : undefined\n return buildAbilityFromSession({ tenantId, session: ctx.req.session, claims })\n}\n\nexport const getUploadSessionAccessQuery = (\n ability: AppAbility,\n action: \"read\" | \"update\" | \"delete\",\n): Record<string, unknown> => getAccessibleByQuery(ability, action, \"RBUploadSession\")\n\nconst timingSafeEqualHex = (left: string, right: string): boolean => {\n if (left.length !== right.length) return false\n try {\n return timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"))\n } catch {\n return false\n }\n}\n\nexport const getOwnershipSelector = (\n ctx: Ctx<SessionUser>,\n session: Pick<UploadSessionDoc, \"userId\" | \"ownerKeyHash\">,\n): { userId?: string; ownerKeyHash?: string } | null => {\n if (session.userId) {\n const userId = getUserId(ctx)\n if (!userId || userId !== session.userId) return null\n return { userId: session.userId }\n }\n\n if (session.ownerKeyHash) {\n const uploadKeyHash = getUploadKeyHash(ctx)\n if (!uploadKeyHash) return null\n if (!timingSafeEqualHex(session.ownerKeyHash, uploadKeyHash)) return null\n return { ownerKeyHash: session.ownerKeyHash }\n }\n\n return null\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initUpload.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/file-uploads/handlers/initUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAKzC,OAAO,KAAK,OAAO,MAAM,UAAU,CAAA;AAEnC,OAAO,EACL,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"initUpload.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/file-uploads/handlers/initUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAKzC,OAAO,KAAK,OAAO,MAAM,UAAU,CAAA;AAEnC,OAAO,EACL,KAAK,WAAW,EAcjB,MAAM,WAAW,CAAA;AAGlB,eAAO,MAAM,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,CAAC,mBAAmB,EAAE,WAAW,CAqIvG,CAAA"}
|
|
@@ -2,7 +2,6 @@ import { Ctx } from '@rpcbase/api';
|
|
|
2
2
|
import { IRBUploadChunk, IRBUploadSession, LoadModelCtx } from '@rpcbase/db';
|
|
3
3
|
import { AppAbility } from '@rpcbase/db/acl';
|
|
4
4
|
import { Db } from 'mongodb';
|
|
5
|
-
import { Model } from 'mongoose';
|
|
6
5
|
export type SessionUser = {
|
|
7
6
|
id?: string;
|
|
8
7
|
currentTenantId?: string;
|
|
@@ -25,7 +24,7 @@ export declare const computeSha256Hex: (data: Buffer) => string;
|
|
|
25
24
|
export declare const normalizeSha256Hex: (value: string) => string;
|
|
26
25
|
export declare const getModelCtx: (_ctx: Ctx<SessionUser>, tenantId: string, ability?: AppAbility) => LoadModelCtx;
|
|
27
26
|
export declare const toBufferPayload: (payload: unknown) => Buffer | null;
|
|
28
|
-
export declare const ensureUploadIndexes: (
|
|
27
|
+
export declare const ensureUploadIndexes: (modelCtx: LoadModelCtx) => Promise<void>;
|
|
29
28
|
export declare const ensureFilesystemUploadIndexes: (db: Db, bucketName: string) => Promise<void>;
|
|
30
29
|
export declare const resolveUploadAccessScope: ({ accessScope, isPublic, }: {
|
|
31
30
|
accessScope?: UploadAccessScope;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/shared.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/shared.ts"],"names":[],"mappings":"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"}
|
package/dist/uploads/worker.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as registerUploadPostProcessor, s as registerUploadPostProcessorTask } from "../postProcessors-iffHvje4.js";
|
|
2
|
-
import { s as getBucketName } from "../shared-
|
|
2
|
+
import { s as getBucketName } from "../shared-BX-oLc9O.js";
|
|
3
3
|
import { n as registerConvertHeifToWebpTask } from "../convertHeifToWebp-BT1n72DQ.js";
|
|
4
4
|
import { GridFSBucket, ObjectId } from "mongodb";
|
|
5
5
|
import { getTenantFilesystemDb } from "@rpcbase/db";
|
package/dist/uploads.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as getUploadProcessors, c as registerUploadProcessor, d as unregisterUploadProcessor, f as uploadPostProcessorTaskName, i as getUploadPostProcessors, l as runUploadPostProcessors, n as clearUploadProcessors, o as registerUploadPostProcessor, r as enqueueUploadPostProcessors, s as registerUploadPostProcessorTask, t as clearUploadPostProcessors, u as unregisterUploadPostProcessor } from "./postProcessors-iffHvje4.js";
|
|
2
2
|
//#region src/uploads/routes.ts
|
|
3
3
|
var routes = Object.entries({ .../* #__PURE__ */ Object.assign({
|
|
4
|
-
"./api/file-uploads/handler.ts": () => import("./handler-
|
|
5
|
-
"./api/files/handler.ts": () => import("./handler-
|
|
4
|
+
"./api/file-uploads/handler.ts": () => import("./handler-D9cQBJyz.js"),
|
|
5
|
+
"./api/files/handler.ts": () => import("./handler-B61RAoQK.js")
|
|
6
6
|
}) }).reduce((acc, [path, mod]) => {
|
|
7
7
|
acc[path.replace("./api/", "@rpcbase/server/uploads/api/")] = mod;
|
|
8
8
|
return acc;
|