@rpcbase/db 0.63.0 → 0.65.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/acl/index.js +2 -1
- package/dist/acl/mongooseAclPlugin.d.ts +1 -0
- package/dist/acl/mongooseAclPlugin.d.ts.map +1 -1
- package/dist/acl/registry.d.ts +1 -0
- package/dist/acl/registry.d.ts.map +1 -1
- package/dist/{can-urGFf45M.js → can--Y-1LK22.js} +11 -11
- package/dist/can--Y-1LK22.js.map +1 -0
- package/dist/createModels.d.ts +7 -0
- package/dist/createModels.d.ts.map +1 -1
- package/dist/{index-Drge1bnG.js → index-g1_0RZ-U.js} +4 -5
- package/dist/{index-Drge1bnG.js.map → index-g1_0RZ-U.js.map} +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.js +904 -311
- package/dist/index.js.map +1 -1
- package/dist/modelsApi.d.ts +3 -0
- package/dist/modelsApi.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/can-urGFf45M.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as registerPoliciesFromModules } from "./can-
|
|
2
|
-
import { b, d, f, e, g, c, r } from "./can-
|
|
1
|
+
import { a as registerPoliciesFromModules, h as hasRegisteredPolicy } from "./can--Y-1LK22.js";
|
|
2
|
+
import { b, d, f, e, g, c, r } from "./can--Y-1LK22.js";
|
|
3
3
|
import mongoose, { Schema as Schema$1, Types } from "mongoose";
|
|
4
4
|
import { default as default2 } from "mongoose";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { timingSafeEqual, createHmac } from "node:crypto";
|
|
7
|
-
import { w as withLocalizedStringFallback } from "./index-
|
|
8
|
-
import { a, E, L, c as c2, e as e2, m, r as r2, z as z2, b as b2, f as f2, d as d2 } from "./index-
|
|
7
|
+
import { w as withLocalizedStringFallback } from "./index-g1_0RZ-U.js";
|
|
8
|
+
import { a, E, L, c as c2, e as e2, m, r as r2, z as z2, b as b2, f as f2, d as d2 } from "./index-g1_0RZ-U.js";
|
|
9
9
|
import assert from "assert";
|
|
10
10
|
import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
|
|
11
11
|
import "@casl/ability";
|
|
@@ -34,33 +34,97 @@ const ZRBUser = z.object({
|
|
|
34
34
|
emailVerificationExpiresAt: z.date().optional()
|
|
35
35
|
});
|
|
36
36
|
const RBUserSchema = new Schema$1({
|
|
37
|
-
email: {
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
email: {
|
|
38
|
+
type: String,
|
|
39
|
+
unique: true,
|
|
40
|
+
sparse: true
|
|
41
|
+
},
|
|
42
|
+
phone: {
|
|
43
|
+
type: String,
|
|
44
|
+
unique: true,
|
|
45
|
+
sparse: true
|
|
46
|
+
},
|
|
47
|
+
password: {
|
|
48
|
+
type: String,
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
40
51
|
name: String,
|
|
41
|
-
tenants: {
|
|
42
|
-
|
|
52
|
+
tenants: {
|
|
53
|
+
type: [String],
|
|
54
|
+
index: true,
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
tenantRoles: {
|
|
58
|
+
type: Map,
|
|
59
|
+
of: [String],
|
|
60
|
+
required: false,
|
|
61
|
+
default: {}
|
|
62
|
+
},
|
|
43
63
|
oauthProviders: {
|
|
44
64
|
type: Map,
|
|
45
65
|
of: new Schema$1({
|
|
46
|
-
subject: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
subject: {
|
|
67
|
+
type: String,
|
|
68
|
+
required: true
|
|
69
|
+
},
|
|
70
|
+
email: {
|
|
71
|
+
type: String,
|
|
72
|
+
required: false
|
|
73
|
+
},
|
|
74
|
+
name: {
|
|
75
|
+
type: String,
|
|
76
|
+
required: false
|
|
77
|
+
},
|
|
78
|
+
accessToken: {
|
|
79
|
+
type: String,
|
|
80
|
+
required: false
|
|
81
|
+
},
|
|
82
|
+
refreshToken: {
|
|
83
|
+
type: String,
|
|
84
|
+
required: false
|
|
85
|
+
},
|
|
86
|
+
idToken: {
|
|
87
|
+
type: String,
|
|
88
|
+
required: false
|
|
89
|
+
},
|
|
90
|
+
scope: {
|
|
91
|
+
type: String,
|
|
92
|
+
required: false
|
|
93
|
+
},
|
|
94
|
+
tokenType: {
|
|
95
|
+
type: String,
|
|
96
|
+
required: false
|
|
97
|
+
},
|
|
98
|
+
expiresAt: {
|
|
99
|
+
type: Date,
|
|
100
|
+
required: false
|
|
101
|
+
},
|
|
102
|
+
rawUserInfo: {
|
|
103
|
+
type: Schema$1.Types.Mixed,
|
|
104
|
+
required: false
|
|
105
|
+
},
|
|
106
|
+
createdAt: {
|
|
107
|
+
type: Date,
|
|
108
|
+
required: false
|
|
109
|
+
},
|
|
110
|
+
updatedAt: {
|
|
111
|
+
type: Date,
|
|
112
|
+
required: false
|
|
113
|
+
}
|
|
114
|
+
}, {
|
|
115
|
+
_id: false
|
|
116
|
+
}),
|
|
59
117
|
default: {},
|
|
60
118
|
required: false
|
|
61
119
|
},
|
|
62
|
-
emailVerificationCode: {
|
|
63
|
-
|
|
120
|
+
emailVerificationCode: {
|
|
121
|
+
type: String,
|
|
122
|
+
required: false
|
|
123
|
+
},
|
|
124
|
+
emailVerificationExpiresAt: {
|
|
125
|
+
type: Date,
|
|
126
|
+
required: false
|
|
127
|
+
}
|
|
64
128
|
});
|
|
65
129
|
const ZRBTenant = z.object({
|
|
66
130
|
tenantId: z.string(),
|
|
@@ -68,18 +132,20 @@ const ZRBTenant = z.object({
|
|
|
68
132
|
name: z.string().optional()
|
|
69
133
|
});
|
|
70
134
|
const RBTenantSchema = new Schema$1({
|
|
71
|
-
tenantId: {
|
|
72
|
-
|
|
73
|
-
|
|
135
|
+
tenantId: {
|
|
136
|
+
type: String,
|
|
137
|
+
required: true,
|
|
138
|
+
unique: true,
|
|
139
|
+
index: true
|
|
140
|
+
},
|
|
141
|
+
parentTenantId: {
|
|
142
|
+
type: String
|
|
143
|
+
},
|
|
144
|
+
name: {
|
|
145
|
+
type: String
|
|
146
|
+
}
|
|
74
147
|
});
|
|
75
|
-
const ZRBTenantSubscriptionStatus = z.enum([
|
|
76
|
-
"trialing",
|
|
77
|
-
"active",
|
|
78
|
-
"past_due",
|
|
79
|
-
"paused",
|
|
80
|
-
"canceled",
|
|
81
|
-
"expired"
|
|
82
|
-
]);
|
|
148
|
+
const ZRBTenantSubscriptionStatus = z.enum(["trialing", "active", "past_due", "paused", "canceled", "expired"]);
|
|
83
149
|
const ZRBTenantSubscriptionIntervalUnit = z.enum(["month", "year"]);
|
|
84
150
|
const ZRBTenantSubscriptionType = z.enum(["primary", "addon"]);
|
|
85
151
|
const ZRBTenantSubscriptionScope = z.enum(["tenant", "shop", "custom"]);
|
|
@@ -108,46 +174,107 @@ const ZRBTenantSubscription = z.object({
|
|
|
108
174
|
latestEventAt: z.date().optional(),
|
|
109
175
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
110
176
|
});
|
|
111
|
-
const RBTenantSubscriptionSchema = new Schema$1(
|
|
112
|
-
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
177
|
+
const RBTenantSubscriptionSchema = new Schema$1({
|
|
178
|
+
tenantId: {
|
|
179
|
+
type: String,
|
|
180
|
+
required: true,
|
|
181
|
+
index: true
|
|
182
|
+
},
|
|
183
|
+
subscriptionId: {
|
|
184
|
+
type: String,
|
|
185
|
+
required: true
|
|
186
|
+
},
|
|
187
|
+
type: {
|
|
188
|
+
type: String,
|
|
189
|
+
required: true,
|
|
190
|
+
enum: ZRBTenantSubscriptionType.options,
|
|
191
|
+
default: "primary"
|
|
192
|
+
},
|
|
193
|
+
parentSubscriptionId: {
|
|
194
|
+
type: String
|
|
195
|
+
},
|
|
196
|
+
scope: {
|
|
197
|
+
type: String,
|
|
198
|
+
enum: ZRBTenantSubscriptionScope.options,
|
|
199
|
+
default: "tenant"
|
|
200
|
+
},
|
|
201
|
+
scopeId: {
|
|
202
|
+
type: String
|
|
203
|
+
},
|
|
204
|
+
planKey: {
|
|
205
|
+
type: String,
|
|
206
|
+
required: true
|
|
207
|
+
},
|
|
208
|
+
status: {
|
|
209
|
+
type: String,
|
|
210
|
+
required: true,
|
|
211
|
+
enum: ZRBTenantSubscriptionStatus.options
|
|
212
|
+
},
|
|
213
|
+
intervalUnit: {
|
|
214
|
+
type: String,
|
|
215
|
+
required: true,
|
|
216
|
+
enum: ZRBTenantSubscriptionIntervalUnit.options
|
|
217
|
+
},
|
|
218
|
+
intervalCount: {
|
|
219
|
+
type: Number,
|
|
220
|
+
min: 1,
|
|
221
|
+
default: 1
|
|
222
|
+
},
|
|
223
|
+
modules: {
|
|
224
|
+
type: [String],
|
|
225
|
+
default: []
|
|
226
|
+
},
|
|
227
|
+
currentPeriodStart: {
|
|
228
|
+
type: Date
|
|
229
|
+
},
|
|
230
|
+
currentPeriodEnd: {
|
|
231
|
+
type: Date
|
|
232
|
+
},
|
|
233
|
+
trialEndsAt: {
|
|
234
|
+
type: Date
|
|
235
|
+
},
|
|
236
|
+
cancelAt: {
|
|
237
|
+
type: Date
|
|
238
|
+
},
|
|
239
|
+
cancelAtPeriodEnd: {
|
|
240
|
+
type: Boolean,
|
|
241
|
+
default: false
|
|
242
|
+
},
|
|
243
|
+
canceledAt: {
|
|
244
|
+
type: Date
|
|
245
|
+
},
|
|
246
|
+
provider: {
|
|
247
|
+
type: String
|
|
248
|
+
},
|
|
249
|
+
providerCustomerId: {
|
|
250
|
+
type: String
|
|
251
|
+
},
|
|
252
|
+
providerSubscriptionId: {
|
|
253
|
+
type: String
|
|
254
|
+
},
|
|
255
|
+
latestEventId: {
|
|
256
|
+
type: String
|
|
257
|
+
},
|
|
258
|
+
latestEventAt: {
|
|
259
|
+
type: Date
|
|
260
|
+
},
|
|
261
|
+
metadata: {
|
|
262
|
+
type: Schema$1.Types.Mixed
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
RBTenantSubscriptionSchema.index({
|
|
266
|
+
tenantId: 1,
|
|
267
|
+
subscriptionId: 1
|
|
268
|
+
}, {
|
|
269
|
+
unique: true
|
|
270
|
+
});
|
|
271
|
+
RBTenantSubscriptionSchema.index({
|
|
272
|
+
tenantId: 1,
|
|
273
|
+
scope: 1,
|
|
274
|
+
scopeId: 1
|
|
275
|
+
});
|
|
276
|
+
const ZRBTenantSubscriptionEventSource = z.enum(["admin", "system", "webhook", "user"]);
|
|
277
|
+
const ZRBTenantSubscriptionChangeDirection = z.enum(["upgrade", "downgrade", "lateral"]);
|
|
151
278
|
const ZRBTenantSubscriptionEvent = z.object({
|
|
152
279
|
tenantId: z.string(),
|
|
153
280
|
subscriptionId: z.string(),
|
|
@@ -173,48 +300,123 @@ const ZRBTenantSubscriptionEvent = z.object({
|
|
|
173
300
|
providerPayload: z.unknown().optional(),
|
|
174
301
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
175
302
|
});
|
|
176
|
-
const RBTenantSubscriptionEventSchema = new Schema$1(
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
303
|
+
const RBTenantSubscriptionEventSchema = new Schema$1({
|
|
304
|
+
tenantId: {
|
|
305
|
+
type: String,
|
|
306
|
+
required: true,
|
|
307
|
+
index: true
|
|
308
|
+
},
|
|
309
|
+
subscriptionId: {
|
|
310
|
+
type: String,
|
|
311
|
+
required: true,
|
|
312
|
+
index: true
|
|
313
|
+
},
|
|
314
|
+
type: {
|
|
315
|
+
type: String,
|
|
316
|
+
required: true
|
|
317
|
+
},
|
|
318
|
+
occurredAt: {
|
|
319
|
+
type: Date,
|
|
320
|
+
required: true,
|
|
321
|
+
default: Date.now
|
|
322
|
+
},
|
|
323
|
+
effectiveAt: {
|
|
324
|
+
type: Date
|
|
325
|
+
},
|
|
326
|
+
fromPlanKey: {
|
|
327
|
+
type: String
|
|
328
|
+
},
|
|
329
|
+
toPlanKey: {
|
|
330
|
+
type: String
|
|
331
|
+
},
|
|
332
|
+
fromStatus: {
|
|
333
|
+
type: String,
|
|
334
|
+
enum: ZRBTenantSubscriptionStatus.options
|
|
335
|
+
},
|
|
336
|
+
toStatus: {
|
|
337
|
+
type: String,
|
|
338
|
+
enum: ZRBTenantSubscriptionStatus.options
|
|
339
|
+
},
|
|
340
|
+
fromModules: {
|
|
341
|
+
type: [String],
|
|
342
|
+
default: void 0
|
|
343
|
+
},
|
|
344
|
+
toModules: {
|
|
345
|
+
type: [String],
|
|
346
|
+
default: void 0
|
|
347
|
+
},
|
|
348
|
+
fromIntervalUnit: {
|
|
349
|
+
type: String,
|
|
350
|
+
enum: ZRBTenantSubscriptionIntervalUnit.options
|
|
351
|
+
},
|
|
352
|
+
toIntervalUnit: {
|
|
353
|
+
type: String,
|
|
354
|
+
enum: ZRBTenantSubscriptionIntervalUnit.options
|
|
355
|
+
},
|
|
356
|
+
fromIntervalCount: {
|
|
357
|
+
type: Number,
|
|
358
|
+
min: 1
|
|
359
|
+
},
|
|
360
|
+
toIntervalCount: {
|
|
361
|
+
type: Number,
|
|
362
|
+
min: 1
|
|
363
|
+
},
|
|
364
|
+
direction: {
|
|
365
|
+
type: String,
|
|
366
|
+
enum: ZRBTenantSubscriptionChangeDirection.options
|
|
367
|
+
},
|
|
368
|
+
actorUserId: {
|
|
369
|
+
type: String
|
|
370
|
+
},
|
|
371
|
+
source: {
|
|
372
|
+
type: String,
|
|
373
|
+
enum: ZRBTenantSubscriptionEventSource.options
|
|
374
|
+
},
|
|
375
|
+
reason: {
|
|
376
|
+
type: String
|
|
377
|
+
},
|
|
378
|
+
provider: {
|
|
379
|
+
type: String
|
|
380
|
+
},
|
|
381
|
+
providerEventId: {
|
|
382
|
+
type: String
|
|
383
|
+
},
|
|
384
|
+
providerPayload: {
|
|
385
|
+
type: Schema$1.Types.Mixed
|
|
386
|
+
},
|
|
387
|
+
metadata: {
|
|
388
|
+
type: Schema$1.Types.Mixed
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
RBTenantSubscriptionEventSchema.index({
|
|
392
|
+
tenantId: 1,
|
|
393
|
+
subscriptionId: 1,
|
|
394
|
+
occurredAt: 1
|
|
395
|
+
});
|
|
396
|
+
RBTenantSubscriptionEventSchema.index({
|
|
397
|
+
provider: 1,
|
|
398
|
+
providerEventId: 1
|
|
399
|
+
}, {
|
|
400
|
+
unique: true,
|
|
401
|
+
sparse: true
|
|
402
|
+
});
|
|
205
403
|
const ZRBRtsCounter = z.object({
|
|
206
404
|
_id: z.string(),
|
|
207
405
|
seq: z.number().int().min(0)
|
|
208
406
|
});
|
|
209
|
-
const RBRtsCounterSchema = new Schema$1(
|
|
210
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
407
|
+
const RBRtsCounterSchema = new Schema$1({
|
|
408
|
+
_id: {
|
|
409
|
+
type: String,
|
|
410
|
+
required: true
|
|
213
411
|
},
|
|
214
|
-
{
|
|
215
|
-
|
|
412
|
+
seq: {
|
|
413
|
+
type: Number,
|
|
414
|
+
required: true,
|
|
415
|
+
default: 0
|
|
216
416
|
}
|
|
217
|
-
|
|
417
|
+
}, {
|
|
418
|
+
versionKey: false
|
|
419
|
+
});
|
|
218
420
|
const ttlSecondsRaw = process.env.RB_RTS_CHANGES_TTL_S ?? "";
|
|
219
421
|
const ttlSeconds = Number.isFinite(Number(ttlSecondsRaw)) ? Math.max(60, Math.floor(Number(ttlSecondsRaw))) : 60 * 60 * 24 * 30;
|
|
220
422
|
const ZRBRtsChangeOp = z.enum(["delete", "reset_model"]);
|
|
@@ -225,20 +427,43 @@ const ZRBRtsChange = z.object({
|
|
|
225
427
|
docId: z.string().optional(),
|
|
226
428
|
ts: z.date()
|
|
227
429
|
});
|
|
228
|
-
const RBRtsChangeSchema = new Schema$1(
|
|
229
|
-
{
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
430
|
+
const RBRtsChangeSchema = new Schema$1({
|
|
431
|
+
seq: {
|
|
432
|
+
type: Number,
|
|
433
|
+
required: true
|
|
434
|
+
},
|
|
435
|
+
modelName: {
|
|
436
|
+
type: String,
|
|
437
|
+
required: true,
|
|
438
|
+
index: true
|
|
439
|
+
},
|
|
440
|
+
op: {
|
|
441
|
+
type: String,
|
|
442
|
+
required: true,
|
|
443
|
+
enum: ZRBRtsChangeOp.options
|
|
444
|
+
},
|
|
445
|
+
docId: {
|
|
446
|
+
type: String,
|
|
447
|
+
required: false
|
|
448
|
+
},
|
|
449
|
+
ts: {
|
|
450
|
+
type: Date,
|
|
451
|
+
required: true,
|
|
452
|
+
default: Date.now
|
|
453
|
+
}
|
|
454
|
+
}, {
|
|
455
|
+
versionKey: false
|
|
456
|
+
});
|
|
457
|
+
RBRtsChangeSchema.index({
|
|
458
|
+
seq: 1
|
|
459
|
+
}, {
|
|
460
|
+
unique: true
|
|
461
|
+
});
|
|
462
|
+
RBRtsChangeSchema.index({
|
|
463
|
+
ts: 1
|
|
464
|
+
}, {
|
|
465
|
+
expireAfterSeconds: ttlSeconds
|
|
466
|
+
});
|
|
242
467
|
const ZRBUploadSessionStatus = z.enum(["uploading", "assembling", "done", "error"]);
|
|
243
468
|
const ZRBUploadSession = z.object({
|
|
244
469
|
_id: z.string(),
|
|
@@ -256,42 +481,100 @@ const ZRBUploadSession = z.object({
|
|
|
256
481
|
isPublic: z.boolean().optional(),
|
|
257
482
|
error: z.string().optional()
|
|
258
483
|
});
|
|
259
|
-
const RBUploadSessionSchema = new Schema$1(
|
|
260
|
-
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
484
|
+
const RBUploadSessionSchema = new Schema$1({
|
|
485
|
+
_id: {
|
|
486
|
+
type: String,
|
|
487
|
+
required: true
|
|
488
|
+
},
|
|
489
|
+
userId: {
|
|
490
|
+
type: String,
|
|
491
|
+
required: false,
|
|
492
|
+
index: true
|
|
493
|
+
},
|
|
494
|
+
ownerKeyHash: {
|
|
495
|
+
type: String,
|
|
496
|
+
required: false
|
|
497
|
+
},
|
|
498
|
+
filename: {
|
|
499
|
+
type: String,
|
|
500
|
+
required: true
|
|
501
|
+
},
|
|
502
|
+
mimeType: {
|
|
503
|
+
type: String,
|
|
504
|
+
required: true
|
|
505
|
+
},
|
|
506
|
+
totalSize: {
|
|
507
|
+
type: Number,
|
|
508
|
+
required: true
|
|
509
|
+
},
|
|
510
|
+
chunkSize: {
|
|
511
|
+
type: Number,
|
|
512
|
+
required: true
|
|
513
|
+
},
|
|
514
|
+
chunksTotal: {
|
|
515
|
+
type: Number,
|
|
516
|
+
required: true
|
|
517
|
+
},
|
|
518
|
+
status: {
|
|
519
|
+
type: String,
|
|
520
|
+
required: true,
|
|
521
|
+
enum: ZRBUploadSessionStatus.options
|
|
522
|
+
},
|
|
523
|
+
createdAt: {
|
|
524
|
+
type: Date,
|
|
525
|
+
required: true,
|
|
526
|
+
default: Date.now
|
|
527
|
+
},
|
|
528
|
+
expiresAt: {
|
|
529
|
+
type: Date,
|
|
530
|
+
required: true
|
|
531
|
+
},
|
|
532
|
+
fileId: {
|
|
533
|
+
type: String,
|
|
534
|
+
required: false
|
|
535
|
+
},
|
|
536
|
+
isPublic: {
|
|
537
|
+
type: Boolean,
|
|
538
|
+
required: false
|
|
539
|
+
},
|
|
540
|
+
error: {
|
|
541
|
+
type: String,
|
|
542
|
+
required: false
|
|
543
|
+
}
|
|
544
|
+
}, {
|
|
545
|
+
versionKey: false
|
|
546
|
+
});
|
|
547
|
+
RBUploadSessionSchema.index({
|
|
548
|
+
expiresAt: 1
|
|
549
|
+
}, {
|
|
550
|
+
expireAfterSeconds: 0
|
|
551
|
+
});
|
|
281
552
|
const RBUploadSessionPolicy = {
|
|
282
553
|
subject: "RBUploadSession",
|
|
283
554
|
define: (builder, ctx) => {
|
|
284
555
|
builder.can("create", "RBUploadSession");
|
|
285
556
|
if (ctx.userId) {
|
|
286
|
-
builder.can("read", "RBUploadSession", {
|
|
287
|
-
|
|
288
|
-
|
|
557
|
+
builder.can("read", "RBUploadSession", {
|
|
558
|
+
userId: ctx.userId
|
|
559
|
+
});
|
|
560
|
+
builder.can("update", "RBUploadSession", {
|
|
561
|
+
userId: ctx.userId
|
|
562
|
+
});
|
|
563
|
+
builder.can("delete", "RBUploadSession", {
|
|
564
|
+
userId: ctx.userId
|
|
565
|
+
});
|
|
289
566
|
}
|
|
290
567
|
const uploadKeyHash = typeof ctx.claims?.uploadKeyHash === "string" ? ctx.claims.uploadKeyHash.trim() : "";
|
|
291
568
|
if (uploadKeyHash) {
|
|
292
|
-
builder.can("read", "RBUploadSession", {
|
|
293
|
-
|
|
294
|
-
|
|
569
|
+
builder.can("read", "RBUploadSession", {
|
|
570
|
+
ownerKeyHash: uploadKeyHash
|
|
571
|
+
});
|
|
572
|
+
builder.can("update", "RBUploadSession", {
|
|
573
|
+
ownerKeyHash: uploadKeyHash
|
|
574
|
+
});
|
|
575
|
+
builder.can("delete", "RBUploadSession", {
|
|
576
|
+
ownerKeyHash: uploadKeyHash
|
|
577
|
+
});
|
|
295
578
|
}
|
|
296
579
|
}
|
|
297
580
|
};
|
|
@@ -304,22 +587,51 @@ const ZRBUploadChunk = z.object({
|
|
|
304
587
|
createdAt: z.date(),
|
|
305
588
|
expiresAt: z.date()
|
|
306
589
|
});
|
|
307
|
-
const RBUploadChunkSchema = new Schema$1(
|
|
308
|
-
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
590
|
+
const RBUploadChunkSchema = new Schema$1({
|
|
591
|
+
uploadId: {
|
|
592
|
+
type: String,
|
|
593
|
+
required: true,
|
|
594
|
+
index: true
|
|
595
|
+
},
|
|
596
|
+
index: {
|
|
597
|
+
type: Number,
|
|
598
|
+
required: true
|
|
599
|
+
},
|
|
600
|
+
data: {
|
|
601
|
+
type: Buffer,
|
|
602
|
+
required: true
|
|
603
|
+
},
|
|
604
|
+
size: {
|
|
605
|
+
type: Number,
|
|
606
|
+
required: true
|
|
607
|
+
},
|
|
608
|
+
sha256: {
|
|
609
|
+
type: String,
|
|
610
|
+
required: false
|
|
611
|
+
},
|
|
612
|
+
createdAt: {
|
|
613
|
+
type: Date,
|
|
614
|
+
required: true,
|
|
615
|
+
default: Date.now
|
|
616
|
+
},
|
|
617
|
+
expiresAt: {
|
|
618
|
+
type: Date,
|
|
619
|
+
required: true
|
|
620
|
+
}
|
|
621
|
+
}, {
|
|
622
|
+
versionKey: false
|
|
623
|
+
});
|
|
624
|
+
RBUploadChunkSchema.index({
|
|
625
|
+
uploadId: 1,
|
|
626
|
+
index: 1
|
|
627
|
+
}, {
|
|
628
|
+
unique: true
|
|
629
|
+
});
|
|
630
|
+
RBUploadChunkSchema.index({
|
|
631
|
+
expiresAt: 1
|
|
632
|
+
}, {
|
|
633
|
+
expireAfterSeconds: 0
|
|
634
|
+
});
|
|
323
635
|
const ZRBNotification = z.object({
|
|
324
636
|
userId: z.string(),
|
|
325
637
|
topic: z.string().optional(),
|
|
@@ -333,35 +645,92 @@ const ZRBNotification = z.object({
|
|
|
333
645
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
334
646
|
});
|
|
335
647
|
const TTL_90_DAYS_S = 60 * 60 * 24 * 90;
|
|
336
|
-
const RBNotificationSchema = new Schema$1(
|
|
337
|
-
{
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
648
|
+
const RBNotificationSchema = new Schema$1({
|
|
649
|
+
userId: {
|
|
650
|
+
type: String,
|
|
651
|
+
required: true,
|
|
652
|
+
index: true
|
|
653
|
+
},
|
|
654
|
+
topic: {
|
|
655
|
+
type: String,
|
|
656
|
+
required: false,
|
|
657
|
+
index: true
|
|
658
|
+
},
|
|
659
|
+
title: {
|
|
660
|
+
type: String,
|
|
661
|
+
required: true
|
|
662
|
+
},
|
|
663
|
+
body: {
|
|
664
|
+
type: String,
|
|
665
|
+
required: false
|
|
666
|
+
},
|
|
667
|
+
url: {
|
|
668
|
+
type: String,
|
|
669
|
+
required: false
|
|
670
|
+
},
|
|
671
|
+
createdAt: {
|
|
672
|
+
type: Date,
|
|
673
|
+
required: true,
|
|
674
|
+
default: Date.now,
|
|
675
|
+
index: true
|
|
676
|
+
},
|
|
677
|
+
seenAt: {
|
|
678
|
+
type: Date,
|
|
679
|
+
required: false,
|
|
680
|
+
index: true
|
|
681
|
+
},
|
|
682
|
+
readAt: {
|
|
683
|
+
type: Date,
|
|
684
|
+
required: false,
|
|
685
|
+
index: true
|
|
686
|
+
},
|
|
687
|
+
archivedAt: {
|
|
688
|
+
type: Date,
|
|
689
|
+
required: false
|
|
690
|
+
},
|
|
691
|
+
metadata: {
|
|
692
|
+
type: Schema$1.Types.Mixed,
|
|
693
|
+
required: false
|
|
694
|
+
}
|
|
695
|
+
}, {
|
|
696
|
+
versionKey: false
|
|
697
|
+
});
|
|
698
|
+
RBNotificationSchema.index({
|
|
699
|
+
userId: 1,
|
|
700
|
+
archivedAt: 1,
|
|
701
|
+
createdAt: -1
|
|
702
|
+
});
|
|
703
|
+
RBNotificationSchema.index({
|
|
704
|
+
userId: 1,
|
|
705
|
+
seenAt: 1,
|
|
706
|
+
archivedAt: 1,
|
|
707
|
+
createdAt: -1
|
|
708
|
+
});
|
|
709
|
+
RBNotificationSchema.index({
|
|
710
|
+
userId: 1,
|
|
711
|
+
readAt: 1,
|
|
712
|
+
archivedAt: 1,
|
|
713
|
+
createdAt: -1
|
|
714
|
+
});
|
|
715
|
+
RBNotificationSchema.index({
|
|
716
|
+
archivedAt: 1
|
|
717
|
+
}, {
|
|
718
|
+
expireAfterSeconds: TTL_90_DAYS_S
|
|
719
|
+
});
|
|
357
720
|
const RBNotificationPolicy = {
|
|
358
721
|
subject: "RBNotification",
|
|
359
722
|
define: (builder, ctx) => {
|
|
360
723
|
if (!ctx.userId) return;
|
|
361
724
|
builder.can("create", "RBNotification");
|
|
362
|
-
builder.can("read", "RBNotification", {
|
|
363
|
-
|
|
364
|
-
|
|
725
|
+
builder.can("read", "RBNotification", {
|
|
726
|
+
userId: ctx.userId
|
|
727
|
+
});
|
|
728
|
+
builder.can("update", "RBNotification", {
|
|
729
|
+
userId: ctx.userId
|
|
730
|
+
});
|
|
731
|
+
builder.can("delete", "RBNotification", {
|
|
732
|
+
userId: ctx.userId
|
|
733
|
+
});
|
|
365
734
|
}
|
|
366
735
|
};
|
|
367
736
|
const ZRBNotificationDigestFrequency = z.enum(["off", "daily", "weekly"]);
|
|
@@ -377,44 +746,72 @@ const ZRBNotificationSettings = z.object({
|
|
|
377
746
|
topicPreferences: z.array(ZRBNotificationTopicPreference).optional(),
|
|
378
747
|
lastDigestSentAt: z.date().optional()
|
|
379
748
|
});
|
|
380
|
-
const TopicPreferenceSchema = new Schema$1(
|
|
381
|
-
{
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
{
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
required: true,
|
|
395
|
-
enum: ZRBNotificationDigestFrequency.options,
|
|
396
|
-
default: "weekly"
|
|
397
|
-
},
|
|
398
|
-
topicPreferences: {
|
|
399
|
-
type: [TopicPreferenceSchema],
|
|
400
|
-
default: []
|
|
401
|
-
},
|
|
402
|
-
lastDigestSentAt: { type: Date, required: false }
|
|
749
|
+
const TopicPreferenceSchema = new Schema$1({
|
|
750
|
+
topic: {
|
|
751
|
+
type: String,
|
|
752
|
+
required: true
|
|
753
|
+
},
|
|
754
|
+
inApp: {
|
|
755
|
+
type: Boolean,
|
|
756
|
+
required: true,
|
|
757
|
+
default: true
|
|
758
|
+
},
|
|
759
|
+
emailDigest: {
|
|
760
|
+
type: Boolean,
|
|
761
|
+
required: true,
|
|
762
|
+
default: true
|
|
403
763
|
},
|
|
404
|
-
{
|
|
405
|
-
|
|
406
|
-
|
|
764
|
+
push: {
|
|
765
|
+
type: Boolean,
|
|
766
|
+
required: true,
|
|
767
|
+
default: false
|
|
407
768
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
769
|
+
}, {
|
|
770
|
+
_id: false
|
|
771
|
+
});
|
|
772
|
+
const RBNotificationSettingsSchema = new Schema$1({
|
|
773
|
+
userId: {
|
|
774
|
+
type: String,
|
|
775
|
+
required: true
|
|
776
|
+
},
|
|
777
|
+
digestFrequency: {
|
|
778
|
+
type: String,
|
|
779
|
+
required: true,
|
|
780
|
+
enum: ZRBNotificationDigestFrequency.options,
|
|
781
|
+
default: "weekly"
|
|
782
|
+
},
|
|
783
|
+
topicPreferences: {
|
|
784
|
+
type: [TopicPreferenceSchema],
|
|
785
|
+
default: []
|
|
786
|
+
},
|
|
787
|
+
lastDigestSentAt: {
|
|
788
|
+
type: Date,
|
|
789
|
+
required: false
|
|
790
|
+
}
|
|
791
|
+
}, {
|
|
792
|
+
versionKey: false,
|
|
793
|
+
timestamps: true
|
|
794
|
+
});
|
|
795
|
+
RBNotificationSettingsSchema.index({
|
|
796
|
+
userId: 1
|
|
797
|
+
}, {
|
|
798
|
+
unique: true
|
|
799
|
+
});
|
|
800
|
+
RBNotificationSettingsSchema.index({
|
|
801
|
+
userId: 1,
|
|
802
|
+
"topicPreferences.topic": 1
|
|
803
|
+
});
|
|
411
804
|
const RBNotificationSettingsPolicy = {
|
|
412
805
|
subject: "RBNotificationSettings",
|
|
413
806
|
define: (builder, ctx) => {
|
|
414
807
|
if (!ctx.userId) return;
|
|
415
808
|
builder.can("create", "RBNotificationSettings");
|
|
416
|
-
builder.can("read", "RBNotificationSettings", {
|
|
417
|
-
|
|
809
|
+
builder.can("read", "RBNotificationSettings", {
|
|
810
|
+
userId: ctx.userId
|
|
811
|
+
});
|
|
812
|
+
builder.can("update", "RBNotificationSettings", {
|
|
813
|
+
userId: ctx.userId
|
|
814
|
+
});
|
|
418
815
|
}
|
|
419
816
|
};
|
|
420
817
|
const ZRBOAuthRequest = z.object({
|
|
@@ -425,18 +822,41 @@ const ZRBOAuthRequest = z.object({
|
|
|
425
822
|
createdAt: z.date(),
|
|
426
823
|
expiresAt: z.date()
|
|
427
824
|
});
|
|
428
|
-
const RBOAuthRequestSchema = new Schema$1(
|
|
429
|
-
{
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
825
|
+
const RBOAuthRequestSchema = new Schema$1({
|
|
826
|
+
_id: {
|
|
827
|
+
type: String,
|
|
828
|
+
required: true
|
|
829
|
+
},
|
|
830
|
+
providerId: {
|
|
831
|
+
type: String,
|
|
832
|
+
required: true,
|
|
833
|
+
index: true
|
|
834
|
+
},
|
|
835
|
+
codeVerifier: {
|
|
836
|
+
type: String,
|
|
837
|
+
required: true
|
|
838
|
+
},
|
|
839
|
+
returnTo: {
|
|
840
|
+
type: String,
|
|
841
|
+
required: false
|
|
842
|
+
},
|
|
843
|
+
createdAt: {
|
|
844
|
+
type: Date,
|
|
845
|
+
required: true,
|
|
846
|
+
default: Date.now
|
|
847
|
+
},
|
|
848
|
+
expiresAt: {
|
|
849
|
+
type: Date,
|
|
850
|
+
required: true
|
|
851
|
+
}
|
|
852
|
+
}, {
|
|
853
|
+
versionKey: false
|
|
854
|
+
});
|
|
855
|
+
RBOAuthRequestSchema.index({
|
|
856
|
+
expiresAt: 1
|
|
857
|
+
}, {
|
|
858
|
+
expireAfterSeconds: 0
|
|
859
|
+
});
|
|
440
860
|
const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
441
861
|
__proto__: null,
|
|
442
862
|
RBNotificationPolicy,
|
|
@@ -493,7 +913,10 @@ function localizedStringField(options) {
|
|
|
493
913
|
get: (value) => withLocalizedStringFallback(userGet ? userGet(value) : value)
|
|
494
914
|
};
|
|
495
915
|
}
|
|
496
|
-
const {
|
|
916
|
+
const {
|
|
917
|
+
Schema,
|
|
918
|
+
model
|
|
919
|
+
} = mongoose;
|
|
497
920
|
class PaginationValidationError extends Error {
|
|
498
921
|
code = "invalid_pagination";
|
|
499
922
|
statusCode = 400;
|
|
@@ -515,18 +938,26 @@ const normalizePaginationSpec = (spec) => {
|
|
|
515
938
|
const direction = spec.direction ?? "next";
|
|
516
939
|
if (direction !== "next" && direction !== "prev") throw new PaginationValidationError("Invalid pagination direction");
|
|
517
940
|
if (!Array.isArray(spec.sort) || spec.sort.length === 0) throw new PaginationValidationError("Invalid pagination sort");
|
|
518
|
-
const sort = spec.sort.map(({
|
|
941
|
+
const sort = spec.sort.map(({
|
|
942
|
+
field,
|
|
943
|
+
order
|
|
944
|
+
}) => ({
|
|
519
945
|
field: assertSafeMongoFieldPath(field),
|
|
520
946
|
order: normalizeOrder(order)
|
|
521
947
|
}));
|
|
522
948
|
const seenFields = /* @__PURE__ */ new Set();
|
|
523
|
-
for (const {
|
|
949
|
+
for (const {
|
|
950
|
+
field
|
|
951
|
+
} of sort) {
|
|
524
952
|
if (seenFields.has(field)) throw new PaginationValidationError(`Duplicate pagination sort field: ${field}`);
|
|
525
953
|
seenFields.add(field);
|
|
526
954
|
}
|
|
527
955
|
const primaryOrder = sort[0]?.order;
|
|
528
956
|
if (!seenFields.has("_id")) {
|
|
529
|
-
sort.push({
|
|
957
|
+
sort.push({
|
|
958
|
+
field: "_id",
|
|
959
|
+
order: primaryOrder
|
|
960
|
+
});
|
|
530
961
|
}
|
|
531
962
|
return {
|
|
532
963
|
...spec,
|
|
@@ -562,14 +993,19 @@ const assertSafeMongoFieldPath = (field) => {
|
|
|
562
993
|
const encodePaginationCursor = (spec, node, options) => {
|
|
563
994
|
const normalized = normalizePaginationSpec(spec);
|
|
564
995
|
const values = /* @__PURE__ */ Object.create(null);
|
|
565
|
-
for (const {
|
|
996
|
+
for (const {
|
|
997
|
+
field
|
|
998
|
+
} of normalized.sort) {
|
|
566
999
|
const value = readFieldValue(node, field);
|
|
567
1000
|
if (typeof value === "undefined") {
|
|
568
1001
|
throw new Error(`Pagination cursor encode failed (missing field: ${field})`);
|
|
569
1002
|
}
|
|
570
1003
|
values[field] = encodeCursorValue(field, value);
|
|
571
1004
|
}
|
|
572
|
-
const payload = {
|
|
1005
|
+
const payload = {
|
|
1006
|
+
v: 1,
|
|
1007
|
+
values
|
|
1008
|
+
};
|
|
573
1009
|
const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
574
1010
|
const sigB64 = signCursorPayloadB64(payloadB64, options.signingSecret);
|
|
575
1011
|
return `${payloadB64}.${sigB64}`;
|
|
@@ -592,7 +1028,9 @@ const decodePaginationCursor = (spec, cursor, options) => {
|
|
|
592
1028
|
if (payload.v !== 1) throw new PaginationValidationError("Unsupported pagination cursor version");
|
|
593
1029
|
if (!payload.values || typeof payload.values !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
|
|
594
1030
|
const decoded = /* @__PURE__ */ Object.create(null);
|
|
595
|
-
for (const {
|
|
1031
|
+
for (const {
|
|
1032
|
+
field
|
|
1033
|
+
} of normalized.sort) {
|
|
596
1034
|
if (!Object.prototype.hasOwnProperty.call(payload.values, field)) {
|
|
597
1035
|
throw new PaginationValidationError(`Pagination cursor missing field: ${field}`);
|
|
598
1036
|
}
|
|
@@ -615,13 +1053,19 @@ const verifyCursorSignature = (payloadB64, sigB64, secret) => {
|
|
|
615
1053
|
const encodeCursorValue = (field, value) => {
|
|
616
1054
|
if (value === null) return null;
|
|
617
1055
|
if (field === "_id") {
|
|
618
|
-
if (value instanceof Types.ObjectId) return {
|
|
1056
|
+
if (value instanceof Types.ObjectId) return {
|
|
1057
|
+
$oid: value.toHexString()
|
|
1058
|
+
};
|
|
619
1059
|
if (typeof value === "string") return value;
|
|
620
1060
|
throw new Error("Pagination cursor encode failed (_id must be an ObjectId or string)");
|
|
621
1061
|
}
|
|
622
|
-
if (value instanceof Date) return {
|
|
1062
|
+
if (value instanceof Date) return {
|
|
1063
|
+
$date: value.toISOString()
|
|
1064
|
+
};
|
|
623
1065
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
624
|
-
if (value instanceof Types.ObjectId) return {
|
|
1066
|
+
if (value instanceof Types.ObjectId) return {
|
|
1067
|
+
$oid: value.toHexString()
|
|
1068
|
+
};
|
|
625
1069
|
throw new Error(`Unsupported pagination cursor value type for field: ${field}`);
|
|
626
1070
|
};
|
|
627
1071
|
const decodeCursorValue = (field, value) => {
|
|
@@ -681,7 +1125,10 @@ const compileMongoPagination = (spec, options) => {
|
|
|
681
1125
|
};
|
|
682
1126
|
const toMongoSort = (spec) => {
|
|
683
1127
|
const mongoSort = /* @__PURE__ */ Object.create(null);
|
|
684
|
-
for (const {
|
|
1128
|
+
for (const {
|
|
1129
|
+
field,
|
|
1130
|
+
order
|
|
1131
|
+
} of spec.sort) {
|
|
685
1132
|
const forQueryOrder = spec.direction === "prev" ? invertOrder(order) : order;
|
|
686
1133
|
mongoSort[field] = forQueryOrder === "asc" ? 1 : -1;
|
|
687
1134
|
}
|
|
@@ -706,9 +1153,13 @@ const buildKeysetFilterDelta = (spec, cursorValues) => {
|
|
|
706
1153
|
const cmp = /* @__PURE__ */ Object.create(null);
|
|
707
1154
|
cmp[current.field] = cmpOp;
|
|
708
1155
|
and.push(cmp);
|
|
709
|
-
branches.push(and.length === 1 ? and[0] : {
|
|
1156
|
+
branches.push(and.length === 1 ? and[0] : {
|
|
1157
|
+
$and: and
|
|
1158
|
+
});
|
|
710
1159
|
}
|
|
711
|
-
return {
|
|
1160
|
+
return {
|
|
1161
|
+
$or: branches
|
|
1162
|
+
};
|
|
712
1163
|
};
|
|
713
1164
|
const getKeysetOp = (order, direction) => {
|
|
714
1165
|
if (direction === "next") return order === "asc" ? "$gt" : "$lt";
|
|
@@ -729,7 +1180,10 @@ const materializeMongoPagination = (compiled, fetchedNodes, options) => {
|
|
|
729
1180
|
hasPrevPage
|
|
730
1181
|
};
|
|
731
1182
|
if (nodes.length === 0) {
|
|
732
|
-
return {
|
|
1183
|
+
return {
|
|
1184
|
+
nodes,
|
|
1185
|
+
pageInfo
|
|
1186
|
+
};
|
|
733
1187
|
}
|
|
734
1188
|
if (hasPrevPage) {
|
|
735
1189
|
pageInfo.prevCursor = encodePaginationCursor(compiled.spec, nodes[0], options.cursor);
|
|
@@ -737,7 +1191,10 @@ const materializeMongoPagination = (compiled, fetchedNodes, options) => {
|
|
|
737
1191
|
if (hasNextPage) {
|
|
738
1192
|
pageInfo.nextCursor = encodePaginationCursor(compiled.spec, nodes[nodes.length - 1], options.cursor);
|
|
739
1193
|
}
|
|
740
|
-
return {
|
|
1194
|
+
return {
|
|
1195
|
+
nodes,
|
|
1196
|
+
pageInfo
|
|
1197
|
+
};
|
|
741
1198
|
};
|
|
742
1199
|
const MongoAdapter = {
|
|
743
1200
|
applyPagination: (query, compiled) => {
|
|
@@ -748,13 +1205,17 @@ const MongoAdapter = {
|
|
|
748
1205
|
}
|
|
749
1206
|
};
|
|
750
1207
|
const paginateMongoQuery = async (query, pagination, options) => {
|
|
751
|
-
const compiled = compileMongoPagination(pagination, {
|
|
1208
|
+
const compiled = compileMongoPagination(pagination, {
|
|
1209
|
+
cursor: options.cursor
|
|
1210
|
+
});
|
|
752
1211
|
MongoAdapter.applyPagination(query, compiled);
|
|
753
1212
|
const fetchedNodes = await query.exec();
|
|
754
1213
|
if (!Array.isArray(fetchedNodes)) {
|
|
755
1214
|
throw new Error("paginateMongoQuery expects query.exec() to return an array");
|
|
756
1215
|
}
|
|
757
|
-
return materializeMongoPagination(compiled, fetchedNodes, {
|
|
1216
|
+
return materializeMongoPagination(compiled, fetchedNodes, {
|
|
1217
|
+
cursor: options.cursor
|
|
1218
|
+
});
|
|
758
1219
|
};
|
|
759
1220
|
const getQueryOptions$1 = (query) => {
|
|
760
1221
|
if (!query || typeof query !== "object") return void 0;
|
|
@@ -775,7 +1236,9 @@ const mongoPaginationPlugin = (schema, pluginOptions) => {
|
|
|
775
1236
|
if (!spec) throw new Error("Missing pagination spec");
|
|
776
1237
|
const cursor = options?.cursor ?? getCursorFromOptions(this) ?? pluginOptions?.cursor;
|
|
777
1238
|
if (!cursor?.signingSecret) throw new Error("Missing pagination cursor signingSecret");
|
|
778
|
-
return await paginateMongoQuery(this, spec, {
|
|
1239
|
+
return await paginateMongoQuery(this, spec, {
|
|
1240
|
+
cursor
|
|
1241
|
+
});
|
|
779
1242
|
};
|
|
780
1243
|
};
|
|
781
1244
|
const buildSearchTextStage = (options) => {
|
|
@@ -793,14 +1256,20 @@ const buildSearchTextStage = (options) => {
|
|
|
793
1256
|
}
|
|
794
1257
|
};
|
|
795
1258
|
if (options.highlightPath) {
|
|
796
|
-
stage.$search.highlight = {
|
|
1259
|
+
stage.$search.highlight = {
|
|
1260
|
+
path: options.highlightPath
|
|
1261
|
+
};
|
|
797
1262
|
}
|
|
798
1263
|
return stage;
|
|
799
1264
|
};
|
|
800
1265
|
const searchMetaProjection = () => {
|
|
801
1266
|
return {
|
|
802
|
-
score: {
|
|
803
|
-
|
|
1267
|
+
score: {
|
|
1268
|
+
$meta: "searchScore"
|
|
1269
|
+
},
|
|
1270
|
+
highlights: {
|
|
1271
|
+
$meta: "searchHighlights"
|
|
1272
|
+
}
|
|
804
1273
|
};
|
|
805
1274
|
};
|
|
806
1275
|
const listResultHasIndex = (listResult, name) => {
|
|
@@ -826,27 +1295,38 @@ const ensureSearchIndex = async (params) => {
|
|
|
826
1295
|
if (!name) throw new Error("Missing search index name");
|
|
827
1296
|
let listResult;
|
|
828
1297
|
try {
|
|
829
|
-
listResult = await params.db.command({
|
|
1298
|
+
listResult = await params.db.command({
|
|
1299
|
+
listSearchIndexes: collection
|
|
1300
|
+
});
|
|
830
1301
|
} catch (error) {
|
|
831
1302
|
const message = error instanceof Error ? error.message : String(error);
|
|
832
1303
|
throw new Error(`listSearchIndexes failed for "${collection}": ${message}`);
|
|
833
1304
|
}
|
|
834
1305
|
if (listResultHasIndex(listResult, name)) {
|
|
835
|
-
return {
|
|
1306
|
+
return {
|
|
1307
|
+
created: false
|
|
1308
|
+
};
|
|
836
1309
|
}
|
|
837
1310
|
try {
|
|
838
1311
|
await params.db.command({
|
|
839
1312
|
createSearchIndexes: collection,
|
|
840
|
-
indexes: [{
|
|
1313
|
+
indexes: [{
|
|
1314
|
+
name,
|
|
1315
|
+
definition: params.definition
|
|
1316
|
+
}]
|
|
841
1317
|
});
|
|
842
1318
|
} catch (error) {
|
|
843
1319
|
if (isIndexAlreadyExistsError(error)) {
|
|
844
|
-
return {
|
|
1320
|
+
return {
|
|
1321
|
+
created: false
|
|
1322
|
+
};
|
|
845
1323
|
}
|
|
846
1324
|
const message = error instanceof Error ? error.message : String(error);
|
|
847
1325
|
throw new Error(`createSearchIndexes failed for "${collection}" (index "${name}"): ${message}`);
|
|
848
1326
|
}
|
|
849
|
-
return {
|
|
1327
|
+
return {
|
|
1328
|
+
created: true
|
|
1329
|
+
};
|
|
850
1330
|
};
|
|
851
1331
|
const getAppName$1 = (env = process.env) => {
|
|
852
1332
|
const appName = env.APP_NAME?.trim();
|
|
@@ -899,7 +1379,9 @@ const ensureMongooseConnection = async (dbName) => {
|
|
|
899
1379
|
});
|
|
900
1380
|
}
|
|
901
1381
|
await waitForOpen(rootConnection);
|
|
902
|
-
const connection = rootConnection.name === normalizedDbName ? rootConnection : rootConnection.useDb(normalizedDbName, {
|
|
1382
|
+
const connection = rootConnection.name === normalizedDbName ? rootConnection : rootConnection.useDb(normalizedDbName, {
|
|
1383
|
+
useCache: true
|
|
1384
|
+
});
|
|
903
1385
|
await waitForOpen(connection);
|
|
904
1386
|
connections.set(normalizedDbName, connection);
|
|
905
1387
|
return connection;
|
|
@@ -937,22 +1419,42 @@ const getQuerySession = (query) => {
|
|
|
937
1419
|
const getRtsModels = (db) => {
|
|
938
1420
|
const RtsCounter = db.models.RBRtsCounter ?? db.model("RBRtsCounter", RBRtsCounterSchema);
|
|
939
1421
|
const RtsChange = db.models.RBRtsChange ?? db.model("RBRtsChange", RBRtsChangeSchema);
|
|
940
|
-
return {
|
|
1422
|
+
return {
|
|
1423
|
+
RtsCounter,
|
|
1424
|
+
RtsChange
|
|
1425
|
+
};
|
|
941
1426
|
};
|
|
942
1427
|
const allocateSeqRange = async (db, count, session) => {
|
|
943
|
-
const {
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1428
|
+
const {
|
|
1429
|
+
RtsCounter
|
|
1430
|
+
} = getRtsModels(db);
|
|
1431
|
+
const updated = await RtsCounter.findOneAndUpdate({
|
|
1432
|
+
_id: RTS_COUNTER_ID
|
|
1433
|
+
}, {
|
|
1434
|
+
$inc: {
|
|
1435
|
+
seq: count
|
|
1436
|
+
}
|
|
1437
|
+
}, {
|
|
1438
|
+
upsert: true,
|
|
1439
|
+
returnDocument: "after",
|
|
1440
|
+
setDefaultsOnInsert: true,
|
|
1441
|
+
projection: {
|
|
1442
|
+
seq: 1
|
|
1443
|
+
},
|
|
1444
|
+
session
|
|
1445
|
+
}).lean();
|
|
949
1446
|
const end = Number(updated?.seq ?? 0);
|
|
950
1447
|
const start = end - count + 1;
|
|
951
|
-
return {
|
|
1448
|
+
return {
|
|
1449
|
+
start,
|
|
1450
|
+
end
|
|
1451
|
+
};
|
|
952
1452
|
};
|
|
953
1453
|
const insertChanges = async (db, changes, session) => {
|
|
954
1454
|
if (!changes.length) return;
|
|
955
|
-
const {
|
|
1455
|
+
const {
|
|
1456
|
+
RtsChange
|
|
1457
|
+
} = getRtsModels(db);
|
|
956
1458
|
const ts = /* @__PURE__ */ new Date();
|
|
957
1459
|
const docs = changes.map((c3) => ({
|
|
958
1460
|
seq: c3.seq,
|
|
@@ -962,7 +1464,9 @@ const insertChanges = async (db, changes, session) => {
|
|
|
962
1464
|
ts
|
|
963
1465
|
}));
|
|
964
1466
|
if (session) {
|
|
965
|
-
await RtsChange.insertMany(docs, {
|
|
1467
|
+
await RtsChange.insertMany(docs, {
|
|
1468
|
+
session
|
|
1469
|
+
});
|
|
966
1470
|
return;
|
|
967
1471
|
}
|
|
968
1472
|
await RtsChange.insertMany(docs);
|
|
@@ -970,7 +1474,9 @@ const insertChanges = async (db, changes, session) => {
|
|
|
970
1474
|
const recordDeleteChanges = async (db, modelName, ids, session) => {
|
|
971
1475
|
const uniqueIds = Array.from(new Set(ids)).filter(Boolean);
|
|
972
1476
|
if (!uniqueIds.length) return;
|
|
973
|
-
const {
|
|
1477
|
+
const {
|
|
1478
|
+
start
|
|
1479
|
+
} = await allocateSeqRange(db, uniqueIds.length, session);
|
|
974
1480
|
await insertChanges(db, uniqueIds.map((docId, idx) => ({
|
|
975
1481
|
seq: start + idx,
|
|
976
1482
|
modelName,
|
|
@@ -979,8 +1485,14 @@ const recordDeleteChanges = async (db, modelName, ids, session) => {
|
|
|
979
1485
|
})), session);
|
|
980
1486
|
};
|
|
981
1487
|
const recordResetModel = async (db, modelName, session) => {
|
|
982
|
-
const {
|
|
983
|
-
|
|
1488
|
+
const {
|
|
1489
|
+
start
|
|
1490
|
+
} = await allocateSeqRange(db, 1, session);
|
|
1491
|
+
await insertChanges(db, [{
|
|
1492
|
+
seq: start,
|
|
1493
|
+
modelName,
|
|
1494
|
+
op: "reset_model"
|
|
1495
|
+
}], session);
|
|
984
1496
|
};
|
|
985
1497
|
const captureDeleteMeta = async (query, mode) => {
|
|
986
1498
|
const modelName = String(query?.model?.modelName ?? "");
|
|
@@ -988,7 +1500,9 @@ const captureDeleteMeta = async (query, mode) => {
|
|
|
988
1500
|
if (isGlobalDb(query?.model?.db)) return;
|
|
989
1501
|
const filter = typeof query.getFilter === "function" ? query.getFilter() : query.getQuery?.() ?? {};
|
|
990
1502
|
const session = getQuerySession(query);
|
|
991
|
-
const findQuery = query.model.find(filter, {
|
|
1503
|
+
const findQuery = query.model.find(filter, {
|
|
1504
|
+
_id: 1
|
|
1505
|
+
});
|
|
992
1506
|
if (session && typeof findQuery.session === "function") {
|
|
993
1507
|
findQuery.session(session);
|
|
994
1508
|
}
|
|
@@ -1027,19 +1541,34 @@ const flushDeleteMeta = async (query) => {
|
|
|
1027
1541
|
}
|
|
1028
1542
|
};
|
|
1029
1543
|
const rtsChangeLogPlugin = (schema) => {
|
|
1030
|
-
schema.pre("deleteOne", {
|
|
1544
|
+
schema.pre("deleteOne", {
|
|
1545
|
+
query: true,
|
|
1546
|
+
document: false
|
|
1547
|
+
}, async function() {
|
|
1031
1548
|
await captureDeleteMeta(this, "one");
|
|
1032
1549
|
});
|
|
1033
|
-
schema.pre("deleteMany", {
|
|
1550
|
+
schema.pre("deleteMany", {
|
|
1551
|
+
query: true,
|
|
1552
|
+
document: false
|
|
1553
|
+
}, async function() {
|
|
1034
1554
|
await captureDeleteMeta(this, "many");
|
|
1035
1555
|
});
|
|
1036
|
-
schema.post("deleteOne", {
|
|
1556
|
+
schema.post("deleteOne", {
|
|
1557
|
+
query: true,
|
|
1558
|
+
document: false
|
|
1559
|
+
}, async function() {
|
|
1037
1560
|
await flushDeleteMeta(this);
|
|
1038
1561
|
});
|
|
1039
|
-
schema.post("deleteMany", {
|
|
1562
|
+
schema.post("deleteMany", {
|
|
1563
|
+
query: true,
|
|
1564
|
+
document: false
|
|
1565
|
+
}, async function() {
|
|
1040
1566
|
await flushDeleteMeta(this);
|
|
1041
1567
|
});
|
|
1042
|
-
schema.post("findOneAndDelete", {
|
|
1568
|
+
schema.post("findOneAndDelete", {
|
|
1569
|
+
query: true,
|
|
1570
|
+
document: false
|
|
1571
|
+
}, async function(doc) {
|
|
1043
1572
|
const modelName = String(this?.model?.modelName ?? "");
|
|
1044
1573
|
if (!modelName || modelName.startsWith("RB") || EXCLUDED_MODEL_NAMES.has(modelName)) return;
|
|
1045
1574
|
const db = this?.model?.db;
|
|
@@ -1060,7 +1589,9 @@ const mergeMongoQuery = (left, right) => {
|
|
|
1060
1589
|
const leftQuery = isRecord(left) ? left : {};
|
|
1061
1590
|
if (Object.keys(leftQuery).length === 0) return right;
|
|
1062
1591
|
if (Object.keys(right).length === 0) return leftQuery;
|
|
1063
|
-
return {
|
|
1592
|
+
return {
|
|
1593
|
+
$and: [leftQuery, right]
|
|
1594
|
+
};
|
|
1064
1595
|
};
|
|
1065
1596
|
const getQueryOptions = (query) => {
|
|
1066
1597
|
if (!query || typeof query !== "object") return void 0;
|
|
@@ -1094,12 +1625,16 @@ const addQueryAclFilter = (query, action) => {
|
|
|
1094
1625
|
};
|
|
1095
1626
|
const injectAggregateMatch = (pipeline, match) => {
|
|
1096
1627
|
if (pipeline.length === 0) {
|
|
1097
|
-
pipeline.unshift({
|
|
1628
|
+
pipeline.unshift({
|
|
1629
|
+
$match: match
|
|
1630
|
+
});
|
|
1098
1631
|
return;
|
|
1099
1632
|
}
|
|
1100
1633
|
const first = pipeline[0];
|
|
1101
1634
|
if (!isRecord(first)) {
|
|
1102
|
-
pipeline.unshift({
|
|
1635
|
+
pipeline.unshift({
|
|
1636
|
+
$match: match
|
|
1637
|
+
});
|
|
1103
1638
|
return;
|
|
1104
1639
|
}
|
|
1105
1640
|
if ("$geoNear" in first) {
|
|
@@ -1110,10 +1645,14 @@ const injectAggregateMatch = (pipeline, match) => {
|
|
|
1110
1645
|
}
|
|
1111
1646
|
}
|
|
1112
1647
|
if ("$search" in first || "$vectorSearch" in first || "$searchMeta" in first) {
|
|
1113
|
-
pipeline.splice(1, 0, {
|
|
1648
|
+
pipeline.splice(1, 0, {
|
|
1649
|
+
$match: match
|
|
1650
|
+
});
|
|
1114
1651
|
return;
|
|
1115
1652
|
}
|
|
1116
|
-
pipeline.unshift({
|
|
1653
|
+
pipeline.unshift({
|
|
1654
|
+
$match: match
|
|
1655
|
+
});
|
|
1117
1656
|
};
|
|
1118
1657
|
const addAggregateAclFilter = (aggregate, action) => {
|
|
1119
1658
|
const storedAcl = getStoredAclFromAggregate(aggregate);
|
|
@@ -1132,13 +1671,23 @@ const patchAggregateAcl = () => {
|
|
|
1132
1671
|
const AggregatePrototype = mongoose.Aggregate.prototype;
|
|
1133
1672
|
if (typeof AggregatePrototype.acl === "function") return;
|
|
1134
1673
|
AggregatePrototype.acl = function(ability, action = "read") {
|
|
1135
|
-
this.option({
|
|
1674
|
+
this.option({
|
|
1675
|
+
rbAcl: {
|
|
1676
|
+
ability,
|
|
1677
|
+
action
|
|
1678
|
+
}
|
|
1679
|
+
});
|
|
1136
1680
|
return this;
|
|
1137
1681
|
};
|
|
1138
1682
|
};
|
|
1139
1683
|
const createModelAclProxy = (model2, ability) => {
|
|
1140
1684
|
return new Proxy(model2, {
|
|
1141
1685
|
get(target, prop, receiver) {
|
|
1686
|
+
if (prop === "acl") {
|
|
1687
|
+
return () => {
|
|
1688
|
+
throw new Error(`Model "${target.modelName}" is already ACL-scoped. Do not call .acl(...) again.`);
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1142
1691
|
const value = Reflect.get(target, prop, receiver);
|
|
1143
1692
|
if (typeof value !== "function") return value;
|
|
1144
1693
|
return (...args) => {
|
|
@@ -1154,7 +1703,12 @@ const createModelAclProxy = (model2, ability) => {
|
|
|
1154
1703
|
const mongooseAclPlugin = (schema) => {
|
|
1155
1704
|
patchAggregateAcl();
|
|
1156
1705
|
schema.query.acl = function(ability, action) {
|
|
1157
|
-
this.setOptions({
|
|
1706
|
+
this.setOptions({
|
|
1707
|
+
rbAcl: {
|
|
1708
|
+
ability,
|
|
1709
|
+
action
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1158
1712
|
return this;
|
|
1159
1713
|
};
|
|
1160
1714
|
schema.statics.acl = function(ability) {
|
|
@@ -1201,20 +1755,10 @@ const mongooseAclPlugin = (schema) => {
|
|
|
1201
1755
|
});
|
|
1202
1756
|
};
|
|
1203
1757
|
let cachedModels = null;
|
|
1204
|
-
const DEFAULT_GLOBAL_RB_MODEL_NAMES_SET = /* @__PURE__ */ new Set([
|
|
1205
|
-
"RBUser",
|
|
1206
|
-
"RBTenant",
|
|
1207
|
-
"RBOAuthRequest"
|
|
1208
|
-
]);
|
|
1758
|
+
const DEFAULT_GLOBAL_RB_MODEL_NAMES_SET = /* @__PURE__ */ new Set(["RBUser", "RBTenant", "RBOAuthRequest"]);
|
|
1209
1759
|
const assertSchema = (exportName, value) => {
|
|
1210
1760
|
if (value instanceof mongoose.Schema) return value;
|
|
1211
|
-
throw new Error(
|
|
1212
|
-
[
|
|
1213
|
-
`Expected ${exportName} to be an instance of mongoose.Schema, but it was not.`,
|
|
1214
|
-
"rpcbase supports mongoose 9+ only.",
|
|
1215
|
-
"Fix: ensure the project is using mongoose 9.x and that all packages resolve the same mongoose instance (try `npm ls mongoose`)."
|
|
1216
|
-
].join(" ")
|
|
1217
|
-
);
|
|
1761
|
+
throw new Error([`Expected ${exportName} to be an instance of mongoose.Schema, but it was not.`, "rpcbase supports mongoose 9+ only.", "Fix: ensure the project is using mongoose 9.x and that all packages resolve the same mongoose instance (try `npm ls mongoose`)."].join(" "));
|
|
1218
1762
|
};
|
|
1219
1763
|
const getFrameworkSchemaForModelName = (modelName) => {
|
|
1220
1764
|
const exportName = `${modelName}Schema`;
|
|
@@ -1237,14 +1781,23 @@ const registerSchema = (target, other, modelName, schema, scope) => {
|
|
|
1237
1781
|
const buildSchemasFromModules = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
|
|
1238
1782
|
const schema = assertSchema(key, schemaValue);
|
|
1239
1783
|
const modelName = key.replace(/Schema$/, "");
|
|
1240
|
-
return {
|
|
1784
|
+
return {
|
|
1785
|
+
modelName,
|
|
1786
|
+
schema
|
|
1787
|
+
};
|
|
1241
1788
|
});
|
|
1242
|
-
const registerModels = ({
|
|
1789
|
+
const registerModels = ({
|
|
1790
|
+
tenant,
|
|
1791
|
+
global
|
|
1792
|
+
}) => {
|
|
1243
1793
|
registerPoliciesFromModules(frameworkSchemas);
|
|
1244
1794
|
registerPoliciesFromModules(tenant);
|
|
1245
1795
|
const tenantSchemas = {};
|
|
1246
1796
|
const globalSchemas = {};
|
|
1247
|
-
for (const {
|
|
1797
|
+
for (const {
|
|
1798
|
+
modelName,
|
|
1799
|
+
schema
|
|
1800
|
+
} of buildSchemasFromModules(frameworkSchemas)) {
|
|
1248
1801
|
if (DEFAULT_GLOBAL_RB_MODEL_NAMES_SET.has(modelName)) {
|
|
1249
1802
|
const cloned = schema.clone();
|
|
1250
1803
|
registerSchema(globalSchemas, tenantSchemas, modelName, cloned);
|
|
@@ -1254,7 +1807,10 @@ const registerModels = ({ tenant, global }) => {
|
|
|
1254
1807
|
registerSchema(tenantSchemas, globalSchemas, modelName, cloned);
|
|
1255
1808
|
}
|
|
1256
1809
|
}
|
|
1257
|
-
for (const {
|
|
1810
|
+
for (const {
|
|
1811
|
+
modelName,
|
|
1812
|
+
schema
|
|
1813
|
+
} of buildSchemasFromModules(tenant)) {
|
|
1258
1814
|
if (modelName === "RBUser" || modelName === "RBTenant") {
|
|
1259
1815
|
throw new Error(`Invalid tenant model name "${modelName}". RBUser/RBTenant are global models.`);
|
|
1260
1816
|
}
|
|
@@ -1267,7 +1823,10 @@ const registerModels = ({ tenant, global }) => {
|
|
|
1267
1823
|
applyTenantPlugins(cloned);
|
|
1268
1824
|
registerSchema(tenantSchemas, globalSchemas, modelName, cloned);
|
|
1269
1825
|
}
|
|
1270
|
-
for (const {
|
|
1826
|
+
for (const {
|
|
1827
|
+
modelName,
|
|
1828
|
+
schema
|
|
1829
|
+
} of buildSchemasFromModules(global ?? {})) {
|
|
1271
1830
|
if (modelName.startsWith("RB")) {
|
|
1272
1831
|
const frameworkSchema = getFrameworkSchemaForModelName(modelName);
|
|
1273
1832
|
if (frameworkSchema && schema === frameworkSchema) continue;
|
|
@@ -1276,15 +1835,24 @@ const registerModels = ({ tenant, global }) => {
|
|
|
1276
1835
|
const cloned = schema.clone();
|
|
1277
1836
|
registerSchema(globalSchemas, tenantSchemas, modelName, cloned);
|
|
1278
1837
|
}
|
|
1279
|
-
const allSchemas = {
|
|
1838
|
+
const allSchemas = {
|
|
1839
|
+
...globalSchemas,
|
|
1840
|
+
...tenantSchemas
|
|
1841
|
+
};
|
|
1280
1842
|
for (const [modelName, schema] of Object.entries(allSchemas)) {
|
|
1281
1843
|
if (!mongoose.models[modelName]) {
|
|
1282
1844
|
mongoose.model(modelName, schema);
|
|
1283
1845
|
}
|
|
1284
1846
|
}
|
|
1285
1847
|
cachedModels = {
|
|
1286
|
-
tenant: {
|
|
1287
|
-
|
|
1848
|
+
tenant: {
|
|
1849
|
+
...cachedModels?.tenant ?? {},
|
|
1850
|
+
...tenantSchemas
|
|
1851
|
+
},
|
|
1852
|
+
global: {
|
|
1853
|
+
...cachedModels?.global ?? {},
|
|
1854
|
+
...globalSchemas
|
|
1855
|
+
}
|
|
1288
1856
|
};
|
|
1289
1857
|
};
|
|
1290
1858
|
const getRegisteredModels = (scope) => {
|
|
@@ -1310,11 +1878,24 @@ const getTenantIdFromCtx = (ctx) => {
|
|
|
1310
1878
|
};
|
|
1311
1879
|
const models = {
|
|
1312
1880
|
register: registerModels,
|
|
1313
|
-
|
|
1881
|
+
getUnsafe: async (modelName, ctx) => {
|
|
1314
1882
|
const tenantId = getTenantIdFromCtx(ctx);
|
|
1315
1883
|
const dbName = getTenantDbName(tenantId);
|
|
1316
1884
|
return loadModelFromDb(modelName, dbName, "tenant");
|
|
1317
1885
|
},
|
|
1886
|
+
get: async (modelName, ctx) => {
|
|
1887
|
+
const model2 = await models.getUnsafe(modelName, ctx);
|
|
1888
|
+
const resolvedAbility = ctx.ability;
|
|
1889
|
+
const isProtected = hasRegisteredPolicy(modelName);
|
|
1890
|
+
if (!isProtected) {
|
|
1891
|
+
return model2;
|
|
1892
|
+
}
|
|
1893
|
+
if (!resolvedAbility) {
|
|
1894
|
+
throw new Error(`Model "${modelName}" is ACL-protected. Set ctx.ability or use models.getUnsafe(...) explicitly.`);
|
|
1895
|
+
}
|
|
1896
|
+
if (typeof model2.acl !== "function") return model2;
|
|
1897
|
+
return model2.acl(resolvedAbility);
|
|
1898
|
+
},
|
|
1318
1899
|
getGlobal: async (modelName, ctx) => {
|
|
1319
1900
|
const dbName = getGlobalDbName();
|
|
1320
1901
|
return loadModelFromDb(modelName, dbName, "global");
|
|
@@ -1328,6 +1909,12 @@ const createModels = (modules) => {
|
|
|
1328
1909
|
}
|
|
1329
1910
|
return models.get(modelNameOrNames, ctx);
|
|
1330
1911
|
});
|
|
1912
|
+
const getUnsafe = (async (modelNameOrNames, ctx) => {
|
|
1913
|
+
if (Array.isArray(modelNameOrNames)) {
|
|
1914
|
+
return Promise.all(modelNameOrNames.map((modelName) => models.getUnsafe(modelName, ctx)));
|
|
1915
|
+
}
|
|
1916
|
+
return models.getUnsafe(modelNameOrNames, ctx);
|
|
1917
|
+
});
|
|
1331
1918
|
const getGlobal = (async (modelNameOrNames, ctx) => {
|
|
1332
1919
|
if (Array.isArray(modelNameOrNames)) {
|
|
1333
1920
|
return Promise.all(modelNameOrNames.map((modelName) => models.getGlobal(modelName, ctx)));
|
|
@@ -1337,6 +1924,7 @@ const createModels = (modules) => {
|
|
|
1337
1924
|
return {
|
|
1338
1925
|
register: registerModels,
|
|
1339
1926
|
get,
|
|
1927
|
+
getUnsafe,
|
|
1340
1928
|
getGlobal
|
|
1341
1929
|
};
|
|
1342
1930
|
};
|
|
@@ -1392,7 +1980,11 @@ async function withTransaction(scope, fn, options) {
|
|
|
1392
1980
|
const filesystemDb = await ensureMongooseConnection(filesystemDbName);
|
|
1393
1981
|
const session = await tenantDb.startSession();
|
|
1394
1982
|
const tenantCtx = typeof scope === "object" && "ctx" in scope ? scope.ctx : buildTenantLoadModelCtx(normalizedTenantId);
|
|
1395
|
-
const globalCtx = {
|
|
1983
|
+
const globalCtx = {
|
|
1984
|
+
req: {
|
|
1985
|
+
session: null
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1396
1988
|
try {
|
|
1397
1989
|
return await session.withTransaction(async () => fn({
|
|
1398
1990
|
tenantId: normalizedTenantId,
|
|
@@ -1467,6 +2059,7 @@ export {
|
|
|
1467
2059
|
getTenantFilesystemDbFromCtx,
|
|
1468
2060
|
getTenantFilesystemDbName,
|
|
1469
2061
|
c as getTenantRolesFromSessionUser,
|
|
2062
|
+
hasRegisteredPolicy,
|
|
1470
2063
|
isPaginationValidationError,
|
|
1471
2064
|
localizedStringField,
|
|
1472
2065
|
m as makeZE164Phone,
|