@infuro/cms-core 1.0.30 → 1.0.33

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 (61) hide show
  1. package/README.md +20 -15
  2. package/dist/admin.cjs +13061 -7814
  3. package/dist/admin.cjs.map +1 -1
  4. package/dist/admin.d.cts +13 -2
  5. package/dist/admin.d.ts +13 -2
  6. package/dist/admin.js +8416 -3167
  7. package/dist/admin.js.map +1 -1
  8. package/dist/api.cjs +5667 -1110
  9. package/dist/api.cjs.map +1 -1
  10. package/dist/api.d.cts +2 -2
  11. package/dist/api.d.ts +2 -2
  12. package/dist/api.js +5418 -855
  13. package/dist/api.js.map +1 -1
  14. package/dist/auth.cjs +620 -76
  15. package/dist/auth.cjs.map +1 -1
  16. package/dist/auth.d.cts +93 -52
  17. package/dist/auth.d.ts +93 -52
  18. package/dist/auth.js +578 -52
  19. package/dist/auth.js.map +1 -1
  20. package/dist/cli.cjs +19 -15
  21. package/dist/cli.cjs.map +1 -1
  22. package/dist/cli.js +19 -15
  23. package/dist/cli.js.map +1 -1
  24. package/dist/{index-BPQSXgXF.d.cts → index-DWd5Gjc4.d.ts} +38 -5
  25. package/dist/{index-D9SdaDJ0.d.ts → index-rZTnpK7y.d.cts} +38 -5
  26. package/dist/index.cjs +5921 -1177
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +334 -59
  29. package/dist/index.d.ts +334 -59
  30. package/dist/index.js +5733 -1026
  31. package/dist/index.js.map +1 -1
  32. package/dist/migrations/1775400000001-AddDiscountAndOrderAddresses.ts +274 -0
  33. package/dist/migrations/1775800000000-VendorsAndVendorUsers.ts +74 -0
  34. package/dist/migrations/1775900000000-StoreVendorIdColumns.ts +124 -0
  35. package/dist/migrations/1775910000000-VendorOwnerRbacSeed.ts +54 -0
  36. package/dist/migrations/1775920000000-VendorOwnerSettingsPermission.ts +25 -0
  37. package/dist/migrations/1775930000000-VendorGroupStorePermissions.ts +54 -0
  38. package/dist/migrations/1775940000000-VendorRls.ts +71 -0
  39. package/dist/migrations/1775950000000-VendorCustomers.ts +87 -0
  40. package/dist/migrations/1775950000001-VendorCustomersPermissions.ts +67 -0
  41. package/dist/migrations/1778652250860-CreateCurrencyTables.ts +16 -0
  42. package/dist/migrations/1778652250861-OrderAddressTables.ts +88 -0
  43. package/dist/migrations/1778652250862-UpdateDiscountandRules.ts +100 -0
  44. package/dist/migrations/1778652250863-MakeCouponCodeNullable.ts +19 -0
  45. package/dist/migrations/1778652250864-RemoveUniqueConstraintFromDiscountCouponCode.ts +22 -0
  46. package/dist/migrations/1778652250865-FixOrdersAddressForeignKeys.ts +71 -0
  47. package/dist/migrations/1778660000000-DiscountVendorId.ts +90 -0
  48. package/dist/migrations/1778660000001-FixOrdersAddressFkToOrderAddresses.ts +79 -0
  49. package/dist/migrations/1778660000002-VendorUserIdInviteStatus.ts +61 -0
  50. package/dist/migrations/1778660000003-AddQrTokenToOrders.ts +32 -0
  51. package/dist/migrations/1779100000000-ChatConversationLeadEmailSent.ts +16 -0
  52. package/dist/migrations/1779200000000-LlmAgentScope.ts +72 -0
  53. package/dist/theme.d.cts +2 -2
  54. package/dist/theme.d.ts +2 -2
  55. package/dist/{types-D34wmivy.d.cts → types-DCVaXlPV.d.cts} +1 -1
  56. package/dist/{types-D34wmivy.d.ts → types-DCVaXlPV.d.ts} +1 -1
  57. package/dist/vendor-scope-DTYhJk_I.d.cts +129 -0
  58. package/dist/vendor-scope-DTYhJk_I.d.ts +129 -0
  59. package/package.json +3 -2
  60. package/dist/helpers-dlrF_49e.d.cts +0 -60
  61. package/dist/helpers-dlrF_49e.d.ts +0 -60
package/dist/auth.js CHANGED
@@ -1,30 +1,14 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
1
11
  // src/auth/permission-entities.ts
2
- var PERMISSION_ENTITY_INTERNAL_EXCLUDE = /* @__PURE__ */ new Set([
3
- "users",
4
- "password_reset_tokens",
5
- "user_groups",
6
- "permissions",
7
- "comments",
8
- "form_fields",
9
- "configs",
10
- "knowledge_base_chunks",
11
- "carts",
12
- "cart_items",
13
- "wishlists",
14
- "wishlist_items",
15
- "message_templates"
16
- ]);
17
- var PERMISSION_LOGICAL_ENTITIES = [
18
- "users",
19
- "forms",
20
- "form_submissions",
21
- "dashboard",
22
- "upload",
23
- "settings",
24
- "analytics",
25
- "chat"
26
- ];
27
- var ADMIN_GROUP_NAME = "Administrator";
28
12
  function isSuperAdminGroupName(name) {
29
13
  return name === ADMIN_GROUP_NAME;
30
14
  }
@@ -54,13 +38,315 @@ function hasEntityPermission(record, entity, action) {
54
38
  if (action === "update") return p.u;
55
39
  return p.d;
56
40
  }
41
+ var PERMISSION_ENTITY_INTERNAL_EXCLUDE, PERMISSION_LOGICAL_ENTITIES, ADMIN_GROUP_NAME;
42
+ var init_permission_entities = __esm({
43
+ "src/auth/permission-entities.ts"() {
44
+ "use strict";
45
+ PERMISSION_ENTITY_INTERNAL_EXCLUDE = /* @__PURE__ */ new Set([
46
+ "users",
47
+ "password_reset_tokens",
48
+ "user_groups",
49
+ "permissions",
50
+ "comments",
51
+ "form_fields",
52
+ "configs",
53
+ "knowledge_base_chunks",
54
+ "carts",
55
+ "cart_items",
56
+ "wishlists",
57
+ "wishlist_items",
58
+ "message_templates",
59
+ "vendor_users"
60
+ ]);
61
+ PERMISSION_LOGICAL_ENTITIES = [
62
+ "users",
63
+ "forms",
64
+ "form_submissions",
65
+ "dashboard",
66
+ "upload",
67
+ "settings",
68
+ "analytics",
69
+ "chat"
70
+ ];
71
+ ADMIN_GROUP_NAME = "Administrator";
72
+ }
73
+ });
74
+
75
+ // src/auth/vendor-scope.ts
76
+ function isVendorGroupName(name) {
77
+ if (!name?.trim()) return false;
78
+ const n = name.trim().toLowerCase();
79
+ return n === VENDOR_OWNER_GROUP_NAME.toLowerCase() || n.includes("vendor");
80
+ }
81
+ function isPlatformAdministrator(user) {
82
+ if (!user?.email) return false;
83
+ return !!(user.isRBACAdmin || isSuperAdminGroupName(user.groupName));
84
+ }
85
+ function isVendorPortalUser(user) {
86
+ if (user?.isVendorPortal === true) return true;
87
+ if (!user?.email || isPlatformAdministrator(user)) return false;
88
+ if ((user.vendorIds?.length ?? 0) > 0) return true;
89
+ return isVendorGroupName(user.groupName);
90
+ }
91
+ function isVendorStaff(user) {
92
+ return isVendorPortalUser(user);
93
+ }
94
+ function isVendorOwner(user) {
95
+ if (user?.isVendorOwner === true) return true;
96
+ if (!user?.email || isPlatformAdministrator(user)) return false;
97
+ if (user.vendorRole === "staff") return false;
98
+ if (user.vendorRole === "owner") return true;
99
+ if (!isVendorPortalUser(user)) return false;
100
+ return user.groupName?.trim().toLowerCase() === VENDOR_OWNER_GROUP_NAME.toLowerCase();
101
+ }
102
+ function canManageVendorTeam(user) {
103
+ return isPlatformAdministrator(user) || isVendorOwner(user);
104
+ }
105
+ function canManageVendorRoles(user) {
106
+ return isPlatformAdministrator(user) || isVendorOwner(user);
107
+ }
108
+ function canOnboardVendors(user) {
109
+ return isPlatformAdministrator(user);
110
+ }
111
+ function resolveVendorScopeFromSessionUser(user) {
112
+ if (!user?.email) return { type: "deny" };
113
+ if (isPlatformAdministrator(user)) return { type: "all" };
114
+ const vendorIds = user.vendorIds ?? [];
115
+ if (vendorIds.length === 0) return { type: "deny" };
116
+ const preferred = user.activeVendorId ?? vendorIds[0];
117
+ const vendorId = vendorIds.includes(preferred) ? preferred : vendorIds[0];
118
+ return { type: "vendor", vendorId, vendorIds };
119
+ }
120
+ function vendorPortalFlagsFromUser(input) {
121
+ const vendorIds = input.vendorIds ?? [];
122
+ const isVendorPortal = !input.isRBACAdmin && (vendorIds.length > 0 || isVendorGroupName(input.groupName));
123
+ const isVendorOwner2 = isVendorPortal && input.vendorRole !== "staff" && (input.vendorRole === "owner" || input.groupName?.trim().toLowerCase() === VENDOR_OWNER_GROUP_NAME.toLowerCase());
124
+ return { isVendorPortal, isVendorOwner: isVendorOwner2 };
125
+ }
126
+ var VENDOR_SCOPED_STORE_ENTITIES, VENDOR_STORE_RBAC_ENTITIES, VENDOR_OWNER_GROUP_NAME;
127
+ var init_vendor_scope = __esm({
128
+ "src/auth/vendor-scope.ts"() {
129
+ "use strict";
130
+ init_permission_entities();
131
+ VENDOR_SCOPED_STORE_ENTITIES = /* @__PURE__ */ new Set([
132
+ "products",
133
+ "collections",
134
+ "brands",
135
+ "product_categories",
136
+ "orders",
137
+ "payments",
138
+ "taxes",
139
+ "attributes",
140
+ "vendor_customers",
141
+ "discounts"
142
+ ]);
143
+ VENDOR_STORE_RBAC_ENTITIES = /* @__PURE__ */ new Set([
144
+ ...VENDOR_SCOPED_STORE_ENTITIES,
145
+ "order_discounts",
146
+ "order_addresses"
147
+ ]);
148
+ VENDOR_OWNER_GROUP_NAME = "Vendor Owner";
149
+ }
150
+ });
151
+
152
+ // src/auth/rbac-debug-db.ts
153
+ var rbac_debug_db_exports = {};
154
+ __export(rbac_debug_db_exports, {
155
+ loadDbPermissionsForUser: () => loadDbPermissionsForUser
156
+ });
157
+ async function loadDbPermissionsForUser(dataSource, userId) {
158
+ const rows = await dataSource.query(
159
+ `
160
+ SELECT u."groupId" AS "groupId", g.name AS "groupName",
161
+ p.entity, p."canCreate", p."canRead", p."canUpdate", p."canDelete"
162
+ FROM users u
163
+ LEFT JOIN user_groups g ON g.id = u."groupId" AND g.deleted = false
164
+ LEFT JOIN permissions p ON p."groupId" = g.id
165
+ WHERE u.id = $1
166
+ `,
167
+ [userId]
168
+ );
169
+ const groupId = rows[0]?.groupId ?? null;
170
+ const groupName = rows[0]?.groupName ?? null;
171
+ const permRows = rows.filter((r) => r.entity).map((r) => ({
172
+ entity: r.entity,
173
+ canCreate: !!r.canCreate,
174
+ canRead: !!r.canRead,
175
+ canUpdate: !!r.canUpdate,
176
+ canDelete: !!r.canDelete
177
+ }));
178
+ return {
179
+ groupId,
180
+ groupName,
181
+ dbEntityPerms: summarizeEntityPerms(permissionRowsToRecord(permRows))
182
+ };
183
+ }
184
+ var init_rbac_debug_db = __esm({
185
+ "src/auth/rbac-debug-db.ts"() {
186
+ "use strict";
187
+ init_permission_entities();
188
+ init_rbac_debug();
189
+ }
190
+ });
191
+
192
+ // src/auth/rbac-debug.ts
193
+ function isRbacDebugEnabled() {
194
+ const v = process.env.CMS_RBAC_DEBUG?.trim().toLowerCase();
195
+ if (v === "1" || v === "true" || v === "yes") return true;
196
+ if (v === "0" || v === "false" || v === "no") return false;
197
+ return process.env.NODE_ENV === "development";
198
+ }
199
+ function logRbac(message, data) {
200
+ if (!isRbacDebugEnabled()) return;
201
+ if (data) console.info(LOG_PREFIX, message, data);
202
+ else console.info(LOG_PREFIX, message);
203
+ }
204
+ function summarizeEntityPerms(entityPerms) {
205
+ const out = {};
206
+ if (!entityPerms) return out;
207
+ for (const [entity, p] of Object.entries(entityPerms)) {
208
+ out[entity] = { c: p.c, r: p.r, u: p.u, d: p.d };
209
+ }
210
+ return out;
211
+ }
212
+ function vendorPortalFallbackReason(user, entity, action) {
213
+ if (!isVendorPortalUser(user)) return null;
214
+ if (entity === "dashboard" && action === "read") return "vendor_portal_dashboard_fallback";
215
+ if (entity === "analytics" && action === "read") return "vendor_portal_analytics_fallback";
216
+ if (entity === "settings" && action === "read") return "vendor_portal_settings_read_fallback";
217
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity) && action === "read") return "vendor_portal_store_read_fallback";
218
+ return null;
219
+ }
220
+ function explainSessionEntityAccess(user, entity, action) {
221
+ const userSnapshot = {
222
+ email: user?.email ?? null,
223
+ id: user?.id ?? null,
224
+ groupId: user?.groupId ?? null,
225
+ groupName: user?.groupName ?? null,
226
+ isRBACAdmin: user?.isRBACAdmin ?? false,
227
+ adminAccess: user?.adminAccess ?? null,
228
+ vendorIds: user?.vendorIds ?? [],
229
+ activeVendorId: user?.activeVendorId ?? null,
230
+ vendorRole: user?.vendorRole ?? null,
231
+ isVendorPortal: user?.isVendorPortal ?? false,
232
+ isVendorOwner: user?.isVendorOwner ?? false,
233
+ isVendorPortalComputed: isVendorPortalUser(user),
234
+ isVendorOwnerComputed: isVendorOwner(user),
235
+ jwtEntityPerms: summarizeEntityPerms(user?.entityPerms),
236
+ jwtEntityPermForTarget: user?.entityPerms?.[entity] ?? null
237
+ };
238
+ if (!user?.email) {
239
+ return { allowed: false, reason: "no_session_email", userSnapshot };
240
+ }
241
+ if (user.isRBACAdmin) {
242
+ return { allowed: true, reason: "platform_administrator", userSnapshot };
243
+ }
244
+ const vendorIds = user.vendorIds ?? [];
245
+ const isStaff = user.vendorRole === "staff";
246
+ if (vendorIds.length > 0) {
247
+ if (entity === "dashboard" && action === "read") {
248
+ return { allowed: true, reason: "vendor_link_dashboard_read", userSnapshot };
249
+ }
250
+ if (entity === "analytics" && action === "read") {
251
+ return { allowed: true, reason: "vendor_link_analytics_read", userSnapshot };
252
+ }
253
+ if (entity === "settings") {
254
+ if (action === "read") {
255
+ return { allowed: true, reason: "vendor_link_settings_read", userSnapshot };
256
+ }
257
+ if (action === "update" && !isStaff) {
258
+ return { allowed: true, reason: "vendor_link_settings_update", userSnapshot };
259
+ }
260
+ }
261
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
262
+ if (isStaff && action === "read") {
263
+ return { allowed: true, reason: "vendor_staff_store_read", userSnapshot };
264
+ }
265
+ if (!isStaff) {
266
+ return { allowed: true, reason: "vendor_link_store_full", userSnapshot };
267
+ }
268
+ return {
269
+ allowed: false,
270
+ reason: "vendor_staff_store_write_denied",
271
+ userSnapshot
272
+ };
273
+ }
274
+ }
275
+ if (isVendorPortalUser(user) && isVendorOwner(user)) {
276
+ if (entity === "dashboard" && action === "read") {
277
+ return { allowed: true, reason: "vendor_owner_group_dashboard", userSnapshot };
278
+ }
279
+ if (entity === "analytics" && action === "read") {
280
+ return { allowed: true, reason: "vendor_owner_group_analytics", userSnapshot };
281
+ }
282
+ if (entity === "settings" && (action === "read" || action === "update")) {
283
+ return { allowed: true, reason: "vendor_owner_group_settings", userSnapshot };
284
+ }
285
+ if (VENDOR_STORE_RBAC_ENTITIES.has(entity)) {
286
+ return { allowed: true, reason: "vendor_owner_group_store", userSnapshot };
287
+ }
288
+ }
289
+ const vendorPortalReason = vendorPortalFallbackReason(user, entity, action);
290
+ if (vendorPortalReason) {
291
+ return { allowed: true, reason: vendorPortalReason, userSnapshot };
292
+ }
293
+ const explicit = user.entityPerms?.[entity];
294
+ if (explicit !== void 0) {
295
+ const allowed2 = hasEntityPermission(user.entityPerms, entity, action);
296
+ return {
297
+ allowed: allowed2,
298
+ reason: allowed2 ? "jwt_entity_perms_allow" : "jwt_entity_perms_deny",
299
+ userSnapshot
300
+ };
301
+ }
302
+ const allowed = hasEntityPermission(user.entityPerms, entity, action);
303
+ return {
304
+ allowed,
305
+ reason: allowed ? "entity_perms_fallback_allow" : "no_matching_rule",
306
+ userSnapshot
307
+ };
308
+ }
309
+ async function logEntityAccessDecision(dataSource, context, user, entity, action, extra) {
310
+ if (!isRbacDebugEnabled()) return;
311
+ const explanation = explainSessionEntityAccess(user, entity, action);
312
+ let dbPermissions;
313
+ if (dataSource && user?.id) {
314
+ const id = Number(user.id);
315
+ if (Number.isFinite(id)) {
316
+ try {
317
+ const { loadDbPermissionsForUser: loadDbPermissionsForUser2 } = await Promise.resolve().then(() => (init_rbac_debug_db(), rbac_debug_db_exports));
318
+ dbPermissions = await loadDbPermissionsForUser2(dataSource, id);
319
+ } catch (e) {
320
+ dbPermissions = { error: e instanceof Error ? e.message : String(e) };
321
+ }
322
+ }
323
+ }
324
+ logRbac(context, {
325
+ entity,
326
+ action,
327
+ allowed: explanation.allowed,
328
+ reason: explanation.reason,
329
+ ...explanation.userSnapshot,
330
+ dbPermissions,
331
+ ...extra
332
+ });
333
+ }
334
+ var LOG_PREFIX;
335
+ var init_rbac_debug = __esm({
336
+ "src/auth/rbac-debug.ts"() {
337
+ "use strict";
338
+ init_permission_entities();
339
+ init_vendor_scope();
340
+ LOG_PREFIX = "[cms-rbac]";
341
+ }
342
+ });
57
343
 
58
344
  // src/auth/helpers.ts
345
+ init_rbac_debug();
346
+ init_vendor_scope();
59
347
  var RBAC_ADMIN_ONLY_ENTITIES = /* @__PURE__ */ new Set(["users", "user_groups", "permissions"]);
60
348
  function sessionHasEntityAccess(user, entity, action) {
61
- if (!user?.email) return false;
62
- if (user.isRBACAdmin && RBAC_ADMIN_ONLY_ENTITIES.has(entity)) return true;
63
- return hasEntityPermission(user.entityPerms, entity, action);
349
+ return explainSessionEntityAccess(user, entity, action).allowed;
64
350
  }
65
351
  function canManageRoles(user) {
66
352
  return !!(user?.email && user.isRBACAdmin);
@@ -85,6 +371,19 @@ function isPublicMethod(pathname, method) {
85
371
  return false;
86
372
  }
87
373
  function createAuthHelpers(getSession, NextResponse) {
374
+ return createAuthHelpersFromGetSession(getSession, NextResponse);
375
+ }
376
+ function createCmsAuthBundle(getSession, NextResponse) {
377
+ const auth = createAuthHelpers(getSession, NextResponse);
378
+ return {
379
+ requireAuth: auth.requireAuth,
380
+ requireAdminAccess: auth.requireAdminAccess,
381
+ requireEntityPermission: auth.requireEntityPermission,
382
+ getSessionUser: auth.getAuthenticatedUser,
383
+ getAuthenticatedUser: auth.getAuthenticatedUser
384
+ };
385
+ }
386
+ function createAuthHelpersFromGetSession(getSession, NextResponse) {
88
387
  return {
89
388
  async requireAuth() {
90
389
  const session = await getSession();
@@ -106,8 +405,21 @@ function createAuthHelpers(getSession, NextResponse) {
106
405
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
107
406
  }
108
407
  const u = session.user;
109
- if (sessionHasEntityAccess(u, entity, action)) return null;
110
- return NextResponse.json({ error: "Forbidden", entity, action }, { status: 403 });
408
+ const allowed = sessionHasEntityAccess(u, entity, action);
409
+ const { reason } = explainSessionEntityAccess(u, entity, action);
410
+ logRbac("authHelpers.requireEntityPermission", {
411
+ entity,
412
+ action,
413
+ allowed,
414
+ reason,
415
+ email: u.email,
416
+ groupName: u.groupName,
417
+ vendorIds: u.vendorIds,
418
+ isVendorPortal: u.isVendorPortal,
419
+ isVendorOwner: u.isVendorOwner
420
+ });
421
+ if (allowed) return null;
422
+ return NextResponse.json({ error: "Forbidden", entity, action, reason }, { status: 403 });
111
423
  },
112
424
  async requireAdminAccess() {
113
425
  const session = await getSession();
@@ -116,8 +428,23 @@ function createAuthHelpers(getSession, NextResponse) {
116
428
  }
117
429
  const u = session.user;
118
430
  if (u.isRBACAdmin) return null;
119
- if (u.adminAccess === false) return NextResponse.json({ error: "Forbidden", reason: "admin_access" }, { status: 403 });
120
- return null;
431
+ if (isVendorPortalUser(u)) return null;
432
+ if (u.adminAccess === true) return null;
433
+ if (u.adminAccess === false) {
434
+ logRbac("requireAdminAccess denied (adminAccess:false)", {
435
+ email: u.email,
436
+ groupName: u.groupName,
437
+ isVendorPortal: u.isVendorPortal
438
+ });
439
+ return NextResponse.json({ error: "Forbidden", reason: "admin_access" }, { status: 403 });
440
+ }
441
+ logRbac("requireAdminAccess denied (no matching rule)", {
442
+ email: u.email,
443
+ adminAccess: u.adminAccess,
444
+ groupName: u.groupName,
445
+ isVendorPortal: u.isVendorPortal
446
+ });
447
+ return NextResponse.json({ error: "Forbidden", reason: "admin_access" }, { status: 403 });
121
448
  },
122
449
  async getAuthenticatedUser() {
123
450
  const session = await getSession();
@@ -126,18 +453,26 @@ function createAuthHelpers(getSession, NextResponse) {
126
453
  };
127
454
  }
128
455
 
456
+ // src/auth/index.ts
457
+ init_rbac_debug();
458
+ init_vendor_scope();
459
+ init_permission_entities();
460
+
129
461
  // src/auth/seed-permissions.ts
462
+ init_permission_entities();
130
463
  async function seedAdministratorPermissions(dataSource, entityMap) {
131
464
  const entities = getPermissionableEntityKeys(entityMap);
132
465
  const groupRepo = dataSource.getRepository(entityMap.user_groups);
133
466
  const permRepo = dataSource.getRepository(entityMap.permissions);
134
467
  const adminGroup = await groupRepo.findOne({ where: { name: ADMIN_GROUP_NAME, deleted: false } });
468
+ console.log("ADMIN GROUP", adminGroup);
135
469
  if (!adminGroup) return;
136
470
  const fullCrud = { canCreate: true, canRead: true, canUpdate: true, canDelete: true };
137
471
  for (const entity of entities) {
138
472
  const existing = await permRepo.findOne({
139
473
  where: { groupId: adminGroup.id, entity }
140
474
  });
475
+ console.log("EXISTING", existing);
141
476
  if (existing) {
142
477
  existing.canCreate = true;
143
478
  existing.canRead = true;
@@ -156,6 +491,59 @@ async function seedAdministratorPermissions(dataSource, entityMap) {
156
491
  }
157
492
  }
158
493
 
494
+ // src/auth/middleware.ts
495
+ import { getToken } from "next-auth/jwt";
496
+
497
+ // src/auth/auth-debug.ts
498
+ var LOG_PREFIX2 = "[cms-auth]";
499
+ function isAuthDebugEnabled() {
500
+ const v = process.env.CMS_AUTH_DEBUG?.trim().toLowerCase();
501
+ if (v === "1" || v === "true" || v === "yes") return true;
502
+ if (v === "0" || v === "false" || v === "no") return false;
503
+ return process.env.NODE_ENV === "development";
504
+ }
505
+ function isAuthDebugClientEnabled() {
506
+ if (typeof window === "undefined") return false;
507
+ const v = process.env.NEXT_PUBLIC_CMS_AUTH_DEBUG?.trim().toLowerCase();
508
+ if (v === "1" || v === "true" || v === "yes") return true;
509
+ if (v === "0" || v === "false" || v === "no") return false;
510
+ return process.env.NODE_ENV === "development";
511
+ }
512
+ function logAuth(message, data) {
513
+ if (!isAuthDebugEnabled()) return;
514
+ if (data) console.info(LOG_PREFIX2, message, data);
515
+ else console.info(LOG_PREFIX2, message);
516
+ }
517
+ function logAuthClient(message, data) {
518
+ if (!isAuthDebugClientEnabled()) return;
519
+ if (data) console.info(LOG_PREFIX2, message, data);
520
+ else console.info(LOG_PREFIX2, message);
521
+ }
522
+ function summarizeSessionUserForLog(user) {
523
+ if (!user || typeof user !== "object") return { present: false };
524
+ const u = user;
525
+ return {
526
+ present: true,
527
+ email: u.email ?? null,
528
+ id: u.id ?? null,
529
+ adminAccess: u.adminAccess ?? null,
530
+ isRBACAdmin: u.isRBACAdmin ?? null,
531
+ isVendorPortal: u.isVendorPortal ?? null,
532
+ groupName: u.groupName ?? null
533
+ };
534
+ }
535
+ function nextAuthCookieDebugInfo() {
536
+ const nextAuthUrl = process.env.NEXTAUTH_URL ?? "(unset)";
537
+ const isHttps = nextAuthUrl.startsWith("https");
538
+ return {
539
+ nextAuthUrl,
540
+ hasSecret: Boolean(process.env.NEXTAUTH_SECRET),
541
+ cookieName: isHttps ? "__Secure-next-auth.session-token" : "next-auth.session-token",
542
+ cookieSecure: isHttps,
543
+ nodeEnv: process.env.NODE_ENV ?? "(unset)"
544
+ };
545
+ }
546
+
159
547
  // src/auth/middleware.ts
160
548
  var defaultPublicApiMethods = {
161
549
  "/api/contacts": ["POST"],
@@ -168,8 +556,21 @@ var defaultPublicApiMethods = {
168
556
  "/api/users/set-password": ["POST"],
169
557
  "/api/users/invite": ["POST"]
170
558
  };
171
- function defaultGetSessionToken(request) {
172
- return request.cookies.get("__Secure-next-auth.session-token")?.value ?? request.cookies.get("next-auth.session-token")?.value;
559
+ function legacyGetSessionToken(request) {
560
+ const secure = request.cookies.get("__Secure-next-auth.session-token")?.value;
561
+ const regular = request.cookies.get("next-auth.session-token")?.value;
562
+ return secure ?? regular;
563
+ }
564
+ function sessionCookiePresence(request) {
565
+ const secure = request.cookies.get("__Secure-next-auth.session-token")?.value;
566
+ const regular = request.cookies.get("next-auth.session-token")?.value;
567
+ const hasChunked = Boolean(request.cookies.get("next-auth.session-token.0")?.value) || Boolean(request.cookies.get("__Secure-next-auth.session-token.0")?.value);
568
+ return {
569
+ hasSecureToken: Boolean(secure),
570
+ hasRegularToken: Boolean(regular),
571
+ hasChunkedToken: hasChunked,
572
+ resolved: Boolean(secure ?? regular)
573
+ };
173
574
  }
174
575
  function isPublicMethod2(pathname, method, publicApiMethods) {
175
576
  for (const [endpoint, methods] of Object.entries(publicApiMethods)) {
@@ -177,31 +578,59 @@ function isPublicMethod2(pathname, method, publicApiMethods) {
177
578
  }
178
579
  return false;
179
580
  }
581
+ async function hasValidSession(request, secret, legacyGetToken) {
582
+ if (request.req) {
583
+ const token = await getToken({ req: request.req, secret });
584
+ return token != null;
585
+ }
586
+ return Boolean(legacyGetToken(request));
587
+ }
180
588
  function createCmsMiddleware(config = {}) {
181
589
  const {
182
590
  publicAdminPaths = ["/admin/signin", "/admin/forgot-password", "/admin/reset-password", "/admin/invite"],
183
591
  publicApiMethods = defaultPublicApiMethods,
184
592
  signInPath = "/admin/signin",
185
- getSessionToken = defaultGetSessionToken
593
+ getSessionToken = legacyGetSessionToken,
594
+ secret = process.env.NEXTAUTH_SECRET
186
595
  } = config;
187
- return function cmsMiddleware(request) {
596
+ return async function cmsMiddleware(request) {
188
597
  const pathname = request.nextUrl.pathname;
189
598
  const method = request.method;
190
599
  if (publicAdminPaths.some((p) => pathname === p || pathname.startsWith(p + "/"))) {
191
600
  return { type: "next" };
192
601
  }
193
602
  if (pathname.startsWith("/admin")) {
194
- const token = getSessionToken(request);
195
- if (!token) {
603
+ const authenticated = await hasValidSession(request, secret, getSessionToken);
604
+ const cookies = sessionCookiePresence(request);
605
+ if (!authenticated) {
606
+ logAuth("middleware: admin redirect to signin (no session)", {
607
+ pathname,
608
+ method,
609
+ usesGetToken: Boolean(request.req),
610
+ ...cookies,
611
+ expectedCookieName: process.env.NEXTAUTH_URL?.startsWith("https") ? "__Secure-next-auth.session-token" : "next-auth.session-token"
612
+ });
196
613
  return { type: "redirect", url: new URL(signInPath, request.url).toString() };
197
614
  }
615
+ logAuth("middleware: admin allowed", {
616
+ pathname,
617
+ method,
618
+ usesGetToken: Boolean(request.req),
619
+ ...cookies
620
+ });
198
621
  }
199
622
  if (pathname.startsWith("/api")) {
200
623
  if (isPublicMethod2(pathname, method, publicApiMethods)) {
201
624
  return { type: "next" };
202
625
  }
203
- const token = getSessionToken(request);
204
- if (!token) {
626
+ const authenticated = await hasValidSession(request, secret, getSessionToken);
627
+ if (!authenticated) {
628
+ logAuth("middleware: api 401 (no session)", {
629
+ pathname,
630
+ method,
631
+ usesGetToken: Boolean(request.req),
632
+ ...sessionCookiePresence(request)
633
+ });
205
634
  return { type: "json", status: 401, body: { error: "Unauthorized" } };
206
635
  }
207
636
  }
@@ -210,21 +639,39 @@ function createCmsMiddleware(config = {}) {
210
639
  }
211
640
 
212
641
  // src/auth/nextauth-options.ts
642
+ init_permission_entities();
643
+ init_vendor_scope();
213
644
  import _CredentialsProvider from "next-auth/providers/credentials";
214
645
  var CredentialsProvider = _CredentialsProvider.default ?? _CredentialsProvider;
215
646
  function sessionUserFromNextAuthUser(user) {
216
647
  const g = user.group;
217
648
  const isRBACAdmin = isSuperAdminGroupName(g?.name);
218
649
  const entityPerms = permissionRowsToRecord(g?.permissions);
219
- const adminAccess = user.adminAccess === true;
650
+ const rawAdminAccess = user.adminAccess;
651
+ const adminAccess = rawAdminAccess === true ? true : rawAdminAccess === false ? false : void 0;
652
+ const vendorIds = user.vendorIds ?? [];
653
+ const vendorRole = user.vendorRole ?? null;
654
+ const { isVendorPortal, isVendorOwner: isVendorOwner2 } = vendorPortalFlagsFromUser({
655
+ email: user.email,
656
+ isRBACAdmin,
657
+ groupName: g?.name,
658
+ vendorIds,
659
+ vendorRole
660
+ });
220
661
  return {
221
662
  id: user.id.toString(),
222
663
  email: user.email,
223
664
  name: user.name,
224
665
  groupId: user.groupId ?? void 0,
666
+ groupName: g?.name ?? void 0,
225
667
  isRBACAdmin,
226
668
  entityPerms,
227
- adminAccess
669
+ adminAccess,
670
+ vendorIds,
671
+ activeVendorId: user.activeVendorId ?? vendorIds[0] ?? null,
672
+ vendorRole,
673
+ isVendorPortal,
674
+ isVendorOwner: isVendorOwner2
228
675
  };
229
676
  }
230
677
  function getNextAuthOptions(config) {
@@ -238,6 +685,7 @@ function getNextAuthOptions(config) {
238
685
  enableOtpLogin = false,
239
686
  authorizeOtp
240
687
  } = config;
688
+ logAuth("getNextAuthOptions init", nextAuthCookieDebugInfo());
241
689
  const providers = [];
242
690
  if (enablePasswordLogin) {
243
691
  providers.push(
@@ -248,14 +696,43 @@ function getNextAuthOptions(config) {
248
696
  password: { label: "Password", type: "password" }
249
697
  },
250
698
  async authorize(credentials) {
251
- if (!credentials?.email || !credentials?.password) return null;
699
+ const email = credentials?.email?.trim() ?? "";
700
+ if (!email || !credentials?.password) {
701
+ logAuth("authorize(credentials) rejected", { reason: "missing_email_or_password" });
702
+ return null;
703
+ }
252
704
  try {
253
- const user = await getUserByEmail(credentials.email);
254
- if (!user || user.blocked || user.deleted || !user.password) return null;
705
+ const user = await getUserByEmail(email);
706
+ if (!user) {
707
+ logAuth("authorize(credentials) rejected", { reason: "user_not_found", email });
708
+ return null;
709
+ }
710
+ if (user.blocked) {
711
+ logAuth("authorize(credentials) rejected", { reason: "blocked", email, userId: user.id });
712
+ return null;
713
+ }
714
+ if (user.deleted) {
715
+ logAuth("authorize(credentials) rejected", { reason: "deleted", email, userId: user.id });
716
+ return null;
717
+ }
718
+ if (!user.password) {
719
+ logAuth("authorize(credentials) rejected", { reason: "no_password", email, userId: user.id });
720
+ return null;
721
+ }
255
722
  const valid = await comparePassword(credentials.password, user.password);
256
- if (!valid) return null;
257
- return sessionUserFromNextAuthUser(user);
258
- } catch {
723
+ if (!valid) {
724
+ logAuth("authorize(credentials) rejected", { reason: "invalid_password", email, userId: user.id });
725
+ return null;
726
+ }
727
+ const sessionUser = sessionUserFromNextAuthUser(user);
728
+ logAuth("authorize(credentials) ok", summarizeSessionUserForLog(sessionUser));
729
+ return sessionUser;
730
+ } catch (err) {
731
+ console.error("[cms-auth] authorize error (credentials):", err instanceof Error ? err.message : err);
732
+ logAuth("authorize(credentials) error", {
733
+ email,
734
+ message: err instanceof Error ? err.message : String(err)
735
+ });
259
736
  return null;
260
737
  }
261
738
  }
@@ -281,7 +758,8 @@ function getNextAuthOptions(config) {
281
758
  const user = await authorizeOtp({ identifier, channel: ch, code });
282
759
  if (!user || user.blocked || user.deleted) return null;
283
760
  return sessionUserFromNextAuthUser(user);
284
- } catch {
761
+ } catch (err) {
762
+ console.error("[cms-auth] authorize error (otp):", err instanceof Error ? err.message : err);
285
763
  return null;
286
764
  }
287
765
  }
@@ -307,31 +785,54 @@ function getNextAuthOptions(config) {
307
785
  callbacks: {
308
786
  async jwt({ token, user, trigger, session }) {
309
787
  if (user) {
788
+ logAuth("jwt callback: new sign-in", {
789
+ trigger,
790
+ user: summarizeSessionUserForLog(user)
791
+ });
310
792
  const u = user;
311
793
  token.id = u.id;
312
794
  token.groupId = u.groupId;
795
+ token.groupName = u.groupName;
313
796
  token.isRBACAdmin = u.isRBACAdmin;
314
797
  token.entityPerms = u.entityPerms;
315
798
  token.adminAccess = u.adminAccess;
799
+ token.vendorIds = u.vendorIds;
800
+ token.activeVendorId = u.activeVendorId;
801
+ token.vendorRole = u.vendorRole;
802
+ token.isVendorPortal = u.isVendorPortal;
803
+ token.isVendorOwner = u.isVendorOwner;
316
804
  }
317
805
  if (trigger === "update" && session && typeof session === "object") {
806
+ logAuth("jwt callback: session update", { trigger });
318
807
  const s = session;
319
808
  const t = token;
320
809
  if (typeof s.name === "string") t.name = s.name;
321
810
  if (typeof s.email === "string") t.email = s.email;
811
+ if (typeof s.activeVendorId === "number") t.activeVendorId = s.activeVendorId;
322
812
  }
323
813
  return token;
324
814
  },
325
815
  async session({ session, token }) {
816
+ const t = token;
817
+ logAuth("session callback", {
818
+ tokenHasId: t.id != null,
819
+ tokenEmail: typeof t.email === "string" ? t.email : null,
820
+ sessionUser: summarizeSessionUserForLog(session.user)
821
+ });
326
822
  if (session.user) {
327
- const t = token;
328
823
  if (typeof t.name === "string") session.user.name = t.name;
329
824
  if (typeof t.email === "string") session.user.email = t.email;
330
825
  session.user.id = t.id;
331
826
  session.user.groupId = t.groupId;
827
+ session.user.groupName = t.groupName;
332
828
  session.user.isRBACAdmin = t.isRBACAdmin;
333
829
  session.user.entityPerms = t.entityPerms;
334
830
  session.user.adminAccess = t.adminAccess;
831
+ session.user.vendorIds = t.vendorIds;
832
+ session.user.activeVendorId = t.activeVendorId;
833
+ session.user.vendorRole = t.vendorRole;
834
+ session.user.isVendorPortal = t.isVendorPortal;
835
+ session.user.isVendorOwner = t.isVendorOwner;
335
836
  }
336
837
  return session;
337
838
  }
@@ -344,19 +845,44 @@ export {
344
845
  OPEN_ENDPOINTS,
345
846
  PERMISSION_REQUIRED_ENDPOINTS,
346
847
  RBAC_ADMIN_ONLY_ENTITIES,
848
+ VENDOR_OWNER_GROUP_NAME,
849
+ VENDOR_SCOPED_STORE_ENTITIES,
850
+ VENDOR_STORE_RBAC_ENTITIES,
347
851
  canManageRoles,
852
+ canManageVendorRoles,
853
+ canManageVendorTeam,
854
+ canOnboardVendors,
348
855
  createAuthHelpers,
856
+ createCmsAuthBundle,
349
857
  createCmsMiddleware,
350
858
  defaultPublicApiMethods,
859
+ explainSessionEntityAccess,
351
860
  getNextAuthOptions,
352
861
  getPermissionableEntityKeys,
353
862
  getRequiredPermission,
354
863
  hasEntityPermission,
864
+ isAuthDebugClientEnabled,
865
+ isAuthDebugEnabled,
355
866
  isOpenEndpoint,
867
+ isPlatformAdministrator,
356
868
  isPublicMethod,
869
+ isRbacDebugEnabled,
357
870
  isSuperAdminGroupName,
871
+ isVendorGroupName,
872
+ isVendorOwner,
873
+ isVendorPortalUser,
874
+ isVendorStaff,
875
+ logAuth,
876
+ logAuthClient,
877
+ logEntityAccessDecision,
878
+ logRbac,
879
+ nextAuthCookieDebugInfo,
358
880
  permissionRowsToRecord,
881
+ resolveVendorScopeFromSessionUser,
359
882
  seedAdministratorPermissions,
360
- sessionHasEntityAccess
883
+ sessionHasEntityAccess,
884
+ summarizeEntityPerms,
885
+ summarizeSessionUserForLog,
886
+ vendorPortalFlagsFromUser
361
887
  };
362
888
  //# sourceMappingURL=auth.js.map