@infuro/cms-core 1.0.40 → 1.0.42
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/README.md +5 -18
- package/dist/admin.cjs +1009 -393
- package/dist/admin.d.cts +11 -1
- package/dist/admin.d.ts +11 -1
- package/dist/admin.js +1009 -393
- package/dist/api.cjs +35 -35
- package/dist/api.js +3 -3
- package/dist/auth.cjs +51 -51
- package/dist/auth.js +3 -3
- package/dist/{chunk-UXMDSNIG.js → chunk-BLR6H5GL.js} +1 -1
- package/dist/{chunk-P7QCLE5W.js → chunk-DN65KDIA.js} +11 -1
- package/dist/{chunk-T7GCOO5Y.cjs → chunk-GINYYPXK.cjs} +828 -219
- package/dist/{chunk-4BG7XYSH.js → chunk-I6NH72MO.js} +1 -1
- package/dist/{chunk-ETZN4OQI.cjs → chunk-TAHX46EI.cjs} +6 -6
- package/dist/{chunk-RCMUL6SZ.js → chunk-W2NL365N.js} +777 -180
- package/dist/{chunk-WMXPKKXU.cjs → chunk-X6UQFV4X.cjs} +11 -1
- package/dist/{chunk-OIGCAGH7.cjs → chunk-ZMRQ72F6.cjs} +7 -7
- package/dist/index.cjs +290 -242
- package/dist/index.d.cts +110 -5
- package/dist/index.d.ts +110 -5
- package/dist/index.js +6 -6
- package/dist/migrations/1781300000000-BrandIsCatalog.ts +61 -0
- package/dist/migrations/1781400000000-ComboEventIdNullable.ts +30 -0
- package/dist/migrations/1781500000000-ProductApprovalWorkflow.ts +30 -0
- package/dist/migrations/1781600000000-ProductApprovalStatusColumn.ts +53 -0
- package/dist/migrations/1781700000000-EventApprovalWorkflow.ts +32 -0
- package/dist/{rbac-debug-db-QWHOENBR.js → rbac-debug-db-2SGNLANF.js} +1 -1
- package/dist/{rbac-debug-db-KHG4FJC3.cjs → rbac-debug-db-4Z6A425H.cjs} +2 -2
- package/package.json +2 -2
|
@@ -5,8 +5,8 @@ import { queueEmail, queueVendorOnboardEmails, registerEmailQueueProcessor } fro
|
|
|
5
5
|
import { getSmsTemplateDefault, SMS_MESSAGE_TEMPLATE_DEFAULTS } from './chunk-MQBT33IV.js';
|
|
6
6
|
import { EVENT_ORDER_MESSAGE_TEMPLATE_DEFAULTS, EVENT_ORDER_TEMPLATE_KEY, EVENT_ORDER_TEMPLATE_VARIABLES } from './chunk-KFFB6EMZ.js';
|
|
7
7
|
import { JOB_RUNNER_QUEUE } from './chunk-GO7PPYNU.js';
|
|
8
|
-
import { canManageRoles, sessionHasEntityAccess } from './chunk-
|
|
9
|
-
import { permissionRowsToRecord, logRbac, isSuperAdmin, vendorPortalFlagsFromUser, VENDOR_SCOPED_STORE_ENTITIES, isVendorPortalUser, getPermissionableEntityKeys, VENDOR_STORE_RBAC_ENTITIES, isSuperAdminGroupName, VENDOR_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, DEFAULT_VENDOR_ROLE_DEFS, VENDOR_OWNER_GROUP_NAME, resolveVendorScopeFromSessionUser, canManageVendorRoles, isPlatformAdministrator, canManageVendorTeam, resolveDisabledMultiVendorScope, logEntityAccessDecision, explainSessionEntityAccess } from './chunk-
|
|
8
|
+
import { canManageRoles, sessionHasEntityAccess } from './chunk-BLR6H5GL.js';
|
|
9
|
+
import { permissionRowsToRecord, logRbac, isSuperAdmin, vendorPortalFlagsFromUser, VENDOR_SCOPED_STORE_ENTITIES, isVendorPortalUser, getPermissionableEntityKeys, VENDOR_STORE_RBAC_ENTITIES, isSuperAdminGroupName, VENDOR_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, DEFAULT_VENDOR_ROLE_DEFS, VENDOR_OWNER_GROUP_NAME, resolveVendorScopeFromSessionUser, canManageVendorRoles, isPlatformAdministrator, canManageVendorTeam, resolveDisabledMultiVendorScope, logEntityAccessDecision, explainSessionEntityAccess } from './chunk-DN65KDIA.js';
|
|
10
10
|
import { queueErp } from './chunk-SYBOCAWB.js';
|
|
11
11
|
import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
|
|
12
12
|
import { __name } from './chunk-SHUYVCID.js';
|
|
@@ -94,6 +94,79 @@ async function loadUserVendorContext(dataSource, userId, preferredVendorId) {
|
|
|
94
94
|
}
|
|
95
95
|
__name(loadUserVendorContext, "loadUserVendorContext");
|
|
96
96
|
|
|
97
|
+
// src/lib/vendor-catalog-create-flags.ts
|
|
98
|
+
var ALL_OFF = {
|
|
99
|
+
categories: false,
|
|
100
|
+
collections: false,
|
|
101
|
+
brands: false
|
|
102
|
+
};
|
|
103
|
+
var cachedFlags = null;
|
|
104
|
+
function invalidateVendorCatalogCreateFlagsCache() {
|
|
105
|
+
cachedFlags = null;
|
|
106
|
+
}
|
|
107
|
+
__name(invalidateVendorCatalogCreateFlagsCache, "invalidateVendorCatalogCreateFlagsCache");
|
|
108
|
+
function parseTrue(value) {
|
|
109
|
+
return value === "true";
|
|
110
|
+
}
|
|
111
|
+
__name(parseTrue, "parseTrue");
|
|
112
|
+
async function isMultiVendorEnabled(dataSource) {
|
|
113
|
+
try {
|
|
114
|
+
const rows = await dataSource.query(`
|
|
115
|
+
SELECT value FROM configs
|
|
116
|
+
WHERE settings = 'multi_vendor' AND key = 'enabled' AND deleted = false
|
|
117
|
+
LIMIT 1
|
|
118
|
+
`);
|
|
119
|
+
return rows.length === 0 || rows[0].value !== "false";
|
|
120
|
+
} catch {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
__name(isMultiVendorEnabled, "isMultiVendorEnabled");
|
|
125
|
+
async function getVendorCatalogCreateFlags(dataSource) {
|
|
126
|
+
if (cachedFlags !== null) return cachedFlags;
|
|
127
|
+
const multiVendorEnabled = await isMultiVendorEnabled(dataSource);
|
|
128
|
+
if (!multiVendorEnabled) {
|
|
129
|
+
cachedFlags = {
|
|
130
|
+
...ALL_OFF
|
|
131
|
+
};
|
|
132
|
+
return cachedFlags;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const rows = await dataSource.query(`
|
|
136
|
+
SELECT key, value FROM configs
|
|
137
|
+
WHERE settings = 'multi_vendor'
|
|
138
|
+
AND key IN (
|
|
139
|
+
'vendorCanCreateCategories',
|
|
140
|
+
'vendorCanCreateCollections',
|
|
141
|
+
'vendorCanCreateBrands'
|
|
142
|
+
)
|
|
143
|
+
AND deleted = false
|
|
144
|
+
`);
|
|
145
|
+
const map = new Map(rows.map((r) => [
|
|
146
|
+
r.key,
|
|
147
|
+
r.value
|
|
148
|
+
]));
|
|
149
|
+
cachedFlags = {
|
|
150
|
+
categories: parseTrue(map.get("vendorCanCreateCategories")),
|
|
151
|
+
collections: parseTrue(map.get("vendorCanCreateCollections")),
|
|
152
|
+
brands: parseTrue(map.get("vendorCanCreateBrands"))
|
|
153
|
+
};
|
|
154
|
+
} catch {
|
|
155
|
+
cachedFlags = {
|
|
156
|
+
...ALL_OFF
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return cachedFlags;
|
|
160
|
+
}
|
|
161
|
+
__name(getVendorCatalogCreateFlags, "getVendorCatalogCreateFlags");
|
|
162
|
+
function vendorCanCreateResource(resource, flags) {
|
|
163
|
+
if (resource === "product_categories") return flags.categories;
|
|
164
|
+
if (resource === "collections") return flags.collections;
|
|
165
|
+
if (resource === "brands") return flags.brands;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
__name(vendorCanCreateResource, "vendorCanCreateResource");
|
|
169
|
+
|
|
97
170
|
// src/lib/hydrate-vendor-session-user.ts
|
|
98
171
|
async function hydrateVendorSessionUser(dataSource, user) {
|
|
99
172
|
if (!user?.email) return user;
|
|
@@ -196,6 +269,7 @@ __name(hydrateVendorSessionUser, "hydrateVendorSessionUser");
|
|
|
196
269
|
var cachedMultiVendorEnabled = null;
|
|
197
270
|
function invalidateMultiVendorCache() {
|
|
198
271
|
cachedMultiVendorEnabled = null;
|
|
272
|
+
invalidateVendorCatalogCreateFlagsCache();
|
|
199
273
|
}
|
|
200
274
|
__name(invalidateMultiVendorCache, "invalidateMultiVendorCache");
|
|
201
275
|
async function checkMultiVendorEnabled(dataSource) {
|
|
@@ -234,6 +308,279 @@ async function checkEventsEnabled(dataSource) {
|
|
|
234
308
|
}
|
|
235
309
|
__name(checkEventsEnabled, "checkEventsEnabled");
|
|
236
310
|
|
|
311
|
+
// src/lib/product-approval.ts
|
|
312
|
+
var PRODUCT_APPROVAL_STATUSES = [
|
|
313
|
+
"pending",
|
|
314
|
+
"approved",
|
|
315
|
+
"rejected"
|
|
316
|
+
];
|
|
317
|
+
var APPROVAL_SET = new Set(PRODUCT_APPROVAL_STATUSES);
|
|
318
|
+
var PRODUCT_STATUSES = /* @__PURE__ */ new Set([
|
|
319
|
+
"draft",
|
|
320
|
+
"available",
|
|
321
|
+
"reserved",
|
|
322
|
+
"sold"
|
|
323
|
+
]);
|
|
324
|
+
var cachedRequireApproval = null;
|
|
325
|
+
function invalidateRequireProductApprovalCache() {
|
|
326
|
+
cachedRequireApproval = null;
|
|
327
|
+
}
|
|
328
|
+
__name(invalidateRequireProductApprovalCache, "invalidateRequireProductApprovalCache");
|
|
329
|
+
function parseTrue2(value) {
|
|
330
|
+
return value === "true";
|
|
331
|
+
}
|
|
332
|
+
__name(parseTrue2, "parseTrue");
|
|
333
|
+
async function getRequireProductApproval(dataSource) {
|
|
334
|
+
if (cachedRequireApproval !== null) return cachedRequireApproval;
|
|
335
|
+
const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
|
|
336
|
+
if (!multiVendorEnabled) {
|
|
337
|
+
cachedRequireApproval = false;
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
const rows = await dataSource.query(`
|
|
342
|
+
SELECT value FROM configs
|
|
343
|
+
WHERE settings = 'multi_vendor' AND key = 'requireProductApproval' AND deleted = false
|
|
344
|
+
LIMIT 1
|
|
345
|
+
`);
|
|
346
|
+
cachedRequireApproval = rows.length > 0 && parseTrue2(rows[0].value);
|
|
347
|
+
} catch {
|
|
348
|
+
cachedRequireApproval = false;
|
|
349
|
+
}
|
|
350
|
+
return cachedRequireApproval;
|
|
351
|
+
}
|
|
352
|
+
__name(getRequireProductApproval, "getRequireProductApproval");
|
|
353
|
+
function applyVendorProductCreateApproval(persistBody, opts) {
|
|
354
|
+
if (!opts.isVendor || !opts.requireApproval) return;
|
|
355
|
+
persistBody.approvalStatus = "pending";
|
|
356
|
+
if (persistBody.status === "available") persistBody.status = "draft";
|
|
357
|
+
persistBody.rejectionReason = null;
|
|
358
|
+
persistBody.rejectedAt = null;
|
|
359
|
+
persistBody.rejectedBy = null;
|
|
360
|
+
}
|
|
361
|
+
__name(applyVendorProductCreateApproval, "applyVendorProductCreateApproval");
|
|
362
|
+
function assertProductApprovalUpdate(opts) {
|
|
363
|
+
const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
|
|
364
|
+
const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
|
|
365
|
+
const fromStatus = String(opts.fromStatus || "draft");
|
|
366
|
+
const toStatus = String(opts.toStatus || fromStatus);
|
|
367
|
+
if (opts.statusChanged && !PRODUCT_STATUSES.has(toStatus)) {
|
|
368
|
+
return {
|
|
369
|
+
ok: false,
|
|
370
|
+
error: `Invalid product status: ${toStatus}`
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (opts.approvalChanged && toApproval != null && !APPROVAL_SET.has(toApproval)) {
|
|
374
|
+
return {
|
|
375
|
+
ok: false,
|
|
376
|
+
error: `Invalid approval status: ${toApproval}`
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (!opts.requireApproval) {
|
|
380
|
+
if (opts.isVendor && opts.approvalChanged) {
|
|
381
|
+
return {
|
|
382
|
+
ok: false,
|
|
383
|
+
error: "Vendors cannot change approval status"
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
ok: true
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (opts.isVendor) {
|
|
391
|
+
if (opts.approvalChanged) {
|
|
392
|
+
return {
|
|
393
|
+
ok: false,
|
|
394
|
+
error: "Vendors cannot change approval status"
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (opts.statusChanged && toStatus === "available" && fromApproval !== "approved") {
|
|
398
|
+
return {
|
|
399
|
+
ok: false,
|
|
400
|
+
error: "Product must be approved before it can be available"
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (fromApproval === "rejected" && opts.statusChanged && toStatus === "available") {
|
|
404
|
+
return {
|
|
405
|
+
ok: false,
|
|
406
|
+
error: "Rejected product cannot be made available"
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
ok: true
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (opts.approvalChanged && toApproval === "rejected") {
|
|
414
|
+
const reason = String(opts.rejectionReason ?? "").trim();
|
|
415
|
+
if (!reason) return {
|
|
416
|
+
ok: false,
|
|
417
|
+
error: "Rejection reason is required"
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
ok: true
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
__name(assertProductApprovalUpdate, "assertProductApprovalUpdate");
|
|
425
|
+
function applyApprovalStatusSideEffects(updatePayload, opts) {
|
|
426
|
+
const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
|
|
427
|
+
if (to == null) return;
|
|
428
|
+
if (to === "approved") {
|
|
429
|
+
updatePayload.approvalStatus = "approved";
|
|
430
|
+
updatePayload.status = "available";
|
|
431
|
+
updatePayload.rejectionReason = null;
|
|
432
|
+
updatePayload.rejectedAt = null;
|
|
433
|
+
updatePayload.rejectedBy = null;
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (to === "rejected") {
|
|
437
|
+
updatePayload.approvalStatus = "rejected";
|
|
438
|
+
updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
|
|
439
|
+
updatePayload.rejectedAt = /* @__PURE__ */ new Date();
|
|
440
|
+
updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
|
|
441
|
+
const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
|
|
442
|
+
if (nextStatus === "available") updatePayload.status = "draft";
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (to === "pending") {
|
|
446
|
+
updatePayload.approvalStatus = "pending";
|
|
447
|
+
updatePayload.rejectionReason = null;
|
|
448
|
+
updatePayload.rejectedAt = null;
|
|
449
|
+
updatePayload.rejectedBy = null;
|
|
450
|
+
const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
|
|
451
|
+
if (nextStatus === "available") updatePayload.status = "draft";
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
__name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
|
|
455
|
+
|
|
456
|
+
// src/lib/event-approval.ts
|
|
457
|
+
var EVENT_APPROVAL_STATUSES = [
|
|
458
|
+
"pending",
|
|
459
|
+
"approved",
|
|
460
|
+
"rejected"
|
|
461
|
+
];
|
|
462
|
+
var APPROVAL_SET2 = new Set(EVENT_APPROVAL_STATUSES);
|
|
463
|
+
var cachedRequireApproval2 = null;
|
|
464
|
+
function invalidateRequireEventApprovalCache() {
|
|
465
|
+
cachedRequireApproval2 = null;
|
|
466
|
+
}
|
|
467
|
+
__name(invalidateRequireEventApprovalCache, "invalidateRequireEventApprovalCache");
|
|
468
|
+
function parseTrue3(value) {
|
|
469
|
+
return value === "true";
|
|
470
|
+
}
|
|
471
|
+
__name(parseTrue3, "parseTrue");
|
|
472
|
+
async function getRequireEventApproval(dataSource) {
|
|
473
|
+
if (cachedRequireApproval2 !== null) return cachedRequireApproval2;
|
|
474
|
+
const [eventsOn, multiVendorOn] = await Promise.all([
|
|
475
|
+
checkEventsEnabled(dataSource),
|
|
476
|
+
checkMultiVendorEnabled(dataSource)
|
|
477
|
+
]);
|
|
478
|
+
if (!eventsOn || !multiVendorOn) {
|
|
479
|
+
cachedRequireApproval2 = false;
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
const rows = await dataSource.query(`
|
|
484
|
+
SELECT value FROM configs
|
|
485
|
+
WHERE settings = 'events' AND key = 'requireEventApproval' AND deleted = false
|
|
486
|
+
LIMIT 1
|
|
487
|
+
`);
|
|
488
|
+
cachedRequireApproval2 = rows.length > 0 && parseTrue3(rows[0].value);
|
|
489
|
+
} catch {
|
|
490
|
+
cachedRequireApproval2 = false;
|
|
491
|
+
}
|
|
492
|
+
return cachedRequireApproval2;
|
|
493
|
+
}
|
|
494
|
+
__name(getRequireEventApproval, "getRequireEventApproval");
|
|
495
|
+
function applyVendorEventCreateApproval(persistBody, opts) {
|
|
496
|
+
if (!opts.isVendor || !opts.requireApproval) return;
|
|
497
|
+
persistBody.approvalStatus = "pending";
|
|
498
|
+
persistBody.isActive = false;
|
|
499
|
+
persistBody.rejectionReason = null;
|
|
500
|
+
persistBody.rejectedAt = null;
|
|
501
|
+
persistBody.rejectedBy = null;
|
|
502
|
+
}
|
|
503
|
+
__name(applyVendorEventCreateApproval, "applyVendorEventCreateApproval");
|
|
504
|
+
function assertEventApprovalUpdate(opts) {
|
|
505
|
+
const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
|
|
506
|
+
const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
|
|
507
|
+
if (opts.approvalChanged && toApproval != null && !APPROVAL_SET2.has(toApproval)) {
|
|
508
|
+
return {
|
|
509
|
+
ok: false,
|
|
510
|
+
error: `Invalid approval status: ${toApproval}`
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
if (!opts.requireApproval) {
|
|
514
|
+
if (opts.isVendor && opts.approvalChanged) {
|
|
515
|
+
return {
|
|
516
|
+
ok: false,
|
|
517
|
+
error: "Vendors cannot change approval status"
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
ok: true
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
if (opts.isVendor) {
|
|
525
|
+
if (opts.approvalChanged) {
|
|
526
|
+
return {
|
|
527
|
+
ok: false,
|
|
528
|
+
error: "Vendors cannot change approval status"
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (opts.activeChanged && opts.toActive && fromApproval !== "approved") {
|
|
532
|
+
return {
|
|
533
|
+
ok: false,
|
|
534
|
+
error: "Event must be approved before it can be activated"
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
ok: true
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
if (opts.approvalChanged && toApproval === "rejected") {
|
|
542
|
+
const reason = String(opts.rejectionReason ?? "").trim();
|
|
543
|
+
if (!reason) return {
|
|
544
|
+
ok: false,
|
|
545
|
+
error: "Rejection reason is required"
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
ok: true
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
__name(assertEventApprovalUpdate, "assertEventApprovalUpdate");
|
|
553
|
+
function applyEventApprovalStatusSideEffects(updatePayload, opts) {
|
|
554
|
+
const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
|
|
555
|
+
if (to == null) return;
|
|
556
|
+
if (to === "approved") {
|
|
557
|
+
updatePayload.approvalStatus = "approved";
|
|
558
|
+
updatePayload.isActive = true;
|
|
559
|
+
updatePayload.rejectionReason = null;
|
|
560
|
+
updatePayload.rejectedAt = null;
|
|
561
|
+
updatePayload.rejectedBy = null;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (to === "rejected") {
|
|
565
|
+
updatePayload.approvalStatus = "rejected";
|
|
566
|
+
updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
|
|
567
|
+
updatePayload.rejectedAt = /* @__PURE__ */ new Date();
|
|
568
|
+
updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
|
|
569
|
+
const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
|
|
570
|
+
if (nextActive) updatePayload.isActive = false;
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (to === "pending") {
|
|
574
|
+
updatePayload.approvalStatus = "pending";
|
|
575
|
+
updatePayload.rejectionReason = null;
|
|
576
|
+
updatePayload.rejectedAt = null;
|
|
577
|
+
updatePayload.rejectedBy = null;
|
|
578
|
+
const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
|
|
579
|
+
if (nextActive) updatePayload.isActive = false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
__name(applyEventApprovalStatusSideEffects, "applyEventApprovalStatusSideEffects");
|
|
583
|
+
|
|
237
584
|
// src/lib/default-vendor-id.ts
|
|
238
585
|
function getDefaultVendorId() {
|
|
239
586
|
const raw = process.env.DEFAULT_VENDOR_ID;
|
|
@@ -269,17 +616,20 @@ async function assertCatalogCategoryId(dataSource, categoriesEntity, categoryId,
|
|
|
269
616
|
const row = await repo.findOne({
|
|
270
617
|
where: {
|
|
271
618
|
id,
|
|
272
|
-
deleted: false
|
|
273
|
-
isCatalog: true
|
|
619
|
+
deleted: false
|
|
274
620
|
}
|
|
275
621
|
});
|
|
276
622
|
if (!row) {
|
|
277
623
|
return "Invalid catalog category. Choose one of the platform product category types.";
|
|
278
624
|
}
|
|
279
|
-
return null;
|
|
625
|
+
if (row.isCatalog === true) return null;
|
|
626
|
+
if (options?.allowVendorId != null && Number(row.vendorId) === options.allowVendorId && row.isCatalog !== true) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return "Invalid catalog category. Choose one of the platform product category types.";
|
|
280
630
|
}
|
|
281
631
|
__name(assertCatalogCategoryId, "assertCatalogCategoryId");
|
|
282
|
-
async function
|
|
632
|
+
async function assertCollectionForVendorProduct(dataSource, collectionsEntity, collectionId, categoryId, vendorId, options) {
|
|
283
633
|
const cid = Number(collectionId);
|
|
284
634
|
if (!Number.isFinite(cid) || cid <= 0) return null;
|
|
285
635
|
if (!collectionsEntity) return "Collections are not configured";
|
|
@@ -290,11 +640,13 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
|
|
|
290
640
|
deleted: false
|
|
291
641
|
}
|
|
292
642
|
});
|
|
293
|
-
if (!row
|
|
294
|
-
return "Invalid collection. Choose
|
|
643
|
+
if (!row) {
|
|
644
|
+
return "Invalid collection. Choose a valid collection in this category.";
|
|
295
645
|
}
|
|
296
|
-
|
|
297
|
-
|
|
646
|
+
const isCatalog = row.isCatalog === true;
|
|
647
|
+
const isOwn = Number(row.vendorId) === vendorId && !isCatalog;
|
|
648
|
+
if (!isCatalog && !(options?.allowVendorOwned && isOwn)) {
|
|
649
|
+
return "Invalid collection. Choose one of the platform collections (or your own if allowed).";
|
|
298
650
|
}
|
|
299
651
|
const catId = Number(categoryId);
|
|
300
652
|
if (Number.isFinite(catId) && catId > 0 && Number(row.categoryId) !== catId) {
|
|
@@ -302,7 +654,7 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
|
|
|
302
654
|
}
|
|
303
655
|
return null;
|
|
304
656
|
}
|
|
305
|
-
__name(
|
|
657
|
+
__name(assertCollectionForVendorProduct, "assertCollectionForVendorProduct");
|
|
306
658
|
|
|
307
659
|
// src/lib/catalog-collection.ts
|
|
308
660
|
function isCatalogCollectionRow(row) {
|
|
@@ -316,7 +668,24 @@ function applyPlatformCatalogCollectionFields(persistBody) {
|
|
|
316
668
|
}
|
|
317
669
|
__name(applyPlatformCatalogCollectionFields, "applyPlatformCatalogCollectionFields");
|
|
318
670
|
|
|
671
|
+
// src/lib/catalog-brand.ts
|
|
672
|
+
function isCatalogBrandRow(row) {
|
|
673
|
+
return row?.isCatalog === true;
|
|
674
|
+
}
|
|
675
|
+
__name(isCatalogBrandRow, "isCatalogBrandRow");
|
|
676
|
+
function applyPlatformCatalogBrandFields(persistBody) {
|
|
677
|
+
persistBody.isCatalog = true;
|
|
678
|
+
delete persistBody.vendorId;
|
|
679
|
+
persistBody.vendorId = getDefaultVendorId();
|
|
680
|
+
}
|
|
681
|
+
__name(applyPlatformCatalogBrandFields, "applyPlatformCatalogBrandFields");
|
|
682
|
+
|
|
319
683
|
// src/api/vendor-scope-crud.ts
|
|
684
|
+
var CATALOG_LIST_RESOURCES = /* @__PURE__ */ new Set([
|
|
685
|
+
"product_categories",
|
|
686
|
+
"collections",
|
|
687
|
+
"brands"
|
|
688
|
+
]);
|
|
320
689
|
function repoHasVendorIdColumn(repo) {
|
|
321
690
|
return repo.metadata.columns.some((c) => c.propertyName === "vendorId");
|
|
322
691
|
}
|
|
@@ -325,7 +694,43 @@ function resourceUsesVendorScope(resource) {
|
|
|
325
694
|
return resource === "vendors" || VENDOR_SCOPED_STORE_ENTITIES.has(resource);
|
|
326
695
|
}
|
|
327
696
|
__name(resourceUsesVendorScope, "resourceUsesVendorScope");
|
|
328
|
-
function
|
|
697
|
+
function mergeWhereClause(where, extra) {
|
|
698
|
+
if (Array.isArray(where)) {
|
|
699
|
+
return where.map((w) => ({
|
|
700
|
+
...w,
|
|
701
|
+
...extra
|
|
702
|
+
}));
|
|
703
|
+
}
|
|
704
|
+
if (where && typeof where === "object" && Object.keys(where).length > 0) {
|
|
705
|
+
return {
|
|
706
|
+
...where,
|
|
707
|
+
...extra
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
return extra;
|
|
711
|
+
}
|
|
712
|
+
__name(mergeWhereClause, "mergeWhereClause");
|
|
713
|
+
function mergeCatalogOrOwnWhere(where, vendorId) {
|
|
714
|
+
const catalog = mergeWhereClause(where, {
|
|
715
|
+
isCatalog: true
|
|
716
|
+
});
|
|
717
|
+
const own = mergeWhereClause(where, {
|
|
718
|
+
vendorId,
|
|
719
|
+
isCatalog: false
|
|
720
|
+
});
|
|
721
|
+
const catalogArr = Array.isArray(catalog) ? catalog : [
|
|
722
|
+
catalog
|
|
723
|
+
];
|
|
724
|
+
const ownArr = Array.isArray(own) ? own : [
|
|
725
|
+
own
|
|
726
|
+
];
|
|
727
|
+
return [
|
|
728
|
+
...catalogArr,
|
|
729
|
+
...ownArr
|
|
730
|
+
];
|
|
731
|
+
}
|
|
732
|
+
__name(mergeCatalogOrOwnWhere, "mergeCatalogOrOwnWhere");
|
|
733
|
+
function mergeVendorScopeIntoWhere(where, scope, resource, flags) {
|
|
329
734
|
if (scope.type === "all") return where;
|
|
330
735
|
if (scope.type === "deny") {
|
|
331
736
|
if (resource === "vendors") {
|
|
@@ -342,38 +747,20 @@ function mergeVendorScopeIntoWhere(where, scope, resource) {
|
|
|
342
747
|
id: In(scope.vendorIds)
|
|
343
748
|
});
|
|
344
749
|
}
|
|
345
|
-
if (resource
|
|
750
|
+
if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
|
|
751
|
+
const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
|
|
752
|
+
if (canCreate) {
|
|
753
|
+
return mergeCatalogOrOwnWhere(where, scope.vendorId);
|
|
754
|
+
}
|
|
346
755
|
return mergeWhereClause(where, {
|
|
347
756
|
isCatalog: true
|
|
348
757
|
});
|
|
349
758
|
}
|
|
350
|
-
if (resource === "collections" && scope.type === "vendor") {
|
|
351
|
-
return mergeWhereClause(where, {
|
|
352
|
-
vendorId: scope.vendorId,
|
|
353
|
-
isCatalog: false
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
759
|
return mergeWhereClause(where, {
|
|
357
760
|
vendorId: scope.vendorId
|
|
358
761
|
});
|
|
359
762
|
}
|
|
360
763
|
__name(mergeVendorScopeIntoWhere, "mergeVendorScopeIntoWhere");
|
|
361
|
-
function mergeWhereClause(where, extra) {
|
|
362
|
-
if (Array.isArray(where)) {
|
|
363
|
-
return where.map((w) => ({
|
|
364
|
-
...w,
|
|
365
|
-
...extra
|
|
366
|
-
}));
|
|
367
|
-
}
|
|
368
|
-
if (where && typeof where === "object" && Object.keys(where).length > 0) {
|
|
369
|
-
return {
|
|
370
|
-
...where,
|
|
371
|
-
...extra
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
return extra;
|
|
375
|
-
}
|
|
376
|
-
__name(mergeWhereClause, "mergeWhereClause");
|
|
377
764
|
function applyVendorScopeToQueryBuilder(qb, alias, scope) {
|
|
378
765
|
if (scope.type === "all") return;
|
|
379
766
|
if (scope.type === "deny") {
|
|
@@ -385,7 +772,7 @@ function applyVendorScopeToQueryBuilder(qb, alias, scope) {
|
|
|
385
772
|
});
|
|
386
773
|
}
|
|
387
774
|
__name(applyVendorScopeToQueryBuilder, "applyVendorScopeToQueryBuilder");
|
|
388
|
-
function rowMatchesVendorScope(row, scope, resource) {
|
|
775
|
+
function rowMatchesVendorScope(row, scope, resource, flags) {
|
|
389
776
|
if (!row) return false;
|
|
390
777
|
if (scope.type === "all") return true;
|
|
391
778
|
if (scope.type === "deny") return false;
|
|
@@ -393,27 +780,32 @@ function rowMatchesVendorScope(row, scope, resource) {
|
|
|
393
780
|
const id = Number(row.id);
|
|
394
781
|
return scope.vendorIds.includes(id);
|
|
395
782
|
}
|
|
396
|
-
if (resource
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return Number(row.vendorId) === scope.vendorId && !
|
|
783
|
+
if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
|
|
784
|
+
const isCatalog = resource === "product_categories" ? isCatalogCategoryRow(row) : resource === "collections" ? isCatalogCollectionRow(row) : isCatalogBrandRow(row);
|
|
785
|
+
if (isCatalog) return true;
|
|
786
|
+
const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
|
|
787
|
+
return canCreate && Number(row.vendorId) === scope.vendorId && !isCatalog;
|
|
401
788
|
}
|
|
402
789
|
return Number(row.vendorId) === scope.vendorId;
|
|
403
790
|
}
|
|
404
791
|
__name(rowMatchesVendorScope, "rowMatchesVendorScope");
|
|
405
|
-
function
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
__name(vendorCannotMutateCatalogCategories, "vendorCannotMutateCatalogCategories");
|
|
409
|
-
function vendorCannotMutateCatalogCollections(resource, scope, body) {
|
|
410
|
-
if (resource !== "collections" || scope.type !== "vendor") return false;
|
|
792
|
+
function vendorCannotMutateCatalogResource(resource, scope, flags, body) {
|
|
793
|
+
if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource)) return false;
|
|
794
|
+
if (!vendorCanCreateResource(resource, flags)) return true;
|
|
411
795
|
return body?.isCatalog === true;
|
|
412
796
|
}
|
|
413
|
-
__name(
|
|
414
|
-
function
|
|
797
|
+
__name(vendorCannotMutateCatalogResource, "vendorCannotMutateCatalogResource");
|
|
798
|
+
function vendorCannotMutateExistingCatalogRow(resource, scope, row) {
|
|
799
|
+
if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource) || !row) return false;
|
|
800
|
+
if (resource === "product_categories") return isCatalogCategoryRow(row);
|
|
801
|
+
if (resource === "collections") return isCatalogCollectionRow(row);
|
|
802
|
+
if (resource === "brands") return isCatalogBrandRow(row);
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
__name(vendorCannotMutateExistingCatalogRow, "vendorCannotMutateExistingCatalogRow");
|
|
806
|
+
function vendorScopeRowAccess(row, scope, resource, flags) {
|
|
415
807
|
if (!row) return "not_found";
|
|
416
|
-
if (rowMatchesVendorScope(row, scope, resource)) return "ok";
|
|
808
|
+
if (rowMatchesVendorScope(row, scope, resource, flags)) return "ok";
|
|
417
809
|
return scope.type === "vendor" ? "forbidden" : "not_found";
|
|
418
810
|
}
|
|
419
811
|
__name(vendorScopeRowAccess, "vendorScopeRowAccess");
|
|
@@ -445,14 +837,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
|
|
|
445
837
|
ok: true
|
|
446
838
|
};
|
|
447
839
|
}
|
|
448
|
-
if (resource === "collections" && persistBody.isCatalog === true) {
|
|
449
|
-
delete persistBody.vendorId;
|
|
450
|
-
persistBody.vendorId = getDefaultVendorId();
|
|
451
|
-
return {
|
|
452
|
-
ok: true
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
if (resource === "product_categories" && persistBody.isCatalog === true) {
|
|
840
|
+
if ((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true) {
|
|
456
841
|
delete persistBody.vendorId;
|
|
457
842
|
persistBody.vendorId = getDefaultVendorId();
|
|
458
843
|
return {
|
|
@@ -464,7 +849,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
|
|
|
464
849
|
};
|
|
465
850
|
enforceVendorIdOnCreateBody(persistBody, scope, repo);
|
|
466
851
|
if (scope.type === "vendor") {
|
|
467
|
-
if (resource === "collections") {
|
|
852
|
+
if (resource === "collections" || resource === "product_categories" || resource === "brands") {
|
|
468
853
|
persistBody.isCatalog = false;
|
|
469
854
|
}
|
|
470
855
|
return {
|
|
@@ -3411,6 +3796,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3411
3796
|
const scope = await resolveScope();
|
|
3412
3797
|
const repo2 = dataSource.getRepository(entity);
|
|
3413
3798
|
const statusFilter = searchParams.get("status")?.trim();
|
|
3799
|
+
const approvalStatusFilter = searchParams.get("approvalStatus")?.trim();
|
|
3414
3800
|
const inventory = searchParams.get("inventory")?.trim();
|
|
3415
3801
|
let productWhere = {
|
|
3416
3802
|
deleted: false,
|
|
@@ -3418,6 +3804,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3418
3804
|
};
|
|
3419
3805
|
productWhere = mergeVendorScopeIntoWhere(productWhere, scope, resource);
|
|
3420
3806
|
if (statusFilter) productWhere.status = statusFilter;
|
|
3807
|
+
if (approvalStatusFilter) productWhere.approvalStatus = approvalStatusFilter;
|
|
3421
3808
|
if (inventory === "in_stock") productWhere.quantity = MoreThan(0);
|
|
3422
3809
|
if (inventory === "out_of_stock") productWhere.quantity = 0;
|
|
3423
3810
|
for (const key of [
|
|
@@ -3718,6 +4105,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3718
4105
|
where = mergeDeletedFalseWhere(repo, where);
|
|
3719
4106
|
if (resourceUsesVendorScope(resource)) {
|
|
3720
4107
|
const scope = await resolveScope();
|
|
4108
|
+
const catalogFlags = await getVendorCatalogCreateFlags(dataSource);
|
|
3721
4109
|
if (scope.type === "deny") {
|
|
3722
4110
|
where = resource === "vendors" ? {
|
|
3723
4111
|
id: -1
|
|
@@ -3726,7 +4114,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3726
4114
|
};
|
|
3727
4115
|
} else if (resource === "collections") {
|
|
3728
4116
|
if (scope.type === "vendor") {
|
|
3729
|
-
where = mergeVendorScopeIntoWhere(where, scope, resource);
|
|
4117
|
+
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
3730
4118
|
} else if (searchParams.get("isCatalog") !== "true") {
|
|
3731
4119
|
where = mergeListWhereAnd(where, {
|
|
3732
4120
|
isCatalog: false
|
|
@@ -3734,14 +4122,22 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3734
4122
|
}
|
|
3735
4123
|
} else if (resource === "product_categories") {
|
|
3736
4124
|
if (scope.type === "vendor") {
|
|
3737
|
-
where = mergeVendorScopeIntoWhere(where, scope, resource);
|
|
4125
|
+
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
4126
|
+
} else if (searchParams.get("isCatalog") !== "false") {
|
|
4127
|
+
where = mergeListWhereAnd(where, {
|
|
4128
|
+
isCatalog: true
|
|
4129
|
+
});
|
|
4130
|
+
}
|
|
4131
|
+
} else if (resource === "brands") {
|
|
4132
|
+
if (scope.type === "vendor") {
|
|
4133
|
+
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
3738
4134
|
} else if (searchParams.get("isCatalog") !== "false") {
|
|
3739
4135
|
where = mergeListWhereAnd(where, {
|
|
3740
4136
|
isCatalog: true
|
|
3741
4137
|
});
|
|
3742
4138
|
}
|
|
3743
4139
|
} else {
|
|
3744
|
-
where = mergeVendorScopeIntoWhere(where, scope, resource);
|
|
4140
|
+
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
3745
4141
|
}
|
|
3746
4142
|
}
|
|
3747
4143
|
let data;
|
|
@@ -3811,16 +4207,11 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3811
4207
|
});
|
|
3812
4208
|
}
|
|
3813
4209
|
const body = rawPostBody;
|
|
3814
|
-
|
|
4210
|
+
const catalogFlagsPost = await getVendorCatalogCreateFlags(dataSource);
|
|
4211
|
+
if (vendorCannotMutateCatalogResource(resource, scopePost, catalogFlagsPost, body)) {
|
|
4212
|
+
const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
|
|
3815
4213
|
return json({
|
|
3816
|
-
error:
|
|
3817
|
-
}, {
|
|
3818
|
-
status: 403
|
|
3819
|
-
});
|
|
3820
|
-
}
|
|
3821
|
-
if (vendorCannotMutateCatalogCollections(resource, scopePost, body)) {
|
|
3822
|
-
return json({
|
|
3823
|
-
error: "Collections are managed by the platform administrator"
|
|
4214
|
+
error: `${label} are managed by the platform administrator`
|
|
3824
4215
|
}, {
|
|
3825
4216
|
status: 403
|
|
3826
4217
|
});
|
|
@@ -4205,8 +4596,15 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4205
4596
|
}
|
|
4206
4597
|
if (resource === "products") {
|
|
4207
4598
|
const scopeForProduct = await resolveScope();
|
|
4599
|
+
const productFlags = await getVendorCatalogCreateFlags(dataSource);
|
|
4600
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
4601
|
+
applyVendorProductCreateApproval(persistBody, {
|
|
4602
|
+
isVendor: scopeForProduct.type === "vendor",
|
|
4603
|
+
requireApproval
|
|
4604
|
+
});
|
|
4208
4605
|
const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
|
|
4209
|
-
required: scopeForProduct.type === "vendor"
|
|
4606
|
+
required: scopeForProduct.type === "vendor",
|
|
4607
|
+
allowVendorId: scopeForProduct.type === "vendor" && productFlags.categories ? scopeForProduct.vendorId : void 0
|
|
4210
4608
|
});
|
|
4211
4609
|
if (categoryErr) return json({
|
|
4212
4610
|
error: categoryErr
|
|
@@ -4215,7 +4613,9 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4215
4613
|
});
|
|
4216
4614
|
if (scopeForProduct.type === "vendor") {
|
|
4217
4615
|
if (entityMap.collections && persistBody.collectionId != null && persistBody.collectionId !== "") {
|
|
4218
|
-
const collectionErr = await
|
|
4616
|
+
const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, persistBody.collectionId, persistBody.categoryId, scopeForProduct.vendorId, {
|
|
4617
|
+
allowVendorOwned: productFlags.collections
|
|
4618
|
+
});
|
|
4219
4619
|
if (collectionErr) return json({
|
|
4220
4620
|
error: collectionErr
|
|
4221
4621
|
}, {
|
|
@@ -4224,6 +4624,14 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4224
4624
|
}
|
|
4225
4625
|
}
|
|
4226
4626
|
}
|
|
4627
|
+
if (resource === "events") {
|
|
4628
|
+
const scopeForEvent = await resolveScope();
|
|
4629
|
+
const requireEventApproval = await getRequireEventApproval(dataSource);
|
|
4630
|
+
applyVendorEventCreateApproval(persistBody, {
|
|
4631
|
+
isVendor: scopeForEvent.type === "vendor",
|
|
4632
|
+
requireApproval: requireEventApproval
|
|
4633
|
+
});
|
|
4634
|
+
}
|
|
4227
4635
|
if (resource === "collections" && scopePost.type === "vendor") {
|
|
4228
4636
|
const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
|
|
4229
4637
|
required: true
|
|
@@ -4428,7 +4836,27 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4428
4836
|
}
|
|
4429
4837
|
}
|
|
4430
4838
|
}
|
|
4431
|
-
if (
|
|
4839
|
+
if (resource === "brands" && scopeCreate.type === "all") {
|
|
4840
|
+
applyPlatformCatalogBrandFields(persistBody);
|
|
4841
|
+
const slug = String(persistBody.slug ?? "").trim();
|
|
4842
|
+
if (slug) {
|
|
4843
|
+
const dup = await repo.findOne({
|
|
4844
|
+
where: {
|
|
4845
|
+
slug,
|
|
4846
|
+
isCatalog: true,
|
|
4847
|
+
deleted: false
|
|
4848
|
+
}
|
|
4849
|
+
});
|
|
4850
|
+
if (dup) {
|
|
4851
|
+
return json({
|
|
4852
|
+
error: "A catalog brand with this slug already exists"
|
|
4853
|
+
}, {
|
|
4854
|
+
status: 400
|
|
4855
|
+
});
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
if (!((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true)) {
|
|
4432
4860
|
await tryAssignSingleVendorOnAdminCreate(resource, scopeCreate, persistBody);
|
|
4433
4861
|
}
|
|
4434
4862
|
const vendorIdCheck = requireVendorIdForScopedCreate(resource, persistBody, scopeCreate, repo, await vendorCreateContext(body));
|
|
@@ -4908,13 +5336,14 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4908
5336
|
}
|
|
4909
5337
|
__name(createCrudHandler, "createCrudHandler");
|
|
4910
5338
|
function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
4911
|
-
const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope } = options;
|
|
5339
|
+
const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope, getHydratedSessionUser } = options;
|
|
4912
5340
|
const syncContactRowToErp = makeContactErpSync(dataSource, entityMap, getCms);
|
|
4913
5341
|
const resolveScopeById = getVendorScope ?? (async () => ({
|
|
4914
5342
|
type: "all"
|
|
4915
5343
|
}));
|
|
4916
|
-
function vendorScopeAccessJson(row, scope, resource) {
|
|
4917
|
-
const
|
|
5344
|
+
async function vendorScopeAccessJson(row, scope, resource) {
|
|
5345
|
+
const flags = await getVendorCatalogCreateFlags(dataSource);
|
|
5346
|
+
const access = vendorScopeRowAccess(row, scope, resource, flags);
|
|
4918
5347
|
if (access === "ok") return null;
|
|
4919
5348
|
if (access === "forbidden") return json({
|
|
4920
5349
|
error: "Forbidden"
|
|
@@ -4994,7 +5423,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
4994
5423
|
"rules"
|
|
4995
5424
|
]
|
|
4996
5425
|
});
|
|
4997
|
-
const discountDenied = vendorScopeAccessJson(discount, scope2, resource);
|
|
5426
|
+
const discountDenied = await vendorScopeAccessJson(discount, scope2, resource);
|
|
4998
5427
|
if (discountDenied) return discountDenied;
|
|
4999
5428
|
const flatRules = discount.rules ?? [];
|
|
5000
5429
|
const nestedRules = nestDiscountRules2(flatRules);
|
|
@@ -5052,7 +5481,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5052
5481
|
"payments"
|
|
5053
5482
|
]
|
|
5054
5483
|
});
|
|
5055
|
-
const orderDenied = vendorScopeAccessJson(order, scope2, resource);
|
|
5484
|
+
const orderDenied = await vendorScopeAccessJson(order, scope2, resource);
|
|
5056
5485
|
if (orderDenied) return orderDenied;
|
|
5057
5486
|
const relatedOrders = await repo.find({
|
|
5058
5487
|
where: {
|
|
@@ -5116,7 +5545,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5116
5545
|
"contact"
|
|
5117
5546
|
]
|
|
5118
5547
|
});
|
|
5119
|
-
const paymentDenied = vendorScopeAccessJson(payment, scope2, resource);
|
|
5548
|
+
const paymentDenied = await vendorScopeAccessJson(payment, scope2, resource);
|
|
5120
5549
|
if (paymentDenied) return paymentDenied;
|
|
5121
5550
|
const p = payment;
|
|
5122
5551
|
const order = p.order;
|
|
@@ -5155,7 +5584,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5155
5584
|
"attributes.attribute"
|
|
5156
5585
|
]
|
|
5157
5586
|
});
|
|
5158
|
-
const productDenied = vendorScopeAccessJson(product, scope2, resource);
|
|
5587
|
+
const productDenied = await vendorScopeAccessJson(product, scope2, resource);
|
|
5159
5588
|
if (productDenied) return productDenied;
|
|
5160
5589
|
return product ? json(hydrateProductDisplayName(product)) : json({
|
|
5161
5590
|
message: "Not found"
|
|
@@ -5173,7 +5602,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5173
5602
|
"contact"
|
|
5174
5603
|
]
|
|
5175
5604
|
});
|
|
5176
|
-
const vcDenied = vendorScopeAccessJson(row, scope2, resource);
|
|
5605
|
+
const vcDenied = await vendorScopeAccessJson(row, scope2, resource);
|
|
5177
5606
|
if (vcDenied) return vcDenied;
|
|
5178
5607
|
const contact = row.contact;
|
|
5179
5608
|
return json({
|
|
@@ -5196,7 +5625,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5196
5625
|
"items.product"
|
|
5197
5626
|
]
|
|
5198
5627
|
});
|
|
5199
|
-
const comboDenied = vendorScopeAccessJson(combo, scope2, resource);
|
|
5628
|
+
const comboDenied = await vendorScopeAccessJson(combo, scope2, resource);
|
|
5200
5629
|
if (comboDenied) return comboDenied;
|
|
5201
5630
|
return combo ? json(combo) : json({
|
|
5202
5631
|
message: "Not found"
|
|
@@ -5233,7 +5662,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5233
5662
|
where: idWhere
|
|
5234
5663
|
});
|
|
5235
5664
|
if (resourceUsesVendorScope(resource)) {
|
|
5236
|
-
const itemDenied = vendorScopeAccessJson(item, scope, resource);
|
|
5665
|
+
const itemDenied = await vendorScopeAccessJson(item, scope, resource);
|
|
5237
5666
|
if (itemDenied) return itemDenied;
|
|
5238
5667
|
} else if (!item) {
|
|
5239
5668
|
return json({
|
|
@@ -5262,16 +5691,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5262
5691
|
}
|
|
5263
5692
|
const rawBody = await req.json();
|
|
5264
5693
|
const scopePutEarly = await resolveScopeById();
|
|
5265
|
-
|
|
5694
|
+
const catalogFlagsPut = await getVendorCatalogCreateFlags(dataSource);
|
|
5695
|
+
if (vendorCannotMutateCatalogResource(resource, scopePutEarly, catalogFlagsPut, rawBody ?? void 0)) {
|
|
5696
|
+
const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
|
|
5266
5697
|
return json({
|
|
5267
|
-
error:
|
|
5268
|
-
}, {
|
|
5269
|
-
status: 403
|
|
5270
|
-
});
|
|
5271
|
-
}
|
|
5272
|
-
if (vendorCannotMutateCatalogCollections(resource, scopePutEarly, rawBody ?? void 0)) {
|
|
5273
|
-
return json({
|
|
5274
|
-
error: "Collections are managed by the platform administrator"
|
|
5698
|
+
error: `${label} are managed by the platform administrator`
|
|
5275
5699
|
}, {
|
|
5276
5700
|
status: 403
|
|
5277
5701
|
});
|
|
@@ -5637,7 +6061,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5637
6061
|
id: numericId
|
|
5638
6062
|
}
|
|
5639
6063
|
});
|
|
5640
|
-
const discountPutDenied = vendorScopeAccessJson(existing, scopePut2, resource);
|
|
6064
|
+
const discountPutDenied = await vendorScopeAccessJson(existing, scopePut2, resource);
|
|
5641
6065
|
if (discountPutDenied) return discountPutDenied;
|
|
5642
6066
|
const updatePayload2 = pickColumnUpdates(repo, rawBody);
|
|
5643
6067
|
delete updatePayload2.vendorId;
|
|
@@ -5695,8 +6119,16 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5695
6119
|
const existingScope = await repo.findOne({
|
|
5696
6120
|
where: idWhereCheck
|
|
5697
6121
|
});
|
|
5698
|
-
const scopePutDenied = vendorScopeAccessJson(existingScope, scopePut, resource);
|
|
6122
|
+
const scopePutDenied = await vendorScopeAccessJson(existingScope, scopePut, resource);
|
|
5699
6123
|
if (scopePutDenied) return scopePutDenied;
|
|
6124
|
+
if (vendorCannotMutateExistingCatalogRow(resource, scopePut, existingScope)) {
|
|
6125
|
+
const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : "Brands";
|
|
6126
|
+
return json({
|
|
6127
|
+
error: `${label} are managed by the platform administrator`
|
|
6128
|
+
}, {
|
|
6129
|
+
status: 403
|
|
6130
|
+
});
|
|
6131
|
+
}
|
|
5700
6132
|
}
|
|
5701
6133
|
const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
|
|
5702
6134
|
if (resourceUsesVendorScope(resource)) {
|
|
@@ -5843,7 +6275,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5843
6275
|
});
|
|
5844
6276
|
const mergedCategoryId = "categoryId" in updatePayload ? updatePayload.categoryId : currentRow.categoryId;
|
|
5845
6277
|
const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, "categoryId" in updatePayload ? updatePayload.categoryId : mergedCategoryId, {
|
|
5846
|
-
required: scopePut.type === "vendor"
|
|
6278
|
+
required: scopePut.type === "vendor",
|
|
6279
|
+
allowVendorId: scopePut.type === "vendor" && catalogFlagsPut.categories ? scopePut.vendorId : void 0
|
|
5847
6280
|
});
|
|
5848
6281
|
if (categoryErr) return json({
|
|
5849
6282
|
error: categoryErr
|
|
@@ -5852,7 +6285,9 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5852
6285
|
});
|
|
5853
6286
|
if (scopePut.type === "vendor") {
|
|
5854
6287
|
if (entityMap.collections && "collectionId" in updatePayload && updatePayload.collectionId != null) {
|
|
5855
|
-
const collectionErr = await
|
|
6288
|
+
const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, updatePayload.collectionId, mergedCategoryId, scopePut.vendorId, {
|
|
6289
|
+
allowVendorOwned: catalogFlagsPut.collections
|
|
6290
|
+
});
|
|
5856
6291
|
if (collectionErr) return json({
|
|
5857
6292
|
error: collectionErr
|
|
5858
6293
|
}, {
|
|
@@ -5860,6 +6295,88 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5860
6295
|
});
|
|
5861
6296
|
}
|
|
5862
6297
|
}
|
|
6298
|
+
if ("status" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
|
|
6299
|
+
const fromStatus = String(currentRow.status ?? "draft");
|
|
6300
|
+
const toStatus = String("status" in updatePayload ? updatePayload.status ?? fromStatus : fromStatus);
|
|
6301
|
+
const fromApproval = currentRow.approvalStatus;
|
|
6302
|
+
const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
|
|
6303
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
6304
|
+
const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentRow.rejectionReason;
|
|
6305
|
+
const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
|
|
6306
|
+
const transition = assertProductApprovalUpdate({
|
|
6307
|
+
fromApproval,
|
|
6308
|
+
toApproval,
|
|
6309
|
+
fromStatus,
|
|
6310
|
+
toStatus,
|
|
6311
|
+
isVendor: scopePut.type === "vendor",
|
|
6312
|
+
requireApproval,
|
|
6313
|
+
approvalChanged: "approvalStatus" in updatePayload,
|
|
6314
|
+
statusChanged: "status" in updatePayload,
|
|
6315
|
+
rejectionReason: reasonStr
|
|
6316
|
+
});
|
|
6317
|
+
if (!transition.ok) return json({
|
|
6318
|
+
error: transition.error
|
|
6319
|
+
}, {
|
|
6320
|
+
status: 400
|
|
6321
|
+
});
|
|
6322
|
+
if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
|
|
6323
|
+
const session = await getHydratedSessionUser?.() ?? null;
|
|
6324
|
+
const rejectedByRaw = session?.id != null ? Number(session.id) : null;
|
|
6325
|
+
applyApprovalStatusSideEffects(updatePayload, {
|
|
6326
|
+
toApproval,
|
|
6327
|
+
currentStatus: fromStatus,
|
|
6328
|
+
rejectionReason: reasonStr,
|
|
6329
|
+
rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
|
|
6330
|
+
});
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
if (resource === "events") {
|
|
6335
|
+
const currentEvent = await repo.findOne({
|
|
6336
|
+
where: {
|
|
6337
|
+
id: numericId,
|
|
6338
|
+
deleted: false
|
|
6339
|
+
}
|
|
6340
|
+
});
|
|
6341
|
+
if (!currentEvent) return json({
|
|
6342
|
+
message: "Not found"
|
|
6343
|
+
}, {
|
|
6344
|
+
status: 404
|
|
6345
|
+
});
|
|
6346
|
+
if ("isActive" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
|
|
6347
|
+
const fromActive = Boolean(currentEvent.isActive);
|
|
6348
|
+
const toActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : fromActive;
|
|
6349
|
+
const fromApproval = currentEvent.approvalStatus;
|
|
6350
|
+
const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
|
|
6351
|
+
const requireEventApproval = await getRequireEventApproval(dataSource);
|
|
6352
|
+
const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentEvent.rejectionReason;
|
|
6353
|
+
const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
|
|
6354
|
+
const transition = assertEventApprovalUpdate({
|
|
6355
|
+
fromApproval,
|
|
6356
|
+
toApproval,
|
|
6357
|
+
toActive,
|
|
6358
|
+
isVendor: scopePut.type === "vendor",
|
|
6359
|
+
requireApproval: requireEventApproval,
|
|
6360
|
+
approvalChanged: "approvalStatus" in updatePayload,
|
|
6361
|
+
activeChanged: "isActive" in updatePayload,
|
|
6362
|
+
rejectionReason: reasonStr
|
|
6363
|
+
});
|
|
6364
|
+
if (!transition.ok) return json({
|
|
6365
|
+
error: transition.error
|
|
6366
|
+
}, {
|
|
6367
|
+
status: 400
|
|
6368
|
+
});
|
|
6369
|
+
if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
|
|
6370
|
+
const session = await getHydratedSessionUser?.() ?? null;
|
|
6371
|
+
const rejectedByRaw = session?.id != null ? Number(session.id) : null;
|
|
6372
|
+
applyEventApprovalStatusSideEffects(updatePayload, {
|
|
6373
|
+
toApproval,
|
|
6374
|
+
currentActive: fromActive,
|
|
6375
|
+
rejectionReason: reasonStr,
|
|
6376
|
+
rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
|
|
6377
|
+
});
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
5863
6380
|
}
|
|
5864
6381
|
if (resource === "product_categories" && scopePut.type === "all") {
|
|
5865
6382
|
const existingCatalogCat = await repo.findOne({
|
|
@@ -5917,6 +6434,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
5917
6434
|
}
|
|
5918
6435
|
}
|
|
5919
6436
|
}
|
|
6437
|
+
if (resource === "brands" && scopePut.type === "all") {
|
|
6438
|
+
const existingCatalogBrand = await repo.findOne({
|
|
6439
|
+
where: {
|
|
6440
|
+
id: numericId,
|
|
6441
|
+
deleted: false
|
|
6442
|
+
}
|
|
6443
|
+
});
|
|
6444
|
+
if (existingCatalogBrand && existingCatalogBrand.isCatalog === true) {
|
|
6445
|
+
applyPlatformCatalogBrandFields(updatePayload);
|
|
6446
|
+
const slug = String(updatePayload.slug ?? existingCatalogBrand.slug ?? "").trim();
|
|
6447
|
+
if (slug && "slug" in updatePayload) {
|
|
6448
|
+
const dup = await repo.findOne({
|
|
6449
|
+
where: {
|
|
6450
|
+
slug,
|
|
6451
|
+
isCatalog: true,
|
|
6452
|
+
deleted: false
|
|
6453
|
+
}
|
|
6454
|
+
});
|
|
6455
|
+
if (dup && Number(dup.id) !== numericId) {
|
|
6456
|
+
return json({
|
|
6457
|
+
error: "A catalog brand with this slug already exists"
|
|
6458
|
+
}, {
|
|
6459
|
+
status: 400
|
|
6460
|
+
});
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
5920
6465
|
if (resource === "addresses" && Object.keys(updatePayload).length > 0) {
|
|
5921
6466
|
const currentRow = await repo.findOne({
|
|
5922
6467
|
where: {
|
|
@@ -6072,9 +6617,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6072
6617
|
const authError = await authz(req, resource, "delete");
|
|
6073
6618
|
if (authError) return authError;
|
|
6074
6619
|
const scopeDeleteEarly = await resolveScopeById();
|
|
6075
|
-
|
|
6620
|
+
const catalogFlagsDelete = await getVendorCatalogCreateFlags(dataSource);
|
|
6621
|
+
if (vendorCannotMutateCatalogResource(resource, scopeDeleteEarly, catalogFlagsDelete)) {
|
|
6622
|
+
const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
|
|
6076
6623
|
return json({
|
|
6077
|
-
error:
|
|
6624
|
+
error: `${label} are managed by the platform administrator`
|
|
6078
6625
|
}, {
|
|
6079
6626
|
status: 403
|
|
6080
6627
|
});
|
|
@@ -6103,7 +6650,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6103
6650
|
}
|
|
6104
6651
|
});
|
|
6105
6652
|
if (resourceUsesVendorScope(resource)) {
|
|
6106
|
-
const deleteDenied = vendorScopeAccessJson(existing, scopeDelete, resource);
|
|
6653
|
+
const deleteDenied = await vendorScopeAccessJson(existing, scopeDelete, resource);
|
|
6107
6654
|
if (deleteDenied) return deleteDenied;
|
|
6108
6655
|
} else if (!existing) {
|
|
6109
6656
|
return json({
|
|
@@ -6161,7 +6708,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6161
6708
|
id: numericId
|
|
6162
6709
|
}
|
|
6163
6710
|
});
|
|
6164
|
-
const hardDeleteDenied = vendorScopeAccessJson(existingHard, scopeDelete, resource);
|
|
6711
|
+
const hardDeleteDenied = await vendorScopeAccessJson(existingHard, scopeDelete, resource);
|
|
6165
6712
|
if (hardDeleteDenied) return hardDeleteDenied;
|
|
6166
6713
|
}
|
|
6167
6714
|
const result = await repo.delete(numericId);
|
|
@@ -12256,6 +12803,13 @@ function createVendorOnboardHandlers(config) {
|
|
|
12256
12803
|
status: 400
|
|
12257
12804
|
});
|
|
12258
12805
|
}
|
|
12806
|
+
if (body.activation === "password" || body.user?.password) {
|
|
12807
|
+
return json({
|
|
12808
|
+
error: "Setting a password during vendor onboard is no longer supported. Create the vendor, then send an invite or copy the invite link so the owner can set their own password."
|
|
12809
|
+
}, {
|
|
12810
|
+
status: 400
|
|
12811
|
+
});
|
|
12812
|
+
}
|
|
12259
12813
|
const profile = parseVendorProfileFromBody(body.vendor, {
|
|
12260
12814
|
defaultRegistrationStatus: "approved"
|
|
12261
12815
|
});
|
|
@@ -12265,34 +12819,8 @@ function createVendorOnboardHandlers(config) {
|
|
|
12265
12819
|
}, {
|
|
12266
12820
|
status: 400
|
|
12267
12821
|
});
|
|
12268
|
-
const activation =
|
|
12269
|
-
const
|
|
12270
|
-
let ownerPasswordHash = null;
|
|
12271
|
-
if (activation === "password") {
|
|
12272
|
-
const plain = body.user?.password?.trim();
|
|
12273
|
-
if (!plain) {
|
|
12274
|
-
return json({
|
|
12275
|
-
error: "Password is required when activating with a set password"
|
|
12276
|
-
}, {
|
|
12277
|
-
status: 400
|
|
12278
|
-
});
|
|
12279
|
-
}
|
|
12280
|
-
if (plain.length < minPasswordLength) {
|
|
12281
|
-
return json({
|
|
12282
|
-
error: `Password must be at least ${minPasswordLength} characters`
|
|
12283
|
-
}, {
|
|
12284
|
-
status: 400
|
|
12285
|
-
});
|
|
12286
|
-
}
|
|
12287
|
-
if (!hashPassword) {
|
|
12288
|
-
return json({
|
|
12289
|
-
error: "Password hashing is not configured on the server"
|
|
12290
|
-
}, {
|
|
12291
|
-
status: 501
|
|
12292
|
-
});
|
|
12293
|
-
}
|
|
12294
|
-
ownerPasswordHash = await hashPassword(plain);
|
|
12295
|
-
}
|
|
12822
|
+
const activation = "invite";
|
|
12823
|
+
const ownerPasswordHash = null;
|
|
12296
12824
|
const slug = trimOrNull(body.vendor?.slug) || slugify(vendorName);
|
|
12297
12825
|
if (!slug) return json({
|
|
12298
12826
|
error: "Could not derive vendor slug"
|
|
@@ -12349,7 +12877,7 @@ function createVendorOnboardHandlers(config) {
|
|
|
12349
12877
|
email: userEmail,
|
|
12350
12878
|
phone: ownerPhone,
|
|
12351
12879
|
password: ownerPasswordHash,
|
|
12352
|
-
blocked:
|
|
12880
|
+
blocked: true,
|
|
12353
12881
|
groupId: ownerGroup.id,
|
|
12354
12882
|
adminAccess: true,
|
|
12355
12883
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -12366,15 +12894,14 @@ function createVendorOnboardHandlers(config) {
|
|
|
12366
12894
|
email: userEmail,
|
|
12367
12895
|
phone: ownerPhone,
|
|
12368
12896
|
password: ownerPasswordHash,
|
|
12369
|
-
blocked:
|
|
12897
|
+
blocked: true,
|
|
12370
12898
|
groupId: ownerGroup.id,
|
|
12371
12899
|
adminAccess: true
|
|
12372
12900
|
}));
|
|
12373
|
-
const
|
|
12901
|
+
const metadata = applyRotatingVendorInvite(buildVendorMetadata(null, {
|
|
12374
12902
|
ownerDesignation,
|
|
12375
12903
|
termsAccepted: true
|
|
12376
|
-
});
|
|
12377
|
-
const metadata = activation === "invite" ? applyRotatingVendorInvite(baseMetadata) : baseMetadata;
|
|
12904
|
+
}));
|
|
12378
12905
|
const vendor = await vendorRepo.save(vendorRepo.create({
|
|
12379
12906
|
name: vendorName,
|
|
12380
12907
|
slug,
|
|
@@ -12442,39 +12969,14 @@ function createVendorOnboardHandlers(config) {
|
|
|
12442
12969
|
};
|
|
12443
12970
|
});
|
|
12444
12971
|
let inviteLink;
|
|
12445
|
-
|
|
12446
|
-
if (
|
|
12447
|
-
|
|
12448
|
-
if (token) {
|
|
12449
|
-
inviteLink = buildVendorInviteLink(baseUrl, token);
|
|
12450
|
-
}
|
|
12451
|
-
}
|
|
12452
|
-
if (sendOwnerEmail) {
|
|
12453
|
-
emailSent = await trySendVendorOnboardEmails({
|
|
12454
|
-
vendorName,
|
|
12455
|
-
vendorSlug: slug,
|
|
12456
|
-
ownerName: userName,
|
|
12457
|
-
ownerEmail: userEmail,
|
|
12458
|
-
activation,
|
|
12459
|
-
inviteLink,
|
|
12460
|
-
sendToOwner: true
|
|
12461
|
-
});
|
|
12462
|
-
} else if (getCms) {
|
|
12463
|
-
await trySendVendorOnboardEmails({
|
|
12464
|
-
vendorName,
|
|
12465
|
-
vendorSlug: slug,
|
|
12466
|
-
ownerName: userName,
|
|
12467
|
-
ownerEmail: userEmail,
|
|
12468
|
-
activation,
|
|
12469
|
-
inviteLink,
|
|
12470
|
-
sendToOwner: false
|
|
12471
|
-
});
|
|
12972
|
+
const token = readVendorInviteToken(result.vendor.metadata);
|
|
12973
|
+
if (token) {
|
|
12974
|
+
inviteLink = buildVendorInviteLink(baseUrl, token);
|
|
12472
12975
|
}
|
|
12473
|
-
const message = activation === "password" ? emailSent ? "Vendor onboarded successfully. Welcome email sent to the owner." : sendOwnerEmail ? "Vendor onboarded successfully. Owner can sign in with the password you set (welcome email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Owner can sign in with the password you set." : emailSent ? "Vendor onboarded successfully. Invite email sent." : sendOwnerEmail ? "Vendor onboarded successfully. Invite link created (email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Share the invite link with the owner.";
|
|
12474
12976
|
return json({
|
|
12475
|
-
message,
|
|
12977
|
+
message: "Vendor created successfully.",
|
|
12476
12978
|
activation,
|
|
12477
|
-
emailSent,
|
|
12979
|
+
emailSent: false,
|
|
12478
12980
|
vendor: result.vendor,
|
|
12479
12981
|
user: {
|
|
12480
12982
|
id: result.user.id,
|
|
@@ -12509,9 +13011,11 @@ function createVendorOnboardHandlers(config) {
|
|
|
12509
13011
|
});
|
|
12510
13012
|
}
|
|
12511
13013
|
let sendEmail = true;
|
|
13014
|
+
let rotate = true;
|
|
12512
13015
|
try {
|
|
12513
13016
|
const body = await req.json().catch(() => ({}));
|
|
12514
13017
|
if (body.sendEmail === false) sendEmail = false;
|
|
13018
|
+
if (body.rotate === false) rotate = false;
|
|
12515
13019
|
} catch {
|
|
12516
13020
|
}
|
|
12517
13021
|
try {
|
|
@@ -12577,12 +13081,16 @@ function createVendorOnboardHandlers(config) {
|
|
|
12577
13081
|
status: 400
|
|
12578
13082
|
});
|
|
12579
13083
|
}
|
|
12580
|
-
const
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
13084
|
+
const nextMetadata = rotate ? applyRotatingVendorInvite(v.metadata) : {
|
|
13085
|
+
...v.metadata ?? {}
|
|
13086
|
+
};
|
|
13087
|
+
if (rotate || v.inviteStatus !== "pending") {
|
|
13088
|
+
await vendorRepo.update(vendorId, {
|
|
13089
|
+
metadata: nextMetadata,
|
|
13090
|
+
inviteStatus: "pending"
|
|
13091
|
+
});
|
|
13092
|
+
}
|
|
13093
|
+
const token = readVendorInviteToken(nextMetadata);
|
|
12586
13094
|
if (!token) {
|
|
12587
13095
|
return json({
|
|
12588
13096
|
error: "Failed to generate invite token"
|
|
@@ -12603,8 +13111,9 @@ function createVendorOnboardHandlers(config) {
|
|
|
12603
13111
|
sendToOwner: true
|
|
12604
13112
|
});
|
|
12605
13113
|
}
|
|
13114
|
+
const rotatedNote = rotate ? " Previous invite link is no longer valid." : "";
|
|
12606
13115
|
return json({
|
|
12607
|
-
message: emailSent ?
|
|
13116
|
+
message: emailSent ? `Invite email sent.${rotatedNote}` : sendEmail ? `Invite link ready (email may not have been sent \u2014 check email plugin).${rotatedNote}` : rotate ? "New invite link created. Previous invite link is no longer valid." : "Invite link ready.",
|
|
12608
13117
|
emailSent,
|
|
12609
13118
|
inviteLink,
|
|
12610
13119
|
inviteStatus: "pending"
|
|
@@ -17072,6 +17581,7 @@ var Brand = class {
|
|
|
17072
17581
|
__name(this, "Brand");
|
|
17073
17582
|
}
|
|
17074
17583
|
id;
|
|
17584
|
+
/** Null for platform catalog brands (`isCatalog`). */
|
|
17075
17585
|
vendorId;
|
|
17076
17586
|
name;
|
|
17077
17587
|
slug;
|
|
@@ -17079,6 +17589,8 @@ var Brand = class {
|
|
|
17079
17589
|
metadata;
|
|
17080
17590
|
description;
|
|
17081
17591
|
active;
|
|
17592
|
+
/** Platform-wide catalog brand — vendors pick these on products unless allowed to create their own. */
|
|
17593
|
+
isCatalog;
|
|
17082
17594
|
sortOrder;
|
|
17083
17595
|
createdAt;
|
|
17084
17596
|
updatedAt;
|
|
@@ -17098,8 +17610,10 @@ _ts_decorate30([
|
|
|
17098
17610
|
_ts_metadata30("design:type", Number)
|
|
17099
17611
|
], Brand.prototype, "id", void 0);
|
|
17100
17612
|
_ts_decorate30([
|
|
17101
|
-
Column("int"
|
|
17102
|
-
|
|
17613
|
+
Column("int", {
|
|
17614
|
+
nullable: true
|
|
17615
|
+
}),
|
|
17616
|
+
_ts_metadata30("design:type", Object)
|
|
17103
17617
|
], Brand.prototype, "vendorId", void 0);
|
|
17104
17618
|
_ts_decorate30([
|
|
17105
17619
|
Column("varchar"),
|
|
@@ -17133,6 +17647,12 @@ _ts_decorate30([
|
|
|
17133
17647
|
}),
|
|
17134
17648
|
_ts_metadata30("design:type", Boolean)
|
|
17135
17649
|
], Brand.prototype, "active", void 0);
|
|
17650
|
+
_ts_decorate30([
|
|
17651
|
+
Column("boolean", {
|
|
17652
|
+
default: false
|
|
17653
|
+
}),
|
|
17654
|
+
_ts_metadata30("design:type", Boolean)
|
|
17655
|
+
], Brand.prototype, "isCatalog", void 0);
|
|
17136
17656
|
_ts_decorate30([
|
|
17137
17657
|
Column("int", {
|
|
17138
17658
|
default: 0
|
|
@@ -17192,12 +17712,13 @@ _ts_decorate30([
|
|
|
17192
17712
|
], Brand.prototype, "seoId", void 0);
|
|
17193
17713
|
_ts_decorate30([
|
|
17194
17714
|
ManyToOne(() => Vendor, {
|
|
17195
|
-
onDelete: "RESTRICT"
|
|
17715
|
+
onDelete: "RESTRICT",
|
|
17716
|
+
nullable: true
|
|
17196
17717
|
}),
|
|
17197
17718
|
JoinColumn({
|
|
17198
17719
|
name: "vendorId"
|
|
17199
17720
|
}),
|
|
17200
|
-
_ts_metadata30("design:type",
|
|
17721
|
+
_ts_metadata30("design:type", Object)
|
|
17201
17722
|
], Brand.prototype, "vendor", void 0);
|
|
17202
17723
|
_ts_decorate30([
|
|
17203
17724
|
ManyToOne(() => Seo, {
|
|
@@ -17481,6 +18002,14 @@ var Product = class {
|
|
|
17481
18002
|
currencyPrices;
|
|
17482
18003
|
quantity;
|
|
17483
18004
|
status;
|
|
18005
|
+
/**
|
|
18006
|
+
* Separate from catalog status. Used when multi_vendor.requireProductApproval is on.
|
|
18007
|
+
* `approved` also forces `status = available` (live).
|
|
18008
|
+
*/
|
|
18009
|
+
approvalStatus;
|
|
18010
|
+
rejectionReason;
|
|
18011
|
+
rejectedAt;
|
|
18012
|
+
rejectedBy;
|
|
17484
18013
|
featured;
|
|
17485
18014
|
metadata;
|
|
17486
18015
|
createdAt;
|
|
@@ -17604,6 +18133,31 @@ _ts_decorate32([
|
|
|
17604
18133
|
}),
|
|
17605
18134
|
_ts_metadata32("design:type", String)
|
|
17606
18135
|
], Product.prototype, "status", void 0);
|
|
18136
|
+
_ts_decorate32([
|
|
18137
|
+
Column("varchar", {
|
|
18138
|
+
nullable: true
|
|
18139
|
+
}),
|
|
18140
|
+
_ts_metadata32("design:type", Object)
|
|
18141
|
+
], Product.prototype, "approvalStatus", void 0);
|
|
18142
|
+
_ts_decorate32([
|
|
18143
|
+
Column("text", {
|
|
18144
|
+
nullable: true
|
|
18145
|
+
}),
|
|
18146
|
+
_ts_metadata32("design:type", Object)
|
|
18147
|
+
], Product.prototype, "rejectionReason", void 0);
|
|
18148
|
+
_ts_decorate32([
|
|
18149
|
+
Column({
|
|
18150
|
+
type: "timestamptz",
|
|
18151
|
+
nullable: true
|
|
18152
|
+
}),
|
|
18153
|
+
_ts_metadata32("design:type", Object)
|
|
18154
|
+
], Product.prototype, "rejectedAt", void 0);
|
|
18155
|
+
_ts_decorate32([
|
|
18156
|
+
Column("int", {
|
|
18157
|
+
nullable: true
|
|
18158
|
+
}),
|
|
18159
|
+
_ts_metadata32("design:type", Object)
|
|
18160
|
+
], Product.prototype, "rejectedBy", void 0);
|
|
17607
18161
|
_ts_decorate32([
|
|
17608
18162
|
Column("boolean", {
|
|
17609
18163
|
default: false
|
|
@@ -20692,6 +21246,14 @@ var Event = class {
|
|
|
20692
21246
|
expectedSpeakers;
|
|
20693
21247
|
expectedParticipants;
|
|
20694
21248
|
isActive;
|
|
21249
|
+
/**
|
|
21250
|
+
* Separate from isActive. Used when events.requireEventApproval is on.
|
|
21251
|
+
* `approved` also forces `isActive = true` (live).
|
|
21252
|
+
*/
|
|
21253
|
+
approvalStatus;
|
|
21254
|
+
rejectionReason;
|
|
21255
|
+
rejectedAt;
|
|
21256
|
+
rejectedBy;
|
|
20695
21257
|
comingSoon;
|
|
20696
21258
|
bannerImageUrl;
|
|
20697
21259
|
logoUrl;
|
|
@@ -20786,6 +21348,31 @@ _ts_decorate60([
|
|
|
20786
21348
|
}),
|
|
20787
21349
|
_ts_metadata60("design:type", Boolean)
|
|
20788
21350
|
], Event.prototype, "isActive", void 0);
|
|
21351
|
+
_ts_decorate60([
|
|
21352
|
+
Column("varchar", {
|
|
21353
|
+
nullable: true
|
|
21354
|
+
}),
|
|
21355
|
+
_ts_metadata60("design:type", Object)
|
|
21356
|
+
], Event.prototype, "approvalStatus", void 0);
|
|
21357
|
+
_ts_decorate60([
|
|
21358
|
+
Column("text", {
|
|
21359
|
+
nullable: true
|
|
21360
|
+
}),
|
|
21361
|
+
_ts_metadata60("design:type", Object)
|
|
21362
|
+
], Event.prototype, "rejectionReason", void 0);
|
|
21363
|
+
_ts_decorate60([
|
|
21364
|
+
Column({
|
|
21365
|
+
type: "timestamptz",
|
|
21366
|
+
nullable: true
|
|
21367
|
+
}),
|
|
21368
|
+
_ts_metadata60("design:type", Object)
|
|
21369
|
+
], Event.prototype, "rejectedAt", void 0);
|
|
21370
|
+
_ts_decorate60([
|
|
21371
|
+
Column("int", {
|
|
21372
|
+
nullable: true
|
|
21373
|
+
}),
|
|
21374
|
+
_ts_metadata60("design:type", Object)
|
|
21375
|
+
], Event.prototype, "rejectedBy", void 0);
|
|
20789
21376
|
_ts_decorate60([
|
|
20790
21377
|
Column("boolean", {
|
|
20791
21378
|
default: false
|
|
@@ -21346,18 +21933,20 @@ _ts_decorate63([
|
|
|
21346
21933
|
], Combo.prototype, "desc", void 0);
|
|
21347
21934
|
_ts_decorate63([
|
|
21348
21935
|
Column({
|
|
21349
|
-
type: "integer"
|
|
21936
|
+
type: "integer",
|
|
21937
|
+
nullable: true
|
|
21350
21938
|
}),
|
|
21351
|
-
_ts_metadata63("design:type",
|
|
21939
|
+
_ts_metadata63("design:type", Object)
|
|
21352
21940
|
], Combo.prototype, "eventId", void 0);
|
|
21353
21941
|
_ts_decorate63([
|
|
21354
21942
|
ManyToOne(() => Event, {
|
|
21355
|
-
onDelete: "CASCADE"
|
|
21943
|
+
onDelete: "CASCADE",
|
|
21944
|
+
nullable: true
|
|
21356
21945
|
}),
|
|
21357
21946
|
JoinColumn({
|
|
21358
21947
|
name: "eventId"
|
|
21359
21948
|
}),
|
|
21360
|
-
_ts_metadata63("design:type",
|
|
21949
|
+
_ts_metadata63("design:type", Object)
|
|
21361
21950
|
], Combo.prototype, "event", void 0);
|
|
21362
21951
|
_ts_decorate63([
|
|
21363
21952
|
Column({
|
|
@@ -26670,16 +27259,24 @@ function createCmsApiHandler(config) {
|
|
|
26670
27259
|
if (pe) return pe;
|
|
26671
27260
|
if (group === "multi_vendor") {
|
|
26672
27261
|
invalidateMultiVendorCache();
|
|
27262
|
+
invalidateVendorCatalogCreateFlagsCache();
|
|
27263
|
+
invalidateRequireProductApprovalCache();
|
|
27264
|
+
invalidateRequireEventApprovalCache();
|
|
26673
27265
|
}
|
|
26674
27266
|
if (group === "events") {
|
|
26675
27267
|
invalidateEventsCache();
|
|
27268
|
+
invalidateRequireEventApprovalCache();
|
|
26676
27269
|
}
|
|
26677
27270
|
const res = await settingsHandlers.PUT(req, group);
|
|
26678
27271
|
if (group === "multi_vendor") {
|
|
26679
27272
|
invalidateMultiVendorCache();
|
|
27273
|
+
invalidateVendorCatalogCreateFlagsCache();
|
|
27274
|
+
invalidateRequireProductApprovalCache();
|
|
27275
|
+
invalidateRequireEventApprovalCache();
|
|
26680
27276
|
}
|
|
26681
27277
|
if (group === "events") {
|
|
26682
27278
|
invalidateEventsCache();
|
|
27279
|
+
invalidateRequireEventApprovalCache();
|
|
26683
27280
|
}
|
|
26684
27281
|
return res;
|
|
26685
27282
|
}
|
|
@@ -30984,4 +31581,4 @@ function createStorefrontApiHandler(config) {
|
|
|
30984
31581
|
}
|
|
30985
31582
|
__name(createStorefrontApiHandler, "createStorefrontApiHandler");
|
|
30986
31583
|
|
|
30987
|
-
export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyRotatingVendorInvite, applyVendorCustomersContactFilter, assertCaptchaOk, assertContactAllowedForVendorOrder, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRssArticleSummaryFromItem, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
|
|
31584
|
+
export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
|