@open-mercato/core 0.10.1-develop.7954.1.48da4a7d13 → 0.10.1-develop.7972.1.03f813eaaf

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 (88) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/logout.js +3 -3
  3. package/dist/modules/auth/api/logout.js.map +2 -2
  4. package/dist/modules/auth/commands/roles.js +42 -31
  5. package/dist/modules/auth/commands/roles.js.map +2 -2
  6. package/dist/modules/communication_channels/setup.js +49 -45
  7. package/dist/modules/communication_channels/setup.js.map +2 -2
  8. package/dist/modules/customer_accounts/services/domainMappingService.js +55 -29
  9. package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
  10. package/dist/modules/customers/components/detail/ActivitiesSection.js +8 -3
  11. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  12. package/dist/modules/customers/components/detail/ActivityCard.js +3 -3
  13. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  14. package/dist/modules/customers/components/detail/ActivityHistorySection.js +19 -7
  15. package/dist/modules/customers/components/detail/ActivityHistorySection.js.map +2 -2
  16. package/dist/modules/customers/components/detail/ActivityTimeline.js +5 -5
  17. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  18. package/dist/modules/directory/subscribers/seedOrganizationDefaults.js +67 -0
  19. package/dist/modules/directory/subscribers/seedOrganizationDefaults.js.map +7 -0
  20. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +1 -1
  21. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +2 -2
  22. package/dist/modules/ecommerce/api/storefront/categories/route.js +1 -1
  23. package/dist/modules/ecommerce/api/storefront/categories/route.js.map +2 -2
  24. package/dist/modules/ecommerce/api/storefront/context/route.js +1 -1
  25. package/dist/modules/ecommerce/api/storefront/context/route.js.map +2 -2
  26. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +1 -1
  27. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +2 -2
  28. package/dist/modules/ecommerce/api/storefront/products/route.js +1 -1
  29. package/dist/modules/ecommerce/api/storefront/products/route.js.map +2 -2
  30. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +1 -1
  31. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +2 -2
  32. package/dist/modules/ecommerce/lib/cacheInvalidation.js +3 -0
  33. package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +2 -2
  34. package/dist/modules/ecommerce/lib/cacheKeys.js +4 -0
  35. package/dist/modules/ecommerce/lib/cacheKeys.js.map +2 -2
  36. package/dist/modules/ecommerce/lib/storeContext.js +36 -12
  37. package/dist/modules/ecommerce/lib/storeContext.js.map +2 -2
  38. package/dist/modules/ecommerce/subscribers/sales-channel-cache-invalidation.js +14 -0
  39. package/dist/modules/ecommerce/subscribers/sales-channel-cache-invalidation.js.map +7 -0
  40. package/dist/modules/integrations/setup.js +34 -24
  41. package/dist/modules/integrations/setup.js.map +2 -2
  42. package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -1
  43. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  44. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js +3 -3
  45. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js.map +2 -2
  46. package/dist/modules/messages/components/message-detail/panels/body-panel.js +2 -2
  47. package/dist/modules/messages/components/message-detail/panels/body-panel.js.map +2 -2
  48. package/dist/modules/payment_gateways/setup.js +3 -0
  49. package/dist/modules/payment_gateways/setup.js.map +2 -2
  50. package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +30 -11
  51. package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +3 -3
  52. package/dist/modules/warranty_claims/setup.js +34 -30
  53. package/dist/modules/warranty_claims/setup.js.map +2 -2
  54. package/dist/modules/workflows/setup.js +7 -0
  55. package/dist/modules/workflows/setup.js.map +2 -2
  56. package/package.json +7 -7
  57. package/src/modules/auth/api/logout.ts +3 -3
  58. package/src/modules/auth/commands/roles.ts +43 -32
  59. package/src/modules/communication_channels/setup.ts +59 -53
  60. package/src/modules/customer_accounts/services/domainMappingService.ts +57 -29
  61. package/src/modules/customers/components/detail/ActivitiesSection.tsx +8 -3
  62. package/src/modules/customers/components/detail/ActivityCard.tsx +3 -3
  63. package/src/modules/customers/components/detail/ActivityHistorySection.tsx +20 -8
  64. package/src/modules/customers/components/detail/ActivityTimeline.tsx +7 -7
  65. package/src/modules/customers/i18n/de.json +1 -0
  66. package/src/modules/customers/i18n/en.json +1 -0
  67. package/src/modules/customers/i18n/es.json +1 -0
  68. package/src/modules/customers/i18n/ko.json +1 -0
  69. package/src/modules/customers/i18n/pl.json +1 -0
  70. package/src/modules/directory/subscribers/seedOrganizationDefaults.ts +84 -0
  71. package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +1 -1
  72. package/src/modules/ecommerce/api/storefront/categories/route.ts +1 -1
  73. package/src/modules/ecommerce/api/storefront/context/route.ts +1 -1
  74. package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +1 -1
  75. package/src/modules/ecommerce/api/storefront/products/route.ts +1 -1
  76. package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +1 -1
  77. package/src/modules/ecommerce/lib/cacheInvalidation.ts +4 -0
  78. package/src/modules/ecommerce/lib/cacheKeys.ts +4 -0
  79. package/src/modules/ecommerce/lib/storeContext.ts +56 -10
  80. package/src/modules/ecommerce/subscribers/sales-channel-cache-invalidation.ts +12 -0
  81. package/src/modules/integrations/setup.ts +41 -24
  82. package/src/modules/messages/components/MessagesInboxPageClient.tsx +1 -1
  83. package/src/modules/messages/components/message-detail/panels/MainMessageHeader.tsx +3 -3
  84. package/src/modules/messages/components/message-detail/panels/body-panel.tsx +2 -2
  85. package/src/modules/payment_gateways/setup.ts +4 -0
  86. package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +55 -28
  87. package/src/modules/warranty_claims/setup.ts +40 -30
  88. package/src/modules/workflows/setup.ts +8 -0
@@ -1,4 +1,4 @@
1
- [build:core] found 6277 entry points
1
+ [build:core] found 6287 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 271 entry points
4
4
  [build:core:generated] built successfully
@@ -41,7 +41,7 @@ function extractAuthTokenClaims(token) {
41
41
  async function POST(req) {
42
42
  const sessToken = parseCookie(req, "session_token");
43
43
  const authToken = parseCookie(req, "auth_token");
44
- if (!sessToken && !authToken) return buildSafeRedirectResponse(req, "/login");
44
+ if (!sessToken && !authToken) return buildSafeRedirectResponse(req, "/login", 303);
45
45
  const claims = extractAuthTokenClaims(authToken);
46
46
  let sessionRevoked = false;
47
47
  if (sessToken || claims.sessionId) {
@@ -69,7 +69,7 @@ async function POST(req) {
69
69
  at: (/* @__PURE__ */ new Date()).toISOString()
70
70
  }, { persistent: true }).catch(() => void 0);
71
71
  }
72
- const res = buildSafeRedirectResponse(req, "/login");
72
+ const res = buildSafeRedirectResponse(req, "/login", 303);
73
73
  res.cookies.set("auth_token", "", { path: "/", maxAge: 0 });
74
74
  res.cookies.set("session_token", "", { path: "/", maxAge: 0 });
75
75
  res.cookies.set("om_selected_org", "", { path: "/", maxAge: 0 });
@@ -87,7 +87,7 @@ const openApi = {
87
87
  summary: "Invalidate session and redirect",
88
88
  description: "Revokes the session identified by the `auth_token` sid claim and the `session_token` cookie, clears both cookies together with the organization-scope cookies (`om_selected_org`, `om_selected_tenant`), and redirects the browser to the login page. A valid access token is not required, so a logout submitted after the access token expired still revokes the remember-me refresh session. A request that carries neither session cookie is only redirected.",
89
89
  responses: [
90
- { status: 307, description: "Redirect to login after successful logout", mediaType: "text/html" }
90
+ { status: 303, description: "See Other: navigate to login with GET after logout", mediaType: "text/html" }
91
91
  ]
92
92
  }
93
93
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/api/logout.ts"],
4
- "sourcesContent": ["import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { verifyJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { buildSafeRedirectResponse } from '@open-mercato/core/modules/auth/lib/requestRedirect'\nimport { emitAuthEvent } from '@open-mercato/core/modules/auth/events'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\n\ntype AuthTokenClaims = {\n userId: string | null\n sessionId: string | null\n tenantId: string | null\n organizationId: string | null\n}\n\nconst emptyAuthTokenClaims: AuthTokenClaims = Object.freeze({\n userId: null,\n sessionId: null,\n tenantId: null,\n organizationId: null,\n})\n\nfunction parseCookie(req: Request, name: string): string | null {\n const cookie = req.headers.get('cookie') || ''\n const m = cookie.match(new RegExp('(?:^|;\\\\s*)' + name + '=([^;]+)'))\n if (!m) return null\n try {\n return decodeURIComponent(m[1])\n } catch {\n return null\n }\n}\n\nfunction readStringClaim(payload: Record<string, unknown>, key: string): string | null {\n const value = payload[key]\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction extractAuthTokenClaims(token: string | null): AuthTokenClaims {\n if (!token) return emptyAuthTokenClaims\n try {\n const payload = verifyJwt(token) as Record<string, unknown> | null\n if (!payload || payload.type === 'customer') return emptyAuthTokenClaims\n return {\n userId: readStringClaim(payload, 'sub'),\n sessionId: readStringClaim(payload, 'sid'),\n tenantId: readStringClaim(payload, 'tenantId'),\n organizationId: readStringClaim(payload, 'orgId'),\n }\n } catch {\n return emptyAuthTokenClaims\n }\n}\n\nexport async function POST(req: Request) {\n const sessToken = parseCookie(req, 'session_token')\n const authToken = parseCookie(req, 'auth_token')\n if (!sessToken && !authToken) return buildSafeRedirectResponse(req, '/login')\n const claims = extractAuthTokenClaims(authToken)\n let sessionRevoked = false\n if (sessToken || claims.sessionId) {\n try {\n const c = await createRequestContainer()\n const auth = c.resolve<AuthService>('authService')\n if (claims.sessionId) {\n await auth.deleteSessionById(claims.sessionId)\n }\n if (sessToken) {\n await auth.deleteSessionByToken(sessToken)\n }\n sessionRevoked = true\n } catch (error) {\n getTelemetryRuntime()?.reportError(error, { module: 'auth', code: 'auth.logout_revoke_failed' })\n }\n }\n if (claims.userId) {\n void emitAuthEvent('auth.logout', {\n id: claims.userId,\n tenantId: claims.tenantId,\n organizationId: claims.organizationId,\n sessionId: claims.sessionId,\n sessionRevoked,\n at: new Date().toISOString(),\n }, { persistent: true }).catch(() => undefined)\n }\n const res = buildSafeRedirectResponse(req, '/login')\n res.cookies.set('auth_token', '', { path: '/', maxAge: 0 })\n res.cookies.set('session_token', '', { path: '/', maxAge: 0 })\n res.cookies.set('om_selected_org', '', { path: '/', maxAge: 0 })\n res.cookies.set('om_selected_tenant', '', { path: '/', maxAge: 0 })\n return res\n}\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Log out current session',\n methods: {\n POST: {\n summary: 'Invalidate session and redirect',\n description: 'Revokes the session identified by the `auth_token` sid claim and the `session_token` cookie, clears both cookies together with the organization-scope cookies (`om_selected_org`, `om_selected_tenant`), and redirects the browser to the login page. A valid access token is not required, so a logout submitted after the access token expired still revokes the remember-me refresh session. A request that carries neither session cookie is only redirected.',\n responses: [\n { status: 307, description: 'Redirect to login after successful logout', mediaType: 'text/html' },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { AuthService } from '@open-mercato/core/modules/auth/services/authService'\nimport { verifyJwt } from '@open-mercato/shared/lib/auth/jwt'\nimport { buildSafeRedirectResponse } from '@open-mercato/core/modules/auth/lib/requestRedirect'\nimport { emitAuthEvent } from '@open-mercato/core/modules/auth/events'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\n\ntype AuthTokenClaims = {\n userId: string | null\n sessionId: string | null\n tenantId: string | null\n organizationId: string | null\n}\n\nconst emptyAuthTokenClaims: AuthTokenClaims = Object.freeze({\n userId: null,\n sessionId: null,\n tenantId: null,\n organizationId: null,\n})\n\nfunction parseCookie(req: Request, name: string): string | null {\n const cookie = req.headers.get('cookie') || ''\n const m = cookie.match(new RegExp('(?:^|;\\\\s*)' + name + '=([^;]+)'))\n if (!m) return null\n try {\n return decodeURIComponent(m[1])\n } catch {\n return null\n }\n}\n\nfunction readStringClaim(payload: Record<string, unknown>, key: string): string | null {\n const value = payload[key]\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction extractAuthTokenClaims(token: string | null): AuthTokenClaims {\n if (!token) return emptyAuthTokenClaims\n try {\n const payload = verifyJwt(token) as Record<string, unknown> | null\n if (!payload || payload.type === 'customer') return emptyAuthTokenClaims\n return {\n userId: readStringClaim(payload, 'sub'),\n sessionId: readStringClaim(payload, 'sid'),\n tenantId: readStringClaim(payload, 'tenantId'),\n organizationId: readStringClaim(payload, 'orgId'),\n }\n } catch {\n return emptyAuthTokenClaims\n }\n}\n\nexport async function POST(req: Request) {\n const sessToken = parseCookie(req, 'session_token')\n const authToken = parseCookie(req, 'auth_token')\n if (!sessToken && !authToken) return buildSafeRedirectResponse(req, '/login', 303)\n const claims = extractAuthTokenClaims(authToken)\n let sessionRevoked = false\n if (sessToken || claims.sessionId) {\n try {\n const c = await createRequestContainer()\n const auth = c.resolve<AuthService>('authService')\n if (claims.sessionId) {\n await auth.deleteSessionById(claims.sessionId)\n }\n if (sessToken) {\n await auth.deleteSessionByToken(sessToken)\n }\n sessionRevoked = true\n } catch (error) {\n getTelemetryRuntime()?.reportError(error, { module: 'auth', code: 'auth.logout_revoke_failed' })\n }\n }\n if (claims.userId) {\n void emitAuthEvent('auth.logout', {\n id: claims.userId,\n tenantId: claims.tenantId,\n organizationId: claims.organizationId,\n sessionId: claims.sessionId,\n sessionRevoked,\n at: new Date().toISOString(),\n }, { persistent: true }).catch(() => undefined)\n }\n const res = buildSafeRedirectResponse(req, '/login', 303)\n res.cookies.set('auth_token', '', { path: '/', maxAge: 0 })\n res.cookies.set('session_token', '', { path: '/', maxAge: 0 })\n res.cookies.set('om_selected_org', '', { path: '/', maxAge: 0 })\n res.cookies.set('om_selected_tenant', '', { path: '/', maxAge: 0 })\n return res\n}\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Log out current session',\n methods: {\n POST: {\n summary: 'Invalidate session and redirect',\n description: 'Revokes the session identified by the `auth_token` sid claim and the `session_token` cookie, clears both cookies together with the organization-scope cookies (`om_selected_org`, `om_selected_tenant`), and redirects the browser to the login page. A valid access token is not required, so a logout submitted after the access token expired still revokes the remember-me refresh session. A request that carries neither session cookie is only redirected.',\n responses: [\n { status: 303, description: 'See Other: navigate to login with GET after logout', mediaType: 'text/html' },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -356,40 +356,51 @@ const updateRoleCommand = {
356
356
  const de = ctx.container.resolve("dataEngine");
357
357
  const redoLogEntry = ctx[ROLE_REDO_LOG];
358
358
  let role = null;
359
- await withAtomicFlush(em, [async () => {
360
- if (redoLogEntry) {
361
- await authorizeRoleReplayAtMutation("update", {
362
- operation: "redo",
363
- input: rawInput,
364
- ctx,
365
- logEntry: redoLogEntry
366
- });
367
- }
368
- await lockRoleWriterAuthorizationState(em, [parsed.id]);
369
- if (wantsTenantChange) {
370
- await em.nativeDelete(RoleAcl, { role: parsed.id });
371
- }
372
- role = await de.updateOrmEntity({
373
- entity: Role,
374
- where: buildScopedRoleFilter(parsed.id, scope),
375
- apply: (entity) => {
376
- if (parsed.name !== void 0) entity.name = parsed.name;
377
- if (parsed.tenantId !== void 0 && scope.isSuperAdmin) entity.tenantId = parsed.tenantId;
359
+ try {
360
+ await withAtomicFlush(em, [async () => {
361
+ if (redoLogEntry) {
362
+ await authorizeRoleReplayAtMutation("update", {
363
+ operation: "redo",
364
+ input: rawInput,
365
+ ctx,
366
+ logEntry: redoLogEntry
367
+ });
378
368
  }
369
+ await lockRoleWriterAuthorizationState(em, [parsed.id]);
370
+ if (wantsTenantChange) {
371
+ await em.nativeDelete(RoleAcl, { role: parsed.id });
372
+ }
373
+ role = await de.updateOrmEntity({
374
+ entity: Role,
375
+ where: buildScopedRoleFilter(parsed.id, scope),
376
+ apply: (entity) => {
377
+ if (parsed.name !== void 0) entity.name = parsed.name;
378
+ if (parsed.tenantId !== void 0 && scope.isSuperAdmin) entity.tenantId = parsed.tenantId;
379
+ }
380
+ });
381
+ if (!role) throw new CrudHttpError(404, { error: "Role not found" });
382
+ await setCustomFieldsIfAny({
383
+ dataEngine: de,
384
+ entityId: E.auth.role,
385
+ recordId: String(role.id),
386
+ organizationId: null,
387
+ tenantId: role.tenantId ? String(role.tenantId) : null,
388
+ values: custom
389
+ });
390
+ }], {
391
+ transaction: true,
392
+ label: redoLogEntry ? "auth.roles.update.redo" : "auth.roles.update"
379
393
  });
380
- if (!role) throw new CrudHttpError(404, { error: "Role not found" });
381
- await setCustomFieldsIfAny({
382
- dataEngine: de,
383
- entityId: E.auth.role,
384
- recordId: String(role.id),
385
- organizationId: null,
386
- tenantId: role.tenantId ? String(role.tenantId) : null,
387
- values: custom
394
+ } catch (error) {
395
+ if (!isUniqueViolation(error, "roles_tenant_id_name_unique")) throw error;
396
+ const { translate } = await resolveTranslations();
397
+ const message = translate("auth.roles.errors.nameExists", "A role with this name already exists");
398
+ throw new CrudHttpError(400, {
399
+ error: message,
400
+ fieldErrors: { name: message },
401
+ details: [{ path: ["name"], message, code: "duplicate", origin: "validation" }]
388
402
  });
389
- }], {
390
- transaction: true,
391
- label: redoLogEntry ? "auth.roles.update.redo" : "auth.roles.update"
392
- });
403
+ }
393
404
  const updatedRole = role;
394
405
  if (!updatedRole) throw new CrudHttpError(404, { error: "Role not found" });
395
406
  await emitCrudSideEffects({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/commands/roles.ts"],
4
- "sourcesContent": ["import type { CommandHandler, CommandReplayAuthorizationArgs, CommandUndoLogEntry } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError, forbidden, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveIsSuperAdmin, normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport {\n assertActorCanAccessRoleTarget,\n assertActorCanGrantAcl,\n assertActorCanModifySuperAdminRoleTarget,\n} from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport {\n assertReplaySnapshotMatches,\n extractStoredReplayInput,\n lockReplayAuthorizationState,\n rerunReplayTransactionGuardAfterLocks,\n requireCurrentReplayFeature,\n requireCurrentReplaySuperAdmin,\n requireTransactionalReplayEntityManager,\n} from '@open-mercato/core/modules/auth/lib/commandReplay'\nimport { lockRoleWriterAuthorizationState } from '@open-mercato/core/modules/auth/lib/authorizationStateLocks'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nconst ROLE_REDO_LOG = Symbol('auth.roles.redoLog')\n\ntype RoleReplayRuntimeContext = CommandRuntimeContext & {\n [ROLE_REDO_LOG]?: CommandUndoLogEntry\n}\n\nfunction withRoleRedoLog(ctx: CommandRuntimeContext, logEntry: CommandUndoLogEntry): RoleReplayRuntimeContext {\n return { ...ctx, [ROLE_REDO_LOG]: logEntry }\n}\n\nfunction resolveCommandEm(ctx: CommandRuntimeContext, forkWhenUnbound = false): EntityManager {\n if (ctx.transactionalEm) return ctx.transactionalEm\n const em = ctx.container.resolve('em') as EntityManager\n return forkWhenUnbound ? em.fork() : em\n}\n\nfunction resolveActorTenantScope(ctx: CommandRuntimeContext): string | null {\n if (ctx.systemActor === true) return null\n const auth = ctx.auth\n if (!auth) return null\n if ((auth as { isSuperAdmin?: boolean }).isSuperAdmin === true) return null\n return normalizeTenantId(auth.tenantId ?? null) ?? null\n}\n\nfunction assertRoleTenantInScope(actorTenantScope: string | null, targetTenantId: unknown): void {\n if (!actorTenantScope) return\n const targetTenant = normalizeTenantId(targetTenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) {\n throw new CrudHttpError(404, { error: 'Role not found' })\n }\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction isReservedRoleName(name: string | undefined | null): boolean {\n if (typeof name !== 'string') return false\n const normalized = name.trim().toLowerCase()\n return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized)\n}\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (isReservedRoleName(name)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\ntype ResolvedActorScope = { isSuperAdmin: boolean; actorTenantId: string | null }\n\nasync function resolveActorScope(ctx: { auth: { tenantId?: string | null; sub?: string | null; orgId?: string | null; isSuperAdmin?: boolean } | null; container: { resolve<T = unknown>(name: string): T } }): Promise<ResolvedActorScope> {\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n const actorTenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n return { isSuperAdmin, actorTenantId }\n}\n\nfunction buildScopedRoleFilter(roleId: string, scope: ResolvedActorScope): FilterQuery<Role> {\n const filter: FilterQuery<Role> = { id: roleId, deletedAt: null }\n if (!scope.isSuperAdmin) {\n ;(filter as Record<string, unknown>).tenantId = scope.actorTenantId\n }\n return filter\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('create', params),\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const scope = await resolveActorScope(ctx)\n const requestedTenantId = parsed.tenantId ?? null\n if (!scope.isSuperAdmin && requestedTenantId && requestedTenantId !== scope.actorTenantId) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const resolvedTenantId = scope.isSuperAdmin\n ? (requestedTenantId ?? scope.actorTenantId)\n : scope.actorTenantId\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role\n try {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n } catch (error) {\n if (!isUniqueViolation(error, 'roles_tenant_id_name_unique')) throw error\n const { translate } = await resolveTranslations()\n const message = translate('auth.roles.errors.nameExists', 'A role with this name already exists')\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { name: message },\n details: [{ path: ['name'], message, code: 'duplicate', origin: 'validation' }],\n })\n }\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('create', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n await lockRoleWriterAuthorizationState(em, [undo.id])\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n }], { transaction: true, label: 'auth.roles.create.undo' })\n },\n redo: async ({ input, logEntry, ctx }) => {\n const after = resolveRedoSnapshot<RoleUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for role create' })\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('create', {\n operation: 'redo',\n input,\n ctx,\n logEntry,\n })\n role = await findOneWithDecryption(em, Role, { id: after.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = after.name\n role.tenantId = after.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: after.id,\n name: after.name,\n tenantId: after.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, after.id, after.acls)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n }], { transaction: true, label: 'auth.roles.create.redo' })\n if (!role) throw new CrudHttpError(400, { error: '[internal] redo failed to restore role row' })\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n return role\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('update', params),\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), existing.tenantId)\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const current = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n const actorTenantScope = resolveActorTenantScope(ctx)\n assertRoleTenantInScope(actorTenantScope, current.tenantId)\n if (parsed.name !== undefined) {\n const nextName = parsed.name\n if (nextName !== current.name) assertRoleNameAllowed(nextName)\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n }\n const wantsTenantChange = parsed.tenantId !== undefined && parsed.tenantId !== current.tenantId\n if (wantsTenantChange) {\n if (!scope.isSuperAdmin) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role cannot be moved to another tenant while users are assigned' })\n }\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const redoLogEntry = (ctx as RoleReplayRuntimeContext)[ROLE_REDO_LOG]\n let role: Role | null = null\n await withAtomicFlush(em, [async () => {\n if (redoLogEntry) {\n await authorizeRoleReplayAtMutation('update', {\n operation: 'redo',\n input: rawInput,\n ctx,\n logEntry: redoLogEntry,\n })\n }\n await lockRoleWriterAuthorizationState(em, [parsed.id])\n if (wantsTenantChange) {\n await em.nativeDelete(RoleAcl, { role: parsed.id as unknown as Role })\n }\n role = await de.updateOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(parsed.id, scope),\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined && scope.isSuperAdmin) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n }], {\n transaction: true,\n label: redoLogEntry ? 'auth.roles.update.redo' : 'auth.roles.update',\n })\n const updatedRole = role as Role | null\n if (!updatedRole) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updatedRole,\n identifiers: {\n id: String(updatedRole.id),\n organizationId: null,\n tenantId: updatedRole.tenantId ? String(updatedRole.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return updatedRole\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = resolveCommandEm(ctx, true)\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const storedReplayInput = extractStoredReplayInput(logEntry.commandPayload, logEntry)\n const replayInput = Object.keys(storedReplayInput).length\n ? storedReplayInput\n : deriveRoleUpdateReplayInput(before, after ?? null)\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let updated: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('update', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n if ('name' in replayInput) entity.name = before.name\n if ('tenantId' in replayInput) entity.tenantId = before.tenantId\n },\n })\n const restoresAcls = 'tenantId' in replayInput && after?.tenantId !== before.tenantId\n if (updated && restoresAcls) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n }], { transaction: true, label: 'auth.roles.update.undo' })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n redo: ({ input, ctx, logEntry }) => updateRoleCommand.execute(input, withRoleRedoLog(ctx, logEntry)),\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('delete', params),\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) return {}\n const actorTenantScope = resolveActorTenantScope(ctx)\n if (actorTenantScope) {\n const targetTenant = normalizeTenantId(existing.tenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) return {}\n }\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const role = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), role.tenantId)\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const redoLogEntry = (ctx as RoleReplayRuntimeContext)[ROLE_REDO_LOG]\n let deleted: Role | null = null\n await withAtomicFlush(em, [async () => {\n if (redoLogEntry) {\n await authorizeRoleReplayAtMutation('delete', {\n operation: 'redo',\n input,\n ctx,\n logEntry: redoLogEntry,\n })\n }\n await lockRoleWriterAuthorizationState(em, [id])\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n await em.nativeDelete(RoleAcl, { role: id })\n deleted = await de.deleteOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(id, scope),\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n }], {\n transaction: true,\n label: redoLogEntry ? 'auth.roles.delete.redo' : 'auth.roles.delete',\n })\n const deletedRole = deleted as Role | null\n if (!deletedRole) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deletedRole,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deletedRole.tenantId ? String(deletedRole.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deletedRole\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('delete', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }], { transaction: true, label: 'auth.roles.delete.undo' })\n if (!role) throw new CrudHttpError(400, { error: '[internal] undo failed to restore role row' })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n redo: ({ input, ctx, logEntry }) => deleteRoleCommand.execute(input, withRoleRedoLog(ctx, logEntry)),\n}\n\ntype RoleReplayCommandKind = 'create' | 'update' | 'delete'\n\nfunction normalizeRoleAclSnapshots(acls: RoleAclSnapshot[]): RoleAclSnapshot[] {\n return acls\n .map((acl) => ({\n ...acl,\n features: acl.features ? [...acl.features].sort((left, right) => left.localeCompare(right)) : null,\n organizations: acl.organizations ? [...acl.organizations].sort((left, right) => left.localeCompare(right)) : null,\n }))\n .sort((left, right) => {\n const tenantOrder = left.tenantId.localeCompare(right.tenantId)\n if (tenantOrder !== 0) return tenantOrder\n return (left.id ?? '').localeCompare(right.id ?? '')\n })\n}\n\nfunction normalizeRoleReplaySnapshot(snapshot: RoleUndoSnapshot): RoleUndoSnapshot {\n return {\n ...snapshot,\n acls: normalizeRoleAclSnapshots(snapshot.acls),\n }\n}\n\nfunction toRoleUpdateReplayState(snapshot: RoleUndoSnapshot): SerializedRole {\n return {\n name: snapshot.name,\n tenantId: snapshot.tenantId,\n ...(snapshot.custom && Object.keys(snapshot.custom).length ? { custom: snapshot.custom } : {}),\n }\n}\n\nfunction deriveRoleUpdateReplayInput(\n before: RoleUndoSnapshot | null,\n after: RoleUndoSnapshot | null,\n): Record<string, unknown> {\n if (!before) return {}\n if (!after) return { id: before.id, name: before.name, tenantId: before.tenantId }\n const input: Record<string, unknown> = { id: after.id }\n if (before.name !== after.name) input.name = after.name\n if (before.tenantId !== after.tenantId) input.tenantId = after.tenantId\n return input\n}\n\nasync function authorizeRoleReplay(\n commandKind: RoleReplayCommandKind,\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const { operation, ctx, logEntry } = params\n const em = requireTransactionalReplayEntityManager(ctx)\n await requireCurrentReplayFeature(ctx, 'auth.roles.manage')\n\n const undoPayload = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undoPayload?.before ?? null\n const after = undoPayload?.after ?? null\n const roleId = logEntry.resourceId ?? before?.id ?? after?.id ?? null\n if (!roleId) {\n await assertReplaySnapshotMatches(null, { required: 'role replay snapshot' })\n return\n }\n\n const currentRole = await findOneWithDecryption(\n em,\n Role,\n { id: roleId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n const rbacService = ctx.systemActor === true\n ? null\n : ctx.container.resolve('rbacService') as RbacService\n if (rbacService && currentRole) {\n await assertActorCanAccessRoleTarget({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n targetRoleId: roleId,\n })\n await assertActorCanModifySuperAdminRoleTarget({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n targetRoleId: roleId,\n })\n }\n\n let currentSnapshot: RoleUndoSnapshot | null = null\n if (currentRole) {\n const currentTenantId = currentRole.tenantId ? String(currentRole.tenantId) : null\n const [acls, custom] = await Promise.all([\n loadRoleAclSnapshots(em, roleId),\n loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: roleId,\n tenantId: currentTenantId,\n }),\n ])\n currentSnapshot = captureRoleSnapshots(currentRole, acls, custom).undo\n }\n\n const expectedCurrent = operation === 'undo'\n ? commandKind === 'create' ? after : commandKind === 'update' ? after : null\n : commandKind === 'create' ? null : before\n const storedReplayInput = extractStoredReplayInput(params.input, logEntry)\n const replayInput = Object.keys(storedReplayInput).length\n ? storedReplayInput\n : deriveRoleUpdateReplayInput(before, after)\n const movesTenant = commandKind === 'update'\n && 'tenantId' in replayInput\n && before?.tenantId !== after?.tenantId\n if (movesTenant) {\n await requireCurrentReplaySuperAdmin(ctx)\n }\n\n if (commandKind === 'update' && !movesTenant) {\n if (!currentSnapshot || !expectedCurrent) {\n await assertReplaySnapshotMatches(currentSnapshot, expectedCurrent)\n } else {\n await assertReplaySnapshotMatches(\n toRoleUpdateReplayState(currentSnapshot),\n toRoleUpdateReplayState(expectedCurrent),\n )\n }\n } else {\n await assertReplaySnapshotMatches(\n currentSnapshot ? normalizeRoleReplaySnapshot(currentSnapshot) : null,\n expectedCurrent ? normalizeRoleReplaySnapshot(expectedCurrent) : null,\n )\n }\n\n const desiredState = operation === 'undo'\n ? commandKind === 'create' ? null : before\n : commandKind === 'delete' ? null : after\n if (desiredState && !currentRole && rbacService) {\n const actorUserId = ctx.auth?.sub\n if (!actorUserId) throw forbidden()\n const actorAcl = await rbacService.loadAclWithEntityManager(em, actorUserId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n })\n if (!actorAcl.isSuperAdmin) assertRoleTenantInScope(ctx.auth?.tenantId ?? null, desiredState.tenantId)\n }\n const restoresAcls = Boolean(\n desiredState\n && desiredState.acls.length > 0\n && (commandKind !== 'update' || movesTenant),\n )\n if (!restoresAcls || !desiredState || !rbacService) return\n\n await requireCurrentReplayFeature(ctx, 'auth.acl.manage')\n for (const acl of desiredState.acls) {\n await assertActorCanGrantAcl({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: acl.tenantId,\n organizationId: null,\n features: acl.features,\n isSuperAdmin: acl.isSuperAdmin,\n organizations: acl.organizations,\n })\n }\n}\n\nasync function authorizeRoleReplayAtMutation(\n commandKind: RoleReplayCommandKind,\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const em = params.ctx.transactionalEm\n ?? params.ctx.container.resolve('em') as EntityManager\n const boundParams = params.ctx.transactionalEm\n ? params\n : { ...params, ctx: { ...params.ctx, transactionalEm: em } }\n await stabilizeRoleReplay(boundParams)\n await rerunReplayTransactionGuardAfterLocks(boundParams, em)\n await authorizeRoleReplay(commandKind, boundParams)\n}\n\nasync function stabilizeRoleReplay(\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const undoPayload = extractUndoPayload<RoleUndoPayload>(params.logEntry)\n const before = undoPayload?.before ?? null\n const after = undoPayload?.after ?? null\n const targetRoleId = params.logEntry.resourceId ?? before?.id ?? after?.id ?? null\n const em = requireTransactionalReplayEntityManager(params.ctx)\n await lockReplayAuthorizationState(em, params.ctx, {\n targetRoleIds: targetRoleId ? [targetRoleId] : [],\n targetTenantIds: [\n ...(before?.tenantId ? [before.tenantId] : []),\n ...(after?.tenantId ? [after.tenantId] : []),\n ...(params.logEntry.tenantId ? [params.logEntry.tenantId] : []),\n ],\n })\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await lockRoleWriterAuthorizationState(em, [roleId])\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler, CommandReplayAuthorizationArgs, CommandUndoLogEntry } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError, forbidden, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveIsSuperAdmin, normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport {\n assertActorCanAccessRoleTarget,\n assertActorCanGrantAcl,\n assertActorCanModifySuperAdminRoleTarget,\n} from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport {\n assertReplaySnapshotMatches,\n extractStoredReplayInput,\n lockReplayAuthorizationState,\n rerunReplayTransactionGuardAfterLocks,\n requireCurrentReplayFeature,\n requireCurrentReplaySuperAdmin,\n requireTransactionalReplayEntityManager,\n} from '@open-mercato/core/modules/auth/lib/commandReplay'\nimport { lockRoleWriterAuthorizationState } from '@open-mercato/core/modules/auth/lib/authorizationStateLocks'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nconst ROLE_REDO_LOG = Symbol('auth.roles.redoLog')\n\ntype RoleReplayRuntimeContext = CommandRuntimeContext & {\n [ROLE_REDO_LOG]?: CommandUndoLogEntry\n}\n\nfunction withRoleRedoLog(ctx: CommandRuntimeContext, logEntry: CommandUndoLogEntry): RoleReplayRuntimeContext {\n return { ...ctx, [ROLE_REDO_LOG]: logEntry }\n}\n\nfunction resolveCommandEm(ctx: CommandRuntimeContext, forkWhenUnbound = false): EntityManager {\n if (ctx.transactionalEm) return ctx.transactionalEm\n const em = ctx.container.resolve('em') as EntityManager\n return forkWhenUnbound ? em.fork() : em\n}\n\nfunction resolveActorTenantScope(ctx: CommandRuntimeContext): string | null {\n if (ctx.systemActor === true) return null\n const auth = ctx.auth\n if (!auth) return null\n if ((auth as { isSuperAdmin?: boolean }).isSuperAdmin === true) return null\n return normalizeTenantId(auth.tenantId ?? null) ?? null\n}\n\nfunction assertRoleTenantInScope(actorTenantScope: string | null, targetTenantId: unknown): void {\n if (!actorTenantScope) return\n const targetTenant = normalizeTenantId(targetTenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) {\n throw new CrudHttpError(404, { error: 'Role not found' })\n }\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction isReservedRoleName(name: string | undefined | null): boolean {\n if (typeof name !== 'string') return false\n const normalized = name.trim().toLowerCase()\n return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized)\n}\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (isReservedRoleName(name)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\ntype ResolvedActorScope = { isSuperAdmin: boolean; actorTenantId: string | null }\n\nasync function resolveActorScope(ctx: { auth: { tenantId?: string | null; sub?: string | null; orgId?: string | null; isSuperAdmin?: boolean } | null; container: { resolve<T = unknown>(name: string): T } }): Promise<ResolvedActorScope> {\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n const actorTenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n return { isSuperAdmin, actorTenantId }\n}\n\nfunction buildScopedRoleFilter(roleId: string, scope: ResolvedActorScope): FilterQuery<Role> {\n const filter: FilterQuery<Role> = { id: roleId, deletedAt: null }\n if (!scope.isSuperAdmin) {\n ;(filter as Record<string, unknown>).tenantId = scope.actorTenantId\n }\n return filter\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('create', params),\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const scope = await resolveActorScope(ctx)\n const requestedTenantId = parsed.tenantId ?? null\n if (!scope.isSuperAdmin && requestedTenantId && requestedTenantId !== scope.actorTenantId) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const resolvedTenantId = scope.isSuperAdmin\n ? (requestedTenantId ?? scope.actorTenantId)\n : scope.actorTenantId\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role\n try {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n } catch (error) {\n if (!isUniqueViolation(error, 'roles_tenant_id_name_unique')) throw error\n const { translate } = await resolveTranslations()\n const message = translate('auth.roles.errors.nameExists', 'A role with this name already exists')\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { name: message },\n details: [{ path: ['name'], message, code: 'duplicate', origin: 'validation' }],\n })\n }\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('create', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n await lockRoleWriterAuthorizationState(em, [undo.id])\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n }], { transaction: true, label: 'auth.roles.create.undo' })\n },\n redo: async ({ input, logEntry, ctx }) => {\n const after = resolveRedoSnapshot<RoleUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for role create' })\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('create', {\n operation: 'redo',\n input,\n ctx,\n logEntry,\n })\n role = await findOneWithDecryption(em, Role, { id: after.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = after.name\n role.tenantId = after.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: after.id,\n name: after.name,\n tenantId: after.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, after.id, after.acls)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n }], { transaction: true, label: 'auth.roles.create.redo' })\n if (!role) throw new CrudHttpError(400, { error: '[internal] redo failed to restore role row' })\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n return role\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('update', params),\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), existing.tenantId)\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const current = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n const actorTenantScope = resolveActorTenantScope(ctx)\n assertRoleTenantInScope(actorTenantScope, current.tenantId)\n if (parsed.name !== undefined) {\n const nextName = parsed.name\n if (nextName !== current.name) assertRoleNameAllowed(nextName)\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n }\n const wantsTenantChange = parsed.tenantId !== undefined && parsed.tenantId !== current.tenantId\n if (wantsTenantChange) {\n if (!scope.isSuperAdmin) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role cannot be moved to another tenant while users are assigned' })\n }\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const redoLogEntry = (ctx as RoleReplayRuntimeContext)[ROLE_REDO_LOG]\n let role: Role | null = null\n try {\n await withAtomicFlush(em, [async () => {\n if (redoLogEntry) {\n await authorizeRoleReplayAtMutation('update', {\n operation: 'redo',\n input: rawInput,\n ctx,\n logEntry: redoLogEntry,\n })\n }\n await lockRoleWriterAuthorizationState(em, [parsed.id])\n if (wantsTenantChange) {\n await em.nativeDelete(RoleAcl, { role: parsed.id as unknown as Role })\n }\n role = await de.updateOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(parsed.id, scope),\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined && scope.isSuperAdmin) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n }], {\n transaction: true,\n label: redoLogEntry ? 'auth.roles.update.redo' : 'auth.roles.update',\n })\n } catch (error) {\n if (!isUniqueViolation(error, 'roles_tenant_id_name_unique')) throw error\n const { translate } = await resolveTranslations()\n const message = translate('auth.roles.errors.nameExists', 'A role with this name already exists')\n throw new CrudHttpError(400, {\n error: message,\n fieldErrors: { name: message },\n details: [{ path: ['name'], message, code: 'duplicate', origin: 'validation' }],\n })\n }\n const updatedRole = role as Role | null\n if (!updatedRole) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updatedRole,\n identifiers: {\n id: String(updatedRole.id),\n organizationId: null,\n tenantId: updatedRole.tenantId ? String(updatedRole.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return updatedRole\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveCommandEm(ctx, true)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = resolveCommandEm(ctx, true)\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const storedReplayInput = extractStoredReplayInput(logEntry.commandPayload, logEntry)\n const replayInput = Object.keys(storedReplayInput).length\n ? storedReplayInput\n : deriveRoleUpdateReplayInput(before, after ?? null)\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let updated: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('update', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n if ('name' in replayInput) entity.name = before.name\n if ('tenantId' in replayInput) entity.tenantId = before.tenantId\n },\n })\n const restoresAcls = 'tenantId' in replayInput && after?.tenantId !== before.tenantId\n if (updated && restoresAcls) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n }], { transaction: true, label: 'auth.roles.update.undo' })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n redo: ({ input, ctx, logEntry }) => updateRoleCommand.execute(input, withRoleRedoLog(ctx, logEntry)),\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n atomicReplay: true,\n stabilizeReplay: stabilizeRoleReplay,\n authorizeReplay: (params) => authorizeRoleReplay('delete', params),\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) return {}\n const actorTenantScope = resolveActorTenantScope(ctx)\n if (actorTenantScope) {\n const targetTenant = normalizeTenantId(existing.tenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) return {}\n }\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = resolveCommandEm(ctx)\n const scope = await resolveActorScope(ctx)\n const role = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), role.tenantId)\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const redoLogEntry = (ctx as RoleReplayRuntimeContext)[ROLE_REDO_LOG]\n let deleted: Role | null = null\n await withAtomicFlush(em, [async () => {\n if (redoLogEntry) {\n await authorizeRoleReplayAtMutation('delete', {\n operation: 'redo',\n input,\n ctx,\n logEntry: redoLogEntry,\n })\n }\n await lockRoleWriterAuthorizationState(em, [id])\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n await em.nativeDelete(RoleAcl, { role: id })\n deleted = await de.deleteOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(id, scope),\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n }], {\n transaction: true,\n label: redoLogEntry ? 'auth.roles.delete.redo' : 'auth.roles.delete',\n })\n const deletedRole = deleted as Role | null\n if (!deletedRole) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deletedRole,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deletedRole.tenantId ? String(deletedRole.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deletedRole\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ input, logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = resolveCommandEm(ctx)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role: Role | null = null\n await withAtomicFlush(em, [async () => {\n await authorizeRoleReplayAtMutation('delete', {\n operation: 'undo',\n input,\n ctx,\n logEntry,\n })\n role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }], { transaction: true, label: 'auth.roles.delete.undo' })\n if (!role) throw new CrudHttpError(400, { error: '[internal] undo failed to restore role row' })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n redo: ({ input, ctx, logEntry }) => deleteRoleCommand.execute(input, withRoleRedoLog(ctx, logEntry)),\n}\n\ntype RoleReplayCommandKind = 'create' | 'update' | 'delete'\n\nfunction normalizeRoleAclSnapshots(acls: RoleAclSnapshot[]): RoleAclSnapshot[] {\n return acls\n .map((acl) => ({\n ...acl,\n features: acl.features ? [...acl.features].sort((left, right) => left.localeCompare(right)) : null,\n organizations: acl.organizations ? [...acl.organizations].sort((left, right) => left.localeCompare(right)) : null,\n }))\n .sort((left, right) => {\n const tenantOrder = left.tenantId.localeCompare(right.tenantId)\n if (tenantOrder !== 0) return tenantOrder\n return (left.id ?? '').localeCompare(right.id ?? '')\n })\n}\n\nfunction normalizeRoleReplaySnapshot(snapshot: RoleUndoSnapshot): RoleUndoSnapshot {\n return {\n ...snapshot,\n acls: normalizeRoleAclSnapshots(snapshot.acls),\n }\n}\n\nfunction toRoleUpdateReplayState(snapshot: RoleUndoSnapshot): SerializedRole {\n return {\n name: snapshot.name,\n tenantId: snapshot.tenantId,\n ...(snapshot.custom && Object.keys(snapshot.custom).length ? { custom: snapshot.custom } : {}),\n }\n}\n\nfunction deriveRoleUpdateReplayInput(\n before: RoleUndoSnapshot | null,\n after: RoleUndoSnapshot | null,\n): Record<string, unknown> {\n if (!before) return {}\n if (!after) return { id: before.id, name: before.name, tenantId: before.tenantId }\n const input: Record<string, unknown> = { id: after.id }\n if (before.name !== after.name) input.name = after.name\n if (before.tenantId !== after.tenantId) input.tenantId = after.tenantId\n return input\n}\n\nasync function authorizeRoleReplay(\n commandKind: RoleReplayCommandKind,\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const { operation, ctx, logEntry } = params\n const em = requireTransactionalReplayEntityManager(ctx)\n await requireCurrentReplayFeature(ctx, 'auth.roles.manage')\n\n const undoPayload = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undoPayload?.before ?? null\n const after = undoPayload?.after ?? null\n const roleId = logEntry.resourceId ?? before?.id ?? after?.id ?? null\n if (!roleId) {\n await assertReplaySnapshotMatches(null, { required: 'role replay snapshot' })\n return\n }\n\n const currentRole = await findOneWithDecryption(\n em,\n Role,\n { id: roleId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n const rbacService = ctx.systemActor === true\n ? null\n : ctx.container.resolve('rbacService') as RbacService\n if (rbacService && currentRole) {\n await assertActorCanAccessRoleTarget({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n targetRoleId: roleId,\n })\n await assertActorCanModifySuperAdminRoleTarget({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n targetRoleId: roleId,\n })\n }\n\n let currentSnapshot: RoleUndoSnapshot | null = null\n if (currentRole) {\n const currentTenantId = currentRole.tenantId ? String(currentRole.tenantId) : null\n const [acls, custom] = await Promise.all([\n loadRoleAclSnapshots(em, roleId),\n loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: roleId,\n tenantId: currentTenantId,\n }),\n ])\n currentSnapshot = captureRoleSnapshots(currentRole, acls, custom).undo\n }\n\n const expectedCurrent = operation === 'undo'\n ? commandKind === 'create' ? after : commandKind === 'update' ? after : null\n : commandKind === 'create' ? null : before\n const storedReplayInput = extractStoredReplayInput(params.input, logEntry)\n const replayInput = Object.keys(storedReplayInput).length\n ? storedReplayInput\n : deriveRoleUpdateReplayInput(before, after)\n const movesTenant = commandKind === 'update'\n && 'tenantId' in replayInput\n && before?.tenantId !== after?.tenantId\n if (movesTenant) {\n await requireCurrentReplaySuperAdmin(ctx)\n }\n\n if (commandKind === 'update' && !movesTenant) {\n if (!currentSnapshot || !expectedCurrent) {\n await assertReplaySnapshotMatches(currentSnapshot, expectedCurrent)\n } else {\n await assertReplaySnapshotMatches(\n toRoleUpdateReplayState(currentSnapshot),\n toRoleUpdateReplayState(expectedCurrent),\n )\n }\n } else {\n await assertReplaySnapshotMatches(\n currentSnapshot ? normalizeRoleReplaySnapshot(currentSnapshot) : null,\n expectedCurrent ? normalizeRoleReplaySnapshot(expectedCurrent) : null,\n )\n }\n\n const desiredState = operation === 'undo'\n ? commandKind === 'create' ? null : before\n : commandKind === 'delete' ? null : after\n if (desiredState && !currentRole && rbacService) {\n const actorUserId = ctx.auth?.sub\n if (!actorUserId) throw forbidden()\n const actorAcl = await rbacService.loadAclWithEntityManager(em, actorUserId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n })\n if (!actorAcl.isSuperAdmin) assertRoleTenantInScope(ctx.auth?.tenantId ?? null, desiredState.tenantId)\n }\n const restoresAcls = Boolean(\n desiredState\n && desiredState.acls.length > 0\n && (commandKind !== 'update' || movesTenant),\n )\n if (!restoresAcls || !desiredState || !rbacService) return\n\n await requireCurrentReplayFeature(ctx, 'auth.acl.manage')\n for (const acl of desiredState.acls) {\n await assertActorCanGrantAcl({\n em,\n rbacService,\n requireBoundRbac: true,\n actorUserId: ctx.auth?.sub,\n tenantId: acl.tenantId,\n organizationId: null,\n features: acl.features,\n isSuperAdmin: acl.isSuperAdmin,\n organizations: acl.organizations,\n })\n }\n}\n\nasync function authorizeRoleReplayAtMutation(\n commandKind: RoleReplayCommandKind,\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const em = params.ctx.transactionalEm\n ?? params.ctx.container.resolve('em') as EntityManager\n const boundParams = params.ctx.transactionalEm\n ? params\n : { ...params, ctx: { ...params.ctx, transactionalEm: em } }\n await stabilizeRoleReplay(boundParams)\n await rerunReplayTransactionGuardAfterLocks(boundParams, em)\n await authorizeRoleReplay(commandKind, boundParams)\n}\n\nasync function stabilizeRoleReplay(\n params: CommandReplayAuthorizationArgs<unknown>,\n): Promise<void> {\n const undoPayload = extractUndoPayload<RoleUndoPayload>(params.logEntry)\n const before = undoPayload?.before ?? null\n const after = undoPayload?.after ?? null\n const targetRoleId = params.logEntry.resourceId ?? before?.id ?? after?.id ?? null\n const em = requireTransactionalReplayEntityManager(params.ctx)\n await lockReplayAuthorizationState(em, params.ctx, {\n targetRoleIds: targetRoleId ? [targetRoleId] : [],\n targetTenantIds: [\n ...(before?.tenantId ? [before.tenantId] : []),\n ...(after?.tenantId ? [after.tenantId] : []),\n ...(params.logEntry.tenantId ? [params.logEntry.tenantId] : []),\n ],\n })\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await lockRoleWriterAuthorizationState(em, [roleId])\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,51 @@ function stableScheduleUuid(stableKey) {
17
17
  function stablePollTickScheduleId(organizationId) {
18
18
  return stableScheduleUuid(`communication_channels:poll-tick:${organizationId}`);
19
19
  }
20
+ async function ensureCommunicationChannelSchedules(ctx) {
21
+ const cradle = ctx.container;
22
+ if (typeof cradle.hasRegistration !== "function" || !cradle.hasRegistration("schedulerService")) return;
23
+ const schedulerService = ctx.container.resolve("schedulerService");
24
+ try {
25
+ await schedulerService.register({
26
+ id: stablePollTickScheduleId(ctx.organizationId),
27
+ name: "Communication channels poll tick",
28
+ description: `Enumerates active polling channels every ${POLL_TICK_INTERVAL_SECONDS}s and enqueues per-channel poll jobs.`,
29
+ scopeType: "organization",
30
+ organizationId: ctx.organizationId,
31
+ tenantId: ctx.tenantId,
32
+ scheduleType: "interval",
33
+ scheduleValue: `${POLL_TICK_INTERVAL_SECONDS}s`,
34
+ timezone: "UTC",
35
+ targetType: "queue",
36
+ targetQueue: COMMUNICATION_CHANNELS_QUEUES.pollTick,
37
+ targetPayload: {
38
+ scope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
39
+ },
40
+ sourceType: "module",
41
+ sourceModule: "communication_channels",
42
+ isEnabled: true
43
+ });
44
+ await schedulerService.register({
45
+ id: stableScheduleUuid(`communication_channels:gmail-renew-watch:${ctx.organizationId}`),
46
+ name: "Gmail watch renewal",
47
+ description: "Daily 04:00 UTC. Re-issues gmail.users.watch for channels within OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS of expiry.",
48
+ scopeType: "organization",
49
+ organizationId: ctx.organizationId,
50
+ tenantId: ctx.tenantId,
51
+ scheduleType: "cron",
52
+ scheduleValue: "0 4 * * *",
53
+ timezone: "UTC",
54
+ targetType: "queue",
55
+ targetQueue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
56
+ targetPayload: { scope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId } },
57
+ sourceType: "module",
58
+ sourceModule: "communication_channels",
59
+ isEnabled: true
60
+ });
61
+ } catch (error) {
62
+ logger.warn("Failed to register module schedules", { err: error });
63
+ }
64
+ }
20
65
  const setup = {
21
66
  defaultRoleFeatures: {
22
67
  superadmin: [
@@ -110,51 +155,10 @@ const setup = {
110
155
  });
111
156
  }
112
157
  }
113
- const cradle = container;
114
- if (typeof cradle.hasRegistration !== "function" || !cradle.hasRegistration("schedulerService")) {
115
- return;
116
- }
117
- const schedulerService = container.resolve("schedulerService");
118
- try {
119
- await schedulerService.register({
120
- id: stablePollTickScheduleId(organizationId),
121
- name: "Communication channels poll tick",
122
- description: `Enumerates active polling channels every ${POLL_TICK_INTERVAL_SECONDS}s and enqueues per-channel poll jobs.`,
123
- scopeType: "organization",
124
- organizationId,
125
- tenantId,
126
- scheduleType: "interval",
127
- scheduleValue: `${POLL_TICK_INTERVAL_SECONDS}s`,
128
- timezone: "UTC",
129
- targetType: "queue",
130
- targetQueue: COMMUNICATION_CHANNELS_QUEUES.pollTick,
131
- targetPayload: {
132
- scope: { tenantId, organizationId }
133
- },
134
- sourceType: "module",
135
- sourceModule: "communication_channels",
136
- isEnabled: true
137
- });
138
- await schedulerService.register({
139
- id: stableScheduleUuid(`communication_channels:gmail-renew-watch:${organizationId}`),
140
- name: "Gmail watch renewal",
141
- description: "Daily 04:00 UTC. Re-issues gmail.users.watch for channels within OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS of expiry.",
142
- scopeType: "organization",
143
- organizationId,
144
- tenantId,
145
- scheduleType: "cron",
146
- scheduleValue: "0 4 * * *",
147
- timezone: "UTC",
148
- targetType: "queue",
149
- targetQueue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
150
- targetPayload: { scope: { tenantId, organizationId } },
151
- sourceType: "module",
152
- sourceModule: "communication_channels",
153
- isEnabled: true
154
- });
155
- } catch (error) {
156
- logger.warn("Failed to register module schedules", { err: error });
157
- }
158
+ await ensureCommunicationChannelSchedules({ container, tenantId, organizationId });
159
+ },
160
+ async onOrganizationCreated({ container, tenantId, organizationId }) {
161
+ await ensureCommunicationChannelSchedules({ container, tenantId, organizationId });
158
162
  }
159
163
  };
160
164
  var setup_default = setup;