@infuro/cms-core 1.0.41 → 1.0.43
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 +959 -303
- package/dist/admin.d.cts +14 -2
- package/dist/admin.d.ts +14 -2
- package/dist/admin.js +959 -303
- 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-4BG7XYSH.js → chunk-I6NH72MO.js} +1 -1
- package/dist/{chunk-EG4ICILK.js → chunk-IX7X4ILO.js} +811 -143
- package/dist/{chunk-QU554KAZ.cjs → chunk-SCPKMZG5.cjs} +862 -182
- package/dist/{chunk-ETZN4OQI.cjs → chunk-TAHX46EI.cjs} +6 -6
- 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 +1 -1
|
@@ -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);
|
|
@@ -8078,6 +8625,12 @@ function createUploadHandler(config) {
|
|
|
8078
8625
|
parentId
|
|
8079
8626
|
});
|
|
8080
8627
|
} catch (err) {
|
|
8628
|
+
console.error("[upload] File upload failed", {
|
|
8629
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8630
|
+
stack: err instanceof Error ? err.stack : void 0,
|
|
8631
|
+
name: err instanceof Error ? err.name : void 0,
|
|
8632
|
+
metadata: err && typeof err === "object" && "$metadata" in err ? err.$metadata : void 0
|
|
8633
|
+
});
|
|
8081
8634
|
return json({
|
|
8082
8635
|
error: "File upload failed"
|
|
8083
8636
|
}, {
|
|
@@ -8783,41 +9336,68 @@ function createUsersApiHandlers(config) {
|
|
|
8783
9336
|
const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
|
|
8784
9337
|
const limit = Math.min(100, parseInt(url.searchParams.get("limit") || "10", 10));
|
|
8785
9338
|
const skip = (page - 1) * limit;
|
|
8786
|
-
const
|
|
9339
|
+
const sortFieldRaw = url.searchParams.get("sortField") || "createdAt";
|
|
9340
|
+
const allowedSort = /* @__PURE__ */ new Set([
|
|
9341
|
+
"createdAt",
|
|
9342
|
+
"updatedAt",
|
|
9343
|
+
"name",
|
|
9344
|
+
"email",
|
|
9345
|
+
"id",
|
|
9346
|
+
"blocked"
|
|
9347
|
+
]);
|
|
9348
|
+
const sortField = allowedSort.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
|
|
8787
9349
|
const sortOrder = url.searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
|
|
8788
|
-
const search = url.searchParams.get("search");
|
|
8789
|
-
const
|
|
8790
|
-
|
|
8791
|
-
name: ILike(`%${search}%`),
|
|
8792
|
-
deleted: false
|
|
8793
|
-
},
|
|
8794
|
-
{
|
|
8795
|
-
email: ILike(`%${search}%`),
|
|
8796
|
-
deleted: false
|
|
8797
|
-
}
|
|
8798
|
-
] : {
|
|
9350
|
+
const search = url.searchParams.get("search")?.trim() || "";
|
|
9351
|
+
const groupName = url.searchParams.get("groupName")?.trim() || "";
|
|
9352
|
+
const qb = userRepo().createQueryBuilder("u").leftJoinAndSelect("u.group", "g").where("u.deleted = :deleted", {
|
|
8799
9353
|
deleted: false
|
|
8800
|
-
}
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
9354
|
+
}).orderBy(`u.${sortField}`, sortOrder).skip(skip).take(limit).select([
|
|
9355
|
+
"u.id",
|
|
9356
|
+
"u.name",
|
|
9357
|
+
"u.email",
|
|
9358
|
+
"u.blocked",
|
|
9359
|
+
"u.createdAt",
|
|
9360
|
+
"u.updatedAt",
|
|
9361
|
+
"u.groupId",
|
|
9362
|
+
"g.id",
|
|
9363
|
+
"g.name"
|
|
9364
|
+
]);
|
|
9365
|
+
if (search) {
|
|
9366
|
+
qb.andWhere("(u.name ILIKE :search OR u.email ILIKE :search)", {
|
|
9367
|
+
search: `%${search}%`
|
|
9368
|
+
});
|
|
9369
|
+
}
|
|
9370
|
+
if (groupName) {
|
|
9371
|
+
if (!entityMap.user_groups) {
|
|
9372
|
+
return json({
|
|
9373
|
+
total: 0,
|
|
9374
|
+
page,
|
|
9375
|
+
limit,
|
|
9376
|
+
totalPages: 0,
|
|
9377
|
+
data: []
|
|
9378
|
+
});
|
|
9379
|
+
}
|
|
9380
|
+
const groupRepo = dataSource.getRepository(entityMap.user_groups);
|
|
9381
|
+
const group = await groupRepo.findOne({
|
|
9382
|
+
where: {
|
|
9383
|
+
name: groupName,
|
|
9384
|
+
deleted: false
|
|
9385
|
+
}
|
|
9386
|
+
});
|
|
9387
|
+
if (!group) {
|
|
9388
|
+
return json({
|
|
9389
|
+
total: 0,
|
|
9390
|
+
page,
|
|
9391
|
+
limit,
|
|
9392
|
+
totalPages: 0,
|
|
9393
|
+
data: []
|
|
9394
|
+
});
|
|
9395
|
+
}
|
|
9396
|
+
qb.andWhere("u.groupId = :groupId", {
|
|
9397
|
+
groupId: Number(group.id)
|
|
9398
|
+
});
|
|
9399
|
+
}
|
|
9400
|
+
const [data, total] = await qb.getManyAndCount();
|
|
8821
9401
|
return json({
|
|
8822
9402
|
total,
|
|
8823
9403
|
page,
|
|
@@ -17034,6 +17614,7 @@ var Brand = class {
|
|
|
17034
17614
|
__name(this, "Brand");
|
|
17035
17615
|
}
|
|
17036
17616
|
id;
|
|
17617
|
+
/** Null for platform catalog brands (`isCatalog`). */
|
|
17037
17618
|
vendorId;
|
|
17038
17619
|
name;
|
|
17039
17620
|
slug;
|
|
@@ -17041,6 +17622,8 @@ var Brand = class {
|
|
|
17041
17622
|
metadata;
|
|
17042
17623
|
description;
|
|
17043
17624
|
active;
|
|
17625
|
+
/** Platform-wide catalog brand — vendors pick these on products unless allowed to create their own. */
|
|
17626
|
+
isCatalog;
|
|
17044
17627
|
sortOrder;
|
|
17045
17628
|
createdAt;
|
|
17046
17629
|
updatedAt;
|
|
@@ -17060,8 +17643,10 @@ _ts_decorate30([
|
|
|
17060
17643
|
_ts_metadata30("design:type", Number)
|
|
17061
17644
|
], Brand.prototype, "id", void 0);
|
|
17062
17645
|
_ts_decorate30([
|
|
17063
|
-
Column("int"
|
|
17064
|
-
|
|
17646
|
+
Column("int", {
|
|
17647
|
+
nullable: true
|
|
17648
|
+
}),
|
|
17649
|
+
_ts_metadata30("design:type", Object)
|
|
17065
17650
|
], Brand.prototype, "vendorId", void 0);
|
|
17066
17651
|
_ts_decorate30([
|
|
17067
17652
|
Column("varchar"),
|
|
@@ -17095,6 +17680,12 @@ _ts_decorate30([
|
|
|
17095
17680
|
}),
|
|
17096
17681
|
_ts_metadata30("design:type", Boolean)
|
|
17097
17682
|
], Brand.prototype, "active", void 0);
|
|
17683
|
+
_ts_decorate30([
|
|
17684
|
+
Column("boolean", {
|
|
17685
|
+
default: false
|
|
17686
|
+
}),
|
|
17687
|
+
_ts_metadata30("design:type", Boolean)
|
|
17688
|
+
], Brand.prototype, "isCatalog", void 0);
|
|
17098
17689
|
_ts_decorate30([
|
|
17099
17690
|
Column("int", {
|
|
17100
17691
|
default: 0
|
|
@@ -17154,12 +17745,13 @@ _ts_decorate30([
|
|
|
17154
17745
|
], Brand.prototype, "seoId", void 0);
|
|
17155
17746
|
_ts_decorate30([
|
|
17156
17747
|
ManyToOne(() => Vendor, {
|
|
17157
|
-
onDelete: "RESTRICT"
|
|
17748
|
+
onDelete: "RESTRICT",
|
|
17749
|
+
nullable: true
|
|
17158
17750
|
}),
|
|
17159
17751
|
JoinColumn({
|
|
17160
17752
|
name: "vendorId"
|
|
17161
17753
|
}),
|
|
17162
|
-
_ts_metadata30("design:type",
|
|
17754
|
+
_ts_metadata30("design:type", Object)
|
|
17163
17755
|
], Brand.prototype, "vendor", void 0);
|
|
17164
17756
|
_ts_decorate30([
|
|
17165
17757
|
ManyToOne(() => Seo, {
|
|
@@ -17443,6 +18035,14 @@ var Product = class {
|
|
|
17443
18035
|
currencyPrices;
|
|
17444
18036
|
quantity;
|
|
17445
18037
|
status;
|
|
18038
|
+
/**
|
|
18039
|
+
* Separate from catalog status. Used when multi_vendor.requireProductApproval is on.
|
|
18040
|
+
* `approved` also forces `status = available` (live).
|
|
18041
|
+
*/
|
|
18042
|
+
approvalStatus;
|
|
18043
|
+
rejectionReason;
|
|
18044
|
+
rejectedAt;
|
|
18045
|
+
rejectedBy;
|
|
17446
18046
|
featured;
|
|
17447
18047
|
metadata;
|
|
17448
18048
|
createdAt;
|
|
@@ -17566,6 +18166,31 @@ _ts_decorate32([
|
|
|
17566
18166
|
}),
|
|
17567
18167
|
_ts_metadata32("design:type", String)
|
|
17568
18168
|
], Product.prototype, "status", void 0);
|
|
18169
|
+
_ts_decorate32([
|
|
18170
|
+
Column("varchar", {
|
|
18171
|
+
nullable: true
|
|
18172
|
+
}),
|
|
18173
|
+
_ts_metadata32("design:type", Object)
|
|
18174
|
+
], Product.prototype, "approvalStatus", void 0);
|
|
18175
|
+
_ts_decorate32([
|
|
18176
|
+
Column("text", {
|
|
18177
|
+
nullable: true
|
|
18178
|
+
}),
|
|
18179
|
+
_ts_metadata32("design:type", Object)
|
|
18180
|
+
], Product.prototype, "rejectionReason", void 0);
|
|
18181
|
+
_ts_decorate32([
|
|
18182
|
+
Column({
|
|
18183
|
+
type: "timestamptz",
|
|
18184
|
+
nullable: true
|
|
18185
|
+
}),
|
|
18186
|
+
_ts_metadata32("design:type", Object)
|
|
18187
|
+
], Product.prototype, "rejectedAt", void 0);
|
|
18188
|
+
_ts_decorate32([
|
|
18189
|
+
Column("int", {
|
|
18190
|
+
nullable: true
|
|
18191
|
+
}),
|
|
18192
|
+
_ts_metadata32("design:type", Object)
|
|
18193
|
+
], Product.prototype, "rejectedBy", void 0);
|
|
17569
18194
|
_ts_decorate32([
|
|
17570
18195
|
Column("boolean", {
|
|
17571
18196
|
default: false
|
|
@@ -20654,6 +21279,14 @@ var Event = class {
|
|
|
20654
21279
|
expectedSpeakers;
|
|
20655
21280
|
expectedParticipants;
|
|
20656
21281
|
isActive;
|
|
21282
|
+
/**
|
|
21283
|
+
* Separate from isActive. Used when events.requireEventApproval is on.
|
|
21284
|
+
* `approved` also forces `isActive = true` (live).
|
|
21285
|
+
*/
|
|
21286
|
+
approvalStatus;
|
|
21287
|
+
rejectionReason;
|
|
21288
|
+
rejectedAt;
|
|
21289
|
+
rejectedBy;
|
|
20657
21290
|
comingSoon;
|
|
20658
21291
|
bannerImageUrl;
|
|
20659
21292
|
logoUrl;
|
|
@@ -20748,6 +21381,31 @@ _ts_decorate60([
|
|
|
20748
21381
|
}),
|
|
20749
21382
|
_ts_metadata60("design:type", Boolean)
|
|
20750
21383
|
], Event.prototype, "isActive", void 0);
|
|
21384
|
+
_ts_decorate60([
|
|
21385
|
+
Column("varchar", {
|
|
21386
|
+
nullable: true
|
|
21387
|
+
}),
|
|
21388
|
+
_ts_metadata60("design:type", Object)
|
|
21389
|
+
], Event.prototype, "approvalStatus", void 0);
|
|
21390
|
+
_ts_decorate60([
|
|
21391
|
+
Column("text", {
|
|
21392
|
+
nullable: true
|
|
21393
|
+
}),
|
|
21394
|
+
_ts_metadata60("design:type", Object)
|
|
21395
|
+
], Event.prototype, "rejectionReason", void 0);
|
|
21396
|
+
_ts_decorate60([
|
|
21397
|
+
Column({
|
|
21398
|
+
type: "timestamptz",
|
|
21399
|
+
nullable: true
|
|
21400
|
+
}),
|
|
21401
|
+
_ts_metadata60("design:type", Object)
|
|
21402
|
+
], Event.prototype, "rejectedAt", void 0);
|
|
21403
|
+
_ts_decorate60([
|
|
21404
|
+
Column("int", {
|
|
21405
|
+
nullable: true
|
|
21406
|
+
}),
|
|
21407
|
+
_ts_metadata60("design:type", Object)
|
|
21408
|
+
], Event.prototype, "rejectedBy", void 0);
|
|
20751
21409
|
_ts_decorate60([
|
|
20752
21410
|
Column("boolean", {
|
|
20753
21411
|
default: false
|
|
@@ -21308,18 +21966,20 @@ _ts_decorate63([
|
|
|
21308
21966
|
], Combo.prototype, "desc", void 0);
|
|
21309
21967
|
_ts_decorate63([
|
|
21310
21968
|
Column({
|
|
21311
|
-
type: "integer"
|
|
21969
|
+
type: "integer",
|
|
21970
|
+
nullable: true
|
|
21312
21971
|
}),
|
|
21313
|
-
_ts_metadata63("design:type",
|
|
21972
|
+
_ts_metadata63("design:type", Object)
|
|
21314
21973
|
], Combo.prototype, "eventId", void 0);
|
|
21315
21974
|
_ts_decorate63([
|
|
21316
21975
|
ManyToOne(() => Event, {
|
|
21317
|
-
onDelete: "CASCADE"
|
|
21976
|
+
onDelete: "CASCADE",
|
|
21977
|
+
nullable: true
|
|
21318
21978
|
}),
|
|
21319
21979
|
JoinColumn({
|
|
21320
21980
|
name: "eventId"
|
|
21321
21981
|
}),
|
|
21322
|
-
_ts_metadata63("design:type",
|
|
21982
|
+
_ts_metadata63("design:type", Object)
|
|
21323
21983
|
], Combo.prototype, "event", void 0);
|
|
21324
21984
|
_ts_decorate63([
|
|
21325
21985
|
Column({
|
|
@@ -26632,16 +27292,24 @@ function createCmsApiHandler(config) {
|
|
|
26632
27292
|
if (pe) return pe;
|
|
26633
27293
|
if (group === "multi_vendor") {
|
|
26634
27294
|
invalidateMultiVendorCache();
|
|
27295
|
+
invalidateVendorCatalogCreateFlagsCache();
|
|
27296
|
+
invalidateRequireProductApprovalCache();
|
|
27297
|
+
invalidateRequireEventApprovalCache();
|
|
26635
27298
|
}
|
|
26636
27299
|
if (group === "events") {
|
|
26637
27300
|
invalidateEventsCache();
|
|
27301
|
+
invalidateRequireEventApprovalCache();
|
|
26638
27302
|
}
|
|
26639
27303
|
const res = await settingsHandlers.PUT(req, group);
|
|
26640
27304
|
if (group === "multi_vendor") {
|
|
26641
27305
|
invalidateMultiVendorCache();
|
|
27306
|
+
invalidateVendorCatalogCreateFlagsCache();
|
|
27307
|
+
invalidateRequireProductApprovalCache();
|
|
27308
|
+
invalidateRequireEventApprovalCache();
|
|
26642
27309
|
}
|
|
26643
27310
|
if (group === "events") {
|
|
26644
27311
|
invalidateEventsCache();
|
|
27312
|
+
invalidateRequireEventApprovalCache();
|
|
26645
27313
|
}
|
|
26646
27314
|
return res;
|
|
26647
27315
|
}
|
|
@@ -30946,4 +31614,4 @@ function createStorefrontApiHandler(config) {
|
|
|
30946
31614
|
}
|
|
30947
31615
|
__name(createStorefrontApiHandler, "createStorefrontApiHandler");
|
|
30948
31616
|
|
|
30949
|
-
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 };
|
|
31617
|
+
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 };
|