@rpcbase/server 0.531.0 → 0.533.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/index.js +68 -24
- package/dist/index.js.map +1 -1
- package/dist/{queryExecutor-DYVlCvns.js → queryExecutor-JadZcQSQ.js} +29 -6
- package/dist/queryExecutor-JadZcQSQ.js.map +1 -0
- package/dist/rts/index.d.ts.map +1 -1
- package/dist/rts/index.js +261 -61
- package/dist/rts/index.js.map +1 -1
- package/dist/rts/queryExecutor.d.ts +7 -0
- package/dist/rts/queryExecutor.d.ts.map +1 -1
- package/dist/rts/ssrHydration.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/queryExecutor-DYVlCvns.js.map +0 -1
|
@@ -253,9 +253,13 @@ var resolveRtsQueryDependencyModelNames = async ({ tenantId, ability, modelName,
|
|
|
253
253
|
return Array.from(dependencyModelNames);
|
|
254
254
|
};
|
|
255
255
|
var runRtsQuery = async ({ tenantId, ability, modelName, query, options, allowInternalModels = false }) => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
const { model, finalQuery } = await prepareRtsExecution({
|
|
257
|
+
tenantId,
|
|
258
|
+
ability,
|
|
259
|
+
modelName,
|
|
260
|
+
query,
|
|
261
|
+
allowInternalModels
|
|
262
|
+
});
|
|
259
263
|
const projection = options.projection ?? void 0;
|
|
260
264
|
const sort = options.sort;
|
|
261
265
|
const limit = normalizeLimit(options.limit);
|
|
@@ -270,7 +274,6 @@ var runRtsQuery = async ({ tenantId, ability, modelName, query, options, allowIn
|
|
|
270
274
|
modelCache,
|
|
271
275
|
dependencyModelNames: /* @__PURE__ */ new Set()
|
|
272
276
|
});
|
|
273
|
-
const finalQuery = { $and: [query, getAccessibleByQuery(ability, "read", modelName)] };
|
|
274
277
|
if (options.pagination) {
|
|
275
278
|
const paginatedQuery = model.find(finalQuery, projection);
|
|
276
279
|
if (populate !== void 0) paginatedQuery.populate(populate);
|
|
@@ -289,7 +292,27 @@ var runRtsQuery = async ({ tenantId, ability, modelName, query, options, allowIn
|
|
|
289
292
|
const data = await queryPromise;
|
|
290
293
|
return { data: Array.isArray(data) ? data : [] };
|
|
291
294
|
};
|
|
295
|
+
var prepareRtsExecution = async ({ tenantId, ability, modelName, query, allowInternalModels = false }) => {
|
|
296
|
+
if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) throw new Error("Model not allowed");
|
|
297
|
+
if (!ability.can("read", modelName)) throw new Error("forbidden");
|
|
298
|
+
return {
|
|
299
|
+
model: await getTenantModel(tenantId, modelName, ability),
|
|
300
|
+
finalQuery: { $and: [query, getAccessibleByQuery(ability, "read", modelName)] }
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
var runRtsCount = async ({ tenantId, ability, modelName, query, allowInternalModels = false }) => {
|
|
304
|
+
const { model, finalQuery } = await prepareRtsExecution({
|
|
305
|
+
tenantId,
|
|
306
|
+
ability,
|
|
307
|
+
modelName,
|
|
308
|
+
query,
|
|
309
|
+
allowInternalModels
|
|
310
|
+
});
|
|
311
|
+
const count = await model.countDocuments(finalQuery);
|
|
312
|
+
if (typeof count !== "number" || !Number.isFinite(count) || count < 0) return 0;
|
|
313
|
+
return Math.floor(count);
|
|
314
|
+
};
|
|
292
315
|
//#endregion
|
|
293
|
-
export { normalizeRtsQueryOptions as a,
|
|
316
|
+
export { normalizeRtsQueryOptions as a, runRtsCount as c, isRtsRequestAuthorized as i, runRtsQuery as l, RTS_USER_ID_HEADER as n, resolveRtsQueryDependencyModelNames as o, buildRtsAbilityFromRequest as r, resolveRtsRequestTenantId as s, RTS_TENANT_ID_QUERY_PARAM as t, getDerivedKey as u };
|
|
294
317
|
|
|
295
|
-
//# sourceMappingURL=queryExecutor-
|
|
318
|
+
//# sourceMappingURL=queryExecutor-JadZcQSQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryExecutor-JadZcQSQ.js","names":["assert","hkdfSync","getDerivedKey","masterKey","info","length","salt","Buffer","from","toString","Request","PaginationPageInfo","PaginationSpec","models","LoadModelCtx","buildAbility","buildAbilityFromSession","getAccessibleByQuery","getTenantRolesFromSessionUser","AclSubjectType","AppAbility","Model","getDerivedKey","JsonObject","Record","SessionUser","id","currentTenantId","signedInTenants","HeaderUserDoc","tenants","tenantRoles","RtsPopulateObject","path","model","select","match","options","sort","limit","populate","RtsPopulateOption","Array","RtsQueryOptions","projection","pagination","RtsQueryResult","data","pageInfo","totalCount","PreparedRtsExecution","finalQuery","RTS_TENANT_ID_QUERY_PARAM","RTS_USER_ID_HEADER","QUERY_MAX_LIMIT","INTERNAL_MODEL_NAMES","Set","paginationCursorSigningSecret","getPaginationCursorSigningSecret","masterKey","process","env","MASTER_KEY","trim","Error","normalizeTenantId","value","normalized","normalizeSignedInTenants","isArray","map","tenantId","String","filter","Boolean","getTenantIdFromRequest","req","rawQuery","query","queryTenantId","normalizedFromQuery","session","user","resolveRtsRequestTenantId","resolveRtsRequestUserId","sessionUserId","headerValue","headers","headerUserId","isRtsRequestAuthorized","sessionUser","length","includes","buildRtsAbilityFromRequest","Promise","ability","userId","headerUserIdRaw","rbCtx","User","getGlobal","findById","lean","tenantsRaw","tenant","roles","getTenantModel","modelName","ctx","get","normalizeLimit","Number","isFinite","Math","min","abs","normalizeString","normalizeObject","undefined","normalizePagination","normalizePopulateSelect","normalizePopulateOptions","raw","max","floor","normalizePopulateObject","nestedPopulate","normalizeRtsPopulateOption","entry","normalizeModelName","resolvePopulateRefModelName","explicitModelName","schema","schemaPath","directRef","ref","arrayRef","caster","virtualPath","virtualpath","virtualRef","mergePopulateMatchWithAcl","populateMatch","aclMatch","Object","keys","$and","PreparedPopulateObject","PreparedPopulateOption","resolvePopulateSpecForModel","allowInternalModels","modelCache","dependencyModelNames","Map","getModelCached","targetModelName","cached","loaded","set","resolveOne","parentModel","refModelName","has","can","add","Exclude","nestedModel","normalizedEntry","resolved","all","filtered","normalizeRtsQueryOptions","resolveRtsQueryDependencyModelNames","from","runRtsQuery","prepareRtsExecution","paginatedQuery","find","paginatedResult","paginate","cursor","signingSecret","nodes","queryPromise","accessQuery","runRtsCount","count","countDocuments"],"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 totalCount?: number\n}\n\ntype PreparedRtsExecution = {\n model: Model<any>\n finalQuery: JsonObject\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, 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 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, 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}: {\n tenantId: string\n ability: AppAbility\n modelName: string\n query: JsonObject\n options: RtsQueryOptions\n allowInternalModels?: 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 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 const totalCount = typeof paginatedResult.totalCount === \"number\"\n && Number.isFinite(paginatedResult.totalCount)\n && paginatedResult.totalCount >= 0\n ? Math.floor(paginatedResult.totalCount)\n : undefined\n\n return {\n data: Array.isArray(paginatedResult.nodes) ? paginatedResult.nodes : [],\n pageInfo: paginatedResult.pageInfo,\n ...(totalCount !== undefined ? { totalCount } : {}),\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\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 count = await model.countDocuments(finalQuery)\n if (typeof count !== \"number\" || !Number.isFinite(count) || count < 0) {\n return 0\n }\n\n return Math.floor(count)\n}\n"],"mappings":"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"}
|
package/dist/rts/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rts/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAA6F,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5I,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rts/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAA6F,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5I,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,IAAI,CAAA;AA+DlE,KAAK,UAAU,GAAG;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,UAAU,CAAA;CACpB,CAAA;AA+BD,KAAK,SAAS,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAA;AA+B3D,cAAM,SAAS;IACb,SAAgB,EAAE,EAAE,MAAM,CAAA;IAC1B,SAAgB,QAAQ,EAAE,MAAM,CAAA;IAChC,SAAgB,MAAM,EAAE,MAAM,CAAA;IAE9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqD;gBAE3D,EACjB,EAAE,EACF,EAAE,EACF,IAAI,GACL,EAAE;QACD,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,SAAS,CAAA;QACb,IAAI,EAAE,UAAU,CAAA;KACjB;IAOM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAOlE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAO7D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,KAAK,IAAI,IAAI;IAQb,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;CAOvD;AAq4BD,eAAO,MAAM,OAAO,GAAI,4NAQrB;IACD,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B,KAAG,IA4HH,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,SAAS,SAAS,KAAG,IAEvD,CAAA;AAED,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,IAE3E,CAAA;AAED,cAAc,UAAU,CAAA"}
|
package/dist/rts/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as normalizeRtsQueryOptions, c as runRtsQuery, n as RTS_USER_ID_HEADER, o as resolveRtsQueryDependencyModelNames, t as RTS_TENANT_ID_QUERY_PARAM } from "../queryExecutor-
|
|
1
|
+
import { a as normalizeRtsQueryOptions, c as runRtsCount, l as runRtsQuery, n as RTS_USER_ID_HEADER, o as resolveRtsQueryDependencyModelNames, t as RTS_TENANT_ID_QUERY_PARAM } from "../queryExecutor-JadZcQSQ.js";
|
|
2
2
|
import { models } from "@rpcbase/db";
|
|
3
3
|
import { buildAbility, buildAbilityFromSession, getTenantRolesFromSessionUser } from "@rpcbase/db/acl";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -22,7 +22,9 @@ var socketMeta = /* @__PURE__ */ new Map();
|
|
|
22
22
|
var socketWrappers = /* @__PURE__ */ new Map();
|
|
23
23
|
var socketCleanup = /* @__PURE__ */ new Map();
|
|
24
24
|
var socketSubscriptions = /* @__PURE__ */ new Map();
|
|
25
|
+
var socketCountSubscriptions = /* @__PURE__ */ new Map();
|
|
25
26
|
var subscriptions = /* @__PURE__ */ new Map();
|
|
27
|
+
var countSubscriptions = /* @__PURE__ */ new Map();
|
|
26
28
|
var changeStreams = /* @__PURE__ */ new Map();
|
|
27
29
|
var dispatchTimers = /* @__PURE__ */ new Map();
|
|
28
30
|
var upgradeMeta = /* @__PURE__ */ new WeakMap();
|
|
@@ -206,7 +208,7 @@ var scheduleDispatchSubscriptionsForModel = (tenantId, modelName) => {
|
|
|
206
208
|
const delay = Math.max(0, Math.min(1e3, Math.floor(dispatchDebounceMs)));
|
|
207
209
|
dispatchTimers.set(key, setTimeout(() => {
|
|
208
210
|
dispatchTimers.delete(key);
|
|
209
|
-
dispatchSubscriptionsForModel(tenantId, modelName);
|
|
211
|
+
Promise.all([dispatchSubscriptionsForModel(tenantId, modelName), dispatchCountSubscriptionsForModel(tenantId, modelName)]).catch(() => {});
|
|
210
212
|
}, delay));
|
|
211
213
|
};
|
|
212
214
|
var runAndSendQuery = async ({ tenantId, targetSocketIds, ability, modelName, queryKey, query, options }) => {
|
|
@@ -232,6 +234,25 @@ var runAndSendQuery = async ({ tenantId, targetSocketIds, ability, modelName, qu
|
|
|
232
234
|
sendWs(ws, payload);
|
|
233
235
|
}
|
|
234
236
|
};
|
|
237
|
+
var runAndSendCount = async ({ tenantId, targetSocketIds, ability, modelName, queryKey, query }) => {
|
|
238
|
+
const payload = {
|
|
239
|
+
type: "count-payload",
|
|
240
|
+
modelName,
|
|
241
|
+
queryKey,
|
|
242
|
+
count: await runRtsCount({
|
|
243
|
+
tenantId,
|
|
244
|
+
ability,
|
|
245
|
+
modelName,
|
|
246
|
+
query,
|
|
247
|
+
allowInternalModels
|
|
248
|
+
})
|
|
249
|
+
};
|
|
250
|
+
for (const socketId of targetSocketIds) {
|
|
251
|
+
const ws = sockets.get(socketId);
|
|
252
|
+
if (!ws) continue;
|
|
253
|
+
sendWs(ws, payload);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
235
256
|
var subscriptionDependsOnModel = (changedModelName, ownerModelName, subscription) => {
|
|
236
257
|
if (ownerModelName === changedModelName) return true;
|
|
237
258
|
return subscription.dependencyModelNames.includes(changedModelName);
|
|
@@ -242,6 +263,12 @@ var hasAnySubscriptionsDependingOnModel = (tenantId, modelName) => {
|
|
|
242
263
|
for (const userSubs of tenantSubs.values()) for (const [ownerModelName, modelSubs] of userSubs.entries()) for (const subscription of modelSubs.values()) if (subscriptionDependsOnModel(modelName, ownerModelName, subscription)) return true;
|
|
243
264
|
return false;
|
|
244
265
|
};
|
|
266
|
+
var hasAnyCountSubscriptionsForModel = (tenantId, modelName) => {
|
|
267
|
+
const tenantSubs = countSubscriptions.get(tenantId);
|
|
268
|
+
if (!tenantSubs) return false;
|
|
269
|
+
for (const userSubs of tenantSubs.values()) if (userSubs.get(modelName)?.size) return true;
|
|
270
|
+
return false;
|
|
271
|
+
};
|
|
245
272
|
var dispatchSubscriptionsForModel = async (tenantId, modelName) => {
|
|
246
273
|
const tenantSubs = subscriptions.get(tenantId);
|
|
247
274
|
if (!tenantSubs || !tenantSubs.size) return;
|
|
@@ -277,6 +304,43 @@ var dispatchSubscriptionsForModel = async (tenantId, modelName) => {
|
|
|
277
304
|
}
|
|
278
305
|
}
|
|
279
306
|
};
|
|
307
|
+
var dispatchCountSubscriptionsForModel = async (tenantId, modelName) => {
|
|
308
|
+
const tenantSubs = countSubscriptions.get(tenantId);
|
|
309
|
+
if (!tenantSubs || !tenantSubs.size) return;
|
|
310
|
+
for (const userSubs of tenantSubs.values()) {
|
|
311
|
+
const modelSubs = userSubs.get(modelName);
|
|
312
|
+
if (!modelSubs) continue;
|
|
313
|
+
for (const [queryKey, sub] of modelSubs.entries()) {
|
|
314
|
+
const targetSocketIds = Array.from(sub.socketIds);
|
|
315
|
+
if (!targetSocketIds.length) continue;
|
|
316
|
+
const socketId = targetSocketIds[0];
|
|
317
|
+
const ability = socketMeta.get(socketId)?.ability;
|
|
318
|
+
if (!ability) continue;
|
|
319
|
+
try {
|
|
320
|
+
await runAndSendCount({
|
|
321
|
+
tenantId,
|
|
322
|
+
targetSocketIds,
|
|
323
|
+
ability,
|
|
324
|
+
modelName,
|
|
325
|
+
queryKey,
|
|
326
|
+
query: sub.query
|
|
327
|
+
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
const payload = {
|
|
330
|
+
type: "count-payload",
|
|
331
|
+
modelName,
|
|
332
|
+
queryKey,
|
|
333
|
+
error: redactErrorMessage(err)
|
|
334
|
+
};
|
|
335
|
+
for (const targetSocketId of targetSocketIds) {
|
|
336
|
+
const ws = sockets.get(targetSocketId);
|
|
337
|
+
if (!ws) continue;
|
|
338
|
+
sendWs(ws, payload);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
280
344
|
var ensureChangeStream = async (tenantId, modelName) => {
|
|
281
345
|
const tenantStreams = changeStreams.get(tenantId) ?? /* @__PURE__ */ new Map();
|
|
282
346
|
changeStreams.set(tenantId, tenantStreams);
|
|
@@ -322,6 +386,25 @@ var addSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKey,
|
|
|
322
386
|
byModel.set(modelName, querySet);
|
|
323
387
|
querySet.add(queryKey);
|
|
324
388
|
};
|
|
389
|
+
var addSocketCountSubscription = ({ socketId, tenantId, userId, modelName, queryKey, query }) => {
|
|
390
|
+
const tenantSubs = countSubscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
|
|
391
|
+
countSubscriptions.set(tenantId, tenantSubs);
|
|
392
|
+
const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
|
|
393
|
+
tenantSubs.set(userId, userSubs);
|
|
394
|
+
const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
|
|
395
|
+
userSubs.set(modelName, modelSubs);
|
|
396
|
+
const existing = modelSubs.get(queryKey);
|
|
397
|
+
if (existing) existing.socketIds.add(socketId);
|
|
398
|
+
else modelSubs.set(queryKey, {
|
|
399
|
+
query,
|
|
400
|
+
socketIds: new Set([socketId])
|
|
401
|
+
});
|
|
402
|
+
const byModel = socketCountSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
|
|
403
|
+
socketCountSubscriptions.set(socketId, byModel);
|
|
404
|
+
const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
|
|
405
|
+
byModel.set(modelName, querySet);
|
|
406
|
+
querySet.add(queryKey);
|
|
407
|
+
};
|
|
325
408
|
var removeSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKey }) => {
|
|
326
409
|
const tenantSubs = subscriptions.get(tenantId);
|
|
327
410
|
const userSubs = tenantSubs?.get(userId);
|
|
@@ -341,7 +424,7 @@ var removeSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKe
|
|
|
341
424
|
if (modelSubs && modelSubs.size === 0) userSubs?.delete(modelName);
|
|
342
425
|
if (userSubs && userSubs.size === 0) tenantSubs?.delete(userId);
|
|
343
426
|
for (const affectedModelName of affectedModelNames) {
|
|
344
|
-
if (hasAnySubscriptionsDependingOnModel(tenantId, affectedModelName)) continue;
|
|
427
|
+
if (hasAnySubscriptionsDependingOnModel(tenantId, affectedModelName) || hasAnyCountSubscriptionsForModel(tenantId, affectedModelName)) continue;
|
|
345
428
|
const tenantStreams = changeStreams.get(tenantId);
|
|
346
429
|
const stream = tenantStreams?.get(affectedModelName);
|
|
347
430
|
if (stream) {
|
|
@@ -356,6 +439,46 @@ var removeSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKe
|
|
|
356
439
|
if (tenantSubs && tenantSubs.size === 0) subscriptions.delete(tenantId);
|
|
357
440
|
if (byModel && byModel.size === 0) socketSubscriptions.delete(socketId);
|
|
358
441
|
};
|
|
442
|
+
var removeSocketCountSubscription = ({ socketId, tenantId, userId, modelName, queryKey }) => {
|
|
443
|
+
const tenantSubs = countSubscriptions.get(tenantId);
|
|
444
|
+
const userSubs = tenantSubs?.get(userId);
|
|
445
|
+
const modelSubs = userSubs?.get(modelName);
|
|
446
|
+
const sub = modelSubs?.get(queryKey);
|
|
447
|
+
if (sub) {
|
|
448
|
+
sub.socketIds.delete(socketId);
|
|
449
|
+
if (!sub.socketIds.size) modelSubs?.delete(queryKey);
|
|
450
|
+
}
|
|
451
|
+
const byModel = socketCountSubscriptions.get(socketId);
|
|
452
|
+
const set = byModel?.get(modelName);
|
|
453
|
+
if (set) {
|
|
454
|
+
set.delete(queryKey);
|
|
455
|
+
if (!set.size) byModel?.delete(modelName);
|
|
456
|
+
}
|
|
457
|
+
if (modelSubs && modelSubs.size === 0) userSubs?.delete(modelName);
|
|
458
|
+
if (userSubs && userSubs.size === 0) tenantSubs?.delete(userId);
|
|
459
|
+
if (!hasAnySubscriptionsDependingOnModel(tenantId, modelName) && !hasAnyCountSubscriptionsForModel(tenantId, modelName)) {
|
|
460
|
+
const tenantStreams = changeStreams.get(tenantId);
|
|
461
|
+
const stream = tenantStreams?.get(modelName);
|
|
462
|
+
if (stream) {
|
|
463
|
+
try {
|
|
464
|
+
stream.close();
|
|
465
|
+
} catch {}
|
|
466
|
+
clearDispatchTimer(tenantId, modelName);
|
|
467
|
+
tenantStreams?.delete(modelName);
|
|
468
|
+
if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (tenantSubs && tenantSubs.size === 0) countSubscriptions.delete(tenantId);
|
|
472
|
+
if (byModel && byModel.size === 0) socketCountSubscriptions.delete(socketId);
|
|
473
|
+
};
|
|
474
|
+
var getSocketSubscriptionCount = (socketId) => {
|
|
475
|
+
let count = 0;
|
|
476
|
+
const querySubscriptionsByModel = socketSubscriptions.get(socketId);
|
|
477
|
+
if (querySubscriptionsByModel) for (const set of querySubscriptionsByModel.values()) count += set.size;
|
|
478
|
+
const countSubscriptionsByModel = socketCountSubscriptions.get(socketId);
|
|
479
|
+
if (countSubscriptionsByModel) for (const set of countSubscriptionsByModel.values()) count += set.size;
|
|
480
|
+
return count;
|
|
481
|
+
};
|
|
359
482
|
var cleanupSocket = (socketId) => {
|
|
360
483
|
const meta = socketMeta.get(socketId);
|
|
361
484
|
if (meta) {
|
|
@@ -367,8 +490,17 @@ var cleanupSocket = (socketId) => {
|
|
|
367
490
|
modelName,
|
|
368
491
|
queryKey
|
|
369
492
|
});
|
|
493
|
+
const countByModel = socketCountSubscriptions.get(socketId);
|
|
494
|
+
if (countByModel) for (const [modelName, keys] of countByModel.entries()) for (const queryKey of keys.values()) removeSocketCountSubscription({
|
|
495
|
+
socketId,
|
|
496
|
+
tenantId: meta.tenantId,
|
|
497
|
+
userId: meta.userId,
|
|
498
|
+
modelName,
|
|
499
|
+
queryKey
|
|
500
|
+
});
|
|
370
501
|
}
|
|
371
502
|
socketSubscriptions.delete(socketId);
|
|
503
|
+
socketCountSubscriptions.delete(socketId);
|
|
372
504
|
const cleanupFns = socketCleanup.get(socketId) ?? [];
|
|
373
505
|
socketCleanup.delete(socketId);
|
|
374
506
|
for (const fn of cleanupFns) try {
|
|
@@ -385,14 +517,27 @@ var handleClientMessage = async ({ socketId, meta, message }) => {
|
|
|
385
517
|
socketWrappers.get(socketId)?.dispatch(message.event, message.payload);
|
|
386
518
|
return;
|
|
387
519
|
}
|
|
388
|
-
|
|
389
|
-
|
|
520
|
+
const isCountMessage = message.type === "run-count" || message.type === "register-count" || message.type === "remove-count";
|
|
521
|
+
const sendAccessError = (error) => {
|
|
522
|
+
if (isCountMessage) {
|
|
523
|
+
sendWs(ws, {
|
|
524
|
+
type: "count-payload",
|
|
525
|
+
modelName: message.modelName ?? "",
|
|
526
|
+
queryKey: message.queryKey ?? "",
|
|
527
|
+
error
|
|
528
|
+
});
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
390
531
|
sendWs(ws, {
|
|
391
532
|
type: "query-payload",
|
|
392
|
-
modelName: message.modelName,
|
|
533
|
+
modelName: message.modelName ?? "",
|
|
393
534
|
queryKey: message.queryKey ?? "",
|
|
394
|
-
error
|
|
535
|
+
error
|
|
395
536
|
});
|
|
537
|
+
};
|
|
538
|
+
if (!message.modelName || typeof message.modelName !== "string") return;
|
|
539
|
+
if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(message.modelName)) {
|
|
540
|
+
sendAccessError("Model not allowed");
|
|
396
541
|
return;
|
|
397
542
|
}
|
|
398
543
|
if (!message.queryKey || typeof message.queryKey !== "string") return;
|
|
@@ -407,24 +552,26 @@ var handleClientMessage = async ({ socketId, meta, message }) => {
|
|
|
407
552
|
});
|
|
408
553
|
return;
|
|
409
554
|
}
|
|
555
|
+
if (message.type === "remove-count") {
|
|
556
|
+
removeSocketCountSubscription({
|
|
557
|
+
socketId,
|
|
558
|
+
tenantId: meta.tenantId,
|
|
559
|
+
userId: meta.userId,
|
|
560
|
+
modelName: message.modelName,
|
|
561
|
+
queryKey: message.queryKey
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
410
565
|
if (!message.query || typeof message.query !== "object") return;
|
|
411
|
-
const options = normalizeRtsQueryOptions(message.options);
|
|
412
566
|
const ability = meta.ability;
|
|
413
567
|
if (!ability.can("read", message.modelName)) {
|
|
414
|
-
|
|
415
|
-
type: "query-payload",
|
|
416
|
-
modelName: message.modelName,
|
|
417
|
-
queryKey: message.queryKey,
|
|
418
|
-
error: "forbidden"
|
|
419
|
-
});
|
|
568
|
+
sendAccessError("forbidden");
|
|
420
569
|
return;
|
|
421
570
|
}
|
|
422
|
-
if (message.type === "register-query") {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (byModel) for (const set of byModel.values()) count += set.size;
|
|
427
|
-
if (count >= maxSubscriptionsPerSocket) {
|
|
571
|
+
if (message.type === "register-query" || message.type === "run-query") {
|
|
572
|
+
const options = normalizeRtsQueryOptions(message.options);
|
|
573
|
+
if (message.type === "register-query") {
|
|
574
|
+
if (!(socketSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false) && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
|
|
428
575
|
sendWs(ws, {
|
|
429
576
|
type: "query-payload",
|
|
430
577
|
modelName: message.modelName,
|
|
@@ -433,15 +580,59 @@ var handleClientMessage = async ({ socketId, meta, message }) => {
|
|
|
433
580
|
});
|
|
434
581
|
return;
|
|
435
582
|
}
|
|
583
|
+
let dependencyModelNames = [];
|
|
584
|
+
if (options.populate !== void 0) try {
|
|
585
|
+
dependencyModelNames = await resolveRtsQueryDependencyModelNames({
|
|
586
|
+
tenantId: meta.tenantId,
|
|
587
|
+
ability,
|
|
588
|
+
modelName: message.modelName,
|
|
589
|
+
options,
|
|
590
|
+
allowInternalModels
|
|
591
|
+
});
|
|
592
|
+
} catch (err) {
|
|
593
|
+
const error = redactErrorMessage(err);
|
|
594
|
+
sendWs(ws, {
|
|
595
|
+
type: "query-payload",
|
|
596
|
+
modelName: message.modelName,
|
|
597
|
+
queryKey: message.queryKey,
|
|
598
|
+
error
|
|
599
|
+
});
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
addSocketSubscription({
|
|
603
|
+
socketId,
|
|
604
|
+
tenantId: meta.tenantId,
|
|
605
|
+
userId: meta.userId,
|
|
606
|
+
modelName: message.modelName,
|
|
607
|
+
queryKey: message.queryKey,
|
|
608
|
+
query: message.query,
|
|
609
|
+
options,
|
|
610
|
+
dependencyModelNames
|
|
611
|
+
});
|
|
612
|
+
try {
|
|
613
|
+
const modelNamesToWatch = new Set([message.modelName, ...dependencyModelNames]);
|
|
614
|
+
for (const modelName of modelNamesToWatch) await ensureChangeStream(meta.tenantId, modelName);
|
|
615
|
+
} catch (err) {
|
|
616
|
+
const error = redactErrorMessage(err);
|
|
617
|
+
sendWs(ws, {
|
|
618
|
+
type: "query-payload",
|
|
619
|
+
modelName: message.modelName,
|
|
620
|
+
queryKey: message.queryKey,
|
|
621
|
+
error
|
|
622
|
+
});
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (message.runInitialQuery === false) return;
|
|
436
626
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
dependencyModelNames = await resolveRtsQueryDependencyModelNames({
|
|
627
|
+
try {
|
|
628
|
+
await runAndSendQuery({
|
|
440
629
|
tenantId: meta.tenantId,
|
|
630
|
+
targetSocketIds: [socketId],
|
|
441
631
|
ability,
|
|
442
632
|
modelName: message.modelName,
|
|
443
|
-
|
|
444
|
-
|
|
633
|
+
queryKey: message.queryKey,
|
|
634
|
+
query: message.query,
|
|
635
|
+
options
|
|
445
636
|
});
|
|
446
637
|
} catch (err) {
|
|
447
638
|
const error = redactErrorMessage(err);
|
|
@@ -451,51 +642,60 @@ var handleClientMessage = async ({ socketId, meta, message }) => {
|
|
|
451
642
|
queryKey: message.queryKey,
|
|
452
643
|
error
|
|
453
644
|
});
|
|
454
|
-
return;
|
|
455
645
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (message.type === "register-count" || message.type === "run-count") {
|
|
649
|
+
if (message.type === "register-count") {
|
|
650
|
+
if (!(socketCountSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false) && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
|
|
651
|
+
sendWs(ws, {
|
|
652
|
+
type: "count-payload",
|
|
653
|
+
modelName: message.modelName,
|
|
654
|
+
queryKey: message.queryKey,
|
|
655
|
+
error: "Too many subscriptions"
|
|
656
|
+
});
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
addSocketCountSubscription({
|
|
660
|
+
socketId,
|
|
661
|
+
tenantId: meta.tenantId,
|
|
662
|
+
userId: meta.userId,
|
|
663
|
+
modelName: message.modelName,
|
|
664
|
+
queryKey: message.queryKey,
|
|
665
|
+
query: message.query
|
|
666
|
+
});
|
|
667
|
+
try {
|
|
668
|
+
await ensureChangeStream(meta.tenantId, message.modelName);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
const error = redactErrorMessage(err);
|
|
671
|
+
sendWs(ws, {
|
|
672
|
+
type: "count-payload",
|
|
673
|
+
modelName: message.modelName,
|
|
674
|
+
queryKey: message.queryKey,
|
|
675
|
+
error
|
|
676
|
+
});
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (message.runInitialQuery === false) return;
|
|
680
|
+
}
|
|
466
681
|
try {
|
|
467
|
-
|
|
468
|
-
|
|
682
|
+
await runAndSendCount({
|
|
683
|
+
tenantId: meta.tenantId,
|
|
684
|
+
targetSocketIds: [socketId],
|
|
685
|
+
ability,
|
|
686
|
+
modelName: message.modelName,
|
|
687
|
+
queryKey: message.queryKey,
|
|
688
|
+
query: message.query
|
|
689
|
+
});
|
|
469
690
|
} catch (err) {
|
|
470
691
|
const error = redactErrorMessage(err);
|
|
471
692
|
sendWs(ws, {
|
|
472
|
-
type: "
|
|
693
|
+
type: "count-payload",
|
|
473
694
|
modelName: message.modelName,
|
|
474
695
|
queryKey: message.queryKey,
|
|
475
696
|
error
|
|
476
697
|
});
|
|
477
|
-
return;
|
|
478
698
|
}
|
|
479
|
-
if (message.runInitialQuery === false) return;
|
|
480
|
-
}
|
|
481
|
-
try {
|
|
482
|
-
await runAndSendQuery({
|
|
483
|
-
tenantId: meta.tenantId,
|
|
484
|
-
targetSocketIds: [socketId],
|
|
485
|
-
ability,
|
|
486
|
-
modelName: message.modelName,
|
|
487
|
-
queryKey: message.queryKey,
|
|
488
|
-
query: message.query,
|
|
489
|
-
options
|
|
490
|
-
});
|
|
491
|
-
} catch (err) {
|
|
492
|
-
const error = redactErrorMessage(err);
|
|
493
|
-
sendWs(ws, {
|
|
494
|
-
type: "query-payload",
|
|
495
|
-
modelName: message.modelName,
|
|
496
|
-
queryKey: message.queryKey,
|
|
497
|
-
error
|
|
498
|
-
});
|
|
499
699
|
}
|
|
500
700
|
};
|
|
501
701
|
var initRts = ({ server, path = "/rts", sessionMiddleware, maxPayloadBytes: maxPayloadBytesArg, maxSubscriptionsPerSocket: maxSubscriptionsPerSocketArg, dispatchDebounceMs: dispatchDebounceMsArg, allowInternalModels: allowInternalModelsArg }) => {
|
|
@@ -581,7 +781,7 @@ var initRts = ({ server, path = "/rts", sessionMiddleware, maxPayloadBytes: maxP
|
|
|
581
781
|
}
|
|
582
782
|
if (!parsed || typeof parsed !== "object") return;
|
|
583
783
|
const message = parsed;
|
|
584
|
-
if (message.type !== "event" && message.type !== "run-query" && message.type !== "register-query" && message.type !== "remove-query") return;
|
|
784
|
+
if (message.type !== "event" && message.type !== "run-query" && message.type !== "register-query" && message.type !== "remove-query" && message.type !== "run-count" && message.type !== "register-count" && message.type !== "remove-count") return;
|
|
585
785
|
handleClientMessage({
|
|
586
786
|
socketId,
|
|
587
787
|
meta,
|