@open-mercato/core 0.5.1-develop.2802.9223828f7f → 0.5.1-develop.2855.9b058b7483

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 (79) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/action_log/index.js +4 -0
  3. package/dist/generated/entities/action_log/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +2 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/audit_logs/data/entities.js +10 -1
  7. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  8. package/dist/modules/audit_logs/data/validators.js +2 -0
  9. package/dist/modules/audit_logs/data/validators.js.map +2 -2
  10. package/dist/modules/audit_logs/migrations/Migration20260423202109.js +15 -0
  11. package/dist/modules/audit_logs/migrations/Migration20260423202109.js.map +7 -0
  12. package/dist/modules/audit_logs/services/accessLogService.js +3 -2
  13. package/dist/modules/audit_logs/services/accessLogService.js.map +3 -3
  14. package/dist/modules/audit_logs/services/actionLogService.js +13 -2
  15. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  16. package/dist/modules/customers/api/entity-roles-factory.js +3 -18
  17. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  18. package/dist/modules/customers/api/interactions/cancel/route.js +7 -2
  19. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  20. package/dist/modules/customers/api/interactions/complete/route.js +7 -2
  21. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/page.js +45 -44
  23. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  24. package/dist/modules/customers/commands/comments.js +6 -0
  25. package/dist/modules/customers/commands/comments.js.map +2 -2
  26. package/dist/modules/customers/components/detail/AssignRoleDialog.js +41 -13
  27. package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
  28. package/dist/modules/customers/components/detail/CompanyDetailHeader.js +30 -0
  29. package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +2 -2
  30. package/dist/modules/customers/components/detail/DealDetailHeader.js +32 -0
  31. package/dist/modules/customers/components/detail/DealDetailHeader.js.map +2 -2
  32. package/dist/modules/customers/components/detail/DealWonPopup.js +2 -2
  33. package/dist/modules/customers/components/detail/DealWonPopup.js.map +2 -2
  34. package/dist/modules/customers/components/detail/InlineActivityComposer.js +62 -6
  35. package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +2 -2
  36. package/dist/modules/customers/components/detail/ObjectHistoryButton.js +39 -0
  37. package/dist/modules/customers/components/detail/ObjectHistoryButton.js.map +7 -0
  38. package/dist/modules/customers/components/detail/PersonDetailHeader.js +30 -0
  39. package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +2 -2
  40. package/dist/modules/customers/components/detail/RolesSection.js +14 -4
  41. package/dist/modules/customers/components/detail/RolesSection.js.map +3 -3
  42. package/dist/modules/customers/components/formConfig.js +16 -2
  43. package/dist/modules/customers/components/formConfig.js.map +2 -2
  44. package/dist/modules/customers/lib/displayName.js +15 -0
  45. package/dist/modules/customers/lib/displayName.js.map +7 -0
  46. package/dist/modules/customers/lib/interactionReadModel.js +1 -2
  47. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  48. package/dist/modules/customers/lib/operationMetadata.js +21 -0
  49. package/dist/modules/customers/lib/operationMetadata.js.map +7 -0
  50. package/generated/entities/action_log/index.ts +2 -0
  51. package/generated/entity-fields-registry.ts +2 -0
  52. package/package.json +3 -3
  53. package/src/modules/audit_logs/data/entities.ts +7 -0
  54. package/src/modules/audit_logs/data/validators.ts +2 -0
  55. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +51 -5
  56. package/src/modules/audit_logs/migrations/Migration20260423202109.ts +15 -0
  57. package/src/modules/audit_logs/services/accessLogService.ts +1 -3
  58. package/src/modules/audit_logs/services/actionLogService.ts +11 -6
  59. package/src/modules/customers/api/entity-roles-factory.ts +3 -23
  60. package/src/modules/customers/api/interactions/cancel/route.ts +7 -2
  61. package/src/modules/customers/api/interactions/complete/route.ts +7 -2
  62. package/src/modules/customers/backend/customers/deals/page.tsx +48 -44
  63. package/src/modules/customers/commands/comments.ts +6 -0
  64. package/src/modules/customers/components/detail/AssignRoleDialog.tsx +37 -9
  65. package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +25 -0
  66. package/src/modules/customers/components/detail/DealDetailHeader.tsx +29 -0
  67. package/src/modules/customers/components/detail/DealWonPopup.tsx +2 -2
  68. package/src/modules/customers/components/detail/InlineActivityComposer.tsx +65 -6
  69. package/src/modules/customers/components/detail/ObjectHistoryButton.tsx +47 -0
  70. package/src/modules/customers/components/detail/PersonDetailHeader.tsx +25 -0
  71. package/src/modules/customers/components/detail/RolesSection.tsx +20 -1
  72. package/src/modules/customers/components/formConfig.tsx +14 -2
  73. package/src/modules/customers/i18n/de.json +12 -0
  74. package/src/modules/customers/i18n/en.json +12 -0
  75. package/src/modules/customers/i18n/es.json +13 -1
  76. package/src/modules/customers/i18n/pl.json +13 -1
  77. package/src/modules/customers/lib/displayName.ts +16 -0
  78. package/src/modules/customers/lib/interactionReadModel.ts +1 -7
  79. package/src/modules/customers/lib/operationMetadata.ts +38 -0
@@ -30,6 +30,8 @@ let ActionLog = class {
30
30
  this.primaryChangedField = null;
31
31
  this.contextJson = null;
32
32
  this.sourceKey = null;
33
+ this.relatedResourceKind = null;
34
+ this.relatedResourceId = null;
33
35
  this.createdAt = /* @__PURE__ */ new Date();
34
36
  this.updatedAt = /* @__PURE__ */ new Date();
35
37
  this.deletedAt = null;
@@ -98,6 +100,12 @@ __decorateClass([
98
100
  __decorateClass([
99
101
  Property({ name: "source_key", type: "text", nullable: true })
100
102
  ], ActionLog.prototype, "sourceKey", 2);
103
+ __decorateClass([
104
+ Property({ name: "related_resource_kind", type: "text", nullable: true })
105
+ ], ActionLog.prototype, "relatedResourceKind", 2);
106
+ __decorateClass([
107
+ Property({ name: "related_resource_id", type: "text", nullable: true })
108
+ ], ActionLog.prototype, "relatedResourceId", 2);
101
109
  __decorateClass([
102
110
  Property({ name: "created_at", type: Date, onCreate: () => /* @__PURE__ */ new Date() })
103
111
  ], ActionLog.prototype, "createdAt", 2);
@@ -116,7 +124,8 @@ ActionLog = __decorateClass([
116
124
  Index({ name: "action_logs_action_type_idx", properties: ["tenantId", "organizationId", "actionType", "createdAt"] }),
117
125
  Index({ name: "action_logs_source_key_idx", properties: ["tenantId", "organizationId", "sourceKey", "createdAt"] }),
118
126
  Index({ name: "action_logs_primary_changed_field_idx", properties: ["tenantId", "organizationId", "primaryChangedField", "createdAt"] }),
119
- Index({ name: "action_logs_changed_fields_idx", properties: ["changedFields"], type: "gin" })
127
+ Index({ name: "action_logs_changed_fields_idx", properties: ["changedFields"], type: "gin" }),
128
+ Index({ name: "action_logs_related_resource_idx", properties: ["tenantId", "relatedResourceKind", "relatedResourceId", "createdAt"] })
120
129
  ], ActionLog);
121
130
  let AccessLog = class {
122
131
  constructor() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/audit_logs/data/entities.ts"],
4
- "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\nimport type { ActionLogProjectionType, ActionLogSourceKey } from '@open-mercato/core/modules/audit_logs/lib/projections'\n\nexport type ActionLogExecutionState = 'done' | 'undone' | 'failed' | 'redone'\n\n@Entity({ tableName: 'action_logs' })\n@Index({ name: 'action_logs_tenant_idx', properties: ['tenantId', 'createdAt'] })\n@Index({ name: 'action_logs_actor_idx', properties: ['actorUserId', 'createdAt'] })\n@Index({ name: 'action_logs_resource_idx', properties: ['tenantId', 'resourceKind', 'resourceId', 'createdAt'] })\n@Index({ name: 'action_logs_parent_resource_idx', properties: ['tenantId', 'parentResourceKind', 'parentResourceId', 'createdAt'] })\n@Index({ name: 'action_logs_action_type_idx', properties: ['tenantId', 'organizationId', 'actionType', 'createdAt'] })\n@Index({ name: 'action_logs_source_key_idx', properties: ['tenantId', 'organizationId', 'sourceKey', 'createdAt'] })\n@Index({ name: 'action_logs_primary_changed_field_idx', properties: ['tenantId', 'organizationId', 'primaryChangedField', 'createdAt'] })\n@Index({ name: 'action_logs_changed_fields_idx', properties: ['changedFields'], type: 'gin' })\nexport class ActionLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId: string | null = null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId: string | null = null\n\n @Property({ name: 'actor_user_id', type: 'uuid', nullable: true })\n actorUserId: string | null = null\n\n @Property({ name: 'command_id', type: 'text' })\n commandId!: string\n\n @Property({ name: 'action_label', type: 'text', nullable: true })\n actionLabel: string | null = null\n\n @Property({ name: 'action_type', type: 'text', nullable: true })\n actionType: ActionLogProjectionType | null = null\n\n @Property({ name: 'resource_kind', type: 'text', nullable: true })\n resourceKind: string | null = null\n\n @Property({ name: 'resource_id', type: 'text', nullable: true })\n resourceId: string | null = null\n\n @Property({ name: 'parent_resource_kind', type: 'text', nullable: true })\n parentResourceKind: string | null = null\n\n @Property({ name: 'parent_resource_id', type: 'text', nullable: true })\n parentResourceId: string | null = null\n\n @Property({ name: 'execution_state', type: 'text', default: 'done' })\n executionState: ActionLogExecutionState = 'done'\n\n @Property({ name: 'undo_token', type: 'text', nullable: true })\n undoToken: string | null = null\n\n @Property({ name: 'command_payload', type: 'jsonb', nullable: true })\n commandPayload: unknown | null = null\n\n @Property({ name: 'snapshot_before', type: 'jsonb', nullable: true })\n snapshotBefore: unknown | null = null\n\n @Property({ name: 'snapshot_after', type: 'jsonb', nullable: true })\n snapshotAfter: unknown | null = null\n\n @Property({ name: 'changes_json', type: 'jsonb', nullable: true })\n changesJson: Record<string, unknown> | null = null\n\n @Property({ name: 'changed_fields', type: 'text[]', nullable: true })\n changedFields: string[] | null = null\n\n @Property({ name: 'primary_changed_field', type: 'text', nullable: true })\n primaryChangedField: string | null = null\n\n @Property({ name: 'context_json', type: 'jsonb', nullable: true })\n contextJson: Record<string, unknown> | null = null\n\n @Property({ name: 'source_key', type: 'text', nullable: true })\n sourceKey: ActionLogSourceKey | null = null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt: Date | null = null\n}\n\n@Entity({ tableName: 'access_logs' })\n@Index({ name: 'access_logs_tenant_idx', properties: ['tenantId', 'createdAt'] })\n@Index({ name: 'access_logs_actor_idx', properties: ['actorUserId', 'createdAt'] })\nexport class AccessLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId: string | null = null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId: string | null = null\n\n @Property({ name: 'actor_user_id', type: 'uuid', nullable: true })\n actorUserId: string | null = null\n\n @Property({ name: 'resource_kind', type: 'text' })\n resourceKind!: string\n\n @Property({ name: 'resource_id', type: 'text' })\n resourceId!: string\n\n @Property({ name: 'access_type', type: 'text' })\n accessType!: string\n\n @Property({ name: 'fields_json', type: 'jsonb', nullable: true })\n fieldsJson: string[] | null = null\n\n @Property({ name: 'context_json', type: 'jsonb', nullable: true })\n contextJson: Record<string, unknown> | null = null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt: Date | null = null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\nimport type { ActionLogProjectionType, ActionLogSourceKey } from '@open-mercato/core/modules/audit_logs/lib/projections'\n\nexport type ActionLogExecutionState = 'done' | 'undone' | 'failed' | 'redone'\n\n@Entity({ tableName: 'action_logs' })\n@Index({ name: 'action_logs_tenant_idx', properties: ['tenantId', 'createdAt'] })\n@Index({ name: 'action_logs_actor_idx', properties: ['actorUserId', 'createdAt'] })\n@Index({ name: 'action_logs_resource_idx', properties: ['tenantId', 'resourceKind', 'resourceId', 'createdAt'] })\n@Index({ name: 'action_logs_parent_resource_idx', properties: ['tenantId', 'parentResourceKind', 'parentResourceId', 'createdAt'] })\n@Index({ name: 'action_logs_action_type_idx', properties: ['tenantId', 'organizationId', 'actionType', 'createdAt'] })\n@Index({ name: 'action_logs_source_key_idx', properties: ['tenantId', 'organizationId', 'sourceKey', 'createdAt'] })\n@Index({ name: 'action_logs_primary_changed_field_idx', properties: ['tenantId', 'organizationId', 'primaryChangedField', 'createdAt'] })\n@Index({ name: 'action_logs_changed_fields_idx', properties: ['changedFields'], type: 'gin' })\n@Index({ name: 'action_logs_related_resource_idx', properties: ['tenantId', 'relatedResourceKind', 'relatedResourceId', 'createdAt'] })\nexport class ActionLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId: string | null = null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId: string | null = null\n\n @Property({ name: 'actor_user_id', type: 'uuid', nullable: true })\n actorUserId: string | null = null\n\n @Property({ name: 'command_id', type: 'text' })\n commandId!: string\n\n @Property({ name: 'action_label', type: 'text', nullable: true })\n actionLabel: string | null = null\n\n @Property({ name: 'action_type', type: 'text', nullable: true })\n actionType: ActionLogProjectionType | null = null\n\n @Property({ name: 'resource_kind', type: 'text', nullable: true })\n resourceKind: string | null = null\n\n @Property({ name: 'resource_id', type: 'text', nullable: true })\n resourceId: string | null = null\n\n @Property({ name: 'parent_resource_kind', type: 'text', nullable: true })\n parentResourceKind: string | null = null\n\n @Property({ name: 'parent_resource_id', type: 'text', nullable: true })\n parentResourceId: string | null = null\n\n @Property({ name: 'execution_state', type: 'text', default: 'done' })\n executionState: ActionLogExecutionState = 'done'\n\n @Property({ name: 'undo_token', type: 'text', nullable: true })\n undoToken: string | null = null\n\n @Property({ name: 'command_payload', type: 'jsonb', nullable: true })\n commandPayload: unknown | null = null\n\n @Property({ name: 'snapshot_before', type: 'jsonb', nullable: true })\n snapshotBefore: unknown | null = null\n\n @Property({ name: 'snapshot_after', type: 'jsonb', nullable: true })\n snapshotAfter: unknown | null = null\n\n @Property({ name: 'changes_json', type: 'jsonb', nullable: true })\n changesJson: Record<string, unknown> | null = null\n\n @Property({ name: 'changed_fields', type: 'text[]', nullable: true })\n changedFields: string[] | null = null\n\n @Property({ name: 'primary_changed_field', type: 'text', nullable: true })\n primaryChangedField: string | null = null\n\n @Property({ name: 'context_json', type: 'jsonb', nullable: true })\n contextJson: Record<string, unknown> | null = null\n\n @Property({ name: 'source_key', type: 'text', nullable: true })\n sourceKey: ActionLogSourceKey | null = null\n\n @Property({ name: 'related_resource_kind', type: 'text', nullable: true })\n relatedResourceKind: string | null = null\n\n @Property({ name: 'related_resource_id', type: 'text', nullable: true })\n relatedResourceId: string | null = null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt: Date | null = null\n}\n\n@Entity({ tableName: 'access_logs' })\n@Index({ name: 'access_logs_tenant_idx', properties: ['tenantId', 'createdAt'] })\n@Index({ name: 'access_logs_actor_idx', properties: ['actorUserId', 'createdAt'] })\nexport class AccessLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId: string | null = null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId: string | null = null\n\n @Property({ name: 'actor_user_id', type: 'uuid', nullable: true })\n actorUserId: string | null = null\n\n @Property({ name: 'resource_kind', type: 'text' })\n resourceKind!: string\n\n @Property({ name: 'resource_id', type: 'text' })\n resourceId!: string\n\n @Property({ name: 'access_type', type: 'text' })\n accessType!: string\n\n @Property({ name: 'fields_json', type: 'jsonb', nullable: true })\n fieldsJson: string[] | null = null\n\n @Property({ name: 'context_json', type: 'jsonb', nullable: true })\n contextJson: Record<string, unknown> | null = null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt: Date | null = null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -22,6 +22,8 @@ const actionLogCreateSchema = baseScopeSchema.extend({
22
22
  commandPayload: z.unknown().optional(),
23
23
  snapshotBefore: z.unknown().optional(),
24
24
  snapshotAfter: z.unknown().optional(),
25
+ relatedResourceKind: z.string().min(1).optional().nullable(),
26
+ relatedResourceId: z.string().min(1).optional().nullable(),
25
27
  changes: recordLike,
26
28
  context: recordLike
27
29
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/audit_logs/data/validators.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\n\nexport const uuid = z.string().uuid()\n\nexport const baseScopeSchema = z.object({\n tenantId: uuid.nullish(),\n organizationId: uuid.nullish(),\n actorUserId: uuid.nullish(),\n})\n\nconst recordLike = z.union([\n z.record(z.string(), z.unknown()),\n z.array(z.unknown()),\n z.null(),\n]).optional()\n\nexport const actionLogCreateSchema = baseScopeSchema.extend({\n commandId: z.string().min(1),\n actionLabel: z.string().min(1).optional(),\n resourceKind: z.string().min(1).optional(),\n resourceId: z.string().min(1).optional(),\n parentResourceKind: z.string().min(1).optional().nullable(),\n parentResourceId: z.string().min(1).optional().nullable(),\n executionState: z.enum(['done', 'undone', 'failed']).optional(),\n undoToken: z.string().min(1).optional(),\n commandPayload: z.unknown().optional(),\n snapshotBefore: z.unknown().optional(),\n snapshotAfter: z.unknown().optional(),\n changes: recordLike,\n context: recordLike,\n})\n\nexport const actionLogListSchema = z.object({\n tenantId: uuid.optional(),\n organizationId: uuid.optional(),\n actorUserId: uuid.optional(),\n actorUserIds: z.array(uuid).optional(),\n undoableOnly: z.boolean().optional(),\n resourceKind: z.string().min(1).optional(),\n resourceId: z.string().min(1).optional(),\n includeRelated: z.boolean().optional(),\n actionType: z.enum(['create', 'edit', 'delete', 'assign']).optional(),\n actionTypes: z.array(z.enum(['create', 'edit', 'delete', 'assign'])).optional(),\n fieldName: z.string().min(1).optional(),\n fieldNames: z.array(z.string().min(1)).optional(),\n sortField: z.enum(['createdAt', 'user', 'action', 'field', 'source']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().max(1000).optional(),\n offset: z.number().int().min(0).optional(),\n page: z.number().int().positive().default(1),\n pageSize: z.number().int().positive().max(200).default(50),\n before: z.date().optional(),\n after: z.date().optional(),\n})\n\nexport const accessLogCreateSchema = baseScopeSchema.extend({\n resourceKind: z.string().min(1),\n resourceId: z.string().min(1),\n accessType: z.string().min(1),\n fields: z.array(z.string().min(1)).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n})\n\nexport const accessLogListSchema = z.object({\n tenantId: uuid.optional(),\n organizationId: uuid.optional(),\n actorUserId: uuid.optional(),\n resourceKind: z.string().optional(),\n accessType: z.string().optional(),\n limit: z.number().int().positive().max(200).optional(),\n page: z.number().int().positive().default(1),\n pageSize: z.number().int().positive().max(200).default(50),\n before: z.date().optional(),\n after: z.date().optional(),\n})\n\nexport type ActionLogCreateInput = z.infer<typeof actionLogCreateSchema>\nexport type ActionLogListQuery = z.infer<typeof actionLogListSchema>\nexport type AccessLogCreateInput = z.infer<typeof accessLogCreateSchema>\nexport type AccessLogListQuery = z.infer<typeof accessLogListSchema>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\n\nexport const uuid = z.string().uuid()\n\nexport const baseScopeSchema = z.object({\n tenantId: uuid.nullish(),\n organizationId: uuid.nullish(),\n actorUserId: uuid.nullish(),\n})\n\nconst recordLike = z.union([\n z.record(z.string(), z.unknown()),\n z.array(z.unknown()),\n z.null(),\n]).optional()\n\nexport const actionLogCreateSchema = baseScopeSchema.extend({\n commandId: z.string().min(1),\n actionLabel: z.string().min(1).optional(),\n resourceKind: z.string().min(1).optional(),\n resourceId: z.string().min(1).optional(),\n parentResourceKind: z.string().min(1).optional().nullable(),\n parentResourceId: z.string().min(1).optional().nullable(),\n executionState: z.enum(['done', 'undone', 'failed']).optional(),\n undoToken: z.string().min(1).optional(),\n commandPayload: z.unknown().optional(),\n snapshotBefore: z.unknown().optional(),\n snapshotAfter: z.unknown().optional(),\n relatedResourceKind: z.string().min(1).optional().nullable(),\n relatedResourceId: z.string().min(1).optional().nullable(),\n changes: recordLike,\n context: recordLike,\n})\n\nexport const actionLogListSchema = z.object({\n tenantId: uuid.optional(),\n organizationId: uuid.optional(),\n actorUserId: uuid.optional(),\n actorUserIds: z.array(uuid).optional(),\n undoableOnly: z.boolean().optional(),\n resourceKind: z.string().min(1).optional(),\n resourceId: z.string().min(1).optional(),\n includeRelated: z.boolean().optional(),\n actionType: z.enum(['create', 'edit', 'delete', 'assign']).optional(),\n actionTypes: z.array(z.enum(['create', 'edit', 'delete', 'assign'])).optional(),\n fieldName: z.string().min(1).optional(),\n fieldNames: z.array(z.string().min(1)).optional(),\n sortField: z.enum(['createdAt', 'user', 'action', 'field', 'source']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().max(1000).optional(),\n offset: z.number().int().min(0).optional(),\n page: z.number().int().positive().default(1),\n pageSize: z.number().int().positive().max(200).default(50),\n before: z.date().optional(),\n after: z.date().optional(),\n})\n\nexport const accessLogCreateSchema = baseScopeSchema.extend({\n resourceKind: z.string().min(1),\n resourceId: z.string().min(1),\n accessType: z.string().min(1),\n fields: z.array(z.string().min(1)).optional(),\n context: z.record(z.string(), z.unknown()).optional(),\n})\n\nexport const accessLogListSchema = z.object({\n tenantId: uuid.optional(),\n organizationId: uuid.optional(),\n actorUserId: uuid.optional(),\n resourceKind: z.string().optional(),\n accessType: z.string().optional(),\n limit: z.number().int().positive().max(200).optional(),\n page: z.number().int().positive().default(1),\n pageSize: z.number().int().positive().max(200).default(50),\n before: z.date().optional(),\n after: z.date().optional(),\n})\n\nexport type ActionLogCreateInput = z.infer<typeof actionLogCreateSchema>\nexport type ActionLogListQuery = z.infer<typeof actionLogListSchema>\nexport type AccessLogCreateInput = z.infer<typeof accessLogCreateSchema>\nexport type AccessLogListQuery = z.infer<typeof accessLogListSchema>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,15 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260423202109 extends Migration {
3
+ up() {
4
+ this.addSql(`alter table "action_logs" add "related_resource_kind" text null, add "related_resource_id" text null;`);
5
+ this.addSql(`create index "action_logs_related_resource_idx" on "action_logs" ("tenant_id", "related_resource_kind", "related_resource_id", "created_at");`);
6
+ }
7
+ down() {
8
+ this.addSql(`drop index "action_logs_related_resource_idx";`);
9
+ this.addSql(`alter table "action_logs" drop column "related_resource_kind", drop column "related_resource_id";`);
10
+ }
11
+ }
12
+ export {
13
+ Migration20260423202109
14
+ };
15
+ //# sourceMappingURL=Migration20260423202109.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/audit_logs/migrations/Migration20260423202109.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260423202109 extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"action_logs\" add \"related_resource_kind\" text null, add \"related_resource_id\" text null;`);\n this.addSql(`create index \"action_logs_related_resource_idx\" on \"action_logs\" (\"tenant_id\", \"related_resource_kind\", \"related_resource_id\", \"created_at\");`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop index \"action_logs_related_resource_idx\";`);\n this.addSql(`alter table \"action_logs\" drop column \"related_resource_kind\", drop column \"related_resource_id\";`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAE5C,KAA2B;AAClC,SAAK,OAAO,uGAAuG;AACnH,SAAK,OAAO,+IAA+I;AAAA,EAC7J;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,gDAAgD;AAC5D,SAAK,OAAO,mGAAmG;AAAA,EACjH;AAEF;",
6
+ "names": []
7
+ }
@@ -16,6 +16,7 @@ const CORE_RETENTION_DAYS = toPositiveNumber(process.env.AUDIT_LOGS_CORE_RETENTI
16
16
  const NON_CORE_RETENTION_HOURS = toPositiveNumber(process.env.AUDIT_LOGS_NON_CORE_RETENTION_HOURS, 8);
17
17
  const CORE_RETENTION_MS = CORE_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
18
18
  const NON_CORE_RETENTION_MS = NON_CORE_RETENTION_HOURS * 60 * 60 * 1e3;
19
+ const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
19
20
  let validationWarningLogged = false;
20
21
  let runtimeValidationAvailable = null;
21
22
  const isZodRuntimeMissing = (err) => err instanceof TypeError && typeof err.message === "string" && err.message.includes("_zod");
@@ -114,11 +115,11 @@ class AccessLogService {
114
115
  context: void 0
115
116
  };
116
117
  }
117
- const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
118
+ const UUID_REGEX2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
118
119
  const toNullableUuid = (value) => {
119
120
  if (typeof value !== "string" || value.length === 0) return null;
120
121
  const candidate = value.startsWith("api_key:") ? value.slice("api_key:".length) : value;
121
- return UUID_REGEX.test(candidate) ? candidate : null;
122
+ return UUID_REGEX2.test(candidate) ? candidate : null;
122
123
  };
123
124
  const fields = Array.isArray(input.fields) ? input.fields.filter((f) => typeof f === "string" && f.length > 0) : void 0;
124
125
  const context = typeof input.context === "object" && input.context !== null ? input.context : void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/audit_logs/services/accessLogService.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { AccessLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport {\n accessLogCreateSchema,\n accessLogListSchema,\n type AccessLogCreateInput,\n type AccessLogListQuery,\n} from '@open-mercato/core/modules/audit_logs/data/validators'\nimport { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'\nimport { E } from '#generated/entities.ids.generated'\n\nconst CORE_RESOURCE_KINDS = new Set<string>(['auth.user', 'auth.role'])\n\nfunction toPositiveNumber(value: string | undefined, fallback: number): number {\n if (!value) return fallback\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) return fallback\n return parsed\n}\n\nconst CORE_RETENTION_DAYS = toPositiveNumber(process.env.AUDIT_LOGS_CORE_RETENTION_DAYS, 7)\nconst NON_CORE_RETENTION_HOURS = toPositiveNumber(process.env.AUDIT_LOGS_NON_CORE_RETENTION_HOURS, 8)\nconst CORE_RETENTION_MS = CORE_RETENTION_DAYS * 24 * 60 * 60 * 1000\nconst NON_CORE_RETENTION_MS = NON_CORE_RETENTION_HOURS * 60 * 60 * 1000\n\nlet validationWarningLogged = false\nlet runtimeValidationAvailable: boolean | null = null\n\nconst isZodRuntimeMissing = (err: unknown) => err instanceof TypeError && typeof err.message === 'string' && err.message.includes('_zod')\n\nexport class AccessLogService {\n constructor(private readonly em: EntityManager) {}\n\n async log(input: AccessLogCreateInput): Promise<AccessLog | null> {\n let data: AccessLogCreateInput\n const schema = accessLogCreateSchema as typeof accessLogCreateSchema & { _zod?: unknown }\n const canValidate = Boolean(schema && typeof schema.parse === 'function')\n const shouldValidate = canValidate && runtimeValidationAvailable !== false\n if (shouldValidate) {\n try {\n data = schema.parse(input)\n runtimeValidationAvailable = true\n } catch (err) {\n if (!isZodRuntimeMissing(err) && !validationWarningLogged) {\n validationWarningLogged = true\n // eslint-disable-next-line no-console\n console.warn('[audit_logs] falling back to permissive access log payload parser', err)\n }\n if (isZodRuntimeMissing(err)) runtimeValidationAvailable = false\n data = this.normalizeInput(input)\n }\n } else {\n data = this.normalizeInput(input)\n }\n const fork = this.em.fork({ useContext: true })\n const fields = Array.isArray(data.fields) && data.fields.length ? data.fields : null\n const context = data.context && Object.keys(data.context).length ? data.context : null\n const createdAt = new Date()\n const tenantId = data.tenantId ?? null\n const organizationId = data.organizationId ?? null\n\n type AccessLogEncryptedFields = {\n resourceKind?: unknown\n resourceId?: unknown\n accessType?: unknown\n fieldsJson?: unknown\n contextJson?: unknown\n }\n const encryption = resolveTenantEncryptionService(fork as any)\n const encrypted = encryption\n ? ((await encryption.encryptEntityPayload(\n E.audit_logs.access_log,\n {\n resourceKind: data.resourceKind,\n resourceId: data.resourceId,\n accessType: data.accessType,\n fieldsJson: fields,\n contextJson: context,\n },\n tenantId,\n organizationId,\n )) as AccessLogEncryptedFields)\n : null\n\n const payload = {\n resourceKind: encrypted?.resourceKind ?? data.resourceKind,\n resourceId: encrypted?.resourceId ?? data.resourceId,\n accessType: encrypted?.accessType ?? data.accessType,\n fieldsJson: encrypted?.fieldsJson ?? fields,\n contextJson: encrypted?.contextJson ?? context,\n }\n\n const rows = await fork.getConnection().execute(\n `insert into \"access_logs\" (\"tenant_id\", \"organization_id\", \"actor_user_id\", \"resource_kind\", \"resource_id\", \"access_type\", \"fields_json\", \"context_json\", \"created_at\", \"deleted_at\") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) returning \"id\"`,\n [\n tenantId,\n organizationId,\n data.actorUserId ?? null,\n payload.resourceKind,\n payload.resourceId,\n payload.accessType,\n payload.fieldsJson !== null && payload.fieldsJson !== undefined ? JSON.stringify(payload.fieldsJson) : null,\n payload.contextJson !== null && payload.contextJson !== undefined ? JSON.stringify(payload.contextJson) : null,\n createdAt,\n null,\n ],\n )\n await this.rotate(fork)\n const id = Array.isArray(rows) && rows.length > 0 ? rows[0]?.id ?? null : null\n if (!id) return null\n const entry = fork.create(AccessLog, {\n id,\n tenantId: data.tenantId ?? null,\n organizationId: data.organizationId ?? null,\n actorUserId: data.actorUserId ?? null,\n resourceKind: data.resourceKind,\n resourceId: data.resourceId,\n accessType: data.accessType,\n fieldsJson: fields,\n contextJson: context,\n createdAt,\n })\n return entry\n }\n\n private normalizeInput(input: Partial<AccessLogCreateInput> | null | undefined): AccessLogCreateInput {\n if (!input) {\n return {\n tenantId: null,\n organizationId: null,\n actorUserId: null,\n resourceKind: 'unknown',\n resourceId: 'unknown',\n accessType: 'unknown',\n fields: undefined,\n context: undefined,\n }\n }\n const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n const toNullableUuid = (value: unknown) => {\n if (typeof value !== 'string' || value.length === 0) return null\n // Extract UUID from \"api_key:<uuid>\" format (used by workflow authentication)\n const candidate = value.startsWith('api_key:') ? value.slice('api_key:'.length) : value\n // System actors (sync workers, scheduler, etc.) use non-UUID subjects like\n // \"system:...\". Reject those so the uuid column stays valid.\n return UUID_REGEX.test(candidate) ? candidate : null\n }\n const fields = Array.isArray(input.fields)\n ? input.fields.filter((f): f is string => typeof f === 'string' && f.length > 0)\n : undefined\n const context = typeof input.context === 'object' && input.context !== null\n ? input.context as Record<string, unknown>\n : undefined\n return {\n tenantId: toNullableUuid(input.tenantId),\n organizationId: toNullableUuid(input.organizationId),\n actorUserId: toNullableUuid(input.actorUserId),\n resourceKind: String(input.resourceKind || 'unknown'),\n resourceId: String(input.resourceId || 'unknown'),\n accessType: String(input.accessType || 'unknown'),\n fields,\n context,\n }\n }\n\n async list(query: Partial<AccessLogListQuery>) {\n const parsed = accessLogListSchema.parse({\n ...query,\n })\n\n const where: FilterQuery<AccessLog> = { deletedAt: null }\n if (parsed.tenantId) where.tenantId = parsed.tenantId\n if (parsed.organizationId) where.organizationId = parsed.organizationId\n if (parsed.actorUserId) where.actorUserId = parsed.actorUserId\n if (parsed.resourceKind) where.resourceKind = parsed.resourceKind\n if (parsed.accessType) where.accessType = parsed.accessType\n if (parsed.before) where.createdAt = { ...(where.createdAt as Record<string, any> | undefined), $lt: parsed.before } as any\n if (parsed.after) where.createdAt = { ...(where.createdAt as Record<string, any> | undefined), $gt: parsed.after } as any\n\n const pageSize = parsed.pageSize ?? parsed.limit ?? 50\n const page = parsed.page ?? 1\n const offset = (page - 1) * pageSize\n\n const [items, total] = await this.em.findAndCount(\n AccessLog,\n where,\n {\n orderBy: { createdAt: 'desc' },\n limit: pageSize,\n offset,\n },\n )\n\n const totalPages = Math.max(1, Math.ceil((total || 0) / (pageSize || 1)))\n return { items, total, page, pageSize, totalPages }\n }\n\n private async rotate(fork: EntityManager) {\n const now = Date.now()\n const coreCutoff = new Date(now - CORE_RETENTION_MS)\n const nonCoreCutoff = new Date(now - NON_CORE_RETENTION_MS)\n try {\n if (CORE_RESOURCE_KINDS.size > 0) {\n await fork.nativeDelete(AccessLog, {\n resourceKind: { $in: Array.from(CORE_RESOURCE_KINDS) },\n createdAt: { $lt: coreCutoff },\n })\n }\n await fork.nativeDelete(AccessLog, {\n resourceKind: { $nin: Array.from(CORE_RESOURCE_KINDS) },\n createdAt: { $lt: nonCoreCutoff },\n })\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('[audit_logs] failed to rotate access logs', err)\n }\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { AccessLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport {\n accessLogCreateSchema,\n accessLogListSchema,\n type AccessLogCreateInput,\n type AccessLogListQuery,\n} from '@open-mercato/core/modules/audit_logs/data/validators'\nimport { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'\nimport { E } from '#generated/entities.ids.generated'\n\nconst CORE_RESOURCE_KINDS = new Set<string>(['auth.user', 'auth.role'])\n\nfunction toPositiveNumber(value: string | undefined, fallback: number): number {\n if (!value) return fallback\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) return fallback\n return parsed\n}\n\nconst CORE_RETENTION_DAYS = toPositiveNumber(process.env.AUDIT_LOGS_CORE_RETENTION_DAYS, 7)\nconst NON_CORE_RETENTION_HOURS = toPositiveNumber(process.env.AUDIT_LOGS_NON_CORE_RETENTION_HOURS, 8)\nconst CORE_RETENTION_MS = CORE_RETENTION_DAYS * 24 * 60 * 60 * 1000\nconst NON_CORE_RETENTION_MS = NON_CORE_RETENTION_HOURS * 60 * 60 * 1000\nconst UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n\nlet validationWarningLogged = false\nlet runtimeValidationAvailable: boolean | null = null\n\nconst isZodRuntimeMissing = (err: unknown) => err instanceof TypeError && typeof err.message === 'string' && err.message.includes('_zod')\n\nexport class AccessLogService {\n constructor(private readonly em: EntityManager) {}\n\n async log(input: AccessLogCreateInput): Promise<AccessLog | null> {\n let data: AccessLogCreateInput\n const schema = accessLogCreateSchema as typeof accessLogCreateSchema & { _zod?: unknown }\n const canValidate = Boolean(schema && typeof schema.parse === 'function')\n const shouldValidate = canValidate && runtimeValidationAvailable !== false\n if (shouldValidate) {\n try {\n data = schema.parse(input)\n runtimeValidationAvailable = true\n } catch (err) {\n if (!isZodRuntimeMissing(err) && !validationWarningLogged) {\n validationWarningLogged = true\n // eslint-disable-next-line no-console\n console.warn('[audit_logs] falling back to permissive access log payload parser', err)\n }\n if (isZodRuntimeMissing(err)) runtimeValidationAvailable = false\n data = this.normalizeInput(input)\n }\n } else {\n data = this.normalizeInput(input)\n }\n const fork = this.em.fork({ useContext: true })\n const fields = Array.isArray(data.fields) && data.fields.length ? data.fields : null\n const context = data.context && Object.keys(data.context).length ? data.context : null\n const createdAt = new Date()\n const tenantId = data.tenantId ?? null\n const organizationId = data.organizationId ?? null\n\n type AccessLogEncryptedFields = {\n resourceKind?: unknown\n resourceId?: unknown\n accessType?: unknown\n fieldsJson?: unknown\n contextJson?: unknown\n }\n const encryption = resolveTenantEncryptionService(fork as any)\n const encrypted = encryption\n ? ((await encryption.encryptEntityPayload(\n E.audit_logs.access_log,\n {\n resourceKind: data.resourceKind,\n resourceId: data.resourceId,\n accessType: data.accessType,\n fieldsJson: fields,\n contextJson: context,\n },\n tenantId,\n organizationId,\n )) as AccessLogEncryptedFields)\n : null\n\n const payload = {\n resourceKind: encrypted?.resourceKind ?? data.resourceKind,\n resourceId: encrypted?.resourceId ?? data.resourceId,\n accessType: encrypted?.accessType ?? data.accessType,\n fieldsJson: encrypted?.fieldsJson ?? fields,\n contextJson: encrypted?.contextJson ?? context,\n }\n\n const rows = await fork.getConnection().execute(\n `insert into \"access_logs\" (\"tenant_id\", \"organization_id\", \"actor_user_id\", \"resource_kind\", \"resource_id\", \"access_type\", \"fields_json\", \"context_json\", \"created_at\", \"deleted_at\") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) returning \"id\"`,\n [\n tenantId,\n organizationId,\n data.actorUserId ?? null,\n payload.resourceKind,\n payload.resourceId,\n payload.accessType,\n payload.fieldsJson !== null && payload.fieldsJson !== undefined ? JSON.stringify(payload.fieldsJson) : null,\n payload.contextJson !== null && payload.contextJson !== undefined ? JSON.stringify(payload.contextJson) : null,\n createdAt,\n null,\n ],\n )\n await this.rotate(fork)\n const id = Array.isArray(rows) && rows.length > 0 ? rows[0]?.id ?? null : null\n if (!id) return null\n const entry = fork.create(AccessLog, {\n id,\n tenantId: data.tenantId ?? null,\n organizationId: data.organizationId ?? null,\n actorUserId: data.actorUserId ?? null,\n resourceKind: data.resourceKind,\n resourceId: data.resourceId,\n accessType: data.accessType,\n fieldsJson: fields,\n contextJson: context,\n createdAt,\n })\n return entry\n }\n\n private normalizeInput(input: Partial<AccessLogCreateInput> | null | undefined): AccessLogCreateInput {\n if (!input) {\n return {\n tenantId: null,\n organizationId: null,\n actorUserId: null,\n resourceKind: 'unknown',\n resourceId: 'unknown',\n accessType: 'unknown',\n fields: undefined,\n context: undefined,\n }\n }\n const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n const toNullableUuid = (value: unknown) => {\n if (typeof value !== 'string' || value.length === 0) return null\n const candidate = value.startsWith('api_key:') ? value.slice('api_key:'.length) : value\n return UUID_REGEX.test(candidate) ? candidate : null\n }\n const fields = Array.isArray(input.fields)\n ? input.fields.filter((f): f is string => typeof f === 'string' && f.length > 0)\n : undefined\n const context = typeof input.context === 'object' && input.context !== null\n ? input.context as Record<string, unknown>\n : undefined\n return {\n tenantId: toNullableUuid(input.tenantId),\n organizationId: toNullableUuid(input.organizationId),\n actorUserId: toNullableUuid(input.actorUserId),\n resourceKind: String(input.resourceKind || 'unknown'),\n resourceId: String(input.resourceId || 'unknown'),\n accessType: String(input.accessType || 'unknown'),\n fields,\n context,\n }\n }\n\n async list(query: Partial<AccessLogListQuery>) {\n const parsed = accessLogListSchema.parse({\n ...query,\n })\n\n const where: FilterQuery<AccessLog> = { deletedAt: null }\n if (parsed.tenantId) where.tenantId = parsed.tenantId\n if (parsed.organizationId) where.organizationId = parsed.organizationId\n if (parsed.actorUserId) where.actorUserId = parsed.actorUserId\n if (parsed.resourceKind) where.resourceKind = parsed.resourceKind\n if (parsed.accessType) where.accessType = parsed.accessType\n if (parsed.before) where.createdAt = { ...(where.createdAt as Record<string, any> | undefined), $lt: parsed.before } as any\n if (parsed.after) where.createdAt = { ...(where.createdAt as Record<string, any> | undefined), $gt: parsed.after } as any\n\n const pageSize = parsed.pageSize ?? parsed.limit ?? 50\n const page = parsed.page ?? 1\n const offset = (page - 1) * pageSize\n\n const [items, total] = await this.em.findAndCount(\n AccessLog,\n where,\n {\n orderBy: { createdAt: 'desc' },\n limit: pageSize,\n offset,\n },\n )\n\n const totalPages = Math.max(1, Math.ceil((total || 0) / (pageSize || 1)))\n return { items, total, page, pageSize, totalPages }\n }\n\n private async rotate(fork: EntityManager) {\n const now = Date.now()\n const coreCutoff = new Date(now - CORE_RETENTION_MS)\n const nonCoreCutoff = new Date(now - NON_CORE_RETENTION_MS)\n try {\n if (CORE_RESOURCE_KINDS.size > 0) {\n await fork.nativeDelete(AccessLog, {\n resourceKind: { $in: Array.from(CORE_RESOURCE_KINDS) },\n createdAt: { $lt: coreCutoff },\n })\n }\n await fork.nativeDelete(AccessLog, {\n resourceKind: { $nin: Array.from(CORE_RESOURCE_KINDS) },\n createdAt: { $lt: nonCoreCutoff },\n })\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('[audit_logs] failed to rotate access logs', err)\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["UUID_REGEX"]
7
7
  }
@@ -18,6 +18,7 @@ const isZodRuntimeMissing = (err) => err instanceof TypeError && typeof err.mess
18
18
  const SORT_FIELDS = {
19
19
  createdAt: "action_logs.created_at"
20
20
  };
21
+ const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
21
22
  function readString(record, ...keys) {
22
23
  for (const key of keys) {
23
24
  const value = record[key];
@@ -170,6 +171,8 @@ class ActionLogService {
170
171
  resourceId: data.resourceId ?? null,
171
172
  parentResourceKind: data.parentResourceKind ?? null,
172
173
  parentResourceId: data.parentResourceId ?? null,
174
+ relatedResourceKind: toOptionalString(data.relatedResourceKind) ?? null,
175
+ relatedResourceId: toOptionalString(data.relatedResourceId) ?? null,
173
176
  executionState: data.executionState ?? "done",
174
177
  undoToken: data.undoToken ?? null,
175
178
  commandPayload: data.commandPayload ?? null,
@@ -194,6 +197,8 @@ class ActionLogService {
194
197
  actionLabel: void 0,
195
198
  resourceKind: void 0,
196
199
  resourceId: void 0,
200
+ relatedResourceKind: null,
201
+ relatedResourceId: null,
197
202
  executionState: "done",
198
203
  undoToken: void 0,
199
204
  commandPayload: void 0,
@@ -203,11 +208,11 @@ class ActionLogService {
203
208
  context: void 0
204
209
  };
205
210
  }
206
- const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
211
+ const UUID_REGEX2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
207
212
  const toNullableUuid = (value) => {
208
213
  if (typeof value !== "string" || value.length === 0) return null;
209
214
  const candidate = value.startsWith("api_key:") ? value.slice("api_key:".length) : value;
210
- return UUID_REGEX.test(candidate) ? candidate : null;
215
+ return UUID_REGEX2.test(candidate) ? candidate : null;
211
216
  };
212
217
  const normalizeRecordLike = (value) => {
213
218
  if (value === null) return null;
@@ -226,6 +231,8 @@ class ActionLogService {
226
231
  resourceId: toOptionalString(input.resourceId) ?? void 0,
227
232
  parentResourceKind: toOptionalString(input.parentResourceKind) ?? null,
228
233
  parentResourceId: toOptionalString(input.parentResourceId) ?? null,
234
+ relatedResourceKind: toOptionalString(input.relatedResourceKind) ?? null,
235
+ relatedResourceId: toOptionalString(input.relatedResourceId) ?? null,
229
236
  executionState: input.executionState === "undone" || input.executionState === "failed" ? input.executionState : "done",
230
237
  undoToken: toOptionalString(input.undoToken) ?? void 0,
231
238
  commandPayload: input.commandPayload,
@@ -295,6 +302,10 @@ class ActionLogService {
295
302
  eb.and([
296
303
  eb("action_logs.parent_resource_kind", "=", parsed.resourceKind),
297
304
  eb("action_logs.parent_resource_id", "=", parsed.resourceId)
305
+ ]),
306
+ eb.and([
307
+ eb("action_logs.related_resource_kind", "=", parsed.resourceKind),
308
+ eb("action_logs.related_resource_id", "=", parsed.resourceId)
298
309
  ])
299
310
  ])
300
311
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/audit_logs/services/actionLogService.ts"],
4
- "sourcesContent": ["import type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { sql } from 'kysely'\nimport { ActionLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport {\n actionLogCreateSchema,\n actionLogListSchema,\n type ActionLogCreateInput,\n type ActionLogListQuery,\n} from '@open-mercato/core/modules/audit_logs/data/validators'\nimport { isRecord } from '@open-mercato/core/modules/audit_logs/lib/changeRows'\nimport {\n ACTION_LOG_FILTER_TYPES,\n type ActionLogFilterType,\n deriveActionLogProjection,\n} from '@open-mercato/core/modules/audit_logs/lib/projections'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { toOptionalString } from '@open-mercato/shared/lib/string/coerce'\n\nlet validationWarningLogged = false\nlet runtimeValidationAvailable: boolean | null = null\nlet decryptionWarningLogged = false\n\nconst isZodRuntimeMissing = (err: unknown) => err instanceof TypeError && typeof err.message === 'string' && err.message.includes('_zod')\n\nconst SORT_FIELDS = {\n createdAt: 'action_logs.created_at',\n} as const\n\ntype ActionLogProjectionBackfillOptions = {\n batchSize?: number\n force?: boolean\n logger?: (message: string) => void\n organizationId?: string | null\n tenantId?: string | null\n}\n\nexport type ActionLogProjectionBackfillResult = {\n errors: number\n scanned: number\n skipped: number\n updated: number\n}\n\ntype BackfillRow = {\n action_label: string | null\n action_type: string | null\n actor_user_id: string | null\n changed_fields: string[] | null\n changes_json: Record<string, unknown> | null\n command_id: string\n context_json: Record<string, unknown> | null\n created_at: Date\n id: string\n organization_id: string | null\n primary_changed_field: string | null\n snapshot_before: unknown | null\n source_key: string | null\n tenant_id: string | null\n}\n\nfunction readString(record: Record<string, unknown>, ...keys: string[]): string | null {\n for (const key of keys) {\n const value = record[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n\n return null\n}\n\nfunction readRecord(record: Record<string, unknown>, ...keys: string[]): Record<string, unknown> | null {\n for (const key of keys) {\n const value = record[key]\n if (isRecord(value)) return value\n }\n\n return null\n}\n\nfunction readValue(record: Record<string, unknown>, ...keys: string[]): unknown {\n for (const key of keys) {\n if (Object.prototype.hasOwnProperty.call(record, key)) return record[key]\n }\n\n return undefined\n}\n\nfunction readStringArray(record: Record<string, unknown>, ...keys: string[]): string[] | null {\n for (const key of keys) {\n const value = record[key]\n if (Array.isArray(value)) {\n return value.filter((entry): entry is string => typeof entry === 'string')\n }\n }\n\n return null\n}\n\nfunction stringArraysEqual(left: string[] | null, right: string[]): boolean {\n if (!Array.isArray(left)) return false\n if (left.length !== right.length) return false\n\n return left.every((value, index) => value === right[index])\n}\n\nexport class ActionLogService {\n constructor(\n private readonly em: EntityManager,\n private readonly tenantEncryptionService?: TenantDataEncryptionService,\n ) {}\n\n private async decryptEntryPayload<T extends Record<string, unknown>>(entry: T): Promise<T> {\n if (!this.tenantEncryptionService?.isEnabled()) return entry\n\n try {\n const tenantId = readString(entry, 'tenantId', 'tenant_id')\n const organizationId = readString(entry, 'organizationId', 'organization_id')\n const dek = await this.tenantEncryptionService.getDek(tenantId)\n const deepDecrypt = (value: unknown): unknown => {\n if (!dek) return value\n if (typeof value === 'string' && value.split(':').length === 4 && value.endsWith(':v1')) {\n const decrypted = decryptWithAesGcm(value, dek.key)\n if (decrypted === null) return value\n try {\n return JSON.parse(decrypted)\n } catch {\n return decrypted\n }\n }\n if (Array.isArray(value)) return value.map((item) => deepDecrypt(item))\n if (value && typeof value === 'object') {\n const copy: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n copy[key] = deepDecrypt(item)\n }\n return copy\n }\n return value\n }\n\n const decrypted = await this.tenantEncryptionService.decryptEntityPayload(\n 'audit_logs:action_log',\n entry,\n tenantId,\n organizationId,\n )\n\n const merged = {\n ...entry,\n ...decrypted,\n } as Record<string, unknown>\n\n merged.changesJson = deepDecrypt(merged.changesJson ?? merged.changes_json ?? entry.changesJson ?? entry.changes_json)\n merged.changes_json = merged.changesJson\n merged.snapshotBefore = deepDecrypt(merged.snapshotBefore ?? merged.snapshot_before ?? entry.snapshotBefore ?? entry.snapshot_before)\n merged.snapshot_before = merged.snapshotBefore\n merged.snapshotAfter = deepDecrypt(merged.snapshotAfter ?? merged.snapshot_after ?? entry.snapshotAfter ?? entry.snapshot_after)\n merged.snapshot_after = merged.snapshotAfter\n merged.commandPayload = deepDecrypt(merged.commandPayload ?? merged.command_payload ?? entry.commandPayload ?? entry.command_payload)\n merged.command_payload = merged.commandPayload\n merged.contextJson = deepDecrypt(merged.contextJson ?? merged.context_json ?? entry.contextJson ?? entry.context_json)\n merged.context_json = merged.contextJson\n\n return merged as T\n } catch (err) {\n if (!decryptionWarningLogged) {\n decryptionWarningLogged = true\n console.warn('[audit_logs] failed to decrypt action log entry', err)\n }\n return entry\n }\n }\n\n private async decryptEntries(entries: ActionLog | ActionLog[] | null | undefined): Promise<void> {\n if (!entries) return\n\n const list = Array.isArray(entries) ? entries : [entries]\n for (const entry of list) {\n Object.assign(entry as unknown as Record<string, unknown>, await this.decryptEntryPayload(entry as unknown as Record<string, unknown>))\n }\n }\n\n async log(input: ActionLogCreateInput): Promise<ActionLog | null> {\n const data = this.parseCreateInput(input)\n const fork = this.em.fork()\n const log = this.createLogEntity(fork, data)\n await fork.persist(log).flush()\n await this.decryptEntries(log)\n return log\n }\n\n private parseCreateInput(input: ActionLogCreateInput): ActionLogCreateInput {\n let data: ActionLogCreateInput\n const schema = actionLogCreateSchema as typeof actionLogCreateSchema & { _zod?: unknown }\n const canValidate = Boolean(schema && typeof schema.parse === 'function')\n const shouldValidate = canValidate && runtimeValidationAvailable !== false\n\n if (shouldValidate) {\n try {\n data = schema.parse(input)\n runtimeValidationAvailable = true\n } catch (err) {\n if (!isZodRuntimeMissing(err) && !validationWarningLogged) {\n validationWarningLogged = true\n console.warn('[audit_logs] falling back to permissive action log payload parser', err)\n }\n if (isZodRuntimeMissing(err)) runtimeValidationAvailable = false\n data = this.normalizeInput(input)\n }\n } else {\n data = this.normalizeInput(input)\n }\n\n return data\n }\n\n private createLogEntity(fork: EntityManager, data: ActionLogCreateInput): ActionLog {\n const projection = deriveActionLogProjection({\n actorUserId: data.actorUserId ?? null,\n actionLabel: data.actionLabel ?? null,\n changes: isRecord(data.changes) ? data.changes : null,\n commandId: data.commandId,\n context: isRecord(data.context) ? data.context : null,\n snapshotBefore: data.snapshotBefore,\n })\n\n return fork.create(ActionLog, {\n tenantId: data.tenantId ?? null,\n organizationId: data.organizationId ?? null,\n actorUserId: data.actorUserId ?? null,\n commandId: data.commandId,\n actionLabel: data.actionLabel ?? null,\n actionType: projection.actionType,\n resourceKind: data.resourceKind ?? null,\n resourceId: data.resourceId ?? null,\n parentResourceKind: data.parentResourceKind ?? null,\n parentResourceId: data.parentResourceId ?? null,\n executionState: data.executionState ?? 'done',\n undoToken: data.undoToken ?? null,\n commandPayload: data.commandPayload ?? null,\n snapshotBefore: data.snapshotBefore ?? null,\n snapshotAfter: data.snapshotAfter ?? null,\n changesJson: isRecord(data.changes) ? data.changes : null,\n changedFields: projection.changedFields,\n primaryChangedField: projection.primaryChangedField,\n contextJson: isRecord(data.context) ? data.context : null,\n sourceKey: projection.sourceKey,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n }\n\n private normalizeInput(input: Partial<ActionLogCreateInput> | null | undefined): ActionLogCreateInput {\n if (!input) {\n return {\n tenantId: null,\n organizationId: null,\n actorUserId: null,\n commandId: 'unknown',\n actionLabel: undefined,\n resourceKind: undefined,\n resourceId: undefined,\n executionState: 'done',\n undoToken: undefined,\n commandPayload: undefined,\n snapshotBefore: undefined,\n snapshotAfter: undefined,\n changes: undefined,\n context: undefined,\n }\n }\n\n const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n const toNullableUuid = (value: unknown) => {\n if (typeof value !== 'string' || value.length === 0) return null\n // Extract UUID from \"api_key:<uuid>\" format (used by workflow authentication).\n const candidate = value.startsWith('api_key:') ? value.slice('api_key:'.length) : value\n // System actors (outbound sync workers, scheduler, etc.) carry subjects like\n // \"system:example_customers_sync:outbound\" that are not UUIDs. Writing them into\n // `actor_user_id` (uuid column) trips the Postgres driver with\n // `invalid input syntax for type uuid`. Reject anything that isn't a UUID so the\n // action log safely records a null actor for system-originated commands.\n return UUID_REGEX.test(candidate) ? candidate : null\n }\n\n const normalizeRecordLike = (value: unknown): ActionLogCreateInput['changes'] => {\n if (value === null) return null\n if (Array.isArray(value)) return value\n if (typeof value === 'object') return value as Record<string, unknown>\n return undefined\n }\n\n const normalizeContext = (value: unknown) => (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n )\n\n return {\n tenantId: toNullableUuid(input.tenantId),\n organizationId: toNullableUuid(input.organizationId),\n actorUserId: toNullableUuid(input.actorUserId),\n commandId: typeof input.commandId === 'string' && input.commandId.length > 0 ? input.commandId : 'unknown',\n actionLabel: toOptionalString(input.actionLabel) ?? undefined,\n resourceKind: toOptionalString(input.resourceKind) ?? undefined,\n resourceId: toOptionalString(input.resourceId) ?? undefined,\n parentResourceKind: toOptionalString(input.parentResourceKind) ?? null,\n parentResourceId: toOptionalString(input.parentResourceId) ?? null,\n executionState: input.executionState === 'undone' || input.executionState === 'failed' ? input.executionState : 'done',\n undoToken: toOptionalString(input.undoToken) ?? undefined,\n commandPayload: input.commandPayload,\n snapshotBefore: input.snapshotBefore,\n snapshotAfter: input.snapshotAfter,\n changes: normalizeRecordLike(input.changes),\n context: normalizeContext(input.context),\n }\n }\n\n private parseListQuery(query: Partial<ActionLogListQuery>) {\n return actionLogListSchema.parse({\n ...query,\n })\n }\n\n private resolveActorUserIds(parsed: ActionLogListQuery): string[] {\n const values = [...(parsed.actorUserIds ?? [])]\n if (parsed.actorUserId) values.push(parsed.actorUserId)\n\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))\n }\n\n private resolveFieldNames(parsed: ActionLogListQuery): string[] {\n const values = [...(parsed.fieldNames ?? [])]\n if (parsed.fieldName) values.push(parsed.fieldName)\n\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))\n }\n\n private resolveActionTypes(parsed: ActionLogListQuery): ActionLogFilterType[] {\n const values = [...(parsed.actionTypes ?? [])]\n if (parsed.actionType) values.push(parsed.actionType)\n\n return Array.from(new Set(values))\n .filter((value): value is ActionLogFilterType => ACTION_LOG_FILTER_TYPES.includes(value as ActionLogFilterType))\n }\n\n private resolvePagination(parsed: ActionLogListQuery): { page: number; pageSize: number; offset: number; limit: number } {\n const pageSize =\n typeof parsed.pageSize === 'number' && parsed.pageSize > 0\n ? parsed.pageSize\n : typeof parsed.limit === 'number' && parsed.limit > 0\n ? parsed.limit\n : 50\n const page = typeof parsed.page === 'number' && parsed.page > 0 ? parsed.page : 1\n const offset =\n typeof parsed.offset === 'number' && parsed.offset >= 0\n ? parsed.offset\n : (page - 1) * pageSize\n return { page, pageSize, offset, limit: pageSize }\n }\n\n private async loadEntries(parsed: ActionLogListQuery, options?: { paginate?: boolean }) {\n let query = (this.buildListQuery(parsed) as any).select('action_logs.id as id')\n\n if (options?.paginate !== false) {\n const { limit, offset } = this.resolvePagination(parsed)\n query = query.limit(limit).offset(offset)\n }\n\n const rows = await query.execute()\n const ids = rows.map((row: any) => row.id).filter(Boolean)\n if (ids.length === 0) return []\n\n const results = await this.em.find(ActionLog, {\n id: { $in: ids } as any,\n deletedAt: null,\n })\n await this.decryptEntries(results)\n\n const byId = new Map(results.map((entry: any) => [entry.id, entry]))\n return ids\n .map((id: any) => byId.get(id))\n .filter((entry: any): entry is ActionLog => Boolean(entry))\n }\n\n private buildListQuery(parsed: ActionLogListQuery): any {\n let query = (this.em.getKysely<any>() as any)\n .selectFrom('action_logs')\n .selectAll()\n .where('action_logs.deleted_at', 'is', null) as any\n\n if (parsed.tenantId) query = query.where('action_logs.tenant_id', '=', parsed.tenantId)\n if (parsed.organizationId) query = query.where('action_logs.organization_id', '=', parsed.organizationId)\n\n const actorUserIds = this.resolveActorUserIds(parsed)\n if (actorUserIds.length === 1) query = query.where('action_logs.actor_user_id', '=', actorUserIds[0])\n if (actorUserIds.length > 1) query = query.where('action_logs.actor_user_id', 'in', actorUserIds)\n\n if (parsed.includeRelated && parsed.resourceKind && parsed.resourceId) {\n query = query.where((eb: any) =>\n eb.or([\n eb.and([\n eb('action_logs.resource_kind', '=', parsed.resourceKind),\n eb('action_logs.resource_id', '=', parsed.resourceId),\n ]),\n eb.and([\n eb('action_logs.parent_resource_kind', '=', parsed.resourceKind),\n eb('action_logs.parent_resource_id', '=', parsed.resourceId),\n ]),\n ])\n )\n } else {\n if (parsed.resourceKind) query = query.where('action_logs.resource_kind', '=', parsed.resourceKind)\n if (parsed.resourceId) query = query.where('action_logs.resource_id', '=', parsed.resourceId)\n }\n\n if (parsed.undoableOnly) query = query.where('action_logs.undo_token', 'is not', null)\n if (parsed.before) query = query.where('action_logs.created_at', '<', parsed.before)\n if (parsed.after) query = query.where('action_logs.created_at', '>', parsed.after)\n\n const fieldNames = this.resolveFieldNames(parsed)\n if (fieldNames.length === 1) query = query.where('action_logs.primary_changed_field', '=', fieldNames[0])\n if (fieldNames.length > 1) query = query.where('action_logs.primary_changed_field', 'in', fieldNames)\n\n const actionTypes = this.resolveActionTypes(parsed)\n if (actionTypes.length === 1) query = query.where('action_logs.action_type', '=', actionTypes[0])\n if (actionTypes.length > 1) query = query.where('action_logs.action_type', 'in', actionTypes)\n\n if (parsed.sortField === 'user') {\n query = query.leftJoin('users as audit_actor', 'audit_actor.id', 'action_logs.actor_user_id')\n }\n\n const sortDir = parsed.sortDir === 'asc' ? 'asc' : 'desc'\n switch (parsed.sortField) {\n case 'user':\n query = query.orderBy(sql`coalesce(nullif(audit_actor.name, ''), audit_actor.email, '')`, sortDir)\n break\n case 'action':\n query = query.orderBy(sql`coalesce(action_logs.action_type, '')`, sortDir)\n break\n case 'field':\n query = query.orderBy(sql`coalesce(action_logs.primary_changed_field, '')`, sortDir)\n break\n case 'source':\n query = query.orderBy(sql`coalesce(action_logs.source_key, '')`, sortDir)\n break\n case 'createdAt':\n default:\n query = query.orderBy(SORT_FIELDS.createdAt, sortDir)\n query = query.orderBy('action_logs.id', sortDir)\n return query\n }\n\n query = query.orderBy('action_logs.created_at', 'desc')\n query = query.orderBy('action_logs.id', 'desc')\n return query\n }\n\n async count(query: Partial<ActionLogListQuery>) {\n const parsed = this.parseListQuery(query)\n const row = await (this.buildListQuery(parsed) as any)\n .clearSelect()\n .clearOrderBy()\n .select(sql<string>`count(*)`.as('count'))\n .executeTakeFirst()\n\n if (!row) return 0\n const rawCount = row.count ?? 0\n return typeof rawCount === 'number' ? rawCount : Number.parseInt(rawCount, 10) || 0\n }\n\n async list(query: Partial<ActionLogListQuery>) {\n const parsed = this.parseListQuery(query)\n const { page, pageSize } = this.resolvePagination(parsed)\n const [items, total] = await Promise.all([\n this.loadEntries(parsed),\n this.count(parsed),\n ])\n const totalPages = Math.max(1, Math.ceil((total || 0) / (pageSize || 1)))\n return { items, total, page, pageSize, totalPages }\n }\n\n async latestUndoableForActor(actorUserId: string, scope: { tenantId?: string | null; organizationId?: string | null }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId,\n undoToken: { $ne: null } as any,\n executionState: 'done',\n deletedAt: null,\n }\n if (scope.tenantId) where.tenantId = scope.tenantId\n if (scope.organizationId) where.organizationId = scope.organizationId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { createdAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async markUndone(id: string, traceInput?: ActionLogCreateInput) {\n const fork = this.em.fork()\n const log = await fork.findOne(ActionLog, { id, deletedAt: null })\n if (!log) return null\n\n log.executionState = 'undone'\n log.undoToken = null\n\n const traceLog = traceInput ? this.createLogEntity(fork, this.parseCreateInput(traceInput)) : null\n if (traceLog) {\n fork.persist(traceLog)\n }\n\n await fork.flush()\n await this.decryptEntries(log)\n if (traceLog) await this.decryptEntries(traceLog)\n\n return log\n }\n\n async findByUndoToken(undoToken: string) {\n const entry = await this.em.findOne(ActionLog, { undoToken, deletedAt: null })\n await this.decryptEntries(entry)\n return entry\n }\n\n async findById(id: string) {\n const entry = await this.em.findOne(ActionLog, { id, deletedAt: null })\n await this.decryptEntries(entry)\n return entry\n }\n\n async latestUndoableForResource(params: {\n actorUserId: string\n tenantId?: string | null\n organizationId?: string | null\n resourceKind?: string | null\n resourceId?: string | null\n }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId: params.actorUserId,\n undoToken: { $ne: null } as any,\n executionState: 'done',\n deletedAt: null,\n }\n if (params.tenantId) where.tenantId = params.tenantId\n if (params.organizationId) where.organizationId = params.organizationId\n if (params.resourceKind) where.resourceKind = params.resourceKind\n if (params.resourceId) where.resourceId = params.resourceId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { createdAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async latestUndoneForActor(actorUserId: string, scope: { tenantId?: string | null; organizationId?: string | null }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId,\n executionState: 'undone',\n deletedAt: null,\n }\n if (scope.tenantId) where.tenantId = scope.tenantId\n if (scope.organizationId) where.organizationId = scope.organizationId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { updatedAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async markRedone(id: string) {\n const log = await this.em.findOne(ActionLog, { id, deletedAt: null })\n if (!log) return null\n\n log.executionState = 'redone'\n log.undoToken = null\n await this.em.flush()\n return log\n }\n\n async backfillProjections(options: ActionLogProjectionBackfillOptions = {}): Promise<ActionLogProjectionBackfillResult> {\n const batchSize = Math.min(Math.max(Math.trunc(options.batchSize ?? 250), 1), 1000)\n const logger = options.logger ?? (() => {})\n const result: ActionLogProjectionBackfillResult = {\n errors: 0,\n scanned: 0,\n skipped: 0,\n updated: 0,\n }\n\n let cursorCreatedAt: Date | null = null\n let cursorId: string | null = null\n\n while (true) {\n const rowsQuery = (this.em.getKysely<any>() as any)\n .selectFrom('action_logs')\n .select([\n 'action_logs.id',\n 'action_logs.tenant_id',\n 'action_logs.organization_id',\n 'action_logs.actor_user_id',\n 'action_logs.command_id',\n 'action_logs.action_label',\n 'action_logs.snapshot_before',\n 'action_logs.changes_json',\n 'action_logs.context_json',\n 'action_logs.action_type',\n 'action_logs.source_key',\n 'action_logs.changed_fields',\n 'action_logs.primary_changed_field',\n 'action_logs.created_at',\n ])\n .where('action_logs.deleted_at', 'is', null) as any\n\n if (options.tenantId) rowsQuery.where('action_logs.tenant_id', '=', options.tenantId)\n if (options.organizationId) rowsQuery.where('action_logs.organization_id', '=', options.organizationId)\n\n if (!options.force) {\n rowsQuery.where((eb: any) =>\n eb.or([\n eb('action_logs.action_type', 'is', null),\n eb('action_logs.source_key', 'is', null),\n eb('action_logs.changed_fields', 'is', null),\n ])\n )\n }\n\n if (cursorCreatedAt && cursorId) {\n rowsQuery.where((eb: any) =>\n eb.or([\n eb('action_logs.created_at', '>', cursorCreatedAt),\n eb.and([\n eb('action_logs.created_at', '=', cursorCreatedAt),\n eb('action_logs.id', '>', cursorId),\n ]),\n ])\n )\n }\n\n const rows = await rowsQuery\n .orderBy('created_at', 'asc')\n .orderBy('id', 'asc')\n .limit(batchSize)\n\n if (rows.length === 0) break\n\n for (const row of rows) {\n result.scanned += 1\n\n try {\n const decrypted = await this.decryptEntryPayload(row as unknown as Record<string, unknown>)\n const projection = deriveActionLogProjection({\n actorUserId: readString(decrypted, 'actorUserId', 'actor_user_id'),\n actionLabel: readString(decrypted, 'actionLabel', 'action_label'),\n changes: readRecord(decrypted, 'changesJson', 'changes_json'),\n commandId: readString(decrypted, 'commandId', 'command_id') ?? 'unknown',\n context: readRecord(decrypted, 'contextJson', 'context_json'),\n snapshotBefore: readValue(decrypted, 'snapshotBefore', 'snapshot_before'),\n })\n\n const needsUpdate = options.force === true\n || row.action_type !== projection.actionType\n || row.source_key !== projection.sourceKey\n || row.primary_changed_field !== projection.primaryChangedField\n || !stringArraysEqual(row.changed_fields, projection.changedFields)\n\n if (!needsUpdate) {\n result.skipped += 1\n continue\n }\n\n await (this.em.getKysely<any>() as any)\n .updateTable('action_logs')\n .set({\n action_type: projection.actionType,\n changed_fields: projection.changedFields,\n primary_changed_field: projection.primaryChangedField,\n source_key: projection.sourceKey,\n })\n .where('id', '=', row.id)\n .execute()\n\n result.updated += 1\n } catch (err) {\n result.errors += 1\n logger(`[backfill] Failed for action log ${row.id}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n const lastRow = rows[rows.length - 1]\n cursorCreatedAt = lastRow.created_at\n cursorId = lastRow.id\n\n logger(\n `[backfill] Processed ${result.scanned} action logs (updated: ${result.updated}, skipped: ${result.skipped}, errors: ${result.errors})`,\n )\n\n if (rows.length < batchSize) break\n }\n\n return result\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["decrypted"]
4
+ "sourcesContent": ["import type { FilterQuery } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { sql } from 'kysely'\nimport { ActionLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport {\n actionLogCreateSchema,\n actionLogListSchema,\n type ActionLogCreateInput,\n type ActionLogListQuery,\n} from '@open-mercato/core/modules/audit_logs/data/validators'\nimport { isRecord } from '@open-mercato/core/modules/audit_logs/lib/changeRows'\nimport {\n ACTION_LOG_FILTER_TYPES,\n type ActionLogFilterType,\n deriveActionLogProjection,\n} from '@open-mercato/core/modules/audit_logs/lib/projections'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { toOptionalString } from '@open-mercato/shared/lib/string/coerce'\n\nlet validationWarningLogged = false\nlet runtimeValidationAvailable: boolean | null = null\nlet decryptionWarningLogged = false\n\nconst isZodRuntimeMissing = (err: unknown) => err instanceof TypeError && typeof err.message === 'string' && err.message.includes('_zod')\n\nconst SORT_FIELDS = {\n createdAt: 'action_logs.created_at',\n} as const\nconst UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n\ntype ActionLogProjectionBackfillOptions = {\n batchSize?: number\n force?: boolean\n logger?: (message: string) => void\n organizationId?: string | null\n tenantId?: string | null\n}\n\nexport type ActionLogProjectionBackfillResult = {\n errors: number\n scanned: number\n skipped: number\n updated: number\n}\n\ntype BackfillRow = {\n action_label: string | null\n action_type: string | null\n actor_user_id: string | null\n changed_fields: string[] | null\n changes_json: Record<string, unknown> | null\n command_id: string\n context_json: Record<string, unknown> | null\n created_at: Date\n id: string\n organization_id: string | null\n primary_changed_field: string | null\n snapshot_before: unknown | null\n source_key: string | null\n tenant_id: string | null\n}\n\nfunction readString(record: Record<string, unknown>, ...keys: string[]): string | null {\n for (const key of keys) {\n const value = record[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n\n return null\n}\n\nfunction readRecord(record: Record<string, unknown>, ...keys: string[]): Record<string, unknown> | null {\n for (const key of keys) {\n const value = record[key]\n if (isRecord(value)) return value\n }\n\n return null\n}\n\nfunction readValue(record: Record<string, unknown>, ...keys: string[]): unknown {\n for (const key of keys) {\n if (Object.prototype.hasOwnProperty.call(record, key)) return record[key]\n }\n\n return undefined\n}\n\nfunction readStringArray(record: Record<string, unknown>, ...keys: string[]): string[] | null {\n for (const key of keys) {\n const value = record[key]\n if (Array.isArray(value)) {\n return value.filter((entry): entry is string => typeof entry === 'string')\n }\n }\n\n return null\n}\n\nfunction stringArraysEqual(left: string[] | null, right: string[]): boolean {\n if (!Array.isArray(left)) return false\n if (left.length !== right.length) return false\n\n return left.every((value, index) => value === right[index])\n}\n\nexport class ActionLogService {\n constructor(\n private readonly em: EntityManager,\n private readonly tenantEncryptionService?: TenantDataEncryptionService,\n ) {}\n\n private async decryptEntryPayload<T extends Record<string, unknown>>(entry: T): Promise<T> {\n if (!this.tenantEncryptionService?.isEnabled()) return entry\n\n try {\n const tenantId = readString(entry, 'tenantId', 'tenant_id')\n const organizationId = readString(entry, 'organizationId', 'organization_id')\n const dek = await this.tenantEncryptionService.getDek(tenantId)\n const deepDecrypt = (value: unknown): unknown => {\n if (!dek) return value\n if (typeof value === 'string' && value.split(':').length === 4 && value.endsWith(':v1')) {\n const decrypted = decryptWithAesGcm(value, dek.key)\n if (decrypted === null) return value\n try {\n return JSON.parse(decrypted)\n } catch {\n return decrypted\n }\n }\n if (Array.isArray(value)) return value.map((item) => deepDecrypt(item))\n if (value && typeof value === 'object') {\n const copy: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n copy[key] = deepDecrypt(item)\n }\n return copy\n }\n return value\n }\n\n const decrypted = await this.tenantEncryptionService.decryptEntityPayload(\n 'audit_logs:action_log',\n entry,\n tenantId,\n organizationId,\n )\n\n const merged = {\n ...entry,\n ...decrypted,\n } as Record<string, unknown>\n\n merged.changesJson = deepDecrypt(merged.changesJson ?? merged.changes_json ?? entry.changesJson ?? entry.changes_json)\n merged.changes_json = merged.changesJson\n merged.snapshotBefore = deepDecrypt(merged.snapshotBefore ?? merged.snapshot_before ?? entry.snapshotBefore ?? entry.snapshot_before)\n merged.snapshot_before = merged.snapshotBefore\n merged.snapshotAfter = deepDecrypt(merged.snapshotAfter ?? merged.snapshot_after ?? entry.snapshotAfter ?? entry.snapshot_after)\n merged.snapshot_after = merged.snapshotAfter\n merged.commandPayload = deepDecrypt(merged.commandPayload ?? merged.command_payload ?? entry.commandPayload ?? entry.command_payload)\n merged.command_payload = merged.commandPayload\n merged.contextJson = deepDecrypt(merged.contextJson ?? merged.context_json ?? entry.contextJson ?? entry.context_json)\n merged.context_json = merged.contextJson\n\n return merged as T\n } catch (err) {\n if (!decryptionWarningLogged) {\n decryptionWarningLogged = true\n console.warn('[audit_logs] failed to decrypt action log entry', err)\n }\n return entry\n }\n }\n\n private async decryptEntries(entries: ActionLog | ActionLog[] | null | undefined): Promise<void> {\n if (!entries) return\n\n const list = Array.isArray(entries) ? entries : [entries]\n for (const entry of list) {\n Object.assign(entry as unknown as Record<string, unknown>, await this.decryptEntryPayload(entry as unknown as Record<string, unknown>))\n }\n }\n\n async log(input: ActionLogCreateInput): Promise<ActionLog | null> {\n const data = this.parseCreateInput(input)\n const fork = this.em.fork()\n const log = this.createLogEntity(fork, data)\n await fork.persist(log).flush()\n await this.decryptEntries(log)\n return log\n }\n\n private parseCreateInput(input: ActionLogCreateInput): ActionLogCreateInput {\n let data: ActionLogCreateInput\n const schema = actionLogCreateSchema as typeof actionLogCreateSchema & { _zod?: unknown }\n const canValidate = Boolean(schema && typeof schema.parse === 'function')\n const shouldValidate = canValidate && runtimeValidationAvailable !== false\n\n if (shouldValidate) {\n try {\n data = schema.parse(input)\n runtimeValidationAvailable = true\n } catch (err) {\n if (!isZodRuntimeMissing(err) && !validationWarningLogged) {\n validationWarningLogged = true\n console.warn('[audit_logs] falling back to permissive action log payload parser', err)\n }\n if (isZodRuntimeMissing(err)) runtimeValidationAvailable = false\n data = this.normalizeInput(input)\n }\n } else {\n data = this.normalizeInput(input)\n }\n\n return data\n }\n\n private createLogEntity(fork: EntityManager, data: ActionLogCreateInput): ActionLog {\n const projection = deriveActionLogProjection({\n actorUserId: data.actorUserId ?? null,\n actionLabel: data.actionLabel ?? null,\n changes: isRecord(data.changes) ? data.changes : null,\n commandId: data.commandId,\n context: isRecord(data.context) ? data.context : null,\n snapshotBefore: data.snapshotBefore,\n })\n\n return fork.create(ActionLog, {\n tenantId: data.tenantId ?? null,\n organizationId: data.organizationId ?? null,\n actorUserId: data.actorUserId ?? null,\n commandId: data.commandId,\n actionLabel: data.actionLabel ?? null,\n actionType: projection.actionType,\n resourceKind: data.resourceKind ?? null,\n resourceId: data.resourceId ?? null,\n parentResourceKind: data.parentResourceKind ?? null,\n parentResourceId: data.parentResourceId ?? null,\n relatedResourceKind: toOptionalString(data.relatedResourceKind) ?? null,\n relatedResourceId: toOptionalString(data.relatedResourceId) ?? null,\n executionState: data.executionState ?? 'done',\n undoToken: data.undoToken ?? null,\n commandPayload: data.commandPayload ?? null,\n snapshotBefore: data.snapshotBefore ?? null,\n snapshotAfter: data.snapshotAfter ?? null,\n changesJson: isRecord(data.changes) ? data.changes : null,\n changedFields: projection.changedFields,\n primaryChangedField: projection.primaryChangedField,\n contextJson: isRecord(data.context) ? data.context : null,\n sourceKey: projection.sourceKey,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n }\n\n private normalizeInput(input: Partial<ActionLogCreateInput> | null | undefined): ActionLogCreateInput {\n if (!input) {\n return {\n tenantId: null,\n organizationId: null,\n actorUserId: null,\n commandId: 'unknown',\n actionLabel: undefined,\n resourceKind: undefined,\n resourceId: undefined,\n relatedResourceKind: null,\n relatedResourceId: null,\n executionState: 'done',\n undoToken: undefined,\n commandPayload: undefined,\n snapshotBefore: undefined,\n snapshotAfter: undefined,\n changes: undefined,\n context: undefined,\n }\n }\n\n const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/\n const toNullableUuid = (value: unknown) => {\n if (typeof value !== 'string' || value.length === 0) return null\n const candidate = value.startsWith('api_key:') ? value.slice('api_key:'.length) : value\n return UUID_REGEX.test(candidate) ? candidate : null\n }\n\n const normalizeRecordLike = (value: unknown): ActionLogCreateInput['changes'] => {\n if (value === null) return null\n if (Array.isArray(value)) return value\n if (typeof value === 'object') return value as Record<string, unknown>\n return undefined\n }\n\n const normalizeContext = (value: unknown) => (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n )\n\n return {\n tenantId: toNullableUuid(input.tenantId),\n organizationId: toNullableUuid(input.organizationId),\n actorUserId: toNullableUuid(input.actorUserId),\n commandId: typeof input.commandId === 'string' && input.commandId.length > 0 ? input.commandId : 'unknown',\n actionLabel: toOptionalString(input.actionLabel) ?? undefined,\n resourceKind: toOptionalString(input.resourceKind) ?? undefined,\n resourceId: toOptionalString(input.resourceId) ?? undefined,\n parentResourceKind: toOptionalString(input.parentResourceKind) ?? null,\n parentResourceId: toOptionalString(input.parentResourceId) ?? null,\n relatedResourceKind: toOptionalString(input.relatedResourceKind) ?? null,\n relatedResourceId: toOptionalString(input.relatedResourceId) ?? null,\n executionState: input.executionState === 'undone' || input.executionState === 'failed' ? input.executionState : 'done',\n undoToken: toOptionalString(input.undoToken) ?? undefined,\n commandPayload: input.commandPayload,\n snapshotBefore: input.snapshotBefore,\n snapshotAfter: input.snapshotAfter,\n changes: normalizeRecordLike(input.changes),\n context: normalizeContext(input.context),\n }\n }\n\n private parseListQuery(query: Partial<ActionLogListQuery>) {\n return actionLogListSchema.parse({\n ...query,\n })\n }\n\n private resolveActorUserIds(parsed: ActionLogListQuery): string[] {\n const values = [...(parsed.actorUserIds ?? [])]\n if (parsed.actorUserId) values.push(parsed.actorUserId)\n\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))\n }\n\n private resolveFieldNames(parsed: ActionLogListQuery): string[] {\n const values = [...(parsed.fieldNames ?? [])]\n if (parsed.fieldName) values.push(parsed.fieldName)\n\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))\n }\n\n private resolveActionTypes(parsed: ActionLogListQuery): ActionLogFilterType[] {\n const values = [...(parsed.actionTypes ?? [])]\n if (parsed.actionType) values.push(parsed.actionType)\n\n return Array.from(new Set(values))\n .filter((value): value is ActionLogFilterType => ACTION_LOG_FILTER_TYPES.includes(value as ActionLogFilterType))\n }\n\n private resolvePagination(parsed: ActionLogListQuery): { page: number; pageSize: number; offset: number; limit: number } {\n const pageSize =\n typeof parsed.pageSize === 'number' && parsed.pageSize > 0\n ? parsed.pageSize\n : typeof parsed.limit === 'number' && parsed.limit > 0\n ? parsed.limit\n : 50\n const page = typeof parsed.page === 'number' && parsed.page > 0 ? parsed.page : 1\n const offset =\n typeof parsed.offset === 'number' && parsed.offset >= 0\n ? parsed.offset\n : (page - 1) * pageSize\n return { page, pageSize, offset, limit: pageSize }\n }\n\n private async loadEntries(parsed: ActionLogListQuery, options?: { paginate?: boolean }) {\n let query = (this.buildListQuery(parsed) as any).select('action_logs.id as id')\n\n if (options?.paginate !== false) {\n const { limit, offset } = this.resolvePagination(parsed)\n query = query.limit(limit).offset(offset)\n }\n\n const rows = await query.execute()\n const ids = rows.map((row: any) => row.id).filter(Boolean)\n if (ids.length === 0) return []\n\n const results = await this.em.find(ActionLog, {\n id: { $in: ids } as any,\n deletedAt: null,\n })\n await this.decryptEntries(results)\n\n const byId = new Map(results.map((entry: any) => [entry.id, entry]))\n return ids\n .map((id: any) => byId.get(id))\n .filter((entry: any): entry is ActionLog => Boolean(entry))\n }\n\n private buildListQuery(parsed: ActionLogListQuery): any {\n let query = (this.em.getKysely<any>() as any)\n .selectFrom('action_logs')\n .selectAll()\n .where('action_logs.deleted_at', 'is', null) as any\n\n if (parsed.tenantId) query = query.where('action_logs.tenant_id', '=', parsed.tenantId)\n if (parsed.organizationId) query = query.where('action_logs.organization_id', '=', parsed.organizationId)\n\n const actorUserIds = this.resolveActorUserIds(parsed)\n if (actorUserIds.length === 1) query = query.where('action_logs.actor_user_id', '=', actorUserIds[0])\n if (actorUserIds.length > 1) query = query.where('action_logs.actor_user_id', 'in', actorUserIds)\n\n if (parsed.includeRelated && parsed.resourceKind && parsed.resourceId) {\n query = query.where((eb: any) =>\n eb.or([\n eb.and([\n eb('action_logs.resource_kind', '=', parsed.resourceKind),\n eb('action_logs.resource_id', '=', parsed.resourceId),\n ]),\n eb.and([\n eb('action_logs.parent_resource_kind', '=', parsed.resourceKind),\n eb('action_logs.parent_resource_id', '=', parsed.resourceId),\n ]),\n eb.and([\n eb('action_logs.related_resource_kind', '=', parsed.resourceKind),\n eb('action_logs.related_resource_id', '=', parsed.resourceId),\n ]),\n ])\n )\n } else {\n if (parsed.resourceKind) query = query.where('action_logs.resource_kind', '=', parsed.resourceKind)\n if (parsed.resourceId) query = query.where('action_logs.resource_id', '=', parsed.resourceId)\n }\n\n if (parsed.undoableOnly) query = query.where('action_logs.undo_token', 'is not', null)\n if (parsed.before) query = query.where('action_logs.created_at', '<', parsed.before)\n if (parsed.after) query = query.where('action_logs.created_at', '>', parsed.after)\n\n const fieldNames = this.resolveFieldNames(parsed)\n if (fieldNames.length === 1) query = query.where('action_logs.primary_changed_field', '=', fieldNames[0])\n if (fieldNames.length > 1) query = query.where('action_logs.primary_changed_field', 'in', fieldNames)\n\n const actionTypes = this.resolveActionTypes(parsed)\n if (actionTypes.length === 1) query = query.where('action_logs.action_type', '=', actionTypes[0])\n if (actionTypes.length > 1) query = query.where('action_logs.action_type', 'in', actionTypes)\n\n if (parsed.sortField === 'user') {\n query = query.leftJoin('users as audit_actor', 'audit_actor.id', 'action_logs.actor_user_id')\n }\n\n const sortDir = parsed.sortDir === 'asc' ? 'asc' : 'desc'\n switch (parsed.sortField) {\n case 'user':\n query = query.orderBy(sql`coalesce(nullif(audit_actor.name, ''), audit_actor.email, '')`, sortDir)\n break\n case 'action':\n query = query.orderBy(sql`coalesce(action_logs.action_type, '')`, sortDir)\n break\n case 'field':\n query = query.orderBy(sql`coalesce(action_logs.primary_changed_field, '')`, sortDir)\n break\n case 'source':\n query = query.orderBy(sql`coalesce(action_logs.source_key, '')`, sortDir)\n break\n case 'createdAt':\n default:\n query = query.orderBy(SORT_FIELDS.createdAt, sortDir)\n query = query.orderBy('action_logs.id', sortDir)\n return query\n }\n\n query = query.orderBy('action_logs.created_at', 'desc')\n query = query.orderBy('action_logs.id', 'desc')\n return query\n }\n\n async count(query: Partial<ActionLogListQuery>) {\n const parsed = this.parseListQuery(query)\n const row = await (this.buildListQuery(parsed) as any)\n .clearSelect()\n .clearOrderBy()\n .select(sql<string>`count(*)`.as('count'))\n .executeTakeFirst()\n\n if (!row) return 0\n const rawCount = row.count ?? 0\n return typeof rawCount === 'number' ? rawCount : Number.parseInt(rawCount, 10) || 0\n }\n\n async list(query: Partial<ActionLogListQuery>) {\n const parsed = this.parseListQuery(query)\n const { page, pageSize } = this.resolvePagination(parsed)\n const [items, total] = await Promise.all([\n this.loadEntries(parsed),\n this.count(parsed),\n ])\n const totalPages = Math.max(1, Math.ceil((total || 0) / (pageSize || 1)))\n return { items, total, page, pageSize, totalPages }\n }\n\n async latestUndoableForActor(actorUserId: string, scope: { tenantId?: string | null; organizationId?: string | null }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId,\n undoToken: { $ne: null } as any,\n executionState: 'done',\n deletedAt: null,\n }\n if (scope.tenantId) where.tenantId = scope.tenantId\n if (scope.organizationId) where.organizationId = scope.organizationId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { createdAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async markUndone(id: string, traceInput?: ActionLogCreateInput) {\n const fork = this.em.fork()\n const log = await fork.findOne(ActionLog, { id, deletedAt: null })\n if (!log) return null\n\n log.executionState = 'undone'\n log.undoToken = null\n\n const traceLog = traceInput ? this.createLogEntity(fork, this.parseCreateInput(traceInput)) : null\n if (traceLog) {\n fork.persist(traceLog)\n }\n\n await fork.flush()\n await this.decryptEntries(log)\n if (traceLog) await this.decryptEntries(traceLog)\n\n return log\n }\n\n async findByUndoToken(undoToken: string) {\n const entry = await this.em.findOne(ActionLog, { undoToken, deletedAt: null })\n await this.decryptEntries(entry)\n return entry\n }\n\n async findById(id: string) {\n const entry = await this.em.findOne(ActionLog, { id, deletedAt: null })\n await this.decryptEntries(entry)\n return entry\n }\n\n async latestUndoableForResource(params: {\n actorUserId: string\n tenantId?: string | null\n organizationId?: string | null\n resourceKind?: string | null\n resourceId?: string | null\n }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId: params.actorUserId,\n undoToken: { $ne: null } as any,\n executionState: 'done',\n deletedAt: null,\n }\n if (params.tenantId) where.tenantId = params.tenantId\n if (params.organizationId) where.organizationId = params.organizationId\n if (params.resourceKind) where.resourceKind = params.resourceKind\n if (params.resourceId) where.resourceId = params.resourceId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { createdAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async latestUndoneForActor(actorUserId: string, scope: { tenantId?: string | null; organizationId?: string | null }) {\n const where: FilterQuery<ActionLog> = {\n actorUserId,\n executionState: 'undone',\n deletedAt: null,\n }\n if (scope.tenantId) where.tenantId = scope.tenantId\n if (scope.organizationId) where.organizationId = scope.organizationId\n\n const entry = await this.em.findOne(ActionLog, where, { orderBy: { updatedAt: 'desc' } })\n await this.decryptEntries(entry)\n return entry\n }\n\n async markRedone(id: string) {\n const log = await this.em.findOne(ActionLog, { id, deletedAt: null })\n if (!log) return null\n\n log.executionState = 'redone'\n log.undoToken = null\n await this.em.flush()\n return log\n }\n\n async backfillProjections(options: ActionLogProjectionBackfillOptions = {}): Promise<ActionLogProjectionBackfillResult> {\n const batchSize = Math.min(Math.max(Math.trunc(options.batchSize ?? 250), 1), 1000)\n const logger = options.logger ?? (() => {})\n const result: ActionLogProjectionBackfillResult = {\n errors: 0,\n scanned: 0,\n skipped: 0,\n updated: 0,\n }\n\n let cursorCreatedAt: Date | null = null\n let cursorId: string | null = null\n\n while (true) {\n const rowsQuery = (this.em.getKysely<any>() as any)\n .selectFrom('action_logs')\n .select([\n 'action_logs.id',\n 'action_logs.tenant_id',\n 'action_logs.organization_id',\n 'action_logs.actor_user_id',\n 'action_logs.command_id',\n 'action_logs.action_label',\n 'action_logs.snapshot_before',\n 'action_logs.changes_json',\n 'action_logs.context_json',\n 'action_logs.action_type',\n 'action_logs.source_key',\n 'action_logs.changed_fields',\n 'action_logs.primary_changed_field',\n 'action_logs.created_at',\n ])\n .where('action_logs.deleted_at', 'is', null) as any\n\n if (options.tenantId) rowsQuery.where('action_logs.tenant_id', '=', options.tenantId)\n if (options.organizationId) rowsQuery.where('action_logs.organization_id', '=', options.organizationId)\n\n if (!options.force) {\n rowsQuery.where((eb: any) =>\n eb.or([\n eb('action_logs.action_type', 'is', null),\n eb('action_logs.source_key', 'is', null),\n eb('action_logs.changed_fields', 'is', null),\n ])\n )\n }\n\n if (cursorCreatedAt && cursorId) {\n rowsQuery.where((eb: any) =>\n eb.or([\n eb('action_logs.created_at', '>', cursorCreatedAt),\n eb.and([\n eb('action_logs.created_at', '=', cursorCreatedAt),\n eb('action_logs.id', '>', cursorId),\n ]),\n ])\n )\n }\n\n const rows = await rowsQuery\n .orderBy('created_at', 'asc')\n .orderBy('id', 'asc')\n .limit(batchSize)\n\n if (rows.length === 0) break\n\n for (const row of rows) {\n result.scanned += 1\n\n try {\n const decrypted = await this.decryptEntryPayload(row as unknown as Record<string, unknown>)\n const projection = deriveActionLogProjection({\n actorUserId: readString(decrypted, 'actorUserId', 'actor_user_id'),\n actionLabel: readString(decrypted, 'actionLabel', 'action_label'),\n changes: readRecord(decrypted, 'changesJson', 'changes_json'),\n commandId: readString(decrypted, 'commandId', 'command_id') ?? 'unknown',\n context: readRecord(decrypted, 'contextJson', 'context_json'),\n snapshotBefore: readValue(decrypted, 'snapshotBefore', 'snapshot_before'),\n })\n\n const needsUpdate = options.force === true\n || row.action_type !== projection.actionType\n || row.source_key !== projection.sourceKey\n || row.primary_changed_field !== projection.primaryChangedField\n || !stringArraysEqual(row.changed_fields, projection.changedFields)\n\n if (!needsUpdate) {\n result.skipped += 1\n continue\n }\n\n await (this.em.getKysely<any>() as any)\n .updateTable('action_logs')\n .set({\n action_type: projection.actionType,\n changed_fields: projection.changedFields,\n primary_changed_field: projection.primaryChangedField,\n source_key: projection.sourceKey,\n })\n .where('id', '=', row.id)\n .execute()\n\n result.updated += 1\n } catch (err) {\n result.errors += 1\n logger(`[backfill] Failed for action log ${row.id}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n const lastRow = rows[rows.length - 1]\n cursorCreatedAt = lastRow.created_at\n cursorId = lastRow.id\n\n logger(\n `[backfill] Processed ${result.scanned} action logs (updated: ${result.updated}, skipped: ${result.skipped}, errors: ${result.errors})`,\n )\n\n if (rows.length < batchSize) break\n }\n\n return result\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["decrypted", "UUID_REGEX"]
7
7
  }
@@ -1,6 +1,5 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
- import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
4
3
  import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
5
4
  import { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from "@open-mercato/shared/lib/crud/mutation-guard";
6
5
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
@@ -11,6 +10,8 @@ import { CustomerEntity, CustomerEntityRole } from "../data/entities.js";
11
10
  import { entityRoleCreateSchema, entityRoleUpdateSchema, entityRoleDeleteSchema } from "../data/validators.js";
12
11
  import { withScopedPayload } from "./utils.js";
13
12
  import { resolveCustomersRequestContext, resolveAuthActorId } from "../lib/interactionRequestContext.js";
13
+ import { deriveDisplayNameFromEmail } from "../lib/displayName.js";
14
+ import { withOperationMetadata } from "../lib/operationMetadata.js";
14
15
  const paramsSchema = z.object({ id: z.string().uuid() });
15
16
  const roleIdQuerySchema = z.object({ roleId: z.string().uuid() });
16
17
  const createBodySchema = z.object({
@@ -46,22 +47,6 @@ function buildValidationErrorResponse(error, translate) {
46
47
  { status: 400 }
47
48
  );
48
49
  }
49
- function withOperationMetadata(response, logEntry, fallback) {
50
- if (!logEntry?.undoToken || !logEntry.id || !logEntry.commandId) return response;
51
- response.headers.set(
52
- "x-om-operation",
53
- serializeOperationMetadata({
54
- id: logEntry.id,
55
- undoToken: logEntry.undoToken,
56
- commandId: logEntry.commandId,
57
- actionLabel: logEntry.actionLabel ?? null,
58
- resourceKind: logEntry.resourceKind ?? fallback.resourceKind,
59
- resourceId: logEntry.resourceId ?? fallback.resourceId,
60
- executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
61
- })
62
- );
63
- return response;
64
- }
65
50
  async function buildContext(request) {
66
51
  const context = await resolveCustomersRequestContext(request);
67
52
  return {
@@ -244,7 +229,7 @@ function createEntityRolesHandlers(entityType) {
244
229
  }
245
230
  ) : [];
246
231
  const userMap = new Map(users.map((user) => [user.id, {
247
- name: user.name ?? null,
232
+ name: user.name ?? deriveDisplayNameFromEmail(user.email) ?? null,
248
233
  email: user.email ?? null,
249
234
  phone: null
250
235
  }]));