@rpcbase/server 0.513.0 → 0.515.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/applyRouteLoaders.d.ts.map +1 -1
- package/dist/{email-DEw8keax.js → email-DK8uUU4X.js} +5 -2
- package/dist/{email-DEw8keax.js.map → email-DK8uUU4X.js.map} +1 -1
- package/dist/{handler-r-HYO3Oy.js → handler-3gksYOQv.js} +299 -99
- package/dist/{handler-r-HYO3Oy.js.map → handler-3gksYOQv.js.map} +1 -1
- package/dist/{handler-BNrqh1Kb.js → handler-BsauvgjA.js} +58 -19
- package/dist/{handler-BNrqh1Kb.js.map → handler-BsauvgjA.js.map} +1 -1
- package/dist/{handler-Cohj3cz3.js → handler-DY5UEwlw.js} +45 -18
- package/dist/{handler-Cohj3cz3.js.map → handler-DY5UEwlw.js.map} +1 -1
- package/dist/{handler-Bh3a6Br1.js → handler-GZgk5k3c.js} +326 -130
- package/dist/{handler-Bh3a6Br1.js.map → handler-GZgk5k3c.js.map} +1 -1
- package/dist/index.js +315 -155
- package/dist/index.js.map +1 -1
- package/dist/notifications.js +103 -32
- package/dist/notifications.js.map +1 -1
- package/dist/{queryExecutor-DSCpsVI8.js → queryExecutor-Dn62mIDL.js} +47 -33
- package/dist/{queryExecutor-DSCpsVI8.js.map → queryExecutor-Dn62mIDL.js.map} +1 -1
- package/dist/renderSSR.d.ts.map +1 -1
- package/dist/rts/index.js +99 -21
- package/dist/rts/index.js.map +1 -1
- package/dist/{shared-BJomDDWK.js → shared-Dy9x-P7F.js} +10 -7
- package/dist/{shared-BJomDDWK.js.map → shared-Dy9x-P7F.js.map} +1 -1
- package/dist/ssrMiddleware.d.ts.map +1 -1
- package/dist/uploads.js +1 -1
- package/dist/uploads.js.map +1 -1
- package/package.json +1 -1
package/dist/notifications.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { models } from "@rpcbase/db";
|
|
2
|
-
import { s as sendEmail } from "./email-
|
|
2
|
+
import { s as sendEmail } from "./email-DK8uUU4X.js";
|
|
3
3
|
const routes = Object.entries({
|
|
4
|
-
.../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-
|
|
4
|
+
.../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-3gksYOQv.js") })
|
|
5
5
|
}).reduce((acc, [path, mod]) => {
|
|
6
6
|
acc[path.replace("./api/", "@rpcbase/server/notifications/api/")] = mod;
|
|
7
7
|
return acc;
|
|
@@ -21,13 +21,23 @@ const createNotification = async (ctx, input) => {
|
|
|
21
21
|
const NotificationModel = await models.get("RBNotification", ctx);
|
|
22
22
|
const doc = await NotificationModel.create({
|
|
23
23
|
userId,
|
|
24
|
-
...topic ? {
|
|
24
|
+
...topic ? {
|
|
25
|
+
topic
|
|
26
|
+
} : {},
|
|
25
27
|
title,
|
|
26
|
-
...body ? {
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
...body ? {
|
|
29
|
+
body
|
|
30
|
+
} : {},
|
|
31
|
+
...url ? {
|
|
32
|
+
url
|
|
33
|
+
} : {},
|
|
34
|
+
...input.metadata ? {
|
|
35
|
+
metadata: input.metadata
|
|
36
|
+
} : {}
|
|
29
37
|
});
|
|
30
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
id: doc._id.toString()
|
|
40
|
+
};
|
|
31
41
|
};
|
|
32
42
|
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
33
43
|
const WEEK_MS = 7 * DAY_MS;
|
|
@@ -49,7 +59,9 @@ const buildPreferencesByTopic = (settings) => {
|
|
|
49
59
|
const topic = typeof pref.topic === "string" ? pref.topic.trim() : "";
|
|
50
60
|
if (!topic) continue;
|
|
51
61
|
const emailDigest = pref.emailDigest;
|
|
52
|
-
record[topic] = {
|
|
62
|
+
record[topic] = {
|
|
63
|
+
emailDigest: emailDigest === true
|
|
64
|
+
};
|
|
53
65
|
}
|
|
54
66
|
return record;
|
|
55
67
|
};
|
|
@@ -59,44 +71,82 @@ const sendNotificationsDigestForUser = async (ctx, {
|
|
|
59
71
|
}) => {
|
|
60
72
|
const SettingsModel = await models.get("RBNotificationSettings", ctx);
|
|
61
73
|
const NotificationModel = await models.get("RBNotification", ctx);
|
|
62
|
-
const settings = await SettingsModel.findOne({
|
|
74
|
+
const settings = await SettingsModel.findOne({
|
|
75
|
+
userId
|
|
76
|
+
}).lean();
|
|
63
77
|
const digestFrequencyRaw = typeof settings?.digestFrequency === "string" ? settings.digestFrequency : "weekly";
|
|
64
78
|
const digestFrequency = digestFrequencyRaw === "daily" || digestFrequencyRaw === "weekly" || digestFrequencyRaw === "off" ? digestFrequencyRaw : "weekly";
|
|
65
79
|
if (digestFrequency === "off") {
|
|
66
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
sent: false,
|
|
83
|
+
skippedReason: "digest_off"
|
|
84
|
+
};
|
|
67
85
|
}
|
|
68
86
|
const now = /* @__PURE__ */ new Date();
|
|
69
87
|
const windowMs = getDigestWindowMs(digestFrequency);
|
|
70
88
|
const lastSentAt = settings?.lastDigestSentAt instanceof Date ? settings.lastDigestSentAt : null;
|
|
71
89
|
const since = lastSentAt ?? new Date(now.getTime() - windowMs);
|
|
72
90
|
if (!force && lastSentAt && now.getTime() - lastSentAt.getTime() < windowMs) {
|
|
73
|
-
return {
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
sent: false,
|
|
94
|
+
skippedReason: "not_due"
|
|
95
|
+
};
|
|
74
96
|
}
|
|
75
97
|
const preferencesByTopic = buildPreferencesByTopic(settings);
|
|
76
98
|
const disabledTopics = Object.entries(preferencesByTopic).filter(([, pref]) => pref.emailDigest === false).map(([topic]) => topic);
|
|
77
99
|
const query = {
|
|
78
100
|
userId,
|
|
79
|
-
archivedAt: {
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
archivedAt: {
|
|
102
|
+
$exists: false
|
|
103
|
+
},
|
|
104
|
+
readAt: {
|
|
105
|
+
$exists: false
|
|
106
|
+
},
|
|
107
|
+
createdAt: {
|
|
108
|
+
$gt: since
|
|
109
|
+
}
|
|
82
110
|
};
|
|
83
111
|
if (disabledTopics.length > 0) {
|
|
84
|
-
query.topic = {
|
|
112
|
+
query.topic = {
|
|
113
|
+
$nin: disabledTopics
|
|
114
|
+
};
|
|
85
115
|
}
|
|
86
|
-
const notifications = await NotificationModel.find(query).sort({
|
|
116
|
+
const notifications = await NotificationModel.find(query).sort({
|
|
117
|
+
createdAt: -1
|
|
118
|
+
}).limit(50).lean();
|
|
87
119
|
if (!notifications.length) {
|
|
88
|
-
await SettingsModel.updateOne(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
120
|
+
await SettingsModel.updateOne({
|
|
121
|
+
userId
|
|
122
|
+
}, {
|
|
123
|
+
$set: {
|
|
124
|
+
lastDigestSentAt: now
|
|
125
|
+
},
|
|
126
|
+
$setOnInsert: {
|
|
127
|
+
userId,
|
|
128
|
+
digestFrequency: "weekly"
|
|
129
|
+
}
|
|
130
|
+
}, {
|
|
131
|
+
upsert: true
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
ok: true,
|
|
135
|
+
sent: false,
|
|
136
|
+
skippedReason: "empty"
|
|
137
|
+
};
|
|
94
138
|
}
|
|
95
139
|
const UserModel = await models.getGlobal("RBUser", ctx);
|
|
96
|
-
const user = await UserModel.findById(userId, {
|
|
140
|
+
const user = await UserModel.findById(userId, {
|
|
141
|
+
email: 1
|
|
142
|
+
}).lean();
|
|
97
143
|
const email = typeof user?.email === "string" ? user.email.trim() : "";
|
|
98
144
|
if (!email) {
|
|
99
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
ok: true,
|
|
147
|
+
sent: false,
|
|
148
|
+
skippedReason: "missing_email"
|
|
149
|
+
};
|
|
100
150
|
}
|
|
101
151
|
const subject = "Notifications digest";
|
|
102
152
|
const rows = notifications.map((n) => {
|
|
@@ -108,16 +158,37 @@ const sendNotificationsDigestForUser = async (ctx, {
|
|
|
108
158
|
return line;
|
|
109
159
|
}).join("");
|
|
110
160
|
const html = `<div><p>Here is your notifications digest:</p><ul>${rows}</ul></div>`;
|
|
111
|
-
const emailResult = await sendEmail({
|
|
161
|
+
const emailResult = await sendEmail({
|
|
162
|
+
to: email,
|
|
163
|
+
subject,
|
|
164
|
+
html
|
|
165
|
+
});
|
|
112
166
|
if (emailResult.error) {
|
|
113
|
-
return {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
error: emailResult.error
|
|
170
|
+
};
|
|
114
171
|
}
|
|
115
|
-
await SettingsModel.updateOne(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
172
|
+
await SettingsModel.updateOne({
|
|
173
|
+
userId
|
|
174
|
+
}, {
|
|
175
|
+
$set: {
|
|
176
|
+
lastDigestSentAt: now
|
|
177
|
+
},
|
|
178
|
+
$setOnInsert: {
|
|
179
|
+
userId,
|
|
180
|
+
digestFrequency: "weekly"
|
|
181
|
+
}
|
|
182
|
+
}, {
|
|
183
|
+
upsert: true
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
sent: emailResult.skipped !== true,
|
|
188
|
+
...emailResult.skipped ? {
|
|
189
|
+
skippedReason: "email_skipped"
|
|
190
|
+
} : {}
|
|
191
|
+
};
|
|
121
192
|
};
|
|
122
193
|
export {
|
|
123
194
|
createNotification,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","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 type { Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n title: string\n body?: string\n url?: string\n metadata?: Record<string, unknown>\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n): Promise<{ id: string }> => {\n const userId = input.userId.trim()\n const title = input.title.trim()\n if (!userId) {\n throw new Error(\"createNotification: userId is required\")\n }\n if (!title) {\n throw new Error(\"createNotification: title is required\")\n }\n\n const topic = typeof input.topic === \"string\" ? input.topic.trim() : \"\"\n const body = typeof input.body === \"string\" ? input.body.trim() : \"\"\n const url = typeof input.url === \"string\" ? input.url.trim() : \"\"\n\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n const doc = await NotificationModel.create({\n userId,\n ...(topic ? { topic } : {}),\n title,\n ...(body ? { body } : {}),\n ...(url ? { url } : {}),\n ...(input.metadata ? { metadata: input.metadata } : {}),\n })\n\n return { id: doc._id.toString() }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\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 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 force = false,\n }: {\n userId: string\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const SettingsModel = await models.get(\"RBNotificationSettings\", ctx)\n const NotificationModel = await models.get(\"RBNotification\", ctx)\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 = typeof n.url === \"string\" ? n.url : \"\"\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 const emailResult = await sendEmail({ to: email, subject, html })\n if (emailResult.error) {\n return { ok: false, error: emailResult.error }\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"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notifications.js","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 type { Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\n\nexport type CreateNotificationInput = {\n userId: string\n topic?: string\n title: string\n body?: string\n url?: string\n metadata?: Record<string, unknown>\n}\n\nexport const createNotification = async (\n ctx: Ctx,\n input: CreateNotificationInput,\n): Promise<{ id: string }> => {\n const userId = input.userId.trim()\n const title = input.title.trim()\n if (!userId) {\n throw new Error(\"createNotification: userId is required\")\n }\n if (!title) {\n throw new Error(\"createNotification: title is required\")\n }\n\n const topic = typeof input.topic === \"string\" ? input.topic.trim() : \"\"\n const body = typeof input.body === \"string\" ? input.body.trim() : \"\"\n const url = typeof input.url === \"string\" ? input.url.trim() : \"\"\n\n const NotificationModel = await models.get(\"RBNotification\", ctx)\n const doc = await NotificationModel.create({\n userId,\n ...(topic ? { topic } : {}),\n title,\n ...(body ? { body } : {}),\n ...(url ? { url } : {}),\n ...(input.metadata ? { metadata: input.metadata } : {}),\n })\n\n return { id: doc._id.toString() }\n}\n","import type { Ctx } from \"@rpcbase/api\"\nimport { models, type IRBNotification, type IRBNotificationSettings } from \"@rpcbase/db\"\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 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 force = false,\n }: {\n userId: string\n force?: boolean\n },\n): Promise<{ ok: true; sent: boolean; skippedReason?: string } | { ok: false; error: string }> => {\n const SettingsModel = await models.get(\"RBNotificationSettings\", ctx)\n const NotificationModel = await models.get(\"RBNotification\", ctx)\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 = typeof n.url === \"string\" ? n.url : \"\"\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 const emailResult = await sendEmail({ to: email, subject, html })\n if (emailResult.error) {\n return { ok: false, error: emailResult.error }\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"],"names":["routes","Object","entries","import","reduce","acc","path","mod","replace","createNotification","ctx","input","userId","trim","title","Error","topic","body","url","NotificationModel","models","get","doc","create","metadata","id","_id","toString","DAY_MS","WEEK_MS","getDigestWindowMs","frequency","formatIso","value","Date","undefined","toISOString","buildPreferencesByTopic","settings","record","raw","topicPreferences","Array","isArray","pref","emailDigest","sendNotificationsDigestForUser","force","SettingsModel","findOne","lean","digestFrequencyRaw","digestFrequency","ok","sent","skippedReason","now","windowMs","lastSentAt","lastDigestSentAt","since","getTime","preferencesByTopic","disabledTopics","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","line","join","html","emailResult","sendEmail","to","error","skipped"],"mappings":"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"}
|
|
@@ -4,13 +4,7 @@ import assert from "assert";
|
|
|
4
4
|
import { hkdfSync } from "crypto";
|
|
5
5
|
const getDerivedKey = (masterKey, info, length = 32, salt = "") => {
|
|
6
6
|
assert(masterKey?.length >= 32, "MASTER_KEY must be 32 chars or longer.");
|
|
7
|
-
return Buffer.from(hkdfSync(
|
|
8
|
-
"sha256",
|
|
9
|
-
masterKey,
|
|
10
|
-
Buffer.from(salt),
|
|
11
|
-
Buffer.from(info),
|
|
12
|
-
length
|
|
13
|
-
)).toString("hex");
|
|
7
|
+
return Buffer.from(hkdfSync("sha256", masterKey, Buffer.from(salt), Buffer.from(info), length)).toString("hex");
|
|
14
8
|
};
|
|
15
9
|
const RTS_TENANT_ID_QUERY_PARAM = "rb-tenant-id";
|
|
16
10
|
const RTS_USER_ID_HEADER = "rb-user-id";
|
|
@@ -59,19 +53,38 @@ const isRtsRequestAuthorized = (req, tenantId) => {
|
|
|
59
53
|
const buildRtsAbilityFromRequest = async (req, tenantId) => {
|
|
60
54
|
const sessionUserId = normalizeTenantId(req.session?.user?.id);
|
|
61
55
|
if (sessionUserId) {
|
|
62
|
-
const ability = buildAbilityFromSession({
|
|
63
|
-
|
|
56
|
+
const ability = buildAbilityFromSession({
|
|
57
|
+
tenantId,
|
|
58
|
+
session: req.session
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
ability,
|
|
62
|
+
userId: sessionUserId
|
|
63
|
+
};
|
|
64
64
|
}
|
|
65
65
|
const headerValue = req.headers[RTS_USER_ID_HEADER];
|
|
66
66
|
const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
67
67
|
const headerUserId = normalizeTenantId(headerUserIdRaw);
|
|
68
68
|
if (!headerUserId) {
|
|
69
|
-
const ability = buildAbilityFromSession({
|
|
70
|
-
|
|
69
|
+
const ability = buildAbilityFromSession({
|
|
70
|
+
tenantId,
|
|
71
|
+
session: req.session
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
ability,
|
|
75
|
+
userId: null
|
|
76
|
+
};
|
|
71
77
|
}
|
|
72
|
-
const rbCtx = {
|
|
78
|
+
const rbCtx = {
|
|
79
|
+
req: {
|
|
80
|
+
session: null
|
|
81
|
+
}
|
|
82
|
+
};
|
|
73
83
|
const User = await models.getGlobal("RBUser", rbCtx);
|
|
74
|
-
const user = await User.findById(headerUserId, {
|
|
84
|
+
const user = await User.findById(headerUserId, {
|
|
85
|
+
tenants: 1,
|
|
86
|
+
tenantRoles: 1
|
|
87
|
+
}).lean();
|
|
75
88
|
const tenantsRaw = user?.tenants;
|
|
76
89
|
const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : [];
|
|
77
90
|
if (!tenants.includes(tenantId)) {
|
|
@@ -79,7 +92,11 @@ const buildRtsAbilityFromRequest = async (req, tenantId) => {
|
|
|
79
92
|
}
|
|
80
93
|
const roles = getTenantRolesFromSessionUser(user, tenantId);
|
|
81
94
|
return {
|
|
82
|
-
ability: buildAbility({
|
|
95
|
+
ability: buildAbility({
|
|
96
|
+
tenantId,
|
|
97
|
+
userId: headerUserId,
|
|
98
|
+
roles: roles.length ? roles : ["owner"]
|
|
99
|
+
}),
|
|
83
100
|
userId: headerUserId
|
|
84
101
|
};
|
|
85
102
|
};
|
|
@@ -136,7 +153,9 @@ const normalizePopulateObject = (value) => {
|
|
|
136
153
|
const raw = value;
|
|
137
154
|
const path = normalizeString(raw.path);
|
|
138
155
|
if (!path) return void 0;
|
|
139
|
-
const normalized = {
|
|
156
|
+
const normalized = {
|
|
157
|
+
path
|
|
158
|
+
};
|
|
140
159
|
const model = normalizeString(raw.model);
|
|
141
160
|
if (model) normalized.model = model;
|
|
142
161
|
const select = normalizePopulateSelect(raw.select);
|
|
@@ -186,7 +205,9 @@ const resolvePopulateRefModelName = (model, path, explicitModelName) => {
|
|
|
186
205
|
};
|
|
187
206
|
const mergePopulateMatchWithAcl = (populateMatch, aclMatch) => {
|
|
188
207
|
if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch;
|
|
189
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
$and: [populateMatch, aclMatch]
|
|
210
|
+
};
|
|
190
211
|
};
|
|
191
212
|
const resolvePopulateSpecForModel = async ({
|
|
192
213
|
tenantId,
|
|
@@ -218,11 +239,7 @@ const resolvePopulateSpecForModel = async ({
|
|
|
218
239
|
throw new Error("forbidden");
|
|
219
240
|
}
|
|
220
241
|
dependencyModelNames.add(refModelName2);
|
|
221
|
-
const aclMatch = getAccessibleByQuery(
|
|
222
|
-
ability,
|
|
223
|
-
"read",
|
|
224
|
-
refModelName2
|
|
225
|
-
);
|
|
242
|
+
const aclMatch = getAccessibleByQuery(ability, "read", refModelName2);
|
|
226
243
|
return {
|
|
227
244
|
path: path2,
|
|
228
245
|
match: aclMatch
|
|
@@ -249,15 +266,8 @@ const resolvePopulateSpecForModel = async ({
|
|
|
249
266
|
}
|
|
250
267
|
dependencyModelNames.add(refModelName);
|
|
251
268
|
nestedModel = await getModelCached(refModelName);
|
|
252
|
-
const aclMatch = getAccessibleByQuery(
|
|
253
|
-
|
|
254
|
-
"read",
|
|
255
|
-
refModelName
|
|
256
|
-
);
|
|
257
|
-
normalizedEntry.match = mergePopulateMatchWithAcl(
|
|
258
|
-
normalizedEntry.match,
|
|
259
|
-
aclMatch
|
|
260
|
-
);
|
|
269
|
+
const aclMatch = getAccessibleByQuery(ability, "read", refModelName);
|
|
270
|
+
normalizedEntry.match = mergePopulateMatchWithAcl(normalizedEntry.match, aclMatch);
|
|
261
271
|
} else if (entry.populate !== void 0) {
|
|
262
272
|
throw new Error("Populate path must reference a model when nested populate is used");
|
|
263
273
|
}
|
|
@@ -347,7 +357,9 @@ const runRtsQuery = async ({
|
|
|
347
357
|
dependencyModelNames: /* @__PURE__ */ new Set()
|
|
348
358
|
});
|
|
349
359
|
const accessQuery = getAccessibleByQuery(ability, "read", modelName);
|
|
350
|
-
const finalQuery = {
|
|
360
|
+
const finalQuery = {
|
|
361
|
+
$and: [query, accessQuery]
|
|
362
|
+
};
|
|
351
363
|
if (options.pagination) {
|
|
352
364
|
const paginatedQuery = model.find(finalQuery, projection);
|
|
353
365
|
if (populate !== void 0) {
|
|
@@ -372,7 +384,9 @@ const runRtsQuery = async ({
|
|
|
372
384
|
}
|
|
373
385
|
queryPromise.limit(limit);
|
|
374
386
|
const data = await queryPromise;
|
|
375
|
-
return {
|
|
387
|
+
return {
|
|
388
|
+
data: Array.isArray(data) ? data : []
|
|
389
|
+
};
|
|
376
390
|
};
|
|
377
391
|
export {
|
|
378
392
|
RTS_TENANT_ID_QUERY_PARAM as R,
|
|
@@ -385,4 +399,4 @@ export {
|
|
|
385
399
|
normalizeRtsQueryOptions as n,
|
|
386
400
|
resolveRtsRequestTenantId as r
|
|
387
401
|
};
|
|
388
|
-
//# sourceMappingURL=queryExecutor-
|
|
402
|
+
//# sourceMappingURL=queryExecutor-Dn62mIDL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryExecutor-DSCpsVI8.js","sources":["../src/getDerivedKey.ts","../src/rts/queryExecutor.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 type { Request } from \"express\"\nimport type { PaginationPageInfo, PaginationSpec } from \"@rpcbase/api\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbility, buildAbilityFromSession, getAccessibleByQuery, getTenantRolesFromSessionUser, 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 signedInTenants?: unknown\n}\n\ntype HeaderUserDoc = {\n tenants?: unknown\n tenantRoles?: 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}\n\nexport const RTS_TENANT_ID_QUERY_PARAM = \"rb-tenant-id\"\nexport const RTS_USER_ID_HEADER = \"rb-user-id\"\n\nconst QUERY_MAX_LIMIT = 4096\nconst INTERNAL_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\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 normalizeSignedInTenants = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((tenantId) => normalizeTenantId(String(tenantId)))\n .filter((tenantId): tenantId is string => Boolean(tenantId))\n}\n\nconst getTenantIdFromRequest = (req: Request): string | null => {\n const rawQuery = req.query?.[RTS_TENANT_ID_QUERY_PARAM]\n const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery\n const normalizedFromQuery = normalizeTenantId(queryTenantId)\n if (normalizedFromQuery) return normalizedFromQuery\n\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 const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) return sessionUserId\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserId = Array.isArray(headerValue) ? headerValue[0] : headerValue\n return normalizeTenantId(headerUserId)\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 signedInTenants = normalizeSignedInTenants(sessionUser.signedInTenants)\n if (signedInTenants.length > 0) {\n return signedInTenants.includes(tenantId)\n }\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 ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: sessionUserId }\n }\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const headerUserId = normalizeTenantId(headerUserIdRaw)\n if (!headerUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: null }\n }\n\n const rbCtx: LoadModelCtx = { req: { session: null } }\n const User = await models.getGlobal(\"RBUser\", rbCtx)\n const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean() as HeaderUserDoc | null\n\n const tenantsRaw = user?.tenants\n const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : []\n if (!tenants.includes(tenantId)) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n const roles = getTenantRolesFromSessionUser(user, tenantId)\n return {\n ability: buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : [\"owner\"] }),\n userId: headerUserId,\n }\n}\n\nconst getTenantModel = async (tenantId: string, modelName: string): Promise<Model<any>> => {\n const ctx: LoadModelCtx = {\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n }\n\n return models.get(modelName, ctx)\n}\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 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 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)\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)\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}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<RtsQueryResult> => {\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)\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 const accessQuery = getAccessibleByQuery(ability, \"read\", modelName as Exclude<AclSubjectType, \"all\">)\n const finalQuery: JsonObject = { $and: [query, accessQuery] }\n\n if (options.pagination) {\n const paginatedQuery = model.find(finalQuery, 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 },\n })\n\n return {\n data: Array.isArray(paginatedResult.nodes) ? paginatedResult.nodes : [],\n pageInfo: paginatedResult.pageInfo,\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"],"names":["path","refModelName","resolved"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"queryExecutor-Dn62mIDL.js","sources":["../src/getDerivedKey.ts","../src/rts/queryExecutor.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 type { Request } from \"express\"\nimport type { PaginationPageInfo, PaginationSpec } from \"@rpcbase/api\"\nimport { models, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbility, buildAbilityFromSession, getAccessibleByQuery, getTenantRolesFromSessionUser, 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 signedInTenants?: unknown\n}\n\ntype HeaderUserDoc = {\n tenants?: unknown\n tenantRoles?: 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}\n\nexport const RTS_TENANT_ID_QUERY_PARAM = \"rb-tenant-id\"\nexport const RTS_USER_ID_HEADER = \"rb-user-id\"\n\nconst QUERY_MAX_LIMIT = 4096\nconst INTERNAL_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\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 normalizeSignedInTenants = (value: unknown): string[] => {\n if (!Array.isArray(value)) return []\n return value\n .map((tenantId) => normalizeTenantId(String(tenantId)))\n .filter((tenantId): tenantId is string => Boolean(tenantId))\n}\n\nconst getTenantIdFromRequest = (req: Request): string | null => {\n const rawQuery = req.query?.[RTS_TENANT_ID_QUERY_PARAM]\n const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery\n const normalizedFromQuery = normalizeTenantId(queryTenantId)\n if (normalizedFromQuery) return normalizedFromQuery\n\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 const sessionUserId = normalizeTenantId((req.session?.user as SessionUser | undefined)?.id)\n if (sessionUserId) return sessionUserId\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserId = Array.isArray(headerValue) ? headerValue[0] : headerValue\n return normalizeTenantId(headerUserId)\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 signedInTenants = normalizeSignedInTenants(sessionUser.signedInTenants)\n if (signedInTenants.length > 0) {\n return signedInTenants.includes(tenantId)\n }\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 ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: sessionUserId }\n }\n\n const headerValue = req.headers[RTS_USER_ID_HEADER]\n const headerUserIdRaw = Array.isArray(headerValue) ? headerValue[0] : headerValue\n const headerUserId = normalizeTenantId(headerUserIdRaw)\n if (!headerUserId) {\n const ability = buildAbilityFromSession({ tenantId, session: req.session })\n return { ability, userId: null }\n }\n\n const rbCtx: LoadModelCtx = { req: { session: null } }\n const User = await models.getGlobal(\"RBUser\", rbCtx)\n const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean() as HeaderUserDoc | null\n\n const tenantsRaw = user?.tenants\n const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((tenant) => String(tenant)) : []\n if (!tenants.includes(tenantId)) {\n throw new Error(\"Tenant not authorized for this session\")\n }\n\n const roles = getTenantRolesFromSessionUser(user, tenantId)\n return {\n ability: buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : [\"owner\"] }),\n userId: headerUserId,\n }\n}\n\nconst getTenantModel = async (tenantId: string, modelName: string): Promise<Model<any>> => {\n const ctx: LoadModelCtx = {\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n }\n\n return models.get(modelName, ctx)\n}\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 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 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)\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)\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}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: boolean\n}): Promise<RtsQueryResult> => {\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)\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 const accessQuery = getAccessibleByQuery(ability, \"read\", modelName as Exclude<AclSubjectType, \"all\">)\n const finalQuery: JsonObject = { $and: [query, accessQuery] }\n\n if (options.pagination) {\n const paginatedQuery = model.find(finalQuery, 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 },\n })\n\n return {\n data: Array.isArray(paginatedResult.nodes) ? paginatedResult.nodes : [],\n pageInfo: paginatedResult.pageInfo,\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"],"names":["getDerivedKey","masterKey","info","length","salt","assert","Buffer","from","hkdfSync","toString","RTS_TENANT_ID_QUERY_PARAM","RTS_USER_ID_HEADER","QUERY_MAX_LIMIT","INTERNAL_MODEL_NAMES","Set","paginationCursorSigningSecret","getPaginationCursorSigningSecret","process","env","MASTER_KEY","trim","Error","normalizeTenantId","value","normalized","normalizeSignedInTenants","Array","isArray","map","tenantId","String","filter","Boolean","getTenantIdFromRequest","req","rawQuery","query","queryTenantId","normalizedFromQuery","session","user","currentTenantId","resolveRtsRequestTenantId","isRtsRequestAuthorized","sessionUser","signedInTenants","includes","buildRtsAbilityFromRequest","sessionUserId","id","ability","buildAbilityFromSession","userId","headerValue","headers","headerUserIdRaw","headerUserId","rbCtx","User","models","getGlobal","findById","tenants","tenantRoles","lean","tenantsRaw","tenant","roles","getTenantRolesFromSessionUser","buildAbility","getTenantModel","modelName","ctx","get","normalizeLimit","limit","Number","isFinite","Math","min","abs","normalizeString","normalizeObject","undefined","normalizePagination","normalizePopulateSelect","normalizePopulateOptions","raw","sort","max","floor","normalizePopulateObject","path","model","select","match","nestedPopulate","normalizeRtsPopulateOption","populate","options","entry","normalizeModelName","resolvePopulateRefModelName","explicitModelName","schema","schemaPath","directRef","ref","arrayRef","caster","virtualPath","virtualpath","virtualRef","mergePopulateMatchWithAcl","populateMatch","aclMatch","Object","keys","$and","resolvePopulateSpecForModel","allowInternalModels","modelCache","dependencyModelNames","getModelCached","targetModelName","cached","loaded","set","resolveOne","parentModel","refModelName","has","can","add","getAccessibleByQuery","nestedModel","normalizedEntry","resolved","Promise","all","filtered","normalizeRtsQueryOptions","projection","pagination","resolveRtsQueryDependencyModelNames","Map","runRtsQuery","accessQuery","finalQuery","paginatedQuery","find","paginatedResult","paginate","cursor","signingSecret","data","nodes","pageInfo","queryPromise"],"mappings":"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"}
|
package/dist/renderSSR.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAE7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAE7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,iBAAiB,CAAA;AA8DxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GACtC,OAAO,CAAC;IACT,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAA;IACvD,gBAAgB,CAAC,EAAE,QAAQ,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAC,CAqGD"}
|