@rpcbase/server 0.476.0 → 0.478.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.
Files changed (33) hide show
  1. package/dist/{handler-DEEir2xV.js → handler-BOTZftAB.js} +29 -29
  2. package/dist/{handler-BITFtEr_.js → handler-B_mMDLBO.js} +80 -39
  3. package/dist/{handler-BYVnU9H-.js → handler-Cl-0-832.js} +1 -1
  4. package/dist/{handler-CHuOXAtH.js → handler-Dd20DHyz.js} +15 -11
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +102 -87
  8. package/dist/notifications/api/notifications/handler.d.ts.map +1 -1
  9. package/dist/notifications.js +1 -1
  10. package/dist/rts/api/changes/handler.d.ts.map +1 -1
  11. package/dist/rts/index.d.ts +3 -1
  12. package/dist/rts/index.d.ts.map +1 -1
  13. package/dist/{index-Ckx0UHs6.js → rts/index.js} +107 -39
  14. package/dist/{schemas-DI7ewltq.js → schemas-D5T9tDtI.js} +609 -12
  15. package/dist/{shared-Chfrv8o6.js → shared-UGuDRAKK.js} +16 -30
  16. package/dist/uploads/api/file-uploads/handlers/completeUpload.d.ts.map +1 -1
  17. package/dist/uploads/api/file-uploads/handlers/getStatus.d.ts.map +1 -1
  18. package/dist/uploads/api/file-uploads/handlers/uploadChunk.d.ts.map +1 -1
  19. package/dist/uploads/api/file-uploads/shared.d.ts +3 -0
  20. package/dist/uploads/api/file-uploads/shared.d.ts.map +1 -1
  21. package/dist/uploads.js +1 -1
  22. package/package.json +4 -4
  23. package/dist/passwordHashStorage.test.d.ts +0 -2
  24. package/dist/passwordHashStorage.test.d.ts.map +0 -1
  25. package/dist/rts/api/changes/handler.test.d.ts +0 -2
  26. package/dist/rts/api/changes/handler.test.d.ts.map +0 -1
  27. package/dist/rts/index.ws.test.d.ts +0 -2
  28. package/dist/rts/index.ws.test.d.ts.map +0 -1
  29. package/dist/rts.d.ts +0 -3
  30. package/dist/rts.d.ts.map +0 -1
  31. package/dist/rts.js +0 -13
  32. package/dist/uploads/api/files/handlers/getFile.test.d.ts +0 -2
  33. package/dist/uploads/api/files/handlers/getFile.test.d.ts.map +0 -1
@@ -1,6 +1,13 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { loadModel } from "@rpcbase/db";
2
+ import { loadRbModel, loadModel } from "@rpcbase/db";
3
+ import { buildAbilityFromSession, getTenantRolesFromSessionUser, buildAbility, getAccessibleByQuery } from "@rpcbase/db/acl";
3
4
  import { WebSocketServer } from "ws";
5
+ const routes = Object.entries({
6
+ .../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-Dd20DHyz.js") })
7
+ }).reduce((acc, [path, mod]) => {
8
+ acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
9
+ return acc;
10
+ }, {});
4
11
  const TENANT_ID_QUERY_PARAM = "rb-tenant-id";
5
12
  const USER_ID_HEADER = "rb-user-id";
6
13
  const QUERY_KEY_MAX_LEN = 4096;
@@ -150,14 +157,25 @@ const parseUpgradeMeta = async ({
150
157
  } else {
151
158
  throw new Error("Tenant not authorized for this session");
152
159
  }
153
- return { tenantId, userId: sessionUserId };
160
+ const ability2 = buildAbilityFromSession({ tenantId, session: upgradeReq.session });
161
+ return { tenantId, userId: sessionUserId, ability: ability2 };
154
162
  }
155
163
  const raw = req.headers[USER_ID_HEADER];
156
164
  const headerUserId = Array.isArray(raw) ? raw[0] : raw;
157
165
  if (!headerUserId) {
158
166
  throw new Error("Missing rb-user-id header (reverse-proxy) and no session middleware configured");
159
167
  }
160
- return { tenantId, userId: headerUserId };
168
+ const rbCtx = { req: { session: null } };
169
+ const User = await loadRbModel("RBUser", rbCtx);
170
+ const user = await User.findById(headerUserId, { tenants: 1, tenantRoles: 1 }).lean();
171
+ const tenantsRaw = user?.tenants;
172
+ const tenants = Array.isArray(tenantsRaw) ? tenantsRaw.map((t) => String(t)) : [];
173
+ if (!tenants.includes(tenantId)) {
174
+ throw new Error("Tenant not authorized for this session");
175
+ }
176
+ const roles = getTenantRolesFromSessionUser(user, tenantId);
177
+ const ability = buildAbility({ tenantId, userId: headerUserId, roles: roles.length ? roles : ["owner"] });
178
+ return { tenantId, userId: headerUserId, ability };
161
179
  };
162
180
  const getTenantModel = async (tenantId, modelName) => {
163
181
  const ctx = {
@@ -208,22 +226,34 @@ const scheduleDispatchSubscriptionsForModel = (tenantId, modelName) => {
208
226
  const runAndSendQuery = async ({
209
227
  tenantId,
210
228
  targetSocketIds,
229
+ ability,
211
230
  modelName,
212
231
  queryKey,
213
232
  query,
214
233
  options
215
234
  }) => {
235
+ if (!ability.can("read", modelName)) {
236
+ const payload2 = { type: "query-payload", modelName, queryKey, error: "forbidden" };
237
+ for (const socketId of targetSocketIds) {
238
+ const ws = sockets.get(socketId);
239
+ if (!ws) continue;
240
+ sendWs(ws, payload2);
241
+ }
242
+ return;
243
+ }
216
244
  const model = await getTenantModel(tenantId, modelName);
217
245
  const projection = options.projection ?? void 0;
218
246
  const sort = options.sort;
219
247
  const limit = normalizeLimit(options.limit);
220
- const queryPromise = model.find(query, projection);
248
+ const accessQuery = getAccessibleByQuery(ability, "read", modelName);
249
+ const finalQuery = { $and: [query, accessQuery] };
250
+ const queryPromise = model.find(finalQuery, projection);
221
251
  if (sort && Object.keys(sort).length) {
222
252
  queryPromise.sort(sort);
223
253
  }
224
254
  queryPromise.limit(limit);
225
255
  const data = await queryPromise;
226
- const payload = { type: "query_payload", modelName, queryKey, data };
256
+ const payload = { type: "query-payload", modelName, queryKey, data };
227
257
  for (const socketId of targetSocketIds) {
228
258
  const ws = sockets.get(socketId);
229
259
  if (!ws) continue;
@@ -232,27 +262,35 @@ const runAndSendQuery = async ({
232
262
  };
233
263
  const dispatchSubscriptionsForModel = async (tenantId, modelName) => {
234
264
  const tenantSubs = subscriptions.get(tenantId);
235
- const modelSubs = tenantSubs?.get(modelName);
236
- if (!modelSubs || !modelSubs.size) return;
237
- for (const [queryKey, sub] of modelSubs.entries()) {
238
- const targetSocketIds = Array.from(sub.socketIds);
239
- if (!targetSocketIds.length) continue;
240
- try {
241
- await runAndSendQuery({
242
- tenantId,
243
- targetSocketIds,
244
- modelName,
245
- queryKey,
246
- query: sub.query,
247
- options: sub.options
248
- });
249
- } catch (err) {
250
- const error = redactErrorMessage(err);
251
- const payload = { type: "query_payload", modelName, queryKey, error };
252
- for (const socketId of targetSocketIds) {
253
- const ws = sockets.get(socketId);
254
- if (!ws) continue;
255
- sendWs(ws, payload);
265
+ if (!tenantSubs || !tenantSubs.size) return;
266
+ for (const userSubs of tenantSubs.values()) {
267
+ const modelSubs = userSubs.get(modelName);
268
+ if (!modelSubs || !modelSubs.size) continue;
269
+ for (const [queryKey, sub] of modelSubs.entries()) {
270
+ const targetSocketIds = Array.from(sub.socketIds);
271
+ if (!targetSocketIds.length) continue;
272
+ const socketId = targetSocketIds[0];
273
+ const meta = socketMeta.get(socketId);
274
+ const ability = meta?.ability;
275
+ if (!ability) continue;
276
+ try {
277
+ await runAndSendQuery({
278
+ tenantId,
279
+ targetSocketIds,
280
+ ability,
281
+ modelName,
282
+ queryKey,
283
+ query: sub.query,
284
+ options: sub.options
285
+ });
286
+ } catch (err) {
287
+ const error = redactErrorMessage(err);
288
+ const payload = { type: "query-payload", modelName, queryKey, error };
289
+ for (const socketId2 of targetSocketIds) {
290
+ const ws = sockets.get(socketId2);
291
+ if (!ws) continue;
292
+ sendWs(ws, payload);
293
+ }
256
294
  }
257
295
  }
258
296
  }
@@ -286,6 +324,7 @@ const ensureChangeStream = async (tenantId, modelName) => {
286
324
  const addSocketSubscription = ({
287
325
  socketId,
288
326
  tenantId,
327
+ userId,
289
328
  modelName,
290
329
  queryKey,
291
330
  query,
@@ -293,8 +332,10 @@ const addSocketSubscription = ({
293
332
  }) => {
294
333
  const tenantSubs = subscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
295
334
  subscriptions.set(tenantId, tenantSubs);
296
- const modelSubs = tenantSubs.get(modelName) ?? /* @__PURE__ */ new Map();
297
- tenantSubs.set(modelName, modelSubs);
335
+ const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
336
+ tenantSubs.set(userId, userSubs);
337
+ const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
338
+ userSubs.set(modelName, modelSubs);
298
339
  const existing = modelSubs.get(queryKey);
299
340
  if (existing) {
300
341
  existing.socketIds.add(socketId);
@@ -314,11 +355,13 @@ const addSocketSubscription = ({
314
355
  const removeSocketSubscription = ({
315
356
  socketId,
316
357
  tenantId,
358
+ userId,
317
359
  modelName,
318
360
  queryKey
319
361
  }) => {
320
362
  const tenantSubs = subscriptions.get(tenantId);
321
- const modelSubs = tenantSubs?.get(modelName);
363
+ const userSubs = tenantSubs?.get(userId);
364
+ const modelSubs = userSubs?.get(modelName);
322
365
  const sub = modelSubs?.get(queryKey);
323
366
  if (sub) {
324
367
  sub.socketIds.delete(socketId);
@@ -335,7 +378,21 @@ const removeSocketSubscription = ({
335
378
  }
336
379
  }
337
380
  if (modelSubs && modelSubs.size === 0) {
338
- tenantSubs?.delete(modelName);
381
+ userSubs?.delete(modelName);
382
+ }
383
+ if (userSubs && userSubs.size === 0) {
384
+ tenantSubs?.delete(userId);
385
+ }
386
+ const hasAnyModelSubs = (() => {
387
+ const byUser = subscriptions.get(tenantId);
388
+ if (!byUser) return false;
389
+ for (const subs of byUser.values()) {
390
+ const modelSubs2 = subs.get(modelName);
391
+ if (modelSubs2 && modelSubs2.size > 0) return true;
392
+ }
393
+ return false;
394
+ })();
395
+ if (!hasAnyModelSubs) {
339
396
  const tenantStreams = changeStreams.get(tenantId);
340
397
  const stream = tenantStreams?.get(modelName);
341
398
  if (stream) {
@@ -361,6 +418,7 @@ const cleanupSocket = (socketId) => {
361
418
  removeSocketSubscription({
362
419
  socketId,
363
420
  tenantId: meta.tenantId,
421
+ userId: meta.userId,
364
422
  modelName,
365
423
  queryKey
366
424
  });
@@ -395,15 +453,16 @@ const handleClientMessage = async ({
395
453
  }
396
454
  if (!message.modelName || typeof message.modelName !== "string") return;
397
455
  if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(message.modelName)) {
398
- sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey ?? "", error: "Model not allowed" });
456
+ sendWs(ws, { type: "query-payload", modelName: message.modelName, queryKey: message.queryKey ?? "", error: "Model not allowed" });
399
457
  return;
400
458
  }
401
459
  if (!message.queryKey || typeof message.queryKey !== "string") return;
402
460
  if (message.queryKey.length > QUERY_KEY_MAX_LEN) return;
403
- if (message.type === "remove_query") {
461
+ if (message.type === "remove-query") {
404
462
  removeSocketSubscription({
405
463
  socketId,
406
464
  tenantId: meta.tenantId,
465
+ userId: meta.userId,
407
466
  modelName: message.modelName,
408
467
  queryKey: message.queryKey
409
468
  });
@@ -411,7 +470,12 @@ const handleClientMessage = async ({
411
470
  }
412
471
  if (!message.query || typeof message.query !== "object") return;
413
472
  const options = normalizeOptions(message.options);
414
- if (message.type === "registerQuery") {
473
+ const ability = meta.ability;
474
+ if (!ability.can("read", message.modelName)) {
475
+ sendWs(ws, { type: "query-payload", modelName: message.modelName, queryKey: message.queryKey, error: "forbidden" });
476
+ return;
477
+ }
478
+ if (message.type === "register-query") {
415
479
  const existing = socketSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false;
416
480
  if (!existing) {
417
481
  let count = 0;
@@ -420,13 +484,14 @@ const handleClientMessage = async ({
420
484
  for (const set of byModel.values()) count += set.size;
421
485
  }
422
486
  if (count >= maxSubscriptionsPerSocket) {
423
- sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey, error: "Too many subscriptions" });
487
+ sendWs(ws, { type: "query-payload", modelName: message.modelName, queryKey: message.queryKey, error: "Too many subscriptions" });
424
488
  return;
425
489
  }
426
490
  }
427
491
  addSocketSubscription({
428
492
  socketId,
429
493
  tenantId: meta.tenantId,
494
+ userId: meta.userId,
430
495
  modelName: message.modelName,
431
496
  queryKey: message.queryKey,
432
497
  query: message.query,
@@ -436,7 +501,7 @@ const handleClientMessage = async ({
436
501
  await ensureChangeStream(meta.tenantId, message.modelName);
437
502
  } catch (err) {
438
503
  const error = redactErrorMessage(err);
439
- sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey, error });
504
+ sendWs(ws, { type: "query-payload", modelName: message.modelName, queryKey: message.queryKey, error });
440
505
  return;
441
506
  }
442
507
  }
@@ -444,6 +509,7 @@ const handleClientMessage = async ({
444
509
  await runAndSendQuery({
445
510
  tenantId: meta.tenantId,
446
511
  targetSocketIds: [socketId],
512
+ ability,
447
513
  modelName: message.modelName,
448
514
  queryKey: message.queryKey,
449
515
  query: message.query,
@@ -451,7 +517,7 @@ const handleClientMessage = async ({
451
517
  });
452
518
  } catch (err) {
453
519
  const error = redactErrorMessage(err);
454
- sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey, error });
520
+ sendWs(ws, { type: "query-payload", modelName: message.modelName, queryKey: message.queryKey, error });
455
521
  }
456
522
  };
457
523
  const initRts = ({
@@ -539,6 +605,7 @@ const initRts = ({
539
605
  }
540
606
  if (!parsed || typeof parsed !== "object") return;
541
607
  const message = parsed;
608
+ if (message.type !== "event" && message.type !== "run-query" && message.type !== "register-query" && message.type !== "remove-query") return;
542
609
  void handleClientMessage({ socketId, meta, message });
543
610
  });
544
611
  ws.on("close", () => {
@@ -556,7 +623,8 @@ const notifyRtsModelChanged = (tenantId, modelName) => {
556
623
  scheduleDispatchSubscriptionsForModel(tenantId, modelName);
557
624
  };
558
625
  export {
559
- initRts as i,
560
- notifyRtsModelChanged as n,
561
- registerRtsHandler as r
626
+ initRts,
627
+ notifyRtsModelChanged,
628
+ registerRtsHandler,
629
+ routes
562
630
  };