@open-mercato/core 0.6.8-develop.7067.1.82e9867ea1 → 0.6.8-develop.7071.1.58862d0c9f

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 (54) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/audit_logs/services/actionLogService.js +57 -12
  3. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  4. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
  5. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
  6. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
  7. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
  8. package/dist/modules/auth/components/AclEditor.js +39 -30
  9. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  10. package/dist/modules/customers/api/companies/[id]/people/route.js +1 -1
  11. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  12. package/dist/modules/customers/api/companies/[id]/route.js +8 -9
  13. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  14. package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
  15. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  16. package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
  17. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  18. package/dist/modules/customers/api/deals/[id]/route.js +8 -25
  19. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  20. package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
  21. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  22. package/dist/modules/customers/api/entity-roles-factory.js +4 -4
  23. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  24. package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
  25. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  26. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
  27. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  28. package/dist/modules/customers/api/people/[id]/route.js +11 -10
  29. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  30. package/dist/modules/customers/lib/detailReadAccess.js +12 -0
  31. package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
  32. package/package.json +7 -7
  33. package/src/modules/audit_logs/services/actionLogService.ts +74 -11
  34. package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
  35. package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
  36. package/src/modules/auth/components/AclEditor.tsx +63 -33
  37. package/src/modules/auth/i18n/de.json +380 -0
  38. package/src/modules/auth/i18n/en.json +380 -0
  39. package/src/modules/auth/i18n/es.json +380 -0
  40. package/src/modules/auth/i18n/ko.json +380 -0
  41. package/src/modules/auth/i18n/pl.json +382 -2
  42. package/src/modules/customers/api/companies/[id]/people/route.ts +4 -1
  43. package/src/modules/customers/api/companies/[id]/route.ts +13 -10
  44. package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
  45. package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
  46. package/src/modules/customers/api/deals/[id]/route.ts +17 -28
  47. package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
  48. package/src/modules/customers/api/entity-roles-factory.ts +8 -4
  49. package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
  50. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
  51. package/src/modules/customers/api/people/[id]/route.ts +16 -11
  52. package/src/modules/customers/lib/detailReadAccess.ts +35 -0
  53. package/src/modules/dashboards/i18n/es.json +4 -4
  54. package/src/modules/dashboards/i18n/pl.json +7 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/api/entity-roles-factory.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerEntity, CustomerEntityRole } from '../data/entities'\nimport { entityRoleCreateSchema, entityRoleUpdateSchema, entityRoleDeleteSchema, type EntityRoleCreateInput, type EntityRoleUpdateInput, type EntityRoleDeleteInput } from '../data/validators'\nimport { withScopedPayload } from './utils'\nimport { resolveCustomersRequestContext, resolveAuthActorId } from '../lib/interactionRequestContext'\nimport { deriveDisplayNameFromEmail } from '../lib/displayName'\nimport { withOperationMetadata } from '../lib/operationMetadata'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\nconst roleIdQuerySchema = z.object({ roleId: z.string().uuid() })\n\nconst createBodySchema = z.object({\n roleType: z.string().trim().min(1).max(100),\n userId: z.string().uuid(),\n})\nconst updateBodySchema = z.object({\n userId: z.string().uuid(),\n})\n\nconst listItemSchema = z.object({\n id: z.string().uuid(),\n entityType: z.enum(['company', 'person']),\n entityId: z.string().uuid(),\n userId: z.string().uuid(),\n userName: z.string().nullable().optional(),\n userEmail: z.string().nullable().optional(),\n userPhone: z.string().nullable().optional(),\n roleType: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst listResponseSchema = z.object({ items: z.array(listItemSchema) })\nconst okResponseSchema = z.object({ ok: z.boolean() })\nconst createResponseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\ntype EntityType = 'company' | 'person'\n\ntype Translator = Awaited<ReturnType<typeof resolveTranslations>>['translate']\n\nfunction getRoleContext(entityType: EntityType, entityId: string) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n return { entityType, entityId, resourceKind, resourceId: entityId }\n}\n\nfunction buildValidationErrorResponse(error: z.ZodError, translate: Translator) {\n return NextResponse.json(\n { error: translate('customers.errors.validationFailed', 'Validation failed'), fieldErrors: error.flatten().fieldErrors },\n { status: 400 },\n )\n}\n\nasync function buildContext(request: Request) {\n const context = await resolveCustomersRequestContext(request)\n return {\n ...context,\n ctx: context.commandContext,\n }\n}\n\nfunction ensureRouteOrganizationAccess(\n organizationId: string,\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n translate: Translator,\n) {\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function ensureFeatureOnOrganization(\n container: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['container'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n feature: string,\n organizationId: string,\n translate: Translator,\n) {\n const actorId = resolveAuthActorId(auth)\n if (!actorId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n let rbac: RbacService | undefined\n try {\n rbac = container.resolve('rbacService') as RbacService | undefined\n } catch (err) {\n logger.error('rbacService resolve failed', { component: 'entity-roles-factory', err })\n rbac = undefined\n }\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const hasFeature = await rbac.userHasAllFeatures(actorId, [feature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function resolveEntityRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n translate: Translator,\n) {\n const entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: entityType, tenantId: auth.tenantId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (!entity || entity.tenantId !== auth.tenantId) {\n throw notFound(translate('customers.errors.customer_not_found', 'Customer not found'))\n }\n ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate)\n return {\n entity,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n }\n}\n\nasync function resolveRoleRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n roleId: string,\n translate: Translator,\n) {\n const role = await findOneWithDecryption(\n em,\n CustomerEntityRole,\n { id: roleId, tenantId: auth.tenantId, entityType, entityId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (\n !role ||\n role.tenantId !== auth.tenantId ||\n role.entityType !== entityType ||\n role.entityId !== entityId\n ) {\n throw notFound(translate('customers.errors.role_not_found', 'Role not found'))\n }\n ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate)\n return {\n role,\n organizationId: role.organizationId,\n tenantId: role.tenantId,\n }\n}\n\nfunction createScopedCommandContext(\n ctx: Awaited<ReturnType<typeof buildContext>>['ctx'],\n organizationId: string,\n) {\n return {\n ...ctx,\n selectedOrganizationId: organizationId,\n organizationIds: [organizationId],\n }\n}\n\nexport const entityRolesMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.roles.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n}\n\nexport function buildEntityRolesOpenApi(entityType: EntityType): OpenApiRouteDoc {\n const label = entityType === 'company' ? 'company' : 'person'\n return {\n tag: 'Customers',\n summary: `${label.charAt(0).toUpperCase() + label.slice(1)} role assignments`,\n pathParams: paramsSchema,\n methods: {\n GET: {\n summary: `List roles for a ${label}`,\n responses: [{ status: 200, description: 'Role assignments', schema: listResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: `Assign a role to a ${label}`,\n requestBody: { contentType: 'application/json', schema: createBodySchema },\n responses: [{ status: 201, description: 'Role created', schema: createResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Role already assigned', schema: errorSchema },\n ],\n },\n PUT: {\n summary: `Update a ${label} role assignment`,\n query: roleIdQuerySchema,\n requestBody: { contentType: 'application/json', schema: updateBodySchema },\n responses: [{ status: 200, description: 'Role updated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: `Remove a ${label} role assignment`,\n query: roleIdQuerySchema,\n responses: [{ status: 200, description: 'Role deleted', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n },\n }\n}\n\nexport function createEntityRolesHandlers(entityType: EntityType) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const logPrefix = entityType === 'company' ? 'customers.company.roles' : 'customers.person.roles'\n\n async function GET(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.view', targetScope.organizationId, translate)\n\n const roles = await findWithDecryption(\n em,\n CustomerEntityRole,\n {\n entityType,\n entityId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n deletedAt: null,\n },\n { orderBy: { roleType: 'asc' } },\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n\n const userIds = Array.from(new Set(roles.map((role) => role.userId).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const users = userIds.length\n ? await findWithDecryption(\n em,\n User,\n {\n id: { $in: userIds },\n deletedAt: null,\n ...(targetScope.tenantId ? { tenantId: targetScope.tenantId } : {}),\n },\n undefined,\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n : []\n const userMap = new Map(users.map((user) => [user.id, {\n name: user.name ?? deriveDisplayNameFromEmail(user.email) ?? null,\n email: user.email ?? null,\n phone: null,\n }]))\n\n return NextResponse.json({\n items: roles.map((role) => ({\n ...(userMap.has(role.userId)\n ? {\n userName: userMap.get(role.userId)?.name ?? null,\n userEmail: userMap.get(role.userId)?.email ?? null,\n userPhone: userMap.get(role.userId)?.phone ?? null,\n }\n : {}),\n id: role.id,\n entityType: role.entityType,\n entityId: role.entityId,\n userId: role.userId,\n roleType: role.roleType,\n createdAt: role.createdAt.toISOString(),\n updatedAt: role.updatedAt.toISOString(),\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles GET failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_load_roles', 'Failed to load roles') }, { status: 500 })\n }\n }\n\n async function POST(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n ...getRoleContext(entityType, entityId),\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleCreateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: rawBody,\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<EntityRoleCreateInput, { roleId: string }>(\n 'customers.entityRoles.create',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ id: result?.roleId ?? null }, { status: 201 }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles POST failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_assign_role', 'Failed to assign role') }, { status: 500 })\n }\n }\n\n async function PUT(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleUpdateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId, ...rawBody },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleUpdateInput, { roleId: string }>(\n 'customers.entityRoles.update',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles PUT failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_update_role', 'Failed to update role') }, { status: 500 })\n }\n }\n\n async function DELETE(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const parsed = entityRoleDeleteSchema.parse(\n withScopedPayload(\n {\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n ),\n )\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleDeleteInput, { roleId: string }>(\n 'customers.entityRoles.delete',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles DELETE failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_delete_role', 'Failed to delete role') }, { status: 500 })\n }\n }\n\n return { GET, POST, PUT, DELETE }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerEntity, CustomerEntityRole } from '../data/entities'\nimport { entityRoleCreateSchema, entityRoleUpdateSchema, entityRoleDeleteSchema, type EntityRoleCreateInput, type EntityRoleUpdateInput, type EntityRoleDeleteInput } from '../data/validators'\nimport { withScopedPayload } from './utils'\nimport { resolveCustomersRequestContext, resolveAuthActorId } from '../lib/interactionRequestContext'\nimport { deriveDisplayNameFromEmail } from '../lib/displayName'\nimport { withOperationMetadata } from '../lib/operationMetadata'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\nconst roleIdQuerySchema = z.object({ roleId: z.string().uuid() })\n\nconst createBodySchema = z.object({\n roleType: z.string().trim().min(1).max(100),\n userId: z.string().uuid(),\n})\nconst updateBodySchema = z.object({\n userId: z.string().uuid(),\n})\n\nconst listItemSchema = z.object({\n id: z.string().uuid(),\n entityType: z.enum(['company', 'person']),\n entityId: z.string().uuid(),\n userId: z.string().uuid(),\n userName: z.string().nullable().optional(),\n userEmail: z.string().nullable().optional(),\n userPhone: z.string().nullable().optional(),\n roleType: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst listResponseSchema = z.object({ items: z.array(listItemSchema) })\nconst okResponseSchema = z.object({ ok: z.boolean() })\nconst createResponseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\ntype EntityType = 'company' | 'person'\n\ntype Translator = Awaited<ReturnType<typeof resolveTranslations>>['translate']\n\nfunction getRoleContext(entityType: EntityType, entityId: string) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n return { entityType, entityId, resourceKind, resourceId: entityId }\n}\n\nfunction buildValidationErrorResponse(error: z.ZodError, translate: Translator) {\n return NextResponse.json(\n { error: translate('customers.errors.validationFailed', 'Validation failed'), fieldErrors: error.flatten().fieldErrors },\n { status: 400 },\n )\n}\n\nasync function buildContext(request: Request) {\n const context = await resolveCustomersRequestContext(request)\n return {\n ...context,\n ctx: context.commandContext,\n }\n}\n\nfunction ensureRouteOrganizationAccess(\n organizationId: string,\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n notFoundMessage: string,\n) {\n // Existence oracle (#5504): a caller who holds the feature but whose scope\n // excludes the record's organization is denied as not-found (the same response\n // the caller gets for a missing record), so this cannot reveal that the record\n // exists in an organization they cannot see.\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId })) {\n throw notFound(notFoundMessage)\n }\n}\n\nasync function ensureFeatureOnOrganization(\n container: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['container'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n feature: string,\n organizationId: string,\n translate: Translator,\n) {\n const actorId = resolveAuthActorId(auth)\n if (!actorId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n let rbac: RbacService | undefined\n try {\n rbac = container.resolve('rbacService') as RbacService | undefined\n } catch (err) {\n logger.error('rbacService resolve failed', { component: 'entity-roles-factory', err })\n rbac = undefined\n }\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const hasFeature = await rbac.userHasAllFeatures(actorId, [feature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function resolveEntityRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n translate: Translator,\n) {\n const entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: entityType, tenantId: auth.tenantId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (!entity || entity.tenantId !== auth.tenantId) {\n throw notFound(translate('customers.errors.customer_not_found', 'Customer not found'))\n }\n ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate('customers.errors.customer_not_found', 'Customer not found'))\n return {\n entity,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n }\n}\n\nasync function resolveRoleRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n roleId: string,\n translate: Translator,\n) {\n const role = await findOneWithDecryption(\n em,\n CustomerEntityRole,\n { id: roleId, tenantId: auth.tenantId, entityType, entityId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (\n !role ||\n role.tenantId !== auth.tenantId ||\n role.entityType !== entityType ||\n role.entityId !== entityId\n ) {\n throw notFound(translate('customers.errors.role_not_found', 'Role not found'))\n }\n ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate('customers.errors.role_not_found', 'Role not found'))\n return {\n role,\n organizationId: role.organizationId,\n tenantId: role.tenantId,\n }\n}\n\nfunction createScopedCommandContext(\n ctx: Awaited<ReturnType<typeof buildContext>>['ctx'],\n organizationId: string,\n) {\n return {\n ...ctx,\n selectedOrganizationId: organizationId,\n organizationIds: [organizationId],\n }\n}\n\nexport const entityRolesMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.roles.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n}\n\nexport function buildEntityRolesOpenApi(entityType: EntityType): OpenApiRouteDoc {\n const label = entityType === 'company' ? 'company' : 'person'\n return {\n tag: 'Customers',\n summary: `${label.charAt(0).toUpperCase() + label.slice(1)} role assignments`,\n pathParams: paramsSchema,\n methods: {\n GET: {\n summary: `List roles for a ${label}`,\n responses: [{ status: 200, description: 'Role assignments', schema: listResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: `Assign a role to a ${label}`,\n requestBody: { contentType: 'application/json', schema: createBodySchema },\n responses: [{ status: 201, description: 'Role created', schema: createResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Role already assigned', schema: errorSchema },\n ],\n },\n PUT: {\n summary: `Update a ${label} role assignment`,\n query: roleIdQuerySchema,\n requestBody: { contentType: 'application/json', schema: updateBodySchema },\n responses: [{ status: 200, description: 'Role updated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: `Remove a ${label} role assignment`,\n query: roleIdQuerySchema,\n responses: [{ status: 200, description: 'Role deleted', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n },\n }\n}\n\nexport function createEntityRolesHandlers(entityType: EntityType) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const logPrefix = entityType === 'company' ? 'customers.company.roles' : 'customers.person.roles'\n\n async function GET(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.view', targetScope.organizationId, translate)\n\n const roles = await findWithDecryption(\n em,\n CustomerEntityRole,\n {\n entityType,\n entityId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n deletedAt: null,\n },\n { orderBy: { roleType: 'asc' } },\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n\n const userIds = Array.from(new Set(roles.map((role) => role.userId).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const users = userIds.length\n ? await findWithDecryption(\n em,\n User,\n {\n id: { $in: userIds },\n deletedAt: null,\n ...(targetScope.tenantId ? { tenantId: targetScope.tenantId } : {}),\n },\n undefined,\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n : []\n const userMap = new Map(users.map((user) => [user.id, {\n name: user.name ?? deriveDisplayNameFromEmail(user.email) ?? null,\n email: user.email ?? null,\n phone: null,\n }]))\n\n return NextResponse.json({\n items: roles.map((role) => ({\n ...(userMap.has(role.userId)\n ? {\n userName: userMap.get(role.userId)?.name ?? null,\n userEmail: userMap.get(role.userId)?.email ?? null,\n userPhone: userMap.get(role.userId)?.phone ?? null,\n }\n : {}),\n id: role.id,\n entityType: role.entityType,\n entityId: role.entityId,\n userId: role.userId,\n roleType: role.roleType,\n createdAt: role.createdAt.toISOString(),\n updatedAt: role.updatedAt.toISOString(),\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles GET failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_load_roles', 'Failed to load roles') }, { status: 500 })\n }\n }\n\n async function POST(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n ...getRoleContext(entityType, entityId),\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleCreateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: rawBody,\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<EntityRoleCreateInput, { roleId: string }>(\n 'customers.entityRoles.create',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ id: result?.roleId ?? null }, { status: 201 }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles POST failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_assign_role', 'Failed to assign role') }, { status: 500 })\n }\n }\n\n async function PUT(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleUpdateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId, ...rawBody },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleUpdateInput, { roleId: string }>(\n 'customers.entityRoles.update',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles PUT failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_update_role', 'Failed to update role') }, { status: 500 })\n }\n }\n\n async function DELETE(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const parsed = entityRoleDeleteSchema.parse(\n withScopedPayload(\n {\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n ),\n )\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleDeleteInput, { roleId: string }>(\n 'customers.entityRoles.delete',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles DELETE failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_delete_role', 'Failed to delete role') }, { status: 500 })\n }\n }\n\n return { GET, POST, PUT, DELETE }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -34,7 +34,7 @@ async function loadPersonContext(req, personId) {
34
34
  throw notFound(translate("customers.errors.person_not_found", "Person not found"));
35
35
  }
36
36
  if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {
37
- throw new CrudHttpError(403, { error: translate("customers.errors.access_denied", "Access denied") });
37
+ throw notFound(translate("customers.errors.person_not_found", "Person not found"));
38
38
  }
39
39
  const profile = await findOneWithDecryption(
40
40
  em,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/customers/api/people/%5Bid%5D/companies/context.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n CustomerEntity,\n CustomerPersonProfile,\n} from '@open-mercato/core/modules/customers/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport async function loadPersonContext(req: Request, personId: string) {\n const { translate } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const authenticatedAuth = auth as typeof auth & { tenantId: string }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth: authenticatedAuth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n const decryptionScope = {\n tenantId: authenticatedAuth.tenantId,\n organizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n }\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: personId, kind: 'person', tenantId: authenticatedAuth.tenantId, deletedAt: null },\n {},\n decryptionScope,\n )\n\n if (!person) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person, tenantId: person.tenantId, organizationId: person.organizationId },\n { populate: ['company'] },\n {\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n )\n if (!profile) {\n throw notFound(translate('customers.errors.person_profile_not_found', 'Person profile not found'))\n }\n\n return {\n container,\n auth: authenticatedAuth,\n selectedOrganizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n em,\n person,\n profile,\n }\n}\n"],
5
- "mappings": "AACA,SAAS,eAAe,gBAAgB;AACxC,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AACnD,SAAS,uCAAuC;AAChD,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AAEtC,eAAsB,kBAAkB,KAAc,UAAkB;AACtE,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAAA,EACpG;AACA,QAAM,oBAAoB;AAE1B,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,mBAAmB,SAAS,IAAI,CAAC;AAC3G,QAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,QAAM,kBAAkB;AAAA,IACtB,UAAU,kBAAkB;AAAA,IAC5B,gBAAgB,OAAO,cAAc,kBAAkB,SAAS;AAAA,EAClE;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,UAAU,MAAM,UAAU,UAAU,kBAAkB,UAAU,WAAW,KAAK;AAAA,IACtF,CAAC;AAAA,IACD;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,UAAM,SAAS,UAAU,qCAAqC,kBAAkB,CAAC;AAAA,EACnF;AAEA,MAAI,CAAC,gCAAgC,EAAE,OAAO,MAAM,mBAAmB,gBAAgB,OAAO,eAAe,CAAC,GAAG;AAC/G,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,eAAe,EAAE,CAAC;AAAA,EACtG;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,QAAQ,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,IACnF,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,IACxB;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,UAAM,SAAS,UAAU,6CAA6C,0BAA0B,CAAC;AAAA,EACnG;AAEA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,wBAAwB,OAAO,cAAc,kBAAkB,SAAS;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n CustomerEntity,\n CustomerPersonProfile,\n} from '@open-mercato/core/modules/customers/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport async function loadPersonContext(req: Request, personId: string) {\n const { translate } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const authenticatedAuth = auth as typeof auth & { tenantId: string }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth: authenticatedAuth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n const decryptionScope = {\n tenantId: authenticatedAuth.tenantId,\n organizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n }\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: personId, kind: 'person', tenantId: authenticatedAuth.tenantId, deletedAt: null },\n {},\n decryptionScope,\n )\n\n if (!person) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n // Existence oracle (#5504): deny a cross-org read as not-found \u2014 identical to\n // the parent-not-found above \u2014 so it cannot reveal that a person exists in an\n // organization the caller cannot see.\n if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person, tenantId: person.tenantId, organizationId: person.organizationId },\n { populate: ['company'] },\n {\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n )\n if (!profile) {\n throw notFound(translate('customers.errors.person_profile_not_found', 'Person profile not found'))\n }\n\n return {\n container,\n auth: authenticatedAuth,\n selectedOrganizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n em,\n person,\n profile,\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,eAAe,gBAAgB;AACxC,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AACnD,SAAS,uCAAuC;AAChD,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AAEtC,eAAsB,kBAAkB,KAAc,UAAkB;AACtE,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAAA,EACpG;AACA,QAAM,oBAAoB;AAE1B,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,mBAAmB,SAAS,IAAI,CAAC;AAC3G,QAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,QAAM,kBAAkB;AAAA,IACtB,UAAU,kBAAkB;AAAA,IAC5B,gBAAgB,OAAO,cAAc,kBAAkB,SAAS;AAAA,EAClE;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,UAAU,MAAM,UAAU,UAAU,kBAAkB,UAAU,WAAW,KAAK;AAAA,IACtF,CAAC;AAAA,IACD;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,UAAM,SAAS,UAAU,qCAAqC,kBAAkB,CAAC;AAAA,EACnF;AAKA,MAAI,CAAC,gCAAgC,EAAE,OAAO,MAAM,mBAAmB,gBAAgB,OAAO,eAAe,CAAC,GAAG;AAC/G,UAAM,SAAS,UAAU,qCAAqC,kBAAkB,CAAC;AAAA,EACnF;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,QAAQ,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,IACnF,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,IACxB;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,UAAM,SAAS,UAAU,6CAA6C,0BAA0B,CAAC;AAAA,EACnG;AAEA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,wBAAwB,OAAO,cAAc,kBAAkB,SAAS;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -153,7 +153,7 @@ async function GET(req, ctx) {
153
153
  throw notFound(translate("customers.errors.person_not_found", "Person not found"));
154
154
  }
155
155
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
156
- throw new CrudHttpError(403, { error: translate("customers.errors.access_denied", "Access denied") });
156
+ throw notFound(translate("customers.errors.person_not_found", "Person not found"));
157
157
  }
158
158
  const entityScope = { tenantId: auth.tenantId, organizationId: person.organizationId };
159
159
  const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/customers/api/people/%5Bid%5D/companies/enriched/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport {\n CustomerEntity,\n CustomerCompanyProfile,\n CustomerPersonCompanyLink,\n CustomerAddress,\n CustomerCompanyBilling,\n CustomerPersonCompanyRole,\n CustomerTagAssignment,\n CustomerDealCompanyLink,\n CustomerDeal,\n CustomerInteraction,\n} from '../../../../../data/entities'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n} from '../../../../../lib/personCompanyLinkTable'\nimport { isOpenDealStatus, isWonDealStatus } from '../../../../../lib/dealStatus'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst querySchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(20),\n search: z.string().optional(),\n sort: z.enum(['name-asc', 'name-desc', 'recent']).default('name-asc'),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n GET: {\n summary: 'Get enriched company data for a person\\'s linked companies',\n query: querySchema,\n responses: [\n {\n status: 200,\n description: 'Enriched company rows with profile, billing, tags, deals and more',\n schema: z.object({\n items: z.array(\n z.object({\n linkId: z.string().uuid(),\n companyId: z.string().uuid(),\n displayName: z.string(),\n isPrimary: z.boolean(),\n subtitle: z.string().nullable(),\n profile: z\n .object({\n industry: z.string().nullable(),\n sizeBucket: z.string().nullable(),\n legalName: z.string().nullable(),\n domain: z.string().nullable(),\n websiteUrl: z.string().nullable(),\n })\n .nullable(),\n billing: z\n .object({\n bankName: z.string().nullable(),\n bankAccountMasked: z.string().nullable(),\n paymentTerms: z.string().nullable(),\n preferredCurrency: z.string().nullable(),\n })\n .nullable(),\n primaryAddress: z.object({ formatted: z.string() }).nullable(),\n tags: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n color: z.string().nullable(),\n }),\n ),\n roles: z.array(\n z.object({\n id: z.string().uuid(),\n roleValue: z.string(),\n }),\n ),\n activeDeal: z\n .object({\n title: z.string(),\n valueAmount: z.string().nullable(),\n valueCurrency: z.string().nullable(),\n })\n .nullable(),\n lastContactAt: z.string().nullable(),\n clv: z.object({ amount: z.number(), currency: z.string() }).nullable(),\n status: z.string().nullable(),\n lifecycleStage: z.string().nullable(),\n temperature: z.string().nullable(),\n renewalQuarter: z.string().nullable(),\n }),\n ),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n },\n}\n\nfunction formatAddress(address: CustomerAddress): string {\n return [address.addressLine1, address.city, address.region, address.postalCode]\n .filter(Boolean)\n .join(', ')\n}\n\nfunction buildSubtitle(industry: string | null | undefined, address: CustomerAddress | null): string | null {\n const parts: string[] = []\n if (industry) parts.push(industry)\n if (address) {\n const locationParts = [address.city, address.region].filter(Boolean)\n if (locationParts.length > 0) parts.push(locationParts.join(', '))\n }\n return parts.length > 0 ? parts.join(' \u00B7 ') : null\n}\n\nfunction matchesSearch(item: Record<string, unknown>, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized.length) return true\n return [\n typeof item.displayName === 'string' ? item.displayName : null,\n typeof item.subtitle === 'string' ? item.subtitle : null,\n typeof item.status === 'string' ? item.status : null,\n typeof item.lifecycleStage === 'string' ? item.lifecycleStage : null,\n typeof item.temperature === 'string' ? item.temperature : null,\n typeof item.renewalQuarter === 'string' ? item.renewalQuarter : null,\n ]\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n .some((value) => value.toLowerCase().includes(normalized))\n}\n\nexport async function GET(req: Request, ctx: { params?: { id?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id } = paramsSchema.parse({ id: ctx.params?.id })\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const url = new URL(req.url)\n const query = querySchema.parse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sort: url.searchParams.get('sort') ?? undefined,\n })\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n\n const decryptionScope = { tenantId: auth.tenantId, organizationId: auth.orgId ?? null }\n const person = await findOneWithDecryption(em, CustomerEntity, { id, kind: 'person', tenantId: auth.tenantId, deletedAt: null }, {}, decryptionScope)\n if (!person) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const entityScope = { tenantId: auth.tenantId, organizationId: person.organizationId }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n {\n person,\n tenantId: auth.tenantId,\n },\n 'customers.people.companiesEnriched.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['company'] },\n entityScope,\n ),\n )\n\n const companyIds = links.map((link) => (link.company as CustomerEntity).id)\n\n if (companyIds.length === 0) {\n return NextResponse.json({\n items: [],\n total: 0,\n page: 1,\n pageSize: query.pageSize,\n totalPages: 1,\n })\n }\n\n const tenantScope = { tenantId: auth.tenantId, organizationId: person.organizationId }\n const [allProfiles, allAddresses, allBillings, allTagAssignments, allRoles, allDealLinks, allInteractions] =\n await Promise.all([\n findWithDecryption(em, CustomerCompanyProfile, { entity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerAddress, { entity: { $in: companyIds }, isPrimary: true, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerCompanyBilling, { entity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerTagAssignment, { entity: { $in: companyIds }, ...tenantScope }, { populate: ['tag'] }, entityScope),\n findWithDecryption(em, CustomerPersonCompanyRole, { personEntity: person, companyEntity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n // CustomerDealCompanyLink is a pure junction table without tenantId/organizationId columns;\n // scoping flows transitively through the already-tenant-scoped `company` filter.\n findWithDecryption(em, CustomerDealCompanyLink, { company: { $in: companyIds } }, { populate: ['deal'] }, entityScope),\n findWithDecryption(em, CustomerInteraction, {\n entity: { $in: companyIds },\n occurredAt: { $ne: null },\n deletedAt: null,\n ...tenantScope,\n }, { orderBy: { occurredAt: 'DESC' } }, entityScope),\n ])\n\n const profileByCompany = new Map(allProfiles.map((p) => [(p.entity as { id: string }).id, p]))\n const addressByCompany = new Map(allAddresses.map((a) => [(a.entity as { id: string }).id, a]))\n const billingByCompany = new Map(allBillings.map((b) => [(b.entity as { id: string }).id, b]))\n\n const tagsByCompany = new Map<string, typeof allTagAssignments>()\n for (const ta of allTagAssignments) {\n const entityId = (ta.entity as { id: string }).id\n const existing = tagsByCompany.get(entityId) ?? []\n existing.push(ta)\n tagsByCompany.set(entityId, existing)\n }\n\n const rolesByCompany = new Map<string, typeof allRoles>()\n for (const role of allRoles) {\n const entityId = (role.companyEntity as { id: string }).id\n const existing = rolesByCompany.get(entityId) ?? []\n existing.push(role)\n rolesByCompany.set(entityId, existing)\n }\n\n const dealLinksByCompany = new Map<string, typeof allDealLinks>()\n for (const dcl of allDealLinks) {\n const entityId = (dcl.company as { id: string }).id\n const existing = dealLinksByCompany.get(entityId) ?? []\n existing.push(dcl)\n dealLinksByCompany.set(entityId, existing)\n }\n\n const lastInteractionByCompany = new Map<string, CustomerInteraction>()\n for (const interaction of allInteractions) {\n const entityId = (interaction.entity as { id: string }).id\n if (!lastInteractionByCompany.has(entityId)) {\n lastInteractionByCompany.set(entityId, interaction)\n }\n }\n\n const items = links.map((link) => {\n const company = link.company as CustomerEntity\n const companyId = company.id\n const profile = profileByCompany.get(companyId) ?? null\n const primaryAddress = addressByCompany.get(companyId) ?? null\n const billing = billingByCompany.get(companyId) ?? null\n const tagAssignments = tagsByCompany.get(companyId) ?? []\n const roles = rolesByCompany.get(companyId) ?? []\n const companyDealLinks = dealLinksByCompany.get(companyId) ?? []\n const lastInteraction = lastInteractionByCompany.get(companyId) ?? null\n\n const activeDeals = companyDealLinks\n .map((dcl) => dcl.deal as CustomerDeal)\n .filter((deal) => isOpenDealStatus(deal.status) && !deal.deletedAt)\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())\n const activeDeal = activeDeals.length > 0 ? activeDeals[0] : null\n\n const wonDeals = companyDealLinks\n .map((dcl) => dcl.deal as CustomerDeal)\n .filter((deal) => isWonDealStatus(deal.status) && !deal.deletedAt)\n let clv: { amount: number; currency: string } | null = null\n if (wonDeals.length > 0) {\n const currencies = new Map<string, number>()\n for (const deal of wonDeals) {\n if (deal.valueAmount) {\n const currency = deal.valueCurrency ?? 'USD'\n currencies.set(currency, (currencies.get(currency) ?? 0) + parseFloat(deal.valueAmount))\n }\n }\n if (currencies.size > 0) {\n const [currency, amount] = currencies.entries().next().value!\n clv = { amount, currency }\n }\n }\n\n return {\n linkId: link.id,\n companyId,\n displayName: company.displayName,\n isPrimary: Boolean(link.isPrimary),\n subtitle: buildSubtitle(profile?.industry, primaryAddress),\n profile: profile\n ? {\n industry: profile.industry ?? null,\n sizeBucket: profile.sizeBucket ?? null,\n legalName: profile.legalName ?? null,\n domain: profile.domain ?? null,\n websiteUrl: profile.websiteUrl ?? null,\n }\n : null,\n billing: billing\n ? {\n bankName: billing.bankName ?? null,\n bankAccountMasked: billing.bankAccountMasked ?? null,\n paymentTerms: billing.paymentTerms ?? null,\n preferredCurrency: billing.preferredCurrency ?? null,\n }\n : null,\n primaryAddress: primaryAddress ? { formatted: formatAddress(primaryAddress) } : null,\n tags: tagAssignments.map((ta) => {\n const tag = ta.tag as { id: string; label: string; color?: string | null }\n return {\n id: tag.id,\n label: tag.label,\n color: tag.color ?? null,\n }\n }),\n roles: roles.map((r) => ({ id: r.id, roleValue: r.roleValue })),\n activeDeal: activeDeal\n ? {\n title: activeDeal.title,\n valueAmount: activeDeal.valueAmount ?? null,\n valueCurrency: activeDeal.valueCurrency ?? null,\n }\n : null,\n lastContactAt: lastInteraction?.occurredAt?.toISOString() ?? null,\n clv,\n status: company.status ?? null,\n lifecycleStage: company.lifecycleStage ?? null,\n temperature: company.temperature ?? null,\n renewalQuarter: company.renewalQuarter ?? null,\n }\n })\n\n const filteredItems = query.search?.trim().length\n ? items.filter((item) => matchesSearch(item as Record<string, unknown>, query.search ?? ''))\n : items\n const sortedItems = [...filteredItems].sort((left, right) => {\n if (query.sort === 'recent') {\n const leftTimestamp = left.lastContactAt ? new Date(left.lastContactAt).getTime() : 0\n const rightTimestamp = right.lastContactAt ? new Date(right.lastContactAt).getTime() : 0\n if (leftTimestamp === rightTimestamp) {\n return left.displayName.localeCompare(right.displayName, undefined, { sensitivity: 'base' })\n }\n return rightTimestamp - leftTimestamp\n }\n const compare = left.displayName.localeCompare(right.displayName, undefined, { sensitivity: 'base' })\n return query.sort === 'name-asc' ? compare : -compare\n })\n\n const total = sortedItems.length\n const totalPages = Math.max(1, Math.ceil(total / query.pageSize))\n const page = Math.min(query.page, totalPages)\n const start = (page - 1) * query.pageSize\n\n return NextResponse.json({\n items: sortedItems.slice(start, start + query.pageSize),\n total,\n page,\n pageSize: query.pageSize,\n totalPages,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('/companies/enriched] GET failed', { component: 'people/[id', err })\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport {\n CustomerEntity,\n CustomerCompanyProfile,\n CustomerPersonCompanyLink,\n CustomerAddress,\n CustomerCompanyBilling,\n CustomerPersonCompanyRole,\n CustomerTagAssignment,\n CustomerDealCompanyLink,\n CustomerDeal,\n CustomerInteraction,\n} from '../../../../../data/entities'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n} from '../../../../../lib/personCompanyLinkTable'\nimport { isOpenDealStatus, isWonDealStatus } from '../../../../../lib/dealStatus'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst querySchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(20),\n search: z.string().optional(),\n sort: z.enum(['name-asc', 'name-desc', 'recent']).default('name-asc'),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n GET: {\n summary: 'Get enriched company data for a person\\'s linked companies',\n query: querySchema,\n responses: [\n {\n status: 200,\n description: 'Enriched company rows with profile, billing, tags, deals and more',\n schema: z.object({\n items: z.array(\n z.object({\n linkId: z.string().uuid(),\n companyId: z.string().uuid(),\n displayName: z.string(),\n isPrimary: z.boolean(),\n subtitle: z.string().nullable(),\n profile: z\n .object({\n industry: z.string().nullable(),\n sizeBucket: z.string().nullable(),\n legalName: z.string().nullable(),\n domain: z.string().nullable(),\n websiteUrl: z.string().nullable(),\n })\n .nullable(),\n billing: z\n .object({\n bankName: z.string().nullable(),\n bankAccountMasked: z.string().nullable(),\n paymentTerms: z.string().nullable(),\n preferredCurrency: z.string().nullable(),\n })\n .nullable(),\n primaryAddress: z.object({ formatted: z.string() }).nullable(),\n tags: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n color: z.string().nullable(),\n }),\n ),\n roles: z.array(\n z.object({\n id: z.string().uuid(),\n roleValue: z.string(),\n }),\n ),\n activeDeal: z\n .object({\n title: z.string(),\n valueAmount: z.string().nullable(),\n valueCurrency: z.string().nullable(),\n })\n .nullable(),\n lastContactAt: z.string().nullable(),\n clv: z.object({ amount: z.number(), currency: z.string() }).nullable(),\n status: z.string().nullable(),\n lifecycleStage: z.string().nullable(),\n temperature: z.string().nullable(),\n renewalQuarter: z.string().nullable(),\n }),\n ),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n },\n}\n\nfunction formatAddress(address: CustomerAddress): string {\n return [address.addressLine1, address.city, address.region, address.postalCode]\n .filter(Boolean)\n .join(', ')\n}\n\nfunction buildSubtitle(industry: string | null | undefined, address: CustomerAddress | null): string | null {\n const parts: string[] = []\n if (industry) parts.push(industry)\n if (address) {\n const locationParts = [address.city, address.region].filter(Boolean)\n if (locationParts.length > 0) parts.push(locationParts.join(', '))\n }\n return parts.length > 0 ? parts.join(' \u00B7 ') : null\n}\n\nfunction matchesSearch(item: Record<string, unknown>, query: string): boolean {\n const normalized = query.trim().toLowerCase()\n if (!normalized.length) return true\n return [\n typeof item.displayName === 'string' ? item.displayName : null,\n typeof item.subtitle === 'string' ? item.subtitle : null,\n typeof item.status === 'string' ? item.status : null,\n typeof item.lifecycleStage === 'string' ? item.lifecycleStage : null,\n typeof item.temperature === 'string' ? item.temperature : null,\n typeof item.renewalQuarter === 'string' ? item.renewalQuarter : null,\n ]\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n .some((value) => value.toLowerCase().includes(normalized))\n}\n\nexport async function GET(req: Request, ctx: { params?: { id?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id } = paramsSchema.parse({ id: ctx.params?.id })\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const url = new URL(req.url)\n const query = querySchema.parse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sort: url.searchParams.get('sort') ?? undefined,\n })\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n\n const decryptionScope = { tenantId: auth.tenantId, organizationId: auth.orgId ?? null }\n const person = await findOneWithDecryption(em, CustomerEntity, { id, kind: 'person', tenantId: auth.tenantId, deletedAt: null }, {}, decryptionScope)\n if (!person) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n // Existence oracle (#5504): deny a cross-org read as not-found \u2014 identical to\n // the parent-not-found above \u2014 so it cannot reveal that a person exists in an\n // organization the caller cannot see.\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n const entityScope = { tenantId: auth.tenantId, organizationId: person.organizationId }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n {\n person,\n tenantId: auth.tenantId,\n },\n 'customers.people.companiesEnriched.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['company'] },\n entityScope,\n ),\n )\n\n const companyIds = links.map((link) => (link.company as CustomerEntity).id)\n\n if (companyIds.length === 0) {\n return NextResponse.json({\n items: [],\n total: 0,\n page: 1,\n pageSize: query.pageSize,\n totalPages: 1,\n })\n }\n\n const tenantScope = { tenantId: auth.tenantId, organizationId: person.organizationId }\n const [allProfiles, allAddresses, allBillings, allTagAssignments, allRoles, allDealLinks, allInteractions] =\n await Promise.all([\n findWithDecryption(em, CustomerCompanyProfile, { entity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerAddress, { entity: { $in: companyIds }, isPrimary: true, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerCompanyBilling, { entity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n findWithDecryption(em, CustomerTagAssignment, { entity: { $in: companyIds }, ...tenantScope }, { populate: ['tag'] }, entityScope),\n findWithDecryption(em, CustomerPersonCompanyRole, { personEntity: person, companyEntity: { $in: companyIds }, ...tenantScope }, {}, entityScope),\n // CustomerDealCompanyLink is a pure junction table without tenantId/organizationId columns;\n // scoping flows transitively through the already-tenant-scoped `company` filter.\n findWithDecryption(em, CustomerDealCompanyLink, { company: { $in: companyIds } }, { populate: ['deal'] }, entityScope),\n findWithDecryption(em, CustomerInteraction, {\n entity: { $in: companyIds },\n occurredAt: { $ne: null },\n deletedAt: null,\n ...tenantScope,\n }, { orderBy: { occurredAt: 'DESC' } }, entityScope),\n ])\n\n const profileByCompany = new Map(allProfiles.map((p) => [(p.entity as { id: string }).id, p]))\n const addressByCompany = new Map(allAddresses.map((a) => [(a.entity as { id: string }).id, a]))\n const billingByCompany = new Map(allBillings.map((b) => [(b.entity as { id: string }).id, b]))\n\n const tagsByCompany = new Map<string, typeof allTagAssignments>()\n for (const ta of allTagAssignments) {\n const entityId = (ta.entity as { id: string }).id\n const existing = tagsByCompany.get(entityId) ?? []\n existing.push(ta)\n tagsByCompany.set(entityId, existing)\n }\n\n const rolesByCompany = new Map<string, typeof allRoles>()\n for (const role of allRoles) {\n const entityId = (role.companyEntity as { id: string }).id\n const existing = rolesByCompany.get(entityId) ?? []\n existing.push(role)\n rolesByCompany.set(entityId, existing)\n }\n\n const dealLinksByCompany = new Map<string, typeof allDealLinks>()\n for (const dcl of allDealLinks) {\n const entityId = (dcl.company as { id: string }).id\n const existing = dealLinksByCompany.get(entityId) ?? []\n existing.push(dcl)\n dealLinksByCompany.set(entityId, existing)\n }\n\n const lastInteractionByCompany = new Map<string, CustomerInteraction>()\n for (const interaction of allInteractions) {\n const entityId = (interaction.entity as { id: string }).id\n if (!lastInteractionByCompany.has(entityId)) {\n lastInteractionByCompany.set(entityId, interaction)\n }\n }\n\n const items = links.map((link) => {\n const company = link.company as CustomerEntity\n const companyId = company.id\n const profile = profileByCompany.get(companyId) ?? null\n const primaryAddress = addressByCompany.get(companyId) ?? null\n const billing = billingByCompany.get(companyId) ?? null\n const tagAssignments = tagsByCompany.get(companyId) ?? []\n const roles = rolesByCompany.get(companyId) ?? []\n const companyDealLinks = dealLinksByCompany.get(companyId) ?? []\n const lastInteraction = lastInteractionByCompany.get(companyId) ?? null\n\n const activeDeals = companyDealLinks\n .map((dcl) => dcl.deal as CustomerDeal)\n .filter((deal) => isOpenDealStatus(deal.status) && !deal.deletedAt)\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())\n const activeDeal = activeDeals.length > 0 ? activeDeals[0] : null\n\n const wonDeals = companyDealLinks\n .map((dcl) => dcl.deal as CustomerDeal)\n .filter((deal) => isWonDealStatus(deal.status) && !deal.deletedAt)\n let clv: { amount: number; currency: string } | null = null\n if (wonDeals.length > 0) {\n const currencies = new Map<string, number>()\n for (const deal of wonDeals) {\n if (deal.valueAmount) {\n const currency = deal.valueCurrency ?? 'USD'\n currencies.set(currency, (currencies.get(currency) ?? 0) + parseFloat(deal.valueAmount))\n }\n }\n if (currencies.size > 0) {\n const [currency, amount] = currencies.entries().next().value!\n clv = { amount, currency }\n }\n }\n\n return {\n linkId: link.id,\n companyId,\n displayName: company.displayName,\n isPrimary: Boolean(link.isPrimary),\n subtitle: buildSubtitle(profile?.industry, primaryAddress),\n profile: profile\n ? {\n industry: profile.industry ?? null,\n sizeBucket: profile.sizeBucket ?? null,\n legalName: profile.legalName ?? null,\n domain: profile.domain ?? null,\n websiteUrl: profile.websiteUrl ?? null,\n }\n : null,\n billing: billing\n ? {\n bankName: billing.bankName ?? null,\n bankAccountMasked: billing.bankAccountMasked ?? null,\n paymentTerms: billing.paymentTerms ?? null,\n preferredCurrency: billing.preferredCurrency ?? null,\n }\n : null,\n primaryAddress: primaryAddress ? { formatted: formatAddress(primaryAddress) } : null,\n tags: tagAssignments.map((ta) => {\n const tag = ta.tag as { id: string; label: string; color?: string | null }\n return {\n id: tag.id,\n label: tag.label,\n color: tag.color ?? null,\n }\n }),\n roles: roles.map((r) => ({ id: r.id, roleValue: r.roleValue })),\n activeDeal: activeDeal\n ? {\n title: activeDeal.title,\n valueAmount: activeDeal.valueAmount ?? null,\n valueCurrency: activeDeal.valueCurrency ?? null,\n }\n : null,\n lastContactAt: lastInteraction?.occurredAt?.toISOString() ?? null,\n clv,\n status: company.status ?? null,\n lifecycleStage: company.lifecycleStage ?? null,\n temperature: company.temperature ?? null,\n renewalQuarter: company.renewalQuarter ?? null,\n }\n })\n\n const filteredItems = query.search?.trim().length\n ? items.filter((item) => matchesSearch(item as Record<string, unknown>, query.search ?? ''))\n : items\n const sortedItems = [...filteredItems].sort((left, right) => {\n if (query.sort === 'recent') {\n const leftTimestamp = left.lastContactAt ? new Date(left.lastContactAt).getTime() : 0\n const rightTimestamp = right.lastContactAt ? new Date(right.lastContactAt).getTime() : 0\n if (leftTimestamp === rightTimestamp) {\n return left.displayName.localeCompare(right.displayName, undefined, { sensitivity: 'base' })\n }\n return rightTimestamp - leftTimestamp\n }\n const compare = left.displayName.localeCompare(right.displayName, undefined, { sensitivity: 'base' })\n return query.sort === 'name-asc' ? compare : -compare\n })\n\n const total = sortedItems.length\n const totalPages = Math.max(1, Math.ceil(total / query.pageSize))\n const page = Math.min(query.page, totalPages)\n const start = (page - 1) * query.pageSize\n\n return NextResponse.json({\n items: sortedItems.slice(start, start + query.pageSize),\n total,\n page,\n pageSize: query.pageSize,\n totalPages,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('/companies/enriched] GET failed', { component: 'people/[id', err })\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -31,7 +31,7 @@ import { resolveCustomerInteractionFeatureFlags } from "../../../lib/interaction
31
31
  import { hydrateCanonicalInteractions } from "../../../lib/interactionReadModel.js";
32
32
  import { findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
33
33
  import { parseBooleanFromUnknown, parseBooleanToken } from "@open-mercato/shared/lib/boolean";
34
- import { isOrganizationReadAccessAllowed } from "@open-mercato/core/modules/directory/utils/organizationScopeGuard";
34
+ import { denyCustomerDetailReadAsNotFound } from "../../../lib/detailReadAccess.js";
35
35
  import { loadPersonCompanyLinks, summarizePersonCompanies } from "../../../lib/personCompanies.js";
36
36
  import { normalizeCustomerDetailCustomFields } from "../../detailCustomFields.js";
37
37
  import { buildEmailVisibilityMikroFilter } from "../../../lib/visibilityFilter.js";
@@ -89,9 +89,6 @@ function parseIncludeParams(request) {
89
89
  });
90
90
  return tokens;
91
91
  }
92
- function forbidden(message) {
93
- return NextResponse.json({ error: message }, { status: 403 });
94
- }
95
92
  function notFound(message) {
96
93
  return NextResponse.json({ error: message }, { status: 404 });
97
94
  }
@@ -443,10 +440,14 @@ async function GET(_req, ctx) {
443
440
  profileMeta = { reason: "person_not_found" };
444
441
  return notFound("Person not found");
445
442
  }
446
- if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
447
- statusCode = 403;
448
- profileMeta = { reason: "organization_forbidden" };
449
- return forbidden("Access denied");
443
+ const organizationReadDenied = denyCustomerDetailReadAsNotFound(
444
+ { scope, auth, organizationId: person.organizationId },
445
+ "Person not found"
446
+ );
447
+ if (organizationReadDenied) {
448
+ statusCode = 404;
449
+ profileMeta = { reason: "organization_not_in_scope" };
450
+ return organizationReadDenied;
450
451
  }
451
452
  const personDetailTenantId = person.tenantId ?? auth.tenantId ?? null;
452
453
  const personDetailOrganizationId = person.organizationId ?? null;
@@ -1113,8 +1114,8 @@ const openApi = {
1113
1114
  errors: [
1114
1115
  { status: 400, description: "Invalid identifier", schema: personDetailErrorSchema },
1115
1116
  { status: 401, description: "Unauthorized", schema: personDetailErrorSchema },
1116
- { status: 403, description: "Forbidden for tenant/organization scope", schema: personDetailErrorSchema },
1117
- { status: 404, description: "Person not found", schema: personDetailErrorSchema }
1117
+ { status: 403, description: "Forbidden \u2014 caller lacks the required feature", schema: personDetailErrorSchema },
1118
+ { status: 404, description: "Person not found, or its organization is not in the caller\u2019s scope", schema: personDetailErrorSchema }
1118
1119
  ]
1119
1120
  }
1120
1121
  }