@infuro/cms-core 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +739 -724
  2. package/dist/admin.cjs +1 -1
  3. package/dist/admin.cjs.map +1 -1
  4. package/dist/admin.js +1 -1
  5. package/dist/admin.js.map +1 -1
  6. package/dist/api.cjs +132 -57
  7. package/dist/api.cjs.map +1 -1
  8. package/dist/api.d.cts +1 -1
  9. package/dist/api.d.ts +1 -1
  10. package/dist/api.js +132 -57
  11. package/dist/api.js.map +1 -1
  12. package/dist/auth.cjs.map +1 -1
  13. package/dist/auth.js.map +1 -1
  14. package/dist/cli.cjs +21 -6
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.js +21 -6
  17. package/dist/cli.js.map +1 -1
  18. package/dist/hooks.cjs.map +1 -1
  19. package/dist/hooks.js.map +1 -1
  20. package/dist/{index-h42MoUNq.d.cts → index-D2C1O9b4.d.cts} +8 -1
  21. package/dist/{index-C85X7cc7.d.ts → index-GMn7-9PX.d.ts} +8 -1
  22. package/dist/index.cjs +135 -57
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +135 -57
  27. package/dist/index.js.map +1 -1
  28. package/dist/migrations/1772178563554-InitialSchema.ts +304 -304
  29. package/dist/migrations/1772178563555-ChatAndKnowledgeBase.ts +55 -55
  30. package/dist/migrations/1772178563556-KnowledgeBaseVector.ts +16 -16
  31. package/dist/migrations/1774300000000-RbacSeedGroupsAndPermissionUnique.ts +24 -24
  32. package/dist/migrations/1774300000001-SeedAdministratorUsersPermission.ts +35 -35
  33. package/dist/migrations/1774400000000-CustomerAdminAccessContactUser.ts +37 -37
  34. package/dist/migrations/1774400000001-StorefrontCartWishlist.ts +100 -100
  35. package/dist/migrations/1774400000002-WishlistGuestId.ts +29 -29
  36. package/dist/migrations/1774500000000-ProductCollectionHsn.ts +15 -15
  37. package/dist/migrations/1774600000000-OrderKindParentOrderNumber.ts +36 -36
  38. package/dist/migrations/1774800000000-OtpChallengesUserPhone.ts +41 -41
  39. package/dist/migrations/1774900000000-MessageTemplates.ts +39 -39
  40. package/dist/migrations/1775000000000-ProductUomTypeOrderItemSnapshots.ts +29 -29
  41. package/dist/migrations/1775200000000-MediaDriveFolders.ts +38 -38
  42. package/dist/migrations/README.md +3 -3
  43. package/dist/theme.cjs.map +1 -1
  44. package/dist/theme.js.map +1 -1
  45. package/package.json +13 -6
  46. package/src/admin/admin.css +72 -72
package/dist/api.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as CmsApiHandlerConfig, f as CmsGetter, g as CrudHandlerOptions, D as DashboardStatsConfig, h as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, i as FormBySlugConfig, G as GetPublicSettingsGroupConfig, j as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, k as SetPasswordConfig, l as SettingsApiConfig, n as StorefrontApiConfig, o as StorefrontOtpFlags, U as UploadHandlerConfig, p as UserAuthApiConfig, q as UserAvatarConfig, r as UserProfileConfig, s as UsersApiConfig, t as createAnalyticsHandlers, u as createBlogBySlugHandler, v as createChangePasswordHandler, w as createCmsApiHandler, x as createCrudByIdHandler, y as createCrudHandler, z as createDashboardStatsHandler, H as createEcommerceAnalyticsHandler, J as createForgotPasswordHandler, K as createFormBySlugHandler, L as createInviteAcceptHandler, M as createMediaZipExtractHandler, N as createSetPasswordHandler, P as createSettingsApiHandlers, Q as createStorefrontApiHandler, R as createUploadHandler, V as createUserAuthApiRouter, W as createUserAvatarHandler, X as createUserProfileHandler, Y as createUsersApiHandlers, _ as getPublicSettingsGroup } from './index-h42MoUNq.cjs';
1
+ export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as CmsApiHandlerConfig, f as CmsGetter, g as CrudHandlerOptions, D as DashboardStatsConfig, h as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, i as FormBySlugConfig, G as GetPublicSettingsGroupConfig, j as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, k as SetPasswordConfig, l as SettingsApiConfig, n as StorefrontApiConfig, o as StorefrontOtpFlags, U as UploadHandlerConfig, p as UserAuthApiConfig, q as UserAvatarConfig, r as UserProfileConfig, s as UsersApiConfig, t as createAnalyticsHandlers, u as createBlogBySlugHandler, v as createChangePasswordHandler, w as createCmsApiHandler, x as createCrudByIdHandler, y as createCrudHandler, z as createDashboardStatsHandler, H as createEcommerceAnalyticsHandler, J as createForgotPasswordHandler, K as createFormBySlugHandler, L as createInviteAcceptHandler, M as createMediaZipExtractHandler, N as createSetPasswordHandler, P as createSettingsApiHandlers, Q as createStorefrontApiHandler, R as createUploadHandler, V as createUserAuthApiRouter, W as createUserAvatarHandler, X as createUserProfileHandler, Y as createUsersApiHandlers, _ as getPublicSettingsGroup } from './index-D2C1O9b4.cjs';
2
2
  import 'typeorm';
3
3
  import './helpers-dlrF_49e.cjs';
package/dist/api.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as CmsApiHandlerConfig, f as CmsGetter, g as CrudHandlerOptions, D as DashboardStatsConfig, h as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, i as FormBySlugConfig, G as GetPublicSettingsGroupConfig, j as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, k as SetPasswordConfig, l as SettingsApiConfig, n as StorefrontApiConfig, o as StorefrontOtpFlags, U as UploadHandlerConfig, p as UserAuthApiConfig, q as UserAvatarConfig, r as UserProfileConfig, s as UsersApiConfig, t as createAnalyticsHandlers, u as createBlogBySlugHandler, v as createChangePasswordHandler, w as createCmsApiHandler, x as createCrudByIdHandler, y as createCrudHandler, z as createDashboardStatsHandler, H as createEcommerceAnalyticsHandler, J as createForgotPasswordHandler, K as createFormBySlugHandler, L as createInviteAcceptHandler, M as createMediaZipExtractHandler, N as createSetPasswordHandler, P as createSettingsApiHandlers, Q as createStorefrontApiHandler, R as createUploadHandler, V as createUserAuthApiRouter, W as createUserAvatarHandler, X as createUserProfileHandler, Y as createUsersApiHandlers, _ as getPublicSettingsGroup } from './index-C85X7cc7.js';
1
+ export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as CmsApiHandlerConfig, f as CmsGetter, g as CrudHandlerOptions, D as DashboardStatsConfig, h as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, i as FormBySlugConfig, G as GetPublicSettingsGroupConfig, j as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, k as SetPasswordConfig, l as SettingsApiConfig, n as StorefrontApiConfig, o as StorefrontOtpFlags, U as UploadHandlerConfig, p as UserAuthApiConfig, q as UserAvatarConfig, r as UserProfileConfig, s as UsersApiConfig, t as createAnalyticsHandlers, u as createBlogBySlugHandler, v as createChangePasswordHandler, w as createCmsApiHandler, x as createCrudByIdHandler, y as createCrudHandler, z as createDashboardStatsHandler, H as createEcommerceAnalyticsHandler, J as createForgotPasswordHandler, K as createFormBySlugHandler, L as createInviteAcceptHandler, M as createMediaZipExtractHandler, N as createSetPasswordHandler, P as createSettingsApiHandlers, Q as createStorefrontApiHandler, R as createUploadHandler, V as createUserAuthApiRouter, W as createUserAvatarHandler, X as createUserProfileHandler, Y as createUsersApiHandlers, _ as getPublicSettingsGroup } from './index-GMn7-9PX.js';
2
2
  import 'typeorm';
3
3
  import './helpers-dlrF_49e.js';
package/dist/api.js CHANGED
@@ -520,6 +520,28 @@ function buildSearchWhereClause(repo, search) {
520
520
  if (ors.length === 0) return {};
521
521
  return ors.length === 1 ? ors[0] : ors;
522
522
  }
523
+ function entityHasSoftDelete(repo) {
524
+ return repo.metadata.columns.some((c) => c.propertyName === "deleted");
525
+ }
526
+ function mergeDeletedFalseWhere(repo, where) {
527
+ if (!entityHasSoftDelete(repo)) return where;
528
+ const d = { deleted: false };
529
+ if (Array.isArray(where)) {
530
+ if (where.length === 0) return [d];
531
+ return where.map((w) => ({ ...w, ...d }));
532
+ }
533
+ return Object.keys(where).length > 0 ? { ...where, ...d } : d;
534
+ }
535
+ function buildSoftDeletePayload(meta, deletedBy) {
536
+ const payload = { deleted: true };
537
+ if (meta.columns.some((c) => c.propertyName === "deletedAt")) {
538
+ payload.deletedAt = /* @__PURE__ */ new Date();
539
+ }
540
+ if (deletedBy != null && meta.columns.some((c) => c.propertyName === "deletedBy")) {
541
+ payload.deletedBy = deletedBy;
542
+ }
543
+ return payload;
544
+ }
523
545
  function makeContactErpSync(dataSource, entityMap, getCms) {
524
546
  return async function syncContactRowToErp(row) {
525
547
  if (!getCms) return;
@@ -544,10 +566,11 @@ function createCrudHandler(dataSource, entityMap, options) {
544
566
  async function authz(req, resource, action) {
545
567
  const authError = await requireAuth(req);
546
568
  if (authError) return authError;
547
- if (reqPerm) {
548
- const pe = await reqPerm(req, resource, action);
549
- if (pe) return pe;
569
+ if (!reqPerm) {
570
+ return json({ error: "Forbidden", reason: "entity_rbac_required", entity: resource, action }, { status: 403 });
550
571
  }
572
+ const pe = await reqPerm(req, resource, action);
573
+ if (pe) return pe;
551
574
  return null;
552
575
  }
553
576
  return {
@@ -583,7 +606,7 @@ function createCrudHandler(dataSource, entityMap, options) {
583
606
  return json({ total: 0, page, limit, totalPages: 0, data: [] });
584
607
  }
585
608
  }
586
- const qb = repo2.createQueryBuilder("order").leftJoinAndSelect("order.contact", "contact").leftJoinAndSelect("order.items", "items").leftJoinAndSelect("items.product", "product").leftJoinAndSelect("product.collection", "collection").orderBy(`order.${sortField2}`, sortOrderOrders).skip(skip).take(limit);
609
+ const qb = repo2.createQueryBuilder("order").leftJoinAndSelect("order.contact", "contact").leftJoinAndSelect("order.items", "items").leftJoinAndSelect("items.product", "product").leftJoinAndSelect("product.collection", "collection").andWhere("order.deleted = :orderDel", { orderDel: false }).orderBy(`order.${sortField2}`, sortOrderOrders).skip(skip).take(limit);
587
610
  if (search && typeof search === "string" && search.trim()) {
588
611
  const term = `%${search.trim()}%`;
589
612
  qb.andWhere(
@@ -621,7 +644,7 @@ function createCrudHandler(dataSource, entityMap, options) {
621
644
  const dateTo = searchParams.get("dateTo")?.trim();
622
645
  const methodFilter = searchParams.get("method")?.trim();
623
646
  const orderNumberParam = searchParams.get("orderNumber")?.trim();
624
- const qb = repo2.createQueryBuilder("payment").leftJoinAndSelect("payment.order", "ord").leftJoinAndSelect("ord.contact", "orderContact").leftJoinAndSelect("payment.contact", "contact").orderBy(`payment.${sortField2}`, sortOrderPayments).skip(skip).take(limit);
647
+ const qb = repo2.createQueryBuilder("payment").leftJoinAndSelect("payment.order", "ord").leftJoinAndSelect("ord.contact", "orderContact").leftJoinAndSelect("payment.contact", "contact").andWhere("payment.deleted = :payDel", { payDel: false }).orderBy(`payment.${sortField2}`, sortOrderPayments).skip(skip).take(limit);
625
648
  if (search && typeof search === "string" && search.trim()) {
626
649
  const term = `%${search.trim()}%`;
627
650
  qb.andWhere(
@@ -652,7 +675,7 @@ function createCrudHandler(dataSource, entityMap, options) {
652
675
  const repo2 = dataSource.getRepository(entity);
653
676
  const statusFilter = searchParams.get("status")?.trim();
654
677
  const inventory = searchParams.get("inventory")?.trim();
655
- const productWhere = {};
678
+ const productWhere = { deleted: false };
656
679
  if (statusFilter) productWhere.status = statusFilter;
657
680
  if (inventory === "in_stock") productWhere.quantity = MoreThan(0);
658
681
  if (inventory === "out_of_stock") productWhere.quantity = 0;
@@ -675,7 +698,7 @@ function createCrudHandler(dataSource, entityMap, options) {
675
698
  const typeFilter2 = searchParams.get("type")?.trim();
676
699
  const orderIdParam = searchParams.get("orderId")?.trim();
677
700
  const includeSummary = searchParams.get("includeSummary") === "1";
678
- const qb = repo2.createQueryBuilder("contact").orderBy(`contact.${sortField2}`, sortOrderContacts).skip(skip).take(limit);
701
+ const qb = repo2.createQueryBuilder("contact").andWhere("contact.deleted = :contactDel", { contactDel: false }).orderBy(`contact.${sortField2}`, sortOrderContacts).skip(skip).take(limit);
679
702
  if (search && typeof search === "string" && search.trim()) {
680
703
  const term = `%${search.trim()}%`;
681
704
  qb.andWhere("(contact.name ILIKE :term OR contact.email ILIKE :term OR contact.phone ILIKE :term)", { term });
@@ -716,9 +739,9 @@ function createCrudHandler(dataSource, entityMap, options) {
716
739
  if (parentIdParam != null && parentIdParam !== "") {
717
740
  const n = Number(parentIdParam);
718
741
  if (!Number.isFinite(n)) return json({ error: "Invalid parentId" }, { status: 400 });
719
- qb.where("m.parentId = :pid", { pid: n });
742
+ qb.where("m.deleted = :mediaDel AND m.parentId = :pid", { mediaDel: false, pid: n });
720
743
  } else {
721
- qb.where("m.parentId IS NULL");
744
+ qb.where("m.deleted = :mediaDel AND m.parentId IS NULL", { mediaDel: false });
722
745
  }
723
746
  if (search && typeof search === "string" && search.trim()) {
724
747
  qb.andWhere("m.filename ILIKE :search", { search: `%${search.trim()}%` });
@@ -762,6 +785,7 @@ function createCrudHandler(dataSource, entityMap, options) {
762
785
  where = extraWhere;
763
786
  }
764
787
  }
788
+ where = mergeDeletedFalseWhere(repo, where);
765
789
  const [data, total] = await repo.findAndCount({
766
790
  skip,
767
791
  take: limit,
@@ -929,15 +953,16 @@ function createCrudHandler(dataSource, entityMap, options) {
929
953
  };
930
954
  }
931
955
  function createCrudByIdHandler(dataSource, entityMap, options) {
932
- const { requireAuth, json, requireEntityPermission: reqPerm, getCms } = options;
956
+ const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId } = options;
933
957
  const syncContactRowToErp = makeContactErpSync(dataSource, entityMap, getCms);
934
958
  async function authz(req, resource, action) {
935
959
  const authError = await requireAuth(req);
936
960
  if (authError) return authError;
937
- if (reqPerm) {
938
- const pe = await reqPerm(req, resource, action);
939
- if (pe) return pe;
961
+ if (!reqPerm) {
962
+ return json({ error: "Forbidden", reason: "entity_rbac_required", entity: resource, action }, { status: 403 });
940
963
  }
964
+ const pe = await reqPerm(req, resource, action);
965
+ if (pe) return pe;
941
966
  return null;
942
967
  }
943
968
  return {
@@ -949,7 +974,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
949
974
  const repo = dataSource.getRepository(entity);
950
975
  if (resource === "orders") {
951
976
  const order = await repo.findOne({
952
- where: { id: Number(id) },
977
+ where: { id: Number(id), deleted: false },
953
978
  relations: ["contact", "billingAddress", "shippingAddress", "items", "items.product", "items.product.collection", "payments"]
954
979
  });
955
980
  if (!order) return json({ message: "Not found" }, { status: 404 });
@@ -961,7 +986,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
961
986
  }
962
987
  if (resource === "contacts") {
963
988
  const contact = await repo.findOne({
964
- where: { id: Number(id) },
989
+ where: { id: Number(id), deleted: false },
965
990
  relations: ["form_submissions", "form_submissions.form", "orders", "payments", "addresses"]
966
991
  });
967
992
  if (!contact) return json({ message: "Not found" }, { status: 404 });
@@ -983,7 +1008,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
983
1008
  }
984
1009
  if (resource === "payments") {
985
1010
  const payment = await repo.findOne({
986
- where: { id: Number(id) },
1011
+ where: { id: Number(id), deleted: false },
987
1012
  relations: ["order", "order.contact", "contact"]
988
1013
  });
989
1014
  if (!payment) return json({ message: "Not found" }, { status: 404 });
@@ -991,12 +1016,13 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
991
1016
  }
992
1017
  if (resource === "blogs") {
993
1018
  const blog = await repo.findOne({
994
- where: { id: Number(id) },
1019
+ where: { id: Number(id), deleted: false },
995
1020
  relations: ["category", "seo", "tags"]
996
1021
  });
997
1022
  return blog ? json(blog) : json({ message: "Not found" }, { status: 404 });
998
1023
  }
999
- const item = await repo.findOne({ where: { id: Number(id) } });
1024
+ const idWhere = entityHasSoftDelete(repo) ? { id: Number(id), deleted: false } : { id: Number(id) };
1025
+ const item = await repo.findOne({ where: idWhere });
1000
1026
  return item ? json(item) : json({ message: "Not found" }, { status: 404 });
1001
1027
  },
1002
1028
  async PUT(req, resource, id) {
@@ -1008,7 +1034,9 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
1008
1034
  const repo = dataSource.getRepository(entity);
1009
1035
  const numericId = Number(id);
1010
1036
  if (resource === "blogs" && rawBody && typeof rawBody === "object" && entityMap.categories && entityMap.seos && entityMap.tags) {
1011
- const existing = await repo.findOne({ where: { id: numericId } });
1037
+ const existing = await repo.findOne({
1038
+ where: { id: numericId, deleted: false }
1039
+ });
1012
1040
  if (!existing) return json({ message: "Not found" }, { status: 404 });
1013
1041
  const updatePayload2 = pickColumnUpdates(repo, rawBody);
1014
1042
  if ("category" in rawBody) {
@@ -1084,6 +1112,12 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
1084
1112
  });
1085
1113
  return updated2 ? json(updated2) : json({ message: "Not found" }, { status: 404 });
1086
1114
  }
1115
+ if (entityHasSoftDelete(repo)) {
1116
+ const cur = await repo.findOne({
1117
+ where: { id: numericId, deleted: false }
1118
+ });
1119
+ if (!cur) return json({ message: "Not found" }, { status: 404 });
1120
+ }
1087
1121
  const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
1088
1122
  if (resource === "media") {
1089
1123
  const u = updatePayload;
@@ -1110,7 +1144,24 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
1110
1144
  const entity = entityMap[resource];
1111
1145
  if (!entity) return json({ error: "Invalid resource" }, { status: 400 });
1112
1146
  const repo = dataSource.getRepository(entity);
1113
- const result = await repo.delete(Number(id));
1147
+ const numericId = Number(id);
1148
+ if (entityHasSoftDelete(repo)) {
1149
+ const existing = await repo.findOne({
1150
+ where: { id: numericId, deleted: false }
1151
+ });
1152
+ if (!existing) return json({ message: "Not found" }, { status: 404 });
1153
+ let deletedBy = null;
1154
+ if (getDeletedByUserId) {
1155
+ try {
1156
+ deletedBy = await getDeletedByUserId(req);
1157
+ } catch {
1158
+ deletedBy = null;
1159
+ }
1160
+ }
1161
+ await repo.update(numericId, buildSoftDeletePayload(repo.metadata, deletedBy));
1162
+ return json({ message: "Deleted successfully" }, { status: 200 });
1163
+ }
1164
+ const result = await repo.delete(numericId);
1114
1165
  if (result.affected === 0) return json({ message: "Not found" }, { status: 404 });
1115
1166
  return json({ message: "Deleted successfully" }, { status: 200 });
1116
1167
  }
@@ -2218,7 +2269,7 @@ function createFormSubmissionHandler(config) {
2218
2269
  };
2219
2270
  }
2220
2271
  function createUsersApiHandlers(config) {
2221
- const { dataSource, entityMap, json, requireAuth, requireEntityPermission, baseUrl, getCms, getCompanyDetails } = config;
2272
+ const { dataSource, entityMap, json, requireAuth, requireEntityPermission, baseUrl, getCms, getCompanyDetails, getSessionUser } = config;
2222
2273
  async function trySendInviteEmail(toEmail, inviteLink, inviteeName) {
2223
2274
  if (!getCms) return;
2224
2275
  try {
@@ -2254,7 +2305,10 @@ function createUsersApiHandlers(config) {
2254
2305
  const sortField = url.searchParams.get("sortField") || "createdAt";
2255
2306
  const sortOrder = url.searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
2256
2307
  const search = url.searchParams.get("search");
2257
- const where = search ? [{ name: ILike2(`%${search}%`) }, { email: ILike2(`%${search}%`) }] : {};
2308
+ const where = search ? [
2309
+ { name: ILike2(`%${search}%`), deleted: false },
2310
+ { email: ILike2(`%${search}%`), deleted: false }
2311
+ ] : { deleted: false };
2258
2312
  const [data, total] = await userRepo().findAndCount({
2259
2313
  skip,
2260
2314
  take: limit,
@@ -2319,7 +2373,7 @@ function createUsersApiHandlers(config) {
2319
2373
  }
2320
2374
  try {
2321
2375
  const user = await userRepo().findOne({
2322
- where: { id: parseInt(id, 10) },
2376
+ where: { id: parseInt(id, 10), deleted: false },
2323
2377
  relations: ["group"],
2324
2378
  select: ["id", "name", "email", "blocked", "createdAt", "updatedAt", "groupId"]
2325
2379
  });
@@ -2337,11 +2391,14 @@ function createUsersApiHandlers(config) {
2337
2391
  if (pe) return pe;
2338
2392
  }
2339
2393
  try {
2394
+ const uid = parseInt(id, 10);
2395
+ const existing = await userRepo().findOne({ where: { id: uid, deleted: false } });
2396
+ if (!existing) return json({ error: "Not found" }, { status: 404 });
2340
2397
  const body = await req.json();
2341
2398
  const { password: _p, ...safe } = body;
2342
- await userRepo().update(parseInt(id, 10), safe);
2399
+ await userRepo().update(uid, safe);
2343
2400
  const updated = await userRepo().findOne({
2344
- where: { id: parseInt(id, 10) },
2401
+ where: { id: uid, deleted: false },
2345
2402
  relations: ["group"],
2346
2403
  select: ["id", "name", "email", "blocked", "createdAt", "updatedAt", "groupId"]
2347
2404
  });
@@ -2358,8 +2415,23 @@ function createUsersApiHandlers(config) {
2358
2415
  if (pe) return pe;
2359
2416
  }
2360
2417
  try {
2361
- const r = await userRepo().delete(parseInt(id, 10));
2362
- if (r.affected === 0) return json({ error: "User not found" }, { status: 404 });
2418
+ const uid = parseInt(id, 10);
2419
+ const existing = await userRepo().findOne({ where: { id: uid, deleted: false } });
2420
+ if (!existing) return json({ error: "User not found" }, { status: 404 });
2421
+ let deletedBy = null;
2422
+ if (getSessionUser) {
2423
+ try {
2424
+ const u = await getSessionUser();
2425
+ if (u?.id) {
2426
+ const n = Number(u.id);
2427
+ if (Number.isFinite(n)) deletedBy = n;
2428
+ }
2429
+ } catch {
2430
+ }
2431
+ }
2432
+ const payload = { deleted: true, deletedAt: /* @__PURE__ */ new Date() };
2433
+ if (deletedBy != null) payload.deletedBy = deletedBy;
2434
+ await userRepo().update(uid, payload);
2363
2435
  return json({ message: "User deleted successfully" });
2364
2436
  } catch {
2365
2437
  return json({ error: "Server Error" }, { status: 500 });
@@ -2373,7 +2445,10 @@ function createUsersApiHandlers(config) {
2373
2445
  if (pe) return pe;
2374
2446
  }
2375
2447
  try {
2376
- const user = await userRepo().findOne({ where: { id: parseInt(id, 10) }, select: ["email", "name"] });
2448
+ const user = await userRepo().findOne({
2449
+ where: { id: parseInt(id, 10), deleted: false },
2450
+ select: ["email", "name"]
2451
+ });
2377
2452
  if (!user) return json({ error: "User not found" }, { status: 404 });
2378
2453
  const emailToken = Buffer.from(user.email).toString("base64");
2379
2454
  const inviteLink = `${baseUrl}/admin/invite?token=${emailToken}`;
@@ -2996,9 +3071,10 @@ function createCmsApiHandler(config) {
2996
3071
  userProfile,
2997
3072
  settings: settingsConfig,
2998
3073
  chat: chatConfig,
2999
- requireEntityPermission: reqEntityPerm,
3074
+ requireEntityPermission: userRequireEntityPermission,
3000
3075
  getSessionUser
3001
3076
  } = config;
3077
+ const requireEntityPermissionEffective = userRequireEntityPermission ?? (async (_req, entity, action) => config.json({ error: "Forbidden", reason: "entity_rbac_required", entity, action }, { status: 403 }));
3002
3078
  const analytics = analyticsConfig ?? (getCms ? {
3003
3079
  json: config.json,
3004
3080
  requireAuth: async () => null,
@@ -3036,12 +3112,20 @@ function createCmsApiHandler(config) {
3036
3112
  const crudOpts = {
3037
3113
  requireAuth: config.requireAuth,
3038
3114
  json: config.json,
3039
- requireEntityPermission: reqEntityPerm,
3040
- getCms
3115
+ requireEntityPermission: requireEntityPermissionEffective,
3116
+ getCms,
3117
+ ...getSessionUser ? {
3118
+ getDeletedByUserId: async () => {
3119
+ const u = await getSessionUser();
3120
+ if (!u?.id) return null;
3121
+ const n = Number(u.id);
3122
+ return Number.isFinite(n) ? n : null;
3123
+ }
3124
+ } : {}
3041
3125
  };
3042
3126
  const crud = createCrudHandler(dataSource, entityMap, crudOpts);
3043
3127
  const crudById = createCrudByIdHandler(dataSource, entityMap, crudOpts);
3044
- const mergePerm = (c) => !c ? void 0 : reqEntityPerm ? { ...c, requireEntityPermission: reqEntityPerm } : c;
3128
+ const mergePerm = (c) => !c ? void 0 : { ...c, requireEntityPermission: requireEntityPermissionEffective };
3045
3129
  const adminRoles = getSessionUser && createAdminRolesHandlers({
3046
3130
  dataSource,
3047
3131
  entityMap,
@@ -3057,12 +3141,7 @@ function createCmsApiHandler(config) {
3057
3141
  json: config.json,
3058
3142
  requireAuth: config.requireAuth
3059
3143
  }
3060
- ) ?? {
3061
- dataSource,
3062
- entityMap,
3063
- json: config.json,
3064
- requireAuth: config.requireAuth
3065
- };
3144
+ );
3066
3145
  const ecommerceAnalyticsGet = createEcommerceAnalyticsHandler(ecommerceAnalyticsResolved);
3067
3146
  const analyticsHandlers = analytics ? createAnalyticsHandlers(analytics) : null;
3068
3147
  const uploadMerged = upload ? {
@@ -3081,7 +3160,11 @@ function createCmsApiHandler(config) {
3081
3160
  const usersApiMerged = usersApi && getCms ? {
3082
3161
  ...usersApi,
3083
3162
  getCms: usersApi.getCms ?? getCms,
3084
- getCompanyDetails: usersApi.getCompanyDetails ?? config.getCompanyDetails
3163
+ getCompanyDetails: usersApi.getCompanyDetails ?? config.getCompanyDetails,
3164
+ ...getSessionUser ? { getSessionUser: usersApi.getSessionUser ?? getSessionUser } : {}
3165
+ } : usersApi ? {
3166
+ ...usersApi,
3167
+ ...getSessionUser ? { getSessionUser: usersApi.getSessionUser ?? getSessionUser } : {}
3085
3168
  } : usersApi;
3086
3169
  const usersHandlers = usersApiMerged ? createUsersApiHandlers(mergePerm(usersApiMerged) ?? usersApiMerged) : null;
3087
3170
  const avatarPost = userAvatar ? createUserAvatarHandler(userAvatar) : null;
@@ -3092,7 +3175,7 @@ function createCmsApiHandler(config) {
3092
3175
  entityMap,
3093
3176
  json: config.json,
3094
3177
  requireAuth: config.requireAuth,
3095
- requireEntityPermission: reqEntityPerm
3178
+ requireEntityPermission: requireEntityPermissionEffective
3096
3179
  });
3097
3180
  const chatHandlers = chatConfig ? createChatHandlers(chatConfig) : null;
3098
3181
  function resolveResource(segment) {
@@ -3101,12 +3184,10 @@ function createCmsApiHandler(config) {
3101
3184
  }
3102
3185
  return {
3103
3186
  async handle(method, path, req) {
3104
- const perm = reqEntityPerm;
3105
3187
  async function analyticsGate() {
3106
3188
  const a = await config.requireAuth(req);
3107
3189
  if (a) return a;
3108
- if (perm) return perm(req, "analytics", "read");
3109
- return null;
3190
+ return requireEntityPermissionEffective(req, "analytics", "read");
3110
3191
  }
3111
3192
  if (path[0] === "admin" && path[1] === "roles") {
3112
3193
  if (!adminRoles) return config.json({ error: "Not found" }, { status: 404 });
@@ -3190,19 +3271,17 @@ function createCmsApiHandler(config) {
3190
3271
  const group = path[1];
3191
3272
  const isPublic = settingsConfig?.publicGetGroups?.includes(group);
3192
3273
  if (method === "GET") {
3193
- if (!isPublic && perm) {
3274
+ if (!isPublic) {
3194
3275
  const a = await config.requireAuth(req);
3195
3276
  if (a) return a;
3196
- const pe = await perm(req, "settings", "read");
3277
+ const pe = await requireEntityPermissionEffective(req, "settings", "read");
3197
3278
  if (pe) return pe;
3198
3279
  }
3199
3280
  return settingsHandlers.GET(req, group);
3200
3281
  }
3201
3282
  if (method === "PUT") {
3202
- if (perm) {
3203
- const pe = await perm(req, "settings", "update");
3204
- if (pe) return pe;
3205
- }
3283
+ const pe = await requireEntityPermissionEffective(req, "settings", "update");
3284
+ if (pe) return pe;
3206
3285
  return settingsHandlers.PUT(req, group);
3207
3286
  }
3208
3287
  }
@@ -3218,10 +3297,8 @@ function createCmsApiHandler(config) {
3218
3297
  if (path[0] === "orders" && path.length === 3 && path[2] === "invoice" && method === "GET" && getCms) {
3219
3298
  const a = await config.requireAuth(req);
3220
3299
  if (a) return a;
3221
- if (perm) {
3222
- const pe = await perm(req, "orders", "read");
3223
- if (pe) return pe;
3224
- }
3300
+ const pe = await requireEntityPermissionEffective(req, "orders", "read");
3301
+ if (pe) return pe;
3225
3302
  const cms = await getCms();
3226
3303
  const { streamOrderInvoicePdf: streamOrderInvoicePdf2 } = await Promise.resolve().then(() => (init_erp_order_invoice(), erp_order_invoice_exports));
3227
3304
  const oid = Number(path[1]);
@@ -3231,10 +3308,8 @@ function createCmsApiHandler(config) {
3231
3308
  if (path[0] === "orders" && path.length === 3 && path[2] === "repost-erp" && getCms) {
3232
3309
  const a = await config.requireAuth(req);
3233
3310
  if (a) return a;
3234
- if (perm) {
3235
- const pe = await perm(req, "orders", method === "GET" ? "read" : "update");
3236
- if (pe) return pe;
3237
- }
3311
+ const pe = await requireEntityPermissionEffective(req, "orders", method === "GET" ? "read" : "update");
3312
+ if (pe) return pe;
3238
3313
  const oid = Number(path[1]);
3239
3314
  if (!Number.isFinite(oid)) return config.json({ error: "Invalid id" }, { status: 400 });
3240
3315
  const cms = await getCms();