@open-mercato/core 0.6.7-develop.6706.1.b3a4c759bb → 0.6.7-develop.6726.1.983ae8a07e

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 (117) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/admin/nav.js +14 -4
  3. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  4. package/dist/modules/auth/api/users/route.js +8 -19
  5. package/dist/modules/auth/api/users/route.js.map +2 -2
  6. package/dist/modules/auth/lib/backendChrome.js +31 -14
  7. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  8. package/dist/modules/customers/api/utils.js +12 -18
  9. package/dist/modules/customers/api/utils.js.map +2 -2
  10. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +17 -3
  11. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +2 -2
  12. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +7 -1
  13. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  14. package/dist/modules/customers/cli.js +11 -4
  15. package/dist/modules/customers/cli.js.map +2 -2
  16. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +4 -2
  17. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  18. package/dist/modules/customers/components/formConfig.js +14 -10
  19. package/dist/modules/customers/components/formConfig.js.map +2 -2
  20. package/dist/modules/dashboards/api/widgets/data/batch/route.js +4 -2
  21. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  22. package/dist/modules/dashboards/api/widgets/data/route.js +20 -2
  23. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  24. package/dist/modules/dashboards/lib/aggregations.js +76 -40
  25. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  26. package/dist/modules/dashboards/lib/exactDecimal.js +69 -0
  27. package/dist/modules/dashboards/lib/exactDecimal.js.map +7 -0
  28. package/dist/modules/dashboards/services/widgetDataService.js +228 -5
  29. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  30. package/dist/modules/feature_toggles/components/FeatureGuard.js +3 -2
  31. package/dist/modules/feature_toggles/components/FeatureGuard.js.map +2 -2
  32. package/dist/modules/feature_toggles/components/hooks/staleTime.js +5 -0
  33. package/dist/modules/feature_toggles/components/hooks/staleTime.js.map +7 -0
  34. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js +5 -2
  35. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js.map +2 -2
  36. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js +5 -2
  37. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js.map +2 -2
  38. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js +5 -2
  39. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js.map +2 -2
  40. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js +5 -2
  41. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js.map +2 -2
  42. package/dist/modules/inbox_ops/api/proposals/route.js +16 -1
  43. package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
  44. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js +32 -0
  45. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js.map +7 -0
  46. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -5
  47. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
  48. package/dist/modules/integrations/widgets/injection/external-ids/widget.js +14 -0
  49. package/dist/modules/integrations/widgets/injection/external-ids/widget.js.map +7 -0
  50. package/dist/modules/messages/lib/searchLookup.js +8 -16
  51. package/dist/modules/messages/lib/searchLookup.js.map +2 -2
  52. package/dist/modules/query_index/api/openapi.js +13 -1
  53. package/dist/modules/query_index/api/openapi.js.map +2 -2
  54. package/dist/modules/query_index/api/status.js +165 -105
  55. package/dist/modules/query_index/api/status.js.map +2 -2
  56. package/dist/modules/query_index/components/QueryIndexesTable.js +128 -41
  57. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  58. package/dist/modules/query_index/lib/batch.js +11 -4
  59. package/dist/modules/query_index/lib/batch.js.map +2 -2
  60. package/dist/modules/query_index/lib/document.js +12 -5
  61. package/dist/modules/query_index/lib/document.js.map +2 -2
  62. package/dist/modules/query_index/lib/engine.js +36 -4
  63. package/dist/modules/query_index/lib/engine.js.map +2 -2
  64. package/dist/modules/query_index/lib/indexer.js +1 -1
  65. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  66. package/dist/modules/query_index/lib/search-tokens.js +7 -4
  67. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  68. package/dist/modules/sales/api/documents/factory.js +14 -3
  69. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  70. package/dist/modules/sales/commands/documents.js +22 -1
  71. package/dist/modules/sales/commands/documents.js.map +2 -2
  72. package/dist/modules/sales/components/useSalesChannelsEnabled.js +2 -35
  73. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +2 -2
  74. package/dist/modules/sales/data/validators.js +25 -4
  75. package/dist/modules/sales/data/validators.js.map +2 -2
  76. package/package.json +7 -7
  77. package/src/modules/auth/api/admin/nav.ts +24 -4
  78. package/src/modules/auth/api/users/route.ts +8 -28
  79. package/src/modules/auth/lib/backendChrome.tsx +51 -13
  80. package/src/modules/customers/api/utils.ts +12 -31
  81. package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +35 -3
  82. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +10 -1
  83. package/src/modules/customers/cli.ts +11 -4
  84. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +4 -0
  85. package/src/modules/customers/components/formConfig.tsx +12 -8
  86. package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -0
  87. package/src/modules/dashboards/api/widgets/data/route.ts +21 -0
  88. package/src/modules/dashboards/lib/aggregations.ts +122 -43
  89. package/src/modules/dashboards/lib/exactDecimal.ts +96 -0
  90. package/src/modules/dashboards/services/widgetDataService.ts +342 -4
  91. package/src/modules/feature_toggles/components/FeatureGuard.tsx +9 -2
  92. package/src/modules/feature_toggles/components/hooks/staleTime.ts +5 -0
  93. package/src/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.ts +13 -1
  94. package/src/modules/feature_toggles/components/hooks/useFeatureFlagJson.ts +12 -3
  95. package/src/modules/feature_toggles/components/hooks/useFeatureFlagNumber.ts +10 -1
  96. package/src/modules/feature_toggles/components/hooks/useFeatureFlagString.ts +10 -1
  97. package/src/modules/inbox_ops/api/proposals/route.ts +20 -1
  98. package/src/modules/inbox_ops/subscribers/search-reindex-proposal-created.ts +39 -0
  99. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -5
  100. package/src/modules/integrations/widgets/injection/external-ids/widget.ts +13 -0
  101. package/src/modules/messages/lib/searchLookup.ts +11 -34
  102. package/src/modules/query_index/api/openapi.ts +12 -0
  103. package/src/modules/query_index/api/status.ts +238 -136
  104. package/src/modules/query_index/components/QueryIndexesTable.tsx +192 -46
  105. package/src/modules/query_index/i18n/de.json +5 -3
  106. package/src/modules/query_index/i18n/en.json +5 -3
  107. package/src/modules/query_index/i18n/es.json +5 -3
  108. package/src/modules/query_index/i18n/pl.json +5 -3
  109. package/src/modules/query_index/lib/batch.ts +11 -4
  110. package/src/modules/query_index/lib/document.ts +35 -4
  111. package/src/modules/query_index/lib/engine.ts +53 -5
  112. package/src/modules/query_index/lib/indexer.ts +1 -1
  113. package/src/modules/query_index/lib/search-tokens.ts +13 -4
  114. package/src/modules/sales/api/documents/factory.ts +28 -3
  115. package/src/modules/sales/commands/documents.ts +26 -2
  116. package/src/modules/sales/components/useSalesChannelsEnabled.ts +2 -48
  117. package/src/modules/sales/data/validators.ts +50 -5
@@ -1,4 +1,4 @@
1
- [build:core] found 3676 entry points
1
+ [build:core] found 3685 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 197 entry points
4
4
  [build:core:generated] built successfully
@@ -4,11 +4,13 @@ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
6
6
  import { getBackendRouteManifests } from "@open-mercato/shared/modules/registry";
7
+ import { getModuleSurfaceFingerprint } from "@open-mercato/shared/lib/modules/surfaceFingerprint";
7
8
  import { resolveFeatureCheckContext } from "@open-mercato/core/modules/directory/utils/organizationScope";
8
9
  import { groupBackendRoutesByModule, resolveBackendChromePayload } from "../../lib/backendChrome.js";
9
10
  const metadata = {
10
11
  GET: { requireAuth: true }
11
12
  };
13
+ const NAV_CACHE_TTL_MS = 30 * 60 * 1e3;
12
14
  const sidebarNavItemSchema = z.lazy(
13
15
  () => z.object({
14
16
  id: z.string().optional(),
@@ -63,7 +65,14 @@ const adminNavResponseSchema = z.object({
63
65
  profileSections: z.array(sectionGroupSchema),
64
66
  profilePathPrefixes: z.array(z.string()),
65
67
  grantedFeatures: z.array(z.string()),
66
- roles: z.array(z.string())
68
+ roles: z.array(z.string()),
69
+ // Present when a single organization is in scope; `null` under an all-organizations selection or
70
+ // when the lookup fails. Declared optional so the response contract stays additive for clients
71
+ // generated against an older schema.
72
+ currentOrganization: z.object({
73
+ id: z.string(),
74
+ name: z.string()
75
+ }).nullable().optional()
67
76
  });
68
77
  const adminNavErrorSchema = z.object({
69
78
  error: z.string()
@@ -104,8 +113,9 @@ async function GET(req) {
104
113
  selectedOrganizationId = auth.orgId ?? null;
105
114
  selectedTenantId = auth.tenantId ?? null;
106
115
  }
107
- const cacheVersion = "v4";
108
- const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || "null"}:${cacheScopeOrganizationId || "null"}`;
116
+ const cacheVersion = `v6:${getModuleSurfaceFingerprint()}`;
117
+ const cacheSelection = selectedOrganizationId === void 0 ? "default" : selectedOrganizationId ?? "null";
118
+ const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || "null"}:${cacheScopeOrganizationId || "null"}:${cacheSelection}`;
109
119
  try {
110
120
  if (cache?.get) {
111
121
  const cached = await cache.get(cacheKey);
@@ -135,7 +145,7 @@ async function GET(req) {
135
145
  `nav:sidebar:scope:${auth.sub}:${cacheScopeTenantId || "null"}:${cacheScopeOrganizationId || "null"}:${locale}`,
136
146
  ...(Array.isArray(auth.roles) ? auth.roles : []).map((role) => `nav:sidebar:role:${role}`)
137
147
  ].filter(Boolean);
138
- await cache.set(cacheKey, payload, { tags });
148
+ await cache.set(cacheKey, payload, { tags, ttl: NAV_CACHE_TTL_MS });
139
149
  }
140
150
  } catch {
141
151
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/auth/api/admin/nav.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getBackendRouteManifests } from '@open-mercato/shared/modules/registry'\nimport { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { groupBackendRoutesByModule, resolveBackendChromePayload } from '../../lib/backendChrome'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\nconst sidebarNavItemSchema: z.ZodType<{\n id?: string\n href: string\n title: string\n defaultTitle?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n iconName?: string\n iconMarkup?: string\n children?: any[]\n}> = z.lazy(() =>\n z.object({\n id: z.string().optional(),\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string().optional(),\n enabled: z.boolean().optional(),\n hidden: z.boolean().optional(),\n pageContext: z.enum(['main', 'admin', 'settings', 'profile']).optional(),\n iconName: z.string().optional(),\n iconMarkup: z.string().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n }),\n)\n\nconst sectionItemSchema: z.ZodType<{\n id: string\n label: string\n labelKey?: string\n href: string\n order?: number\n iconName?: string\n iconMarkup?: string\n children?: any[]\n}> = z.lazy(() =>\n z.object({\n id: z.string(),\n label: z.string(),\n labelKey: z.string().optional(),\n href: z.string(),\n order: z.number().optional(),\n iconName: z.string().optional(),\n iconMarkup: z.string().optional(),\n children: z.array(sectionItemSchema).optional(),\n }),\n)\n\nconst sectionGroupSchema = z.object({\n id: z.string(),\n label: z.string(),\n labelKey: z.string().optional(),\n order: z.number().optional(),\n items: z.array(sectionItemSchema),\n})\n\nconst adminNavResponseSchema = z.object({\n brand: z.object({\n name: z.string().optional(),\n logo: z.object({\n src: z.string(),\n alt: z.string().optional(),\n }).nullable().optional(),\n }).nullable().optional(),\n groups: z.array(\n z.object({\n id: z.string().optional(),\n name: z.string(),\n defaultName: z.string().optional(),\n items: z.array(sidebarNavItemSchema),\n }),\n ),\n settingsSections: z.array(sectionGroupSchema),\n settingsPathPrefixes: z.array(z.string()),\n profileSections: z.array(sectionGroupSchema),\n profilePathPrefixes: z.array(z.string()),\n grantedFeatures: z.array(z.string()),\n roles: z.array(z.string()),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n const container = await createRequestContainer()\n const cache = container.resolve('cache') as {\n get?: (key: string) => Promise<unknown>\n set?: (key: string, value: unknown, options?: { tags?: string[] }) => Promise<void>\n } | null\n let selectedOrganizationId: string | null | undefined\n let selectedTenantId: string | null | undefined\n try {\n const url = new URL(req.url)\n const orgParam = url.searchParams.get('orgId')\n const tenantParam = url.searchParams.get('tenantId')\n selectedOrganizationId = orgParam === null ? undefined : orgParam || null\n selectedTenantId = tenantParam === null ? undefined : tenantParam || null\n } catch {\n selectedOrganizationId = undefined\n selectedTenantId = undefined\n }\n\n let cacheScopeTenantId = auth.tenantId ?? null\n let cacheScopeOrganizationId = auth.orgId ?? null\n try {\n const { organizationId, scope } = await resolveFeatureCheckContext({\n container,\n auth,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n request: req,\n })\n cacheScopeOrganizationId = organizationId\n cacheScopeTenantId = scope.tenantId ?? auth.tenantId ?? null\n } catch {\n cacheScopeOrganizationId = auth.orgId ?? null\n cacheScopeTenantId = auth.tenantId ?? null\n selectedOrganizationId = auth.orgId ?? null\n selectedTenantId = auth.tenantId ?? null\n }\n\n const cacheVersion = 'v4'\n const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}`\n try {\n if (cache?.get) {\n const cached = await cache.get(cacheKey)\n if (cached) return NextResponse.json(cached)\n }\n } catch {\n // ignore cache read failures\n }\n\n const payload = await resolveBackendChromePayload({\n auth,\n locale,\n modules: groupBackendRoutesByModule(getBackendRouteManifests()),\n translate: (key, fallback) => (key ? translate(key, fallback) : fallback),\n request: req,\n selectedOrganizationId,\n selectedTenantId,\n })\n\n try {\n if (cache?.set) {\n const tags = [\n `rbac:user:${auth.sub}`,\n cacheScopeTenantId ? `rbac:tenant:${cacheScopeTenantId}` : undefined,\n `nav:entities:${cacheScopeTenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n cacheScopeTenantId ? `nav:sidebar:tenant:${cacheScopeTenantId}` : undefined,\n cacheScopeOrganizationId ? `nav:sidebar:organization:${cacheScopeOrganizationId}` : undefined,\n `nav:sidebar:scope:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}:${locale}`,\n ...((Array.isArray(auth.roles) ? auth.roles : []).map((role) => `nav:sidebar:role:${role}`)),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags })\n }\n } catch {\n // ignore cache write failures\n }\n\n return NextResponse.json(payload)\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve backend chrome bootstrap payload',\n description:\n 'Returns the backend chrome payload available to the authenticated administrator after applying scope, RBAC, role defaults, and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Backend chrome payload', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { z } from 'zod'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getBackendRouteManifests } from '@open-mercato/shared/modules/registry'\nimport { getModuleSurfaceFingerprint } from '@open-mercato/shared/lib/modules/surfaceFingerprint'\nimport { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { groupBackendRoutesByModule, resolveBackendChromePayload } from '../../lib/backendChrome'\n\nexport const metadata = {\n GET: { requireAuth: true },\n}\n\n// The fingerprint covers the module set, each module's declared features and\n// the serializable route manifest, so the TTL only has to bound what it cannot\n// see \u2014 chiefly a route `icon` swap, which serializes identically. Rebuilding\n// this payload is expensive (a `renderToStaticMarkup` per nav icon plus several\n// scoped queries), so the bound is generous rather than aggressive.\nconst NAV_CACHE_TTL_MS = 30 * 60 * 1000\n\nconst sidebarNavItemSchema: z.ZodType<{\n id?: string\n href: string\n title: string\n defaultTitle?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n iconName?: string\n iconMarkup?: string\n children?: any[]\n}> = z.lazy(() =>\n z.object({\n id: z.string().optional(),\n href: z.string(),\n title: z.string(),\n defaultTitle: z.string().optional(),\n enabled: z.boolean().optional(),\n hidden: z.boolean().optional(),\n pageContext: z.enum(['main', 'admin', 'settings', 'profile']).optional(),\n iconName: z.string().optional(),\n iconMarkup: z.string().optional(),\n children: z.array(sidebarNavItemSchema).optional(),\n }),\n)\n\nconst sectionItemSchema: z.ZodType<{\n id: string\n label: string\n labelKey?: string\n href: string\n order?: number\n iconName?: string\n iconMarkup?: string\n children?: any[]\n}> = z.lazy(() =>\n z.object({\n id: z.string(),\n label: z.string(),\n labelKey: z.string().optional(),\n href: z.string(),\n order: z.number().optional(),\n iconName: z.string().optional(),\n iconMarkup: z.string().optional(),\n children: z.array(sectionItemSchema).optional(),\n }),\n)\n\nconst sectionGroupSchema = z.object({\n id: z.string(),\n label: z.string(),\n labelKey: z.string().optional(),\n order: z.number().optional(),\n items: z.array(sectionItemSchema),\n})\n\nconst adminNavResponseSchema = z.object({\n brand: z.object({\n name: z.string().optional(),\n logo: z.object({\n src: z.string(),\n alt: z.string().optional(),\n }).nullable().optional(),\n }).nullable().optional(),\n groups: z.array(\n z.object({\n id: z.string().optional(),\n name: z.string(),\n defaultName: z.string().optional(),\n items: z.array(sidebarNavItemSchema),\n }),\n ),\n settingsSections: z.array(sectionGroupSchema),\n settingsPathPrefixes: z.array(z.string()),\n profileSections: z.array(sectionGroupSchema),\n profilePathPrefixes: z.array(z.string()),\n grantedFeatures: z.array(z.string()),\n roles: z.array(z.string()),\n // Present when a single organization is in scope; `null` under an all-organizations selection or\n // when the lookup fails. Declared optional so the response contract stays additive for clients\n // generated against an older schema.\n currentOrganization: z.object({\n id: z.string(),\n name: z.string(),\n }).nullable().optional(),\n})\n\nconst adminNavErrorSchema = z.object({\n error: z.string(),\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { translate, locale } = await resolveTranslations()\n const container = await createRequestContainer()\n const cache = container.resolve('cache') as {\n get?: (key: string) => Promise<unknown>\n set?: (key: string, value: unknown, options?: { tags?: string[]; ttl?: number }) => Promise<void>\n } | null\n let selectedOrganizationId: string | null | undefined\n let selectedTenantId: string | null | undefined\n try {\n const url = new URL(req.url)\n const orgParam = url.searchParams.get('orgId')\n const tenantParam = url.searchParams.get('tenantId')\n selectedOrganizationId = orgParam === null ? undefined : orgParam || null\n selectedTenantId = tenantParam === null ? undefined : tenantParam || null\n } catch {\n selectedOrganizationId = undefined\n selectedTenantId = undefined\n }\n\n let cacheScopeTenantId = auth.tenantId ?? null\n let cacheScopeOrganizationId = auth.orgId ?? null\n try {\n const { organizationId, scope } = await resolveFeatureCheckContext({\n container,\n auth,\n selectedId: selectedOrganizationId,\n tenantId: selectedTenantId,\n request: req,\n })\n cacheScopeOrganizationId = organizationId\n cacheScopeTenantId = scope.tenantId ?? auth.tenantId ?? null\n } catch {\n cacheScopeOrganizationId = auth.orgId ?? null\n cacheScopeTenantId = auth.tenantId ?? null\n selectedOrganizationId = auth.orgId ?? null\n selectedTenantId = auth.tenantId ?? null\n }\n\n // v6: the payload gained `currentOrganization`, and the payload also embeds the enabled-module set,\n // its declared features, and the backend route manifest. The selection is part of the key because\n // the resolved organization cannot distinguish \"all organizations\" from \"my own organization\".\n // The fingerprint invalidates module-surface changes; the TTL bounds anything it cannot observe.\n const cacheVersion = `v6:${getModuleSurfaceFingerprint()}`\n const cacheSelection = selectedOrganizationId === undefined ? 'default' : (selectedOrganizationId ?? 'null')\n const cacheKey = `nav:sidebar:${cacheVersion}:${locale}:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}:${cacheSelection}`\n try {\n if (cache?.get) {\n const cached = await cache.get(cacheKey)\n if (cached) return NextResponse.json(cached)\n }\n } catch {\n // ignore cache read failures\n }\n\n const payload = await resolveBackendChromePayload({\n auth,\n locale,\n modules: groupBackendRoutesByModule(getBackendRouteManifests()),\n translate: (key, fallback) => (key ? translate(key, fallback) : fallback),\n request: req,\n selectedOrganizationId,\n selectedTenantId,\n })\n\n try {\n if (cache?.set) {\n const tags = [\n `rbac:user:${auth.sub}`,\n cacheScopeTenantId ? `rbac:tenant:${cacheScopeTenantId}` : undefined,\n `nav:entities:${cacheScopeTenantId || 'null'}`,\n `nav:locale:${locale}`,\n `nav:sidebar:user:${auth.sub}`,\n cacheScopeTenantId ? `nav:sidebar:tenant:${cacheScopeTenantId}` : undefined,\n cacheScopeOrganizationId ? `nav:sidebar:organization:${cacheScopeOrganizationId}` : undefined,\n `nav:sidebar:scope:${auth.sub}:${cacheScopeTenantId || 'null'}:${cacheScopeOrganizationId || 'null'}:${locale}`,\n ...((Array.isArray(auth.roles) ? auth.roles : []).map((role) => `nav:sidebar:role:${role}`)),\n ].filter(Boolean) as string[]\n await cache.set(cacheKey, payload, { tags, ttl: NAV_CACHE_TTL_MS })\n }\n } catch {\n // ignore cache write failures\n }\n\n return NextResponse.json(payload)\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'Admin sidebar navigation',\n methods: {\n GET: {\n summary: 'Resolve backend chrome bootstrap payload',\n description:\n 'Returns the backend chrome payload available to the authenticated administrator after applying scope, RBAC, role defaults, and personal sidebar preferences.',\n responses: [\n { status: 200, description: 'Backend chrome payload', schema: adminNavResponseSchema },\n { status: 401, description: 'Unauthorized', schema: adminNavErrorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -19,9 +19,7 @@ import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/
19
19
  import { buildPasswordSchema } from "@open-mercato/shared/lib/auth/passwordPolicy";
20
20
  import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
21
21
  import { parseBooleanFlag } from "@open-mercato/shared/lib/boolean";
22
- import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
23
- import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
24
- import { sql } from "kysely";
22
+ import { findEntityIdsBySearchTokensCompat } from "@open-mercato/shared/lib/search/tokenLookup";
25
23
  import { normalizeDisplayNameInput } from "@open-mercato/core/modules/auth/lib/displayName";
26
24
  import {
27
25
  getSelectedTenantFromRequest,
@@ -444,22 +442,13 @@ const DELETE = async (req) => {
444
442
  return crud.DELETE(req);
445
443
  };
446
444
  async function findUserIdsBySearchTokens(em, entityType, search, tenantScope, field) {
447
- const trimmed = search.trim();
448
- if (!trimmed) return null;
449
- const searchConfig = resolveSearchConfig();
450
- if (!searchConfig.enabled) return [];
451
- const { hashes } = tokenizeText(trimmed, searchConfig);
452
- if (!hashes.length) return [];
453
- const db = em.getKysely();
454
- let query = db.selectFrom("search_tokens").select("entity_id").where("entity_type", "=", entityType).where("token_hash", "in", hashes).groupBy("entity_id").having(sql`count(distinct token_hash) >= ${hashes.length}`);
455
- if (field) {
456
- query = query.where("field", "=", field);
457
- }
458
- if (tenantScope !== void 0) {
459
- query = query.where(sql`tenant_id is not distinct from ${tenantScope}`);
460
- }
461
- const rows = await query.execute();
462
- return rows.map((row) => typeof row.entity_id === "string" ? row.entity_id : null).filter((id) => typeof id === "string" && id.length > 0);
445
+ return findEntityIdsBySearchTokensCompat({
446
+ db: em.getKysely(),
447
+ entityType,
448
+ query: search,
449
+ fields: field ? [field] : void 0,
450
+ scope: { tenantId: tenantScope }
451
+ });
463
452
  }
464
453
  async function assertCanModifySuperAdminTarget(req, targetUserId) {
465
454
  const auth = await getAuthFromRequest(req);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/auth/api/users/route.ts"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport {\n assertActorCanAccessUserTarget,\n assertActorCanGrantRoleTokens,\n assertActorCanModifySuperAdminUserTarget,\n listSuperAdminUserIds,\n} from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { parseBooleanFlag } from '@open-mercato/shared/lib/boolean'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\nimport { normalizeDisplayNameInput } from '@open-mercato/core/modules/auth/lib/displayName'\nimport {\n getSelectedTenantFromRequest,\n resolveOrganizationScopeForRequest,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'users' })\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n name: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n scopeToActiveOrganization: z.boolean().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst passwordSchema = buildPasswordSchema()\n\nconst displayNameSchema = z.preprocess(\n normalizeDisplayNameInput,\n z.string().trim().min(1).max(120).nullable().optional(),\n)\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n name: z.string().nullable(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n roleIds: z.array(z.string().uuid()).optional(),\n hasPassword: z.boolean().optional(),\n updatedAt: z.string().nullable().optional(),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\ntype UserListFilter = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: ({ result }) => ({\n id: String(result.user.id),\n ...(result.warning ? { _warning: result.warning } : {}),\n }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n if (typeof parsed.id === 'string' && parsed.id.length) {\n await assertCanModifySuperAdminTarget(ctx.request, parsed.id)\n await assertCanAccessUserTarget(ctx.request, parsed.id)\n }\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n mapInput: async ({ parsed, raw, ctx }) => {\n const targetId = resolveDeleteTargetId(parsed, raw)\n if (ctx.request && targetId) {\n await assertCanModifySuperAdminTarget(ctx.request, targetId)\n await assertCanAccessUserTarget(ctx.request, targetId)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n name: url.searchParams.get('name') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n scopeToActiveOrganization: parseBooleanFlag(url.searchParams.get('scopeToActiveOrganization') || undefined),\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = auth.isSuperAdmin === true\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = isSuperAdmin || !!acl?.isSuperAdmin\n }\n } catch (err) {\n logger.error('Failed to resolve rbac', { err })\n }\n const { id, page, pageSize, search, name, organizationId, scopeToActiveOrganization, roleIds } = parsed.data\n const filters: any[] = [{ deletedAt: null }]\n const actorTenantId = auth.tenantId ? String(auth.tenantId) : null\n let effectiveTenantId: string | null = null\n let effectiveOrganizationIds: string[] | null = null\n let effectiveSelectedOrganizationId: string | null = null\n let usesSelectedTenantScope = false\n if (!isSuperAdmin) {\n if (!actorTenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = actorTenantId\n const superAdminUserIds = await listSuperAdminUserIds(em, actorTenantId)\n if (superAdminUserIds.size) {\n filters.push({ id: { $nin: Array.from(superAdminUserIds) as any } })\n }\n } else {\n const selectedTenantId = getSelectedTenantFromRequest(req)\n if (typeof selectedTenantId === 'string' && selectedTenantId.trim().length > 0) {\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n tenantId: selectedTenantId.trim(),\n })\n if (!scope.tenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = scope.tenantId\n effectiveSelectedOrganizationId = scope.selectedId\n usesSelectedTenantScope = true\n if (Array.isArray(scope.filterIds)) {\n if (scope.filterIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveOrganizationIds = scope.filterIds\n }\n }\n }\n if (effectiveTenantId) {\n filters.push({ tenantId: effectiveTenantId })\n }\n if (effectiveOrganizationIds) {\n filters.push({ organizationId: { $in: effectiveOrganizationIds as any } })\n }\n const scopeOrganizationId = usesSelectedTenantScope\n ? effectiveSelectedOrganizationId\n : auth.orgId ?? null\n if (organizationId) filters.push({ organizationId })\n // Recipient/assignee pickers scope to the caller's active organization so they never\n // suggest users outside it. A message composed here is stamped with the caller's\n // active org (auth.orgId), and the message detail endpoint enforces\n // hasOrganizationAccess(scope.organizationId, message.organizationId); scoping the\n // suggestions to the same org keeps a picked recipient able to open what they were sent.\n if (scopeToActiveOrganization) filters.push({ organizationId: auth.orgId ?? null })\n const trimmedName = typeof name === 'string' ? name.trim() : ''\n if (trimmedName) {\n const searchPattern = `%${escapeLikePattern(trimmedName)}%`\n const displayNameFilters: UserListFilter[] = [{ name: { $ilike: searchPattern } }]\n const nameTokenScope: string | null | undefined = isSuperAdmin ? (effectiveTenantId ?? undefined) : auth.tenantId ?? null\n const matchedDisplayNameIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedName, nameTokenScope, 'name')\n if (matchedDisplayNameIds && matchedDisplayNameIds.length) {\n displayNameFilters.push({ id: { $in: matchedDisplayNameIds } })\n }\n filters.push(displayNameFilters.length > 1 ? { $or: displayNameFilters } : displayNameFilters[0])\n }\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n const trimmedSearch = typeof search === 'string' ? search.trim() : ''\n if (trimmedSearch) {\n // Email is encrypted at rest, so plaintext search must go through search_tokens.\n const tenantScope: string | null | undefined = isSuperAdmin ? (effectiveTenantId ?? undefined) : auth.tenantId ?? null\n const searchFilters: any[] = []\n\n const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope)\n if (matchedIds && matchedIds.length) {\n searchFilters.push({ id: { $in: matchedIds as any } })\n }\n\n const searchPattern = `%${escapeLikePattern(trimmedSearch)}%`\n const organizationSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n organizationSearchFilters.push({ tenant: tenantScope })\n }\n const matchingOrganizations = await em.find(\n Organization,\n organizationSearchFilters.length > 1 ? { $and: organizationSearchFilters } : organizationSearchFilters[0],\n )\n const matchingOrganizationIds = matchingOrganizations\n .map((org) => (org?.id ? String(org.id) : null))\n .filter((orgId): orgId is string => typeof orgId === 'string' && orgId.length > 0)\n if (matchingOrganizationIds.length) {\n searchFilters.push({ organizationId: { $in: matchingOrganizationIds as any } })\n }\n\n const roleSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n roleSearchFilters.push({ $or: [{ tenantId: tenantScope }, { tenantId: null }] })\n }\n const matchingRoles = await em.find(\n Role,\n roleSearchFilters.length > 1 ? { $and: roleSearchFilters } : roleSearchFilters[0],\n )\n const matchingRoleIds = matchingRoles\n .map((role) => (role?.id ? String(role.id) : null))\n .filter((roleId): roleId is string => typeof roleId === 'string' && roleId.length > 0)\n if (matchingRoleIds.length) {\n const roleSearchLinks = await em.find(\n UserRole,\n { role: { $in: matchingRoleIds as any } } as any,\n )\n const matchingRoleUserIds = Array.from(new Set(\n roleSearchLinks\n .map((link) => {\n const userRef = (link as any).user\n const userId = userRef?.id ?? userRef\n return userId ? String(userId) : null\n })\n .filter((userId): userId is string => typeof userId === 'string' && userId.length > 0),\n ))\n if (matchingRoleUserIds.length) {\n searchFilters.push({ id: { $in: matchingRoleUserIds as any } })\n }\n }\n\n if (!searchFilters.length) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n\n filters.push(searchFilters.length > 1 ? { $or: searchFilters } : searchFilters[0])\n }\n if (idFilter && idFilter.size) {\n filters.push({ id: { $in: Array.from(idFilter) as any } })\n } else if (id) {\n filters.push({ id })\n }\n const where = filters.length > 1 ? { $and: filters } : filters[0]\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n {\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n organizationId: scopeOrganizationId,\n },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n const roleIdMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n const rid = String((l as any).role?.id ?? '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (!roleIdMap[uid]) roleIdMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n if (rid) roleIdMap[uid].push(rid)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: effectiveTenantId ? [effectiveTenantId] : auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n name: u.name ? String(u.name) : null,\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n roleIds: roleIdMap[uid] || [],\n ...(id ? { hasPassword: !!u.passwordHash } : {}),\n updatedAt: u.updatedAt instanceof Date ? u.updatedAt.toISOString() : null,\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: effectiveSelectedOrganizationId,\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n return crud.PUT(req)\n}\n\nexport const DELETE = async (req: Request) => {\n const targetId = new URL(req.url).searchParams.get('id')\n if (targetId) {\n try {\n await assertCanModifySuperAdminTarget(req, targetId)\n await assertCanAccessUserTarget(req, targetId)\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n }\n return crud.DELETE(req)\n}\n\nasync function findUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (field) {\n query = query.where('field', '=', field)\n }\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nasync function assertCanModifySuperAdminTarget(req: Request, targetUserId: string) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n await assertActorCanModifySuperAdminUserTarget({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n targetUserId,\n })\n}\n\nasync function assertCanAccessUserTarget(req: Request, targetUserId: string) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n await assertActorCanAccessUserTarget({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n targetUserId,\n })\n}\n\nfunction resolveDeleteTargetId(parsed: unknown, raw: unknown): string | null {\n const fromParsed = readId((parsed as Record<string, unknown> | null | undefined))\n if (fromParsed) return fromParsed\n const rawRecord = raw as { body?: Record<string, unknown>; query?: Record<string, unknown> } | null | undefined\n return readId(rawRecord?.query) ?? readId(rawRecord?.body)\n}\n\nfunction readId(record: Record<string, unknown> | null | undefined): string | null {\n const value = record?.id\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nasync function assertCanAssignRoles(req: Request, roles: unknown, payload: Record<string, unknown>) {\n if (!Array.isArray(roles)) return\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const tenantId = await resolveTargetTenantIdForRoleGrant(em, payload, auth.tenantId ?? null)\n await assertActorCanGrantRoleTokens({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId,\n organizationId: auth.orgId ?? null,\n roleTokens: roles,\n })\n}\n\nasync function resolveTargetTenantIdForRoleGrant(\n em: EntityManager,\n payload: Record<string, unknown>,\n fallbackTenantId: string | null,\n): Promise<string | null> {\n const organizationId = typeof payload.organizationId === 'string' ? payload.organizationId : null\n if (organizationId) {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )\n return organization?.tenant?.id ? String(organization.tenant.id) : fallbackTenantId\n }\n\n const userId = typeof payload.id === 'string' ? payload.id : null\n if (userId) {\n const user = await findOneWithDecryption(\n em,\n User,\n { id: userId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n return user?.tenantId ? String(user.tenantId) : fallbackTenantId\n }\n\n return fallbackTenantId\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters. Pass scopeToActiveOrganization=1 to restrict results to the caller\\'s active organization (used by recipient/assignee pickers so suggestions stay within the org that owns the resulting record).',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization, optional display name, and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields including display name, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport {\n assertActorCanAccessUserTarget,\n assertActorCanGrantRoleTokens,\n assertActorCanModifySuperAdminUserTarget,\n listSuperAdminUserIds,\n} from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { parseBooleanFlag } from '@open-mercato/shared/lib/boolean'\nimport { findEntityIdsBySearchTokensCompat, type SearchTokenDatabase } from '@open-mercato/shared/lib/search/tokenLookup'\nimport { normalizeDisplayNameInput } from '@open-mercato/core/modules/auth/lib/displayName'\nimport {\n getSelectedTenantFromRequest,\n resolveOrganizationScopeForRequest,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'users' })\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n name: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n scopeToActiveOrganization: z.boolean().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst passwordSchema = buildPasswordSchema()\n\nconst displayNameSchema = z.preprocess(\n normalizeDisplayNameInput,\n z.string().trim().min(1).max(120).nullable().optional(),\n)\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n name: z.string().nullable(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n roleIds: z.array(z.string().uuid()).optional(),\n hasPassword: z.boolean().optional(),\n updatedAt: z.string().nullable().optional(),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\ntype UserListFilter = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: ({ result }) => ({\n id: String(result.user.id),\n ...(result.warning ? { _warning: result.warning } : {}),\n }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n if (typeof parsed.id === 'string' && parsed.id.length) {\n await assertCanModifySuperAdminTarget(ctx.request, parsed.id)\n await assertCanAccessUserTarget(ctx.request, parsed.id)\n }\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n mapInput: async ({ parsed, raw, ctx }) => {\n const targetId = resolveDeleteTargetId(parsed, raw)\n if (ctx.request && targetId) {\n await assertCanModifySuperAdminTarget(ctx.request, targetId)\n await assertCanAccessUserTarget(ctx.request, targetId)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n name: url.searchParams.get('name') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n scopeToActiveOrganization: parseBooleanFlag(url.searchParams.get('scopeToActiveOrganization') || undefined),\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = auth.isSuperAdmin === true\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = isSuperAdmin || !!acl?.isSuperAdmin\n }\n } catch (err) {\n logger.error('Failed to resolve rbac', { err })\n }\n const { id, page, pageSize, search, name, organizationId, scopeToActiveOrganization, roleIds } = parsed.data\n const filters: any[] = [{ deletedAt: null }]\n const actorTenantId = auth.tenantId ? String(auth.tenantId) : null\n let effectiveTenantId: string | null = null\n let effectiveOrganizationIds: string[] | null = null\n let effectiveSelectedOrganizationId: string | null = null\n let usesSelectedTenantScope = false\n if (!isSuperAdmin) {\n if (!actorTenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = actorTenantId\n const superAdminUserIds = await listSuperAdminUserIds(em, actorTenantId)\n if (superAdminUserIds.size) {\n filters.push({ id: { $nin: Array.from(superAdminUserIds) as any } })\n }\n } else {\n const selectedTenantId = getSelectedTenantFromRequest(req)\n if (typeof selectedTenantId === 'string' && selectedTenantId.trim().length > 0) {\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n tenantId: selectedTenantId.trim(),\n })\n if (!scope.tenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = scope.tenantId\n effectiveSelectedOrganizationId = scope.selectedId\n usesSelectedTenantScope = true\n if (Array.isArray(scope.filterIds)) {\n if (scope.filterIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveOrganizationIds = scope.filterIds\n }\n }\n }\n if (effectiveTenantId) {\n filters.push({ tenantId: effectiveTenantId })\n }\n if (effectiveOrganizationIds) {\n filters.push({ organizationId: { $in: effectiveOrganizationIds as any } })\n }\n const scopeOrganizationId = usesSelectedTenantScope\n ? effectiveSelectedOrganizationId\n : auth.orgId ?? null\n if (organizationId) filters.push({ organizationId })\n // Recipient/assignee pickers scope to the caller's active organization so they never\n // suggest users outside it. A message composed here is stamped with the caller's\n // active org (auth.orgId), and the message detail endpoint enforces\n // hasOrganizationAccess(scope.organizationId, message.organizationId); scoping the\n // suggestions to the same org keeps a picked recipient able to open what they were sent.\n if (scopeToActiveOrganization) filters.push({ organizationId: auth.orgId ?? null })\n const trimmedName = typeof name === 'string' ? name.trim() : ''\n if (trimmedName) {\n const searchPattern = `%${escapeLikePattern(trimmedName)}%`\n const displayNameFilters: UserListFilter[] = [{ name: { $ilike: searchPattern } }]\n const nameTokenScope: string | null | undefined = isSuperAdmin ? (effectiveTenantId ?? undefined) : auth.tenantId ?? null\n const matchedDisplayNameIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedName, nameTokenScope, 'name')\n if (matchedDisplayNameIds && matchedDisplayNameIds.length) {\n displayNameFilters.push({ id: { $in: matchedDisplayNameIds } })\n }\n filters.push(displayNameFilters.length > 1 ? { $or: displayNameFilters } : displayNameFilters[0])\n }\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n const trimmedSearch = typeof search === 'string' ? search.trim() : ''\n if (trimmedSearch) {\n // Email is encrypted at rest, so plaintext search must go through search_tokens.\n const tenantScope: string | null | undefined = isSuperAdmin ? (effectiveTenantId ?? undefined) : auth.tenantId ?? null\n const searchFilters: any[] = []\n\n const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope)\n if (matchedIds && matchedIds.length) {\n searchFilters.push({ id: { $in: matchedIds as any } })\n }\n\n const searchPattern = `%${escapeLikePattern(trimmedSearch)}%`\n const organizationSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n organizationSearchFilters.push({ tenant: tenantScope })\n }\n const matchingOrganizations = await em.find(\n Organization,\n organizationSearchFilters.length > 1 ? { $and: organizationSearchFilters } : organizationSearchFilters[0],\n )\n const matchingOrganizationIds = matchingOrganizations\n .map((org) => (org?.id ? String(org.id) : null))\n .filter((orgId): orgId is string => typeof orgId === 'string' && orgId.length > 0)\n if (matchingOrganizationIds.length) {\n searchFilters.push({ organizationId: { $in: matchingOrganizationIds as any } })\n }\n\n const roleSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n roleSearchFilters.push({ $or: [{ tenantId: tenantScope }, { tenantId: null }] })\n }\n const matchingRoles = await em.find(\n Role,\n roleSearchFilters.length > 1 ? { $and: roleSearchFilters } : roleSearchFilters[0],\n )\n const matchingRoleIds = matchingRoles\n .map((role) => (role?.id ? String(role.id) : null))\n .filter((roleId): roleId is string => typeof roleId === 'string' && roleId.length > 0)\n if (matchingRoleIds.length) {\n const roleSearchLinks = await em.find(\n UserRole,\n { role: { $in: matchingRoleIds as any } } as any,\n )\n const matchingRoleUserIds = Array.from(new Set(\n roleSearchLinks\n .map((link) => {\n const userRef = (link as any).user\n const userId = userRef?.id ?? userRef\n return userId ? String(userId) : null\n })\n .filter((userId): userId is string => typeof userId === 'string' && userId.length > 0),\n ))\n if (matchingRoleUserIds.length) {\n searchFilters.push({ id: { $in: matchingRoleUserIds as any } })\n }\n }\n\n if (!searchFilters.length) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n\n filters.push(searchFilters.length > 1 ? { $or: searchFilters } : searchFilters[0])\n }\n if (idFilter && idFilter.size) {\n filters.push({ id: { $in: Array.from(idFilter) as any } })\n } else if (id) {\n filters.push({ id })\n }\n const where = filters.length > 1 ? { $and: filters } : filters[0]\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n {\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n organizationId: scopeOrganizationId,\n },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n const roleIdMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n const rid = String((l as any).role?.id ?? '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (!roleIdMap[uid]) roleIdMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n if (rid) roleIdMap[uid].push(rid)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: effectiveTenantId ? [effectiveTenantId] : auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n name: u.name ? String(u.name) : null,\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n roleIds: roleIdMap[uid] || [],\n ...(id ? { hasPassword: !!u.passwordHash } : {}),\n updatedAt: u.updatedAt instanceof Date ? u.updatedAt.toISOString() : null,\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: effectiveSelectedOrganizationId,\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n return crud.PUT(req)\n}\n\nexport const DELETE = async (req: Request) => {\n const targetId = new URL(req.url).searchParams.get('id')\n if (targetId) {\n try {\n await assertCanModifySuperAdminTarget(req, targetId)\n await assertCanAccessUserTarget(req, targetId)\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n }\n return crud.DELETE(req)\n}\n\nasync function findUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n return findEntityIdsBySearchTokensCompat({\n db: em.getKysely<SearchTokenDatabase>(),\n entityType,\n query: search,\n fields: field ? [field] : undefined,\n scope: { tenantId: tenantScope },\n })\n}\n\nasync function assertCanModifySuperAdminTarget(req: Request, targetUserId: string) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n await assertActorCanModifySuperAdminUserTarget({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n targetUserId,\n })\n}\n\nasync function assertCanAccessUserTarget(req: Request, targetUserId: string) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n await assertActorCanAccessUserTarget({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n targetUserId,\n })\n}\n\nfunction resolveDeleteTargetId(parsed: unknown, raw: unknown): string | null {\n const fromParsed = readId((parsed as Record<string, unknown> | null | undefined))\n if (fromParsed) return fromParsed\n const rawRecord = raw as { body?: Record<string, unknown>; query?: Record<string, unknown> } | null | undefined\n return readId(rawRecord?.query) ?? readId(rawRecord?.body)\n}\n\nfunction readId(record: Record<string, unknown> | null | undefined): string | null {\n const value = record?.id\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nasync function assertCanAssignRoles(req: Request, roles: unknown, payload: Record<string, unknown>) {\n if (!Array.isArray(roles)) return\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const tenantId = await resolveTargetTenantIdForRoleGrant(em, payload, auth.tenantId ?? null)\n await assertActorCanGrantRoleTokens({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId,\n organizationId: auth.orgId ?? null,\n roleTokens: roles,\n })\n}\n\nasync function resolveTargetTenantIdForRoleGrant(\n em: EntityManager,\n payload: Record<string, unknown>,\n fallbackTenantId: string | null,\n): Promise<string | null> {\n const organizationId = typeof payload.organizationId === 'string' ? payload.organizationId : null\n if (organizationId) {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )\n return organization?.tenant?.id ? String(organization.tenant.id) : fallbackTenantId\n }\n\n const userId = typeof payload.id === 'string' ? payload.id : null\n if (userId) {\n const user = await findOneWithDecryption(\n em,\n User,\n { id: userId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n return user?.tenantId ? String(user.tenantId) : fallbackTenantId\n }\n\n return fallbackTenantId\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters. Pass scopeToActiveOrganization=1 to restrict results to the caller\\'s active organization (used by recipient/assignee pickers so suggestions stay within the org that owns the resulting record).',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization, optional display name, and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields including display name, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["id"]
7
7
  }
@@ -9,6 +9,7 @@ import { resolveRegisteredLucideIconNode } from "@open-mercato/ui/backend/icons/
9
9
  import { profilePathPrefixes, profileSections } from "./profile-sections.js";
10
10
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
11
11
  import { filterGrantsByEnabledModules } from "@open-mercato/shared/security/enabledModulesRegistry";
12
+ import { getNavGroupOrderOverride } from "@open-mercato/shared/modules/overrides";
12
13
  import {
13
14
  getSelectedOrganizationFromRequest,
14
15
  resolveFeatureCheckContext
@@ -73,19 +74,26 @@ async function serializeNavItem(item) {
73
74
  children: item.children ? await Promise.all(item.children.map((child) => serializeNavItem(child))) : void 0
74
75
  };
75
76
  }
77
+ const defaultGroupOrder = [
78
+ "customers.nav.group",
79
+ "catalog.nav.group",
80
+ "customers~sales.nav.group",
81
+ "wms.nav.group",
82
+ "resources.nav.group",
83
+ "staff.nav.group",
84
+ "entities.nav.group",
85
+ "directory.nav.group",
86
+ "attachments.nav.group"
87
+ ];
88
+ function resolveGroupOrder() {
89
+ const override = getNavGroupOrderOverride();
90
+ if (!override || override.length === 0) return defaultGroupOrder;
91
+ const overridden = new Set(override);
92
+ return [...override, ...defaultGroupOrder.filter((id) => !overridden.has(id))];
93
+ }
76
94
  function normalizeGroupWeights(groups) {
77
- const defaultGroupOrder = [
78
- "customers.nav.group",
79
- "catalog.nav.group",
80
- "customers~sales.nav.group",
81
- "wms.nav.group",
82
- "resources.nav.group",
83
- "staff.nav.group",
84
- "entities.nav.group",
85
- "directory.nav.group",
86
- "attachments.nav.group"
87
- ];
88
- const groupOrderIndex = new Map(defaultGroupOrder.map((id, index) => [id, index]));
95
+ const groupOrder = resolveGroupOrder();
96
+ const groupOrderIndex = new Map(groupOrder.map((id, index) => [id, index]));
89
97
  groups.sort((a, b) => {
90
98
  const aIndex = groupOrderIndex.get(a.id);
91
99
  const bIndex = groupOrderIndex.get(b.id);
@@ -97,7 +105,7 @@ function normalizeGroupWeights(groups) {
97
105
  if (a.weight !== b.weight) return a.weight - b.weight;
98
106
  return a.name.localeCompare(b.name);
99
107
  });
100
- const defaultGroupCount = defaultGroupOrder.length;
108
+ const defaultGroupCount = groupOrder.length;
101
109
  groups.forEach((group, index) => {
102
110
  const rank = groupOrderIndex.get(group.id);
103
111
  const fallbackWeight = typeof group.weight === "number" ? group.weight : 1e4;
@@ -176,6 +184,7 @@ async function resolveBackendChromePayload({
176
184
  const rbac = container.resolve("rbacService");
177
185
  let scopedOrganizationId = auth.orgId ?? null;
178
186
  let scopedTenantId = auth.tenantId ?? null;
187
+ let concretelySelectedOrganizationId = null;
179
188
  let allowNavigation = true;
180
189
  try {
181
190
  const { organizationId, scope, allowedOrganizationIds } = await resolveFeatureCheckContext({
@@ -187,12 +196,14 @@ async function resolveBackendChromePayload({
187
196
  });
188
197
  scopedOrganizationId = organizationId;
189
198
  scopedTenantId = scope.tenantId ?? auth.tenantId ?? null;
199
+ concretelySelectedOrganizationId = scope.selectedId ?? null;
190
200
  if (Array.isArray(allowedOrganizationIds) && allowedOrganizationIds.length === 0) {
191
201
  allowNavigation = false;
192
202
  }
193
203
  } catch {
194
204
  scopedOrganizationId = auth.orgId ?? null;
195
205
  scopedTenantId = auth.tenantId ?? null;
206
+ concretelySelectedOrganizationId = null;
196
207
  }
197
208
  const acl = allowNavigation ? await rbac.loadAcl(auth.sub, {
198
209
  tenantId: scopedTenantId,
@@ -288,6 +299,7 @@ async function resolveBackendChromePayload({
288
299
  const fallbackOrganizationId = selectedOrganizationId ?? requestOrganizationId ?? auth.orgId ?? null;
289
300
  const brandOrganizationId = scopedOrganizationId ?? (fallbackOrganizationId && !isAllOrganizationsSelection(fallbackOrganizationId) ? fallbackOrganizationId : null);
290
301
  let brand = null;
302
+ let currentOrganization = null;
291
303
  if (brandOrganizationId && scopedTenantId) {
292
304
  try {
293
305
  const organization = await findOneWithDecryption(
@@ -297,6 +309,9 @@ async function resolveBackendChromePayload({
297
309
  void 0,
298
310
  { tenantId: scopedTenantId, organizationId: brandOrganizationId }
299
311
  );
312
+ if (organization && concretelySelectedOrganizationId === brandOrganizationId) {
313
+ currentOrganization = { id: String(organization.id), name: organization.name };
314
+ }
300
315
  if (organization?.logoUrl) {
301
316
  brand = {
302
317
  name: organization.name,
@@ -308,6 +323,7 @@ async function resolveBackendChromePayload({
308
323
  }
309
324
  } catch {
310
325
  brand = null;
326
+ currentOrganization = null;
311
327
  }
312
328
  }
313
329
  return {
@@ -318,7 +334,8 @@ async function resolveBackendChromePayload({
318
334
  profilePathPrefixes,
319
335
  grantedFeatures,
320
336
  roles: Array.isArray(auth.roles) ? auth.roles : [],
321
- brand
337
+ brand,
338
+ currentOrganization
322
339
  };
323
340
  }
324
341
  export {