nestjs-ddd-cli 2.2.1 → 3.2.1

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 (254) hide show
  1. package/README.md +247 -408
  2. package/ddd.schema.json +111 -0
  3. package/dist/commands/aggregate-validator.d.ts +9 -0
  4. package/dist/commands/aggregate-validator.js +953 -0
  5. package/dist/commands/aggregate-validator.js.map +1 -0
  6. package/dist/commands/ai-assist.d.ts +8 -0
  7. package/dist/commands/ai-assist.js +337 -0
  8. package/dist/commands/ai-assist.js.map +1 -0
  9. package/dist/commands/api-contracts.d.ts +9 -0
  10. package/dist/commands/api-contracts.js +1368 -0
  11. package/dist/commands/api-contracts.js.map +1 -0
  12. package/dist/commands/api-docs.d.ts +8 -0
  13. package/dist/commands/api-docs.js +408 -0
  14. package/dist/commands/api-docs.js.map +1 -0
  15. package/dist/commands/api-versioning.d.ts +11 -0
  16. package/dist/commands/api-versioning.js +643 -0
  17. package/dist/commands/api-versioning.js.map +1 -0
  18. package/dist/commands/audit-logging.d.ts +9 -0
  19. package/dist/commands/audit-logging.js +1129 -0
  20. package/dist/commands/audit-logging.js.map +1 -0
  21. package/dist/commands/batch-generate.d.ts +10 -0
  22. package/dist/commands/batch-generate.js +405 -0
  23. package/dist/commands/batch-generate.js.map +1 -0
  24. package/dist/commands/caching-strategies.d.ts +9 -0
  25. package/dist/commands/caching-strategies.js +874 -0
  26. package/dist/commands/caching-strategies.js.map +1 -0
  27. package/dist/commands/code-analyzer.d.ts +42 -0
  28. package/dist/commands/code-analyzer.js +474 -0
  29. package/dist/commands/code-analyzer.js.map +1 -0
  30. package/dist/commands/database-seeding.d.ts +6 -0
  31. package/dist/commands/database-seeding.js +621 -0
  32. package/dist/commands/database-seeding.js.map +1 -0
  33. package/dist/commands/db-optimization.d.ts +7 -0
  34. package/dist/commands/db-optimization.js +687 -0
  35. package/dist/commands/db-optimization.js.map +1 -0
  36. package/dist/commands/dependency-graph.d.ts +6 -0
  37. package/dist/commands/dependency-graph.js +329 -0
  38. package/dist/commands/dependency-graph.js.map +1 -0
  39. package/dist/commands/doctor-enhanced.d.ts +22 -0
  40. package/dist/commands/doctor-enhanced.js +543 -0
  41. package/dist/commands/doctor-enhanced.js.map +1 -0
  42. package/dist/commands/doctor.d.ts +4 -0
  43. package/dist/commands/doctor.js +151 -0
  44. package/dist/commands/doctor.js.map +1 -0
  45. package/dist/commands/env-manager.d.ts +6 -0
  46. package/dist/commands/env-manager.js +419 -0
  47. package/dist/commands/env-manager.js.map +1 -0
  48. package/dist/commands/event-sourcing-full.d.ts +10 -0
  49. package/dist/commands/event-sourcing-full.js +1107 -0
  50. package/dist/commands/event-sourcing-full.js.map +1 -0
  51. package/dist/commands/feature-flags.d.ts +9 -0
  52. package/dist/commands/feature-flags.js +824 -0
  53. package/dist/commands/feature-flags.js.map +1 -0
  54. package/dist/commands/filter-dsl.d.ts +10 -0
  55. package/dist/commands/filter-dsl.js +1407 -0
  56. package/dist/commands/filter-dsl.js.map +1 -0
  57. package/dist/commands/generate-all.js +485 -32
  58. package/dist/commands/generate-all.js.map +1 -1
  59. package/dist/commands/generate-deployment.d.ts +8 -0
  60. package/dist/commands/generate-deployment.js +746 -0
  61. package/dist/commands/generate-deployment.js.map +1 -0
  62. package/dist/commands/generate-domain-service.d.ts +14 -0
  63. package/dist/commands/generate-domain-service.js +796 -0
  64. package/dist/commands/generate-domain-service.js.map +1 -0
  65. package/dist/commands/generate-entity.js +82 -24
  66. package/dist/commands/generate-entity.js.map +1 -1
  67. package/dist/commands/generate-from-schema.d.ts +56 -0
  68. package/dist/commands/generate-from-schema.js +222 -0
  69. package/dist/commands/generate-from-schema.js.map +1 -0
  70. package/dist/commands/generate-orchestrator.d.ts +14 -0
  71. package/dist/commands/generate-orchestrator.js +887 -0
  72. package/dist/commands/generate-orchestrator.js.map +1 -0
  73. package/dist/commands/generate-repository.d.ts +14 -0
  74. package/dist/commands/generate-repository.js +1019 -0
  75. package/dist/commands/generate-repository.js.map +1 -0
  76. package/dist/commands/generate-shared.d.ts +4 -0
  77. package/dist/commands/generate-shared.js +388 -0
  78. package/dist/commands/generate-shared.js.map +1 -0
  79. package/dist/commands/generate-value-object.d.ts +32 -0
  80. package/dist/commands/generate-value-object.js +700 -0
  81. package/dist/commands/generate-value-object.js.map +1 -0
  82. package/dist/commands/graphql-subscriptions.d.ts +6 -0
  83. package/dist/commands/graphql-subscriptions.js +607 -0
  84. package/dist/commands/graphql-subscriptions.js.map +1 -0
  85. package/dist/commands/graphql-types.d.ts +5 -0
  86. package/dist/commands/graphql-types.js +423 -0
  87. package/dist/commands/graphql-types.js.map +1 -0
  88. package/dist/commands/health-probes-advanced.d.ts +6 -0
  89. package/dist/commands/health-probes-advanced.js +655 -0
  90. package/dist/commands/health-probes-advanced.js.map +1 -0
  91. package/dist/commands/i18n-setup.d.ts +10 -0
  92. package/dist/commands/i18n-setup.js +677 -0
  93. package/dist/commands/i18n-setup.js.map +1 -0
  94. package/dist/commands/init-config.d.ts +6 -0
  95. package/dist/commands/init-config.js +370 -0
  96. package/dist/commands/init-config.js.map +1 -0
  97. package/dist/commands/init-project.js +56 -6
  98. package/dist/commands/init-project.js.map +1 -1
  99. package/dist/commands/interactive-scaffold.d.ts +5 -0
  100. package/dist/commands/interactive-scaffold.js +271 -0
  101. package/dist/commands/interactive-scaffold.js.map +1 -0
  102. package/dist/commands/metrics-prometheus.d.ts +6 -0
  103. package/dist/commands/metrics-prometheus.js +681 -0
  104. package/dist/commands/metrics-prometheus.js.map +1 -0
  105. package/dist/commands/migration-engine.d.ts +6 -0
  106. package/dist/commands/migration-engine.js +446 -0
  107. package/dist/commands/migration-engine.js.map +1 -0
  108. package/dist/commands/migration.d.ts +12 -0
  109. package/dist/commands/migration.js +484 -0
  110. package/dist/commands/migration.js.map +1 -0
  111. package/dist/commands/monorepo.d.ts +8 -0
  112. package/dist/commands/monorepo.js +483 -0
  113. package/dist/commands/monorepo.js.map +1 -0
  114. package/dist/commands/multi-database.d.ts +5 -0
  115. package/dist/commands/multi-database.js +439 -0
  116. package/dist/commands/multi-database.js.map +1 -0
  117. package/dist/commands/observability-tracing.d.ts +10 -0
  118. package/dist/commands/observability-tracing.js +740 -0
  119. package/dist/commands/observability-tracing.js.map +1 -0
  120. package/dist/commands/openapi-export.d.ts +8 -0
  121. package/dist/commands/openapi-export.js +359 -0
  122. package/dist/commands/openapi-export.js.map +1 -0
  123. package/dist/commands/perf-analyzer.d.ts +8 -0
  124. package/dist/commands/perf-analyzer.js +423 -0
  125. package/dist/commands/perf-analyzer.js.map +1 -0
  126. package/dist/commands/rate-limiting.d.ts +10 -0
  127. package/dist/commands/rate-limiting.js +953 -0
  128. package/dist/commands/rate-limiting.js.map +1 -0
  129. package/dist/commands/recipe-plugin.d.ts +56 -0
  130. package/dist/commands/recipe-plugin.js +315 -0
  131. package/dist/commands/recipe-plugin.js.map +1 -0
  132. package/dist/commands/recipe.d.ts +6 -0
  133. package/dist/commands/recipe.js +3941 -0
  134. package/dist/commands/recipe.js.map +1 -0
  135. package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
  136. package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
  137. package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
  138. package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
  139. package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
  140. package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
  141. package/dist/commands/recipes/index.d.ts +7 -0
  142. package/dist/commands/recipes/index.js +24 -0
  143. package/dist/commands/recipes/index.js.map +1 -0
  144. package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
  145. package/dist/commands/recipes/message-queue.recipe.js +706 -0
  146. package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
  147. package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
  148. package/dist/commands/recipes/middleware.recipe.js +383 -0
  149. package/dist/commands/recipes/middleware.recipe.js.map +1 -0
  150. package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
  151. package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
  152. package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
  153. package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
  154. package/dist/commands/recipes/oauth2.recipe.js +472 -0
  155. package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
  156. package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
  157. package/dist/commands/recipes/websocket.recipe.js +453 -0
  158. package/dist/commands/recipes/websocket.recipe.js.map +1 -0
  159. package/dist/commands/resilience-patterns.d.ts +13 -0
  160. package/dist/commands/resilience-patterns.js +1029 -0
  161. package/dist/commands/resilience-patterns.js.map +1 -0
  162. package/dist/commands/security-patterns.d.ts +11 -0
  163. package/dist/commands/security-patterns.js +2233 -0
  164. package/dist/commands/security-patterns.js.map +1 -0
  165. package/dist/commands/template-debug.d.ts +27 -0
  166. package/dist/commands/template-debug.js +388 -0
  167. package/dist/commands/template-debug.js.map +1 -0
  168. package/dist/commands/test-factory-full.d.ts +9 -0
  169. package/dist/commands/test-factory-full.js +1570 -0
  170. package/dist/commands/test-factory-full.js.map +1 -0
  171. package/dist/commands/test-scaffold.d.ts +7 -0
  172. package/dist/commands/test-scaffold.js +621 -0
  173. package/dist/commands/test-scaffold.js.map +1 -0
  174. package/dist/index.js +1088 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
  177. package/dist/templates/ai-context/conventions.md.hbs +154 -0
  178. package/dist/templates/command/create-command.hbs +6 -14
  179. package/dist/templates/command/delete-command.hbs +19 -0
  180. package/dist/templates/command/update-command.hbs +24 -0
  181. package/dist/templates/controller/controller.hbs +64 -17
  182. package/dist/templates/dto/create-dto.hbs +29 -5
  183. package/dist/templates/dto/filter-dto.hbs +52 -0
  184. package/dist/templates/dto/filter-query.dto.hbs +148 -0
  185. package/dist/templates/dto/paginated-response.dto.hbs +29 -0
  186. package/dist/templates/dto/pagination-query.dto.hbs +30 -0
  187. package/dist/templates/dto/response-dto.hbs +38 -0
  188. package/dist/templates/dto/update-dto.hbs +11 -0
  189. package/dist/templates/entity/entity.hbs +32 -1
  190. package/dist/templates/event/domain-event.hbs +33 -7
  191. package/dist/templates/event/event-handler.hbs +40 -0
  192. package/dist/templates/exception/base-exceptions.hbs +69 -0
  193. package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
  194. package/dist/templates/mapper/mapper.hbs +49 -24
  195. package/dist/templates/module/module.hbs +34 -10
  196. package/dist/templates/orm-entity/orm-entity.hbs +63 -12
  197. package/dist/templates/prisma/prisma-mapper.hbs +71 -0
  198. package/dist/templates/prisma/prisma-repository.hbs +114 -0
  199. package/dist/templates/prisma/prisma-schema.hbs +20 -0
  200. package/dist/templates/prisma/prisma-service.hbs +51 -0
  201. package/dist/templates/query/get-all.query.hbs +50 -0
  202. package/dist/templates/query/get-by-id.query.hbs +31 -0
  203. package/dist/templates/repository/repository.hbs +55 -13
  204. package/dist/templates/resolver/graphql-input.hbs +54 -0
  205. package/dist/templates/resolver/graphql-type.hbs +58 -0
  206. package/dist/templates/resolver/pagination-args.hbs +33 -0
  207. package/dist/templates/resolver/resolver.hbs +62 -0
  208. package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
  209. package/dist/templates/shared/query-builder.util.hbs +218 -0
  210. package/dist/templates/test/controller.spec.hbs +124 -0
  211. package/dist/templates/test/repository.spec.hbs +158 -0
  212. package/dist/templates/test/usecase.spec.hbs +116 -0
  213. package/dist/templates/usecase/create-usecase.hbs +19 -7
  214. package/dist/templates/usecase/delete-usecase.hbs +17 -0
  215. package/dist/templates/usecase/update-usecase.hbs +31 -0
  216. package/dist/utils/config.utils.d.ts +45 -0
  217. package/dist/utils/config.utils.js +211 -0
  218. package/dist/utils/config.utils.js.map +1 -0
  219. package/dist/utils/error.utils.d.ts +145 -0
  220. package/dist/utils/error.utils.js +422 -0
  221. package/dist/utils/error.utils.js.map +1 -0
  222. package/dist/utils/field.utils.d.ts +54 -0
  223. package/dist/utils/field.utils.js +389 -0
  224. package/dist/utils/field.utils.js.map +1 -0
  225. package/dist/utils/file.utils.d.ts +19 -8
  226. package/dist/utils/file.utils.js +135 -4
  227. package/dist/utils/file.utils.js.map +1 -1
  228. package/dist/utils/idempotency.utils.d.ts +123 -0
  229. package/dist/utils/idempotency.utils.js +444 -0
  230. package/dist/utils/idempotency.utils.js.map +1 -0
  231. package/dist/utils/naming.utils.js +24 -5
  232. package/dist/utils/naming.utils.js.map +1 -1
  233. package/dist/utils/performance.utils.d.ts +37 -0
  234. package/dist/utils/performance.utils.js +158 -0
  235. package/dist/utils/performance.utils.js.map +1 -0
  236. package/dist/utils/relation.utils.d.ts +92 -0
  237. package/dist/utils/relation.utils.js +388 -0
  238. package/dist/utils/relation.utils.js.map +1 -0
  239. package/dist/utils/rollback.utils.d.ts +49 -0
  240. package/dist/utils/rollback.utils.js +306 -0
  241. package/dist/utils/rollback.utils.js.map +1 -0
  242. package/dist/utils/schema.utils.d.ts +123 -0
  243. package/dist/utils/schema.utils.js +419 -0
  244. package/dist/utils/schema.utils.js.map +1 -0
  245. package/dist/utils/security.utils.d.ts +57 -0
  246. package/dist/utils/security.utils.js +315 -0
  247. package/dist/utils/security.utils.js.map +1 -0
  248. package/dist/utils/template-engine.utils.d.ts +80 -0
  249. package/dist/utils/template-engine.utils.js +463 -0
  250. package/dist/utils/template-engine.utils.js.map +1 -0
  251. package/dist/utils/validation-registry.utils.d.ts +160 -0
  252. package/dist/utils/validation-registry.utils.js +526 -0
  253. package/dist/utils/validation-registry.utils.js.map +1 -0
  254. package/package.json +3 -1
@@ -0,0 +1,1129 @@
1
+ "use strict";
2
+ /**
3
+ * Audit Logging & Compliance Framework Generator
4
+ * Generates comprehensive audit trail infrastructure
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.setupAuditLogging = setupAuditLogging;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ async function setupAuditLogging(basePath, options = {}) {
48
+ console.log(chalk_1.default.bold.blue('\n📝 Setting up Audit Logging Framework\n'));
49
+ const sharedPath = path.join(basePath, 'src/shared/audit');
50
+ if (!fs.existsSync(sharedPath)) {
51
+ fs.mkdirSync(sharedPath, { recursive: true });
52
+ }
53
+ // Generate audit module
54
+ const moduleContent = generateAuditModule();
55
+ fs.writeFileSync(path.join(sharedPath, 'audit.module.ts'), moduleContent);
56
+ console.log(chalk_1.default.green(` ✓ Created audit module`));
57
+ // Generate audit service
58
+ const serviceContent = generateAuditService(options);
59
+ fs.writeFileSync(path.join(sharedPath, 'audit.service.ts'), serviceContent);
60
+ console.log(chalk_1.default.green(` ✓ Created audit service`));
61
+ // Generate audit interceptor
62
+ const interceptorContent = generateAuditInterceptor();
63
+ fs.writeFileSync(path.join(sharedPath, 'audit.interceptor.ts'), interceptorContent);
64
+ console.log(chalk_1.default.green(` ✓ Created audit interceptor`));
65
+ // Generate audit entity
66
+ const entityContent = generateAuditEntity();
67
+ fs.writeFileSync(path.join(sharedPath, 'audit-log.entity.ts'), entityContent);
68
+ console.log(chalk_1.default.green(` ✓ Created audit log entity`));
69
+ // Generate audit decorators
70
+ const decoratorContent = generateAuditDecorators();
71
+ fs.writeFileSync(path.join(sharedPath, 'audit.decorators.ts'), decoratorContent);
72
+ console.log(chalk_1.default.green(` ✓ Created audit decorators`));
73
+ // Generate compliance reporter
74
+ const reporterContent = generateComplianceReporter();
75
+ fs.writeFileSync(path.join(sharedPath, 'compliance.reporter.ts'), reporterContent);
76
+ console.log(chalk_1.default.green(` ✓ Created compliance reporter`));
77
+ console.log(chalk_1.default.bold.green('\n✅ Audit logging framework ready!\n'));
78
+ }
79
+ function generateAuditModule() {
80
+ return `import { Module, Global, DynamicModule } from '@nestjs/common';
81
+ import { TypeOrmModule } from '@nestjs/typeorm';
82
+ import { APP_INTERCEPTOR } from '@nestjs/core';
83
+ import { AuditService } from './audit.service';
84
+ import { AuditInterceptor } from './audit.interceptor';
85
+ import { AuditLog } from './audit-log.entity';
86
+ import { ComplianceReporter } from './compliance.reporter';
87
+
88
+ export interface AuditModuleOptions {
89
+ storage: 'database' | 'file' | 'elasticsearch';
90
+ retentionDays?: number;
91
+ excludePaths?: string[];
92
+ excludeMethods?: string[];
93
+ sensitiveFields?: string[];
94
+ enableCompression?: boolean;
95
+ }
96
+
97
+ @Global()
98
+ @Module({})
99
+ export class AuditModule {
100
+ static forRoot(options: AuditModuleOptions): DynamicModule {
101
+ return {
102
+ module: AuditModule,
103
+ imports: [
104
+ TypeOrmModule.forFeature([AuditLog]),
105
+ ],
106
+ providers: [
107
+ {
108
+ provide: 'AUDIT_OPTIONS',
109
+ useValue: options,
110
+ },
111
+ AuditService,
112
+ ComplianceReporter,
113
+ {
114
+ provide: APP_INTERCEPTOR,
115
+ useClass: AuditInterceptor,
116
+ },
117
+ ],
118
+ exports: [AuditService, ComplianceReporter],
119
+ };
120
+ }
121
+ }
122
+ `;
123
+ }
124
+ function generateAuditService(options) {
125
+ return `import { Injectable, Inject, Logger } from '@nestjs/common';
126
+ import { InjectRepository } from '@nestjs/typeorm';
127
+ import { Repository, Between, Like } from 'typeorm';
128
+ import { AuditLog, AuditAction, AuditCategory } from './audit-log.entity';
129
+
130
+ export interface AuditEntry {
131
+ action: AuditAction;
132
+ category: AuditCategory;
133
+ userId?: string;
134
+ resourceType: string;
135
+ resourceId?: string;
136
+ description: string;
137
+ oldValue?: any;
138
+ newValue?: any;
139
+ metadata?: Record<string, any>;
140
+ ipAddress?: string;
141
+ userAgent?: string;
142
+ }
143
+
144
+ export interface AuditQuery {
145
+ userId?: string;
146
+ action?: AuditAction;
147
+ category?: AuditCategory;
148
+ resourceType?: string;
149
+ resourceId?: string;
150
+ startDate?: Date;
151
+ endDate?: Date;
152
+ page?: number;
153
+ pageSize?: number;
154
+ }
155
+
156
+ @Injectable()
157
+ export class AuditService {
158
+ private readonly logger = new Logger(AuditService.name);
159
+
160
+ constructor(
161
+ @InjectRepository(AuditLog)
162
+ private readonly auditRepository: Repository<AuditLog>,
163
+ @Inject('AUDIT_OPTIONS') private readonly options: any,
164
+ ) {}
165
+
166
+ /**
167
+ * Log an audit entry
168
+ */
169
+ async log(entry: AuditEntry): Promise<AuditLog> {
170
+ const auditLog = this.auditRepository.create({
171
+ ...entry,
172
+ oldValue: this.sanitize(entry.oldValue),
173
+ newValue: this.sanitize(entry.newValue),
174
+ timestamp: new Date(),
175
+ });
176
+
177
+ const saved = await this.auditRepository.save(auditLog);
178
+ this.logger.debug(\`Audit log created: \${entry.action} on \${entry.resourceType}\`);
179
+
180
+ return saved;
181
+ }
182
+
183
+ /**
184
+ * Log a create action
185
+ */
186
+ async logCreate(params: {
187
+ userId?: string;
188
+ resourceType: string;
189
+ resourceId: string;
190
+ newValue: any;
191
+ metadata?: Record<string, any>;
192
+ }): Promise<AuditLog> {
193
+ return this.log({
194
+ action: AuditAction.CREATE,
195
+ category: AuditCategory.DATA_CHANGE,
196
+ description: \`Created \${params.resourceType} with ID \${params.resourceId}\`,
197
+ ...params,
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Log an update action
203
+ */
204
+ async logUpdate(params: {
205
+ userId?: string;
206
+ resourceType: string;
207
+ resourceId: string;
208
+ oldValue: any;
209
+ newValue: any;
210
+ metadata?: Record<string, any>;
211
+ }): Promise<AuditLog> {
212
+ const changes = this.computeChanges(params.oldValue, params.newValue);
213
+
214
+ return this.log({
215
+ action: AuditAction.UPDATE,
216
+ category: AuditCategory.DATA_CHANGE,
217
+ description: \`Updated \${params.resourceType} with ID \${params.resourceId}\`,
218
+ metadata: { ...params.metadata, changes },
219
+ ...params,
220
+ });
221
+ }
222
+
223
+ /**
224
+ * Log a delete action
225
+ */
226
+ async logDelete(params: {
227
+ userId?: string;
228
+ resourceType: string;
229
+ resourceId: string;
230
+ oldValue?: any;
231
+ metadata?: Record<string, any>;
232
+ }): Promise<AuditLog> {
233
+ return this.log({
234
+ action: AuditAction.DELETE,
235
+ category: AuditCategory.DATA_CHANGE,
236
+ description: \`Deleted \${params.resourceType} with ID \${params.resourceId}\`,
237
+ ...params,
238
+ });
239
+ }
240
+
241
+ /**
242
+ * Log an access action
243
+ */
244
+ async logAccess(params: {
245
+ userId?: string;
246
+ resourceType: string;
247
+ resourceId?: string;
248
+ metadata?: Record<string, any>;
249
+ }): Promise<AuditLog> {
250
+ return this.log({
251
+ action: AuditAction.READ,
252
+ category: AuditCategory.DATA_ACCESS,
253
+ description: \`Accessed \${params.resourceType}\${params.resourceId ? \` with ID \${params.resourceId}\` : ''}\`,
254
+ ...params,
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Log authentication event
260
+ */
261
+ async logAuth(params: {
262
+ action: 'login' | 'logout' | 'failed_login' | 'password_change';
263
+ userId?: string;
264
+ ipAddress?: string;
265
+ userAgent?: string;
266
+ metadata?: Record<string, any>;
267
+ }): Promise<AuditLog> {
268
+ const actionMap: Record<string, AuditAction> = {
269
+ login: AuditAction.LOGIN,
270
+ logout: AuditAction.LOGOUT,
271
+ failed_login: AuditAction.LOGIN,
272
+ password_change: AuditAction.UPDATE,
273
+ };
274
+
275
+ return this.log({
276
+ action: actionMap[params.action],
277
+ category: AuditCategory.AUTHENTICATION,
278
+ resourceType: 'user',
279
+ resourceId: params.userId,
280
+ description: \`User \${params.action.replace('_', ' ')}\`,
281
+ ipAddress: params.ipAddress,
282
+ userAgent: params.userAgent,
283
+ metadata: {
284
+ ...params.metadata,
285
+ success: params.action !== 'failed_login',
286
+ },
287
+ });
288
+ }
289
+
290
+ /**
291
+ * Query audit logs
292
+ */
293
+ async query(query: AuditQuery): Promise<{ items: AuditLog[]; total: number }> {
294
+ const {
295
+ page = 1,
296
+ pageSize = 50,
297
+ startDate,
298
+ endDate,
299
+ ...filters
300
+ } = query;
301
+
302
+ const where: any = {};
303
+
304
+ if (filters.userId) where.userId = filters.userId;
305
+ if (filters.action) where.action = filters.action;
306
+ if (filters.category) where.category = filters.category;
307
+ if (filters.resourceType) where.resourceType = filters.resourceType;
308
+ if (filters.resourceId) where.resourceId = filters.resourceId;
309
+
310
+ if (startDate && endDate) {
311
+ where.timestamp = Between(startDate, endDate);
312
+ }
313
+
314
+ const [items, total] = await this.auditRepository.findAndCount({
315
+ where,
316
+ order: { timestamp: 'DESC' },
317
+ skip: (page - 1) * pageSize,
318
+ take: pageSize,
319
+ });
320
+
321
+ return { items, total };
322
+ }
323
+
324
+ /**
325
+ * Get audit trail for a resource
326
+ */
327
+ async getResourceAuditTrail(
328
+ resourceType: string,
329
+ resourceId: string,
330
+ ): Promise<AuditLog[]> {
331
+ return this.auditRepository.find({
332
+ where: { resourceType, resourceId },
333
+ order: { timestamp: 'DESC' },
334
+ });
335
+ }
336
+
337
+ /**
338
+ * Get user activity log
339
+ */
340
+ async getUserActivity(userId: string, days: number = 30): Promise<AuditLog[]> {
341
+ const startDate = new Date();
342
+ startDate.setDate(startDate.getDate() - days);
343
+
344
+ return this.auditRepository.find({
345
+ where: {
346
+ userId,
347
+ timestamp: Between(startDate, new Date()),
348
+ },
349
+ order: { timestamp: 'DESC' },
350
+ });
351
+ }
352
+
353
+ /**
354
+ * Sanitize sensitive data - removes/masks PII and secrets
355
+ * OWASP A09:2021 - Security Logging and Monitoring Failures
356
+ */
357
+ private sanitize(data: any, depth = 0): any {
358
+ // Prevent infinite recursion
359
+ if (depth > 10 || !data) return data;
360
+
361
+ // Handle strings with patterns
362
+ if (typeof data === 'string') {
363
+ return this.sanitizeString(data);
364
+ }
365
+
366
+ // Handle arrays
367
+ if (Array.isArray(data)) {
368
+ return data.map(item => this.sanitize(item, depth + 1));
369
+ }
370
+
371
+ // Handle objects
372
+ if (typeof data === 'object' && data !== null) {
373
+ const sanitized: Record<string, any> = {};
374
+
375
+ // Default sensitive field names (case-insensitive matching)
376
+ const sensitiveFieldPatterns = this.options.sensitiveFields || [
377
+ /password/i,
378
+ /token/i,
379
+ /secret/i,
380
+ /api[_-]?key/i,
381
+ /auth/i,
382
+ /credential/i,
383
+ /credit[_-]?card/i,
384
+ /card[_-]?number/i,
385
+ /cvv/i,
386
+ /cvc/i,
387
+ /ssn/i,
388
+ /social[_-]?security/i,
389
+ /tax[_-]?id/i,
390
+ /passport/i,
391
+ /license/i,
392
+ /pin/i,
393
+ /private[_-]?key/i,
394
+ /access[_-]?token/i,
395
+ /refresh[_-]?token/i,
396
+ /bearer/i,
397
+ /authorization/i,
398
+ /cookie/i,
399
+ /session/i,
400
+ ];
401
+
402
+ for (const [key, value] of Object.entries(data)) {
403
+ // Check if field name matches sensitive patterns
404
+ const isSensitive = sensitiveFieldPatterns.some(pattern =>
405
+ typeof pattern === 'string'
406
+ ? key.toLowerCase().includes(pattern.toLowerCase())
407
+ : pattern.test(key)
408
+ );
409
+
410
+ if (isSensitive && value) {
411
+ sanitized[key] = '[REDACTED]';
412
+ } else {
413
+ sanitized[key] = this.sanitize(value, depth + 1);
414
+ }
415
+ }
416
+
417
+ return sanitized;
418
+ }
419
+
420
+ return data;
421
+ }
422
+
423
+ /**
424
+ * Sanitize string values that may contain sensitive patterns
425
+ */
426
+ private sanitizeString(value: string): string {
427
+ if (!value || typeof value !== 'string') return value;
428
+
429
+ let sanitized = value;
430
+
431
+ // Mask credit card numbers (13-19 digits)
432
+ sanitized = sanitized.replace(/\\b(\\d{4})[\\s-]?(\\d{4,6})[\\s-]?(\\d{4,5})[\\s-]?(\\d{4})\\b/g, '$1****$4');
433
+
434
+ // Mask email addresses
435
+ sanitized = sanitized.replace(/([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})/g, (match, user, domain) => {
436
+ const maskedUser = user.substring(0, 2) + '***';
437
+ return \`\${maskedUser}@\${domain}\`;
438
+ });
439
+
440
+ // Mask phone numbers (various formats)
441
+ sanitized = sanitized.replace(/\\b(\\+?\\d{1,3}[-.\\s]?)?(\\(?\\d{3}\\)?[-.\\s]?)(\\d{3})[-.\\s]?(\\d{4})\\b/g, '$1$2***-$4');
442
+
443
+ // Mask SSN-like patterns
444
+ sanitized = sanitized.replace(/\\b(\\d{3})[-.]?(\\d{2})[-.]?(\\d{4})\\b/g, '***-**-$3');
445
+
446
+ // Mask bearer tokens
447
+ sanitized = sanitized.replace(/Bearer\\s+[a-zA-Z0-9._-]+/gi, 'Bearer [REDACTED]');
448
+
449
+ // Mask API keys (common formats)
450
+ sanitized = sanitized.replace(/([a-z]{2,}_)?[a-zA-Z0-9]{20,}/g, (match) => {
451
+ if (match.length > 10) {
452
+ return match.substring(0, 4) + '****' + match.substring(match.length - 4);
453
+ }
454
+ return match;
455
+ });
456
+
457
+ return sanitized;
458
+ }
459
+
460
+ /**
461
+ * Compute changes between old and new values
462
+ */
463
+ private computeChanges(oldValue: any, newValue: any): Record<string, { from: any; to: any }> {
464
+ const changes: Record<string, { from: any; to: any }> = {};
465
+
466
+ if (!oldValue || !newValue) return changes;
467
+
468
+ const allKeys = new Set([
469
+ ...Object.keys(oldValue),
470
+ ...Object.keys(newValue),
471
+ ]);
472
+
473
+ for (const key of allKeys) {
474
+ if (JSON.stringify(oldValue[key]) !== JSON.stringify(newValue[key])) {
475
+ changes[key] = {
476
+ from: oldValue[key],
477
+ to: newValue[key],
478
+ };
479
+ }
480
+ }
481
+
482
+ return changes;
483
+ }
484
+
485
+ /**
486
+ * Archive old audit logs
487
+ */
488
+ async archive(olderThan: Date): Promise<number> {
489
+ const result = await this.auditRepository
490
+ .createQueryBuilder()
491
+ .update()
492
+ .set({ archived: true })
493
+ .where('timestamp < :date', { date: olderThan })
494
+ .andWhere('archived = :archived', { archived: false })
495
+ .execute();
496
+
497
+ return result.affected || 0;
498
+ }
499
+
500
+ /**
501
+ * Cleanup old audit logs
502
+ */
503
+ async cleanup(retentionDays?: number): Promise<number> {
504
+ const days = retentionDays || this.options.retentionDays || 365;
505
+ const cutoffDate = new Date();
506
+ cutoffDate.setDate(cutoffDate.getDate() - days);
507
+
508
+ const result = await this.auditRepository.delete({
509
+ timestamp: Between(new Date(0), cutoffDate),
510
+ archived: true,
511
+ });
512
+
513
+ return result.affected || 0;
514
+ }
515
+ }
516
+ `;
517
+ }
518
+ function generateAuditInterceptor() {
519
+ return `import {
520
+ Injectable,
521
+ NestInterceptor,
522
+ ExecutionContext,
523
+ CallHandler,
524
+ Inject,
525
+ } from '@nestjs/common';
526
+ import { Observable } from 'rxjs';
527
+ import { tap } from 'rxjs/operators';
528
+ import { Reflector } from '@nestjs/core';
529
+ import { AuditService } from './audit.service';
530
+ import { AuditAction, AuditCategory } from './audit-log.entity';
531
+
532
+ @Injectable()
533
+ export class AuditInterceptor implements NestInterceptor {
534
+ constructor(
535
+ private readonly auditService: AuditService,
536
+ private readonly reflector: Reflector,
537
+ @Inject('AUDIT_OPTIONS') private readonly options: any,
538
+ ) {}
539
+
540
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
541
+ // Check if auditing is disabled for this handler
542
+ const skipAudit = this.reflector.get<boolean>('skipAudit', context.getHandler());
543
+ if (skipAudit) {
544
+ return next.handle();
545
+ }
546
+
547
+ // Check if path is excluded
548
+ const request = context.switchToHttp().getRequest();
549
+ if (this.shouldExclude(request)) {
550
+ return next.handle();
551
+ }
552
+
553
+ // Get audit metadata
554
+ const auditMeta = this.reflector.get<AuditMetadata>('audit', context.getHandler());
555
+ const startTime = Date.now();
556
+
557
+ return next.handle().pipe(
558
+ tap({
559
+ next: (data) => {
560
+ this.logRequest(context, request, data, startTime, auditMeta);
561
+ },
562
+ error: (error) => {
563
+ this.logError(context, request, error, startTime, auditMeta);
564
+ },
565
+ }),
566
+ );
567
+ }
568
+
569
+ private shouldExclude(request: any): boolean {
570
+ const excludePaths = this.options.excludePaths || ['/health', '/metrics'];
571
+ const excludeMethods = this.options.excludeMethods || ['OPTIONS'];
572
+
573
+ if (excludeMethods.includes(request.method)) {
574
+ return true;
575
+ }
576
+
577
+ for (const path of excludePaths) {
578
+ if (request.path.startsWith(path)) {
579
+ return true;
580
+ }
581
+ }
582
+
583
+ return false;
584
+ }
585
+
586
+ private async logRequest(
587
+ context: ExecutionContext,
588
+ request: any,
589
+ response: any,
590
+ startTime: number,
591
+ auditMeta?: AuditMetadata,
592
+ ): Promise<void> {
593
+ const action = this.getActionFromMethod(request.method);
594
+ const duration = Date.now() - startTime;
595
+
596
+ await this.auditService.log({
597
+ action,
598
+ category: auditMeta?.category || AuditCategory.API_CALL,
599
+ userId: request.user?.id,
600
+ resourceType: auditMeta?.resourceType || this.extractResourceType(request.path),
601
+ resourceId: auditMeta?.resourceId || request.params?.id,
602
+ description: auditMeta?.description || \`\${request.method} \${request.path}\`,
603
+ metadata: {
604
+ method: request.method,
605
+ path: request.path,
606
+ query: request.query,
607
+ duration,
608
+ statusCode: context.switchToHttp().getResponse().statusCode,
609
+ },
610
+ ipAddress: request.ip,
611
+ userAgent: request.headers['user-agent'],
612
+ });
613
+ }
614
+
615
+ private async logError(
616
+ context: ExecutionContext,
617
+ request: any,
618
+ error: Error,
619
+ startTime: number,
620
+ auditMeta?: AuditMetadata,
621
+ ): Promise<void> {
622
+ const duration = Date.now() - startTime;
623
+
624
+ await this.auditService.log({
625
+ action: AuditAction.ERROR,
626
+ category: AuditCategory.ERROR,
627
+ userId: request.user?.id,
628
+ resourceType: auditMeta?.resourceType || this.extractResourceType(request.path),
629
+ resourceId: request.params?.id,
630
+ description: \`Error: \${error.message}\`,
631
+ metadata: {
632
+ method: request.method,
633
+ path: request.path,
634
+ duration,
635
+ errorName: error.name,
636
+ errorStack: error.stack,
637
+ },
638
+ ipAddress: request.ip,
639
+ userAgent: request.headers['user-agent'],
640
+ });
641
+ }
642
+
643
+ private getActionFromMethod(method: string): AuditAction {
644
+ const actionMap: Record<string, AuditAction> = {
645
+ GET: AuditAction.READ,
646
+ POST: AuditAction.CREATE,
647
+ PUT: AuditAction.UPDATE,
648
+ PATCH: AuditAction.UPDATE,
649
+ DELETE: AuditAction.DELETE,
650
+ };
651
+ return actionMap[method] || AuditAction.OTHER;
652
+ }
653
+
654
+ private extractResourceType(path: string): string {
655
+ const parts = path.split('/').filter(Boolean);
656
+ // Remove version prefix and get resource name
657
+ return parts.find(p => !p.startsWith('v') && !p.match(/^\\d+$/)) || 'unknown';
658
+ }
659
+ }
660
+
661
+ interface AuditMetadata {
662
+ action?: AuditAction;
663
+ category?: AuditCategory;
664
+ resourceType?: string;
665
+ resourceId?: string;
666
+ description?: string;
667
+ }
668
+ `;
669
+ }
670
+ function generateAuditEntity() {
671
+ return `import {
672
+ Entity,
673
+ PrimaryGeneratedColumn,
674
+ Column,
675
+ CreateDateColumn,
676
+ Index,
677
+ } from 'typeorm';
678
+
679
+ export enum AuditAction {
680
+ CREATE = 'CREATE',
681
+ READ = 'READ',
682
+ UPDATE = 'UPDATE',
683
+ DELETE = 'DELETE',
684
+ LOGIN = 'LOGIN',
685
+ LOGOUT = 'LOGOUT',
686
+ EXPORT = 'EXPORT',
687
+ IMPORT = 'IMPORT',
688
+ ERROR = 'ERROR',
689
+ OTHER = 'OTHER',
690
+ }
691
+
692
+ export enum AuditCategory {
693
+ AUTHENTICATION = 'AUTHENTICATION',
694
+ AUTHORIZATION = 'AUTHORIZATION',
695
+ DATA_CHANGE = 'DATA_CHANGE',
696
+ DATA_ACCESS = 'DATA_ACCESS',
697
+ CONFIGURATION = 'CONFIGURATION',
698
+ SECURITY = 'SECURITY',
699
+ API_CALL = 'API_CALL',
700
+ ERROR = 'ERROR',
701
+ COMPLIANCE = 'COMPLIANCE',
702
+ }
703
+
704
+ @Entity('audit_logs')
705
+ @Index(['userId', 'timestamp'])
706
+ @Index(['resourceType', 'resourceId'])
707
+ @Index(['action', 'timestamp'])
708
+ @Index(['category', 'timestamp'])
709
+ export class AuditLog {
710
+ @PrimaryGeneratedColumn('uuid')
711
+ id: string;
712
+
713
+ @Column({ type: 'enum', enum: AuditAction })
714
+ action: AuditAction;
715
+
716
+ @Column({ type: 'enum', enum: AuditCategory })
717
+ category: AuditCategory;
718
+
719
+ @Column({ nullable: true })
720
+ @Index()
721
+ userId: string;
722
+
723
+ @Column()
724
+ resourceType: string;
725
+
726
+ @Column({ nullable: true })
727
+ resourceId: string;
728
+
729
+ @Column('text')
730
+ description: string;
731
+
732
+ @Column({ type: 'jsonb', nullable: true })
733
+ oldValue: any;
734
+
735
+ @Column({ type: 'jsonb', nullable: true })
736
+ newValue: any;
737
+
738
+ @Column({ type: 'jsonb', nullable: true })
739
+ metadata: Record<string, any>;
740
+
741
+ @Column({ nullable: true })
742
+ ipAddress: string;
743
+
744
+ @Column({ nullable: true })
745
+ userAgent: string;
746
+
747
+ @CreateDateColumn()
748
+ @Index()
749
+ timestamp: Date;
750
+
751
+ @Column({ default: false })
752
+ archived: boolean;
753
+ }
754
+ `;
755
+ }
756
+ function generateAuditDecorators() {
757
+ return `import { SetMetadata, applyDecorators } from '@nestjs/common';
758
+ import { AuditAction, AuditCategory } from './audit-log.entity';
759
+
760
+ /**
761
+ * Skip auditing for a handler
762
+ */
763
+ export function SkipAudit() {
764
+ return SetMetadata('skipAudit', true);
765
+ }
766
+
767
+ /**
768
+ * Custom audit metadata
769
+ */
770
+ export function Audit(options: {
771
+ action?: AuditAction;
772
+ category?: AuditCategory;
773
+ resourceType?: string;
774
+ description?: string;
775
+ }) {
776
+ return SetMetadata('audit', options);
777
+ }
778
+
779
+ /**
780
+ * Audit as data access
781
+ */
782
+ export function AuditDataAccess(resourceType: string) {
783
+ return Audit({
784
+ action: AuditAction.READ,
785
+ category: AuditCategory.DATA_ACCESS,
786
+ resourceType,
787
+ });
788
+ }
789
+
790
+ /**
791
+ * Audit as data change
792
+ */
793
+ export function AuditDataChange(resourceType: string, action: AuditAction) {
794
+ return Audit({
795
+ action,
796
+ category: AuditCategory.DATA_CHANGE,
797
+ resourceType,
798
+ });
799
+ }
800
+
801
+ /**
802
+ * Audit as security event
803
+ */
804
+ export function AuditSecurity(description: string) {
805
+ return Audit({
806
+ category: AuditCategory.SECURITY,
807
+ description,
808
+ });
809
+ }
810
+
811
+ /**
812
+ * Audit as compliance event
813
+ */
814
+ export function AuditCompliance(description: string) {
815
+ return Audit({
816
+ category: AuditCategory.COMPLIANCE,
817
+ description,
818
+ });
819
+ }
820
+
821
+ /**
822
+ * Sensitive operation (logs with extra detail)
823
+ */
824
+ export function SensitiveOperation(description: string) {
825
+ return applyDecorators(
826
+ Audit({
827
+ category: AuditCategory.SECURITY,
828
+ description,
829
+ }),
830
+ SetMetadata('sensitiveOperation', true),
831
+ );
832
+ }
833
+ `;
834
+ }
835
+ function generateComplianceReporter() {
836
+ return `import { Injectable, Logger } from '@nestjs/common';
837
+ import { InjectRepository } from '@nestjs/typeorm';
838
+ import { Repository, Between } from 'typeorm';
839
+ import { AuditLog, AuditAction, AuditCategory } from './audit-log.entity';
840
+
841
+ export interface ComplianceReport {
842
+ reportId: string;
843
+ generatedAt: Date;
844
+ period: { start: Date; end: Date };
845
+ summary: ComplianceSummary;
846
+ userActivity: UserActivityReport[];
847
+ dataAccess: DataAccessReport[];
848
+ securityEvents: SecurityEventReport[];
849
+ anomalies: AnomalyReport[];
850
+ }
851
+
852
+ export interface ComplianceSummary {
853
+ totalEvents: number;
854
+ byCategory: Record<AuditCategory, number>;
855
+ byAction: Record<AuditAction, number>;
856
+ uniqueUsers: number;
857
+ uniqueResources: number;
858
+ }
859
+
860
+ export interface UserActivityReport {
861
+ userId: string;
862
+ totalActions: number;
863
+ lastActivity: Date;
864
+ actionBreakdown: Record<AuditAction, number>;
865
+ }
866
+
867
+ export interface DataAccessReport {
868
+ resourceType: string;
869
+ accessCount: number;
870
+ uniqueUsers: number;
871
+ lastAccess: Date;
872
+ }
873
+
874
+ export interface SecurityEventReport {
875
+ type: string;
876
+ count: number;
877
+ severity: 'low' | 'medium' | 'high' | 'critical';
878
+ details: string[];
879
+ }
880
+
881
+ export interface AnomalyReport {
882
+ type: string;
883
+ description: string;
884
+ severity: 'low' | 'medium' | 'high' | 'critical';
885
+ timestamp: Date;
886
+ context: any;
887
+ }
888
+
889
+ @Injectable()
890
+ export class ComplianceReporter {
891
+ private readonly logger = new Logger(ComplianceReporter.name);
892
+
893
+ constructor(
894
+ @InjectRepository(AuditLog)
895
+ private readonly auditRepository: Repository<AuditLog>,
896
+ ) {}
897
+
898
+ /**
899
+ * Generate compliance report for a period
900
+ */
901
+ async generateReport(startDate: Date, endDate: Date): Promise<ComplianceReport> {
902
+ const logs = await this.auditRepository.find({
903
+ where: {
904
+ timestamp: Between(startDate, endDate),
905
+ },
906
+ });
907
+
908
+ const reportId = \`compliance_\${Date.now()}\`;
909
+
910
+ return {
911
+ reportId,
912
+ generatedAt: new Date(),
913
+ period: { start: startDate, end: endDate },
914
+ summary: this.generateSummary(logs),
915
+ userActivity: this.analyzeUserActivity(logs),
916
+ dataAccess: this.analyzeDataAccess(logs),
917
+ securityEvents: this.analyzeSecurityEvents(logs),
918
+ anomalies: this.detectAnomalies(logs),
919
+ };
920
+ }
921
+
922
+ /**
923
+ * Generate GDPR data subject report
924
+ */
925
+ async generateGDPRReport(userId: string): Promise<any> {
926
+ const logs = await this.auditRepository.find({
927
+ where: { userId },
928
+ order: { timestamp: 'DESC' },
929
+ });
930
+
931
+ const dataAccessed = new Set<string>();
932
+ const dataModified = new Set<string>();
933
+
934
+ for (const log of logs) {
935
+ if (log.action === AuditAction.READ) {
936
+ dataAccessed.add(\`\${log.resourceType}:\${log.resourceId}\`);
937
+ }
938
+ if ([AuditAction.CREATE, AuditAction.UPDATE, AuditAction.DELETE].includes(log.action)) {
939
+ dataModified.add(\`\${log.resourceType}:\${log.resourceId}\`);
940
+ }
941
+ }
942
+
943
+ return {
944
+ userId,
945
+ generatedAt: new Date(),
946
+ totalActivities: logs.length,
947
+ firstActivity: logs[logs.length - 1]?.timestamp,
948
+ lastActivity: logs[0]?.timestamp,
949
+ dataAccessed: Array.from(dataAccessed),
950
+ dataModified: Array.from(dataModified),
951
+ activities: logs.map(log => ({
952
+ timestamp: log.timestamp,
953
+ action: log.action,
954
+ resourceType: log.resourceType,
955
+ resourceId: log.resourceId,
956
+ description: log.description,
957
+ })),
958
+ };
959
+ }
960
+
961
+ /**
962
+ * Generate SOC2 audit report
963
+ */
964
+ async generateSOC2Report(startDate: Date, endDate: Date): Promise<any> {
965
+ const report = await this.generateReport(startDate, endDate);
966
+
967
+ return {
968
+ ...report,
969
+ soc2Controls: {
970
+ accessControl: this.analyzeAccessControl(report),
971
+ changeManagement: this.analyzeChangeManagement(report),
972
+ incidentResponse: this.analyzeIncidentResponse(report),
973
+ dataProtection: this.analyzeDataProtection(report),
974
+ },
975
+ };
976
+ }
977
+
978
+ private generateSummary(logs: AuditLog[]): ComplianceSummary {
979
+ const byCategory: Record<AuditCategory, number> = {} as any;
980
+ const byAction: Record<AuditAction, number> = {} as any;
981
+ const users = new Set<string>();
982
+ const resources = new Set<string>();
983
+
984
+ for (const log of logs) {
985
+ byCategory[log.category] = (byCategory[log.category] || 0) + 1;
986
+ byAction[log.action] = (byAction[log.action] || 0) + 1;
987
+ if (log.userId) users.add(log.userId);
988
+ if (log.resourceId) resources.add(\`\${log.resourceType}:\${log.resourceId}\`);
989
+ }
990
+
991
+ return {
992
+ totalEvents: logs.length,
993
+ byCategory,
994
+ byAction,
995
+ uniqueUsers: users.size,
996
+ uniqueResources: resources.size,
997
+ };
998
+ }
999
+
1000
+ private analyzeUserActivity(logs: AuditLog[]): UserActivityReport[] {
1001
+ const userMap = new Map<string, { actions: AuditLog[]; breakdown: Record<AuditAction, number> }>();
1002
+
1003
+ for (const log of logs) {
1004
+ if (!log.userId) continue;
1005
+
1006
+ const existing = userMap.get(log.userId) || { actions: [], breakdown: {} as any };
1007
+ existing.actions.push(log);
1008
+ existing.breakdown[log.action] = (existing.breakdown[log.action] || 0) + 1;
1009
+ userMap.set(log.userId, existing);
1010
+ }
1011
+
1012
+ return Array.from(userMap.entries()).map(([userId, data]) => ({
1013
+ userId,
1014
+ totalActions: data.actions.length,
1015
+ lastActivity: data.actions[0]?.timestamp || new Date(),
1016
+ actionBreakdown: data.breakdown,
1017
+ }));
1018
+ }
1019
+
1020
+ private analyzeDataAccess(logs: AuditLog[]): DataAccessReport[] {
1021
+ const resourceMap = new Map<string, { count: number; users: Set<string>; lastAccess: Date }>();
1022
+
1023
+ for (const log of logs) {
1024
+ if (log.action !== AuditAction.READ) continue;
1025
+
1026
+ const existing = resourceMap.get(log.resourceType) || {
1027
+ count: 0,
1028
+ users: new Set(),
1029
+ lastAccess: new Date(0),
1030
+ };
1031
+ existing.count++;
1032
+ if (log.userId) existing.users.add(log.userId);
1033
+ if (log.timestamp > existing.lastAccess) existing.lastAccess = log.timestamp;
1034
+ resourceMap.set(log.resourceType, existing);
1035
+ }
1036
+
1037
+ return Array.from(resourceMap.entries()).map(([resourceType, data]) => ({
1038
+ resourceType,
1039
+ accessCount: data.count,
1040
+ uniqueUsers: data.users.size,
1041
+ lastAccess: data.lastAccess,
1042
+ }));
1043
+ }
1044
+
1045
+ private analyzeSecurityEvents(logs: AuditLog[]): SecurityEventReport[] {
1046
+ const securityLogs = logs.filter(l => l.category === AuditCategory.SECURITY);
1047
+ const events: SecurityEventReport[] = [];
1048
+
1049
+ // Failed logins
1050
+ const failedLogins = securityLogs.filter(
1051
+ l => l.action === AuditAction.LOGIN && l.metadata?.success === false
1052
+ );
1053
+ if (failedLogins.length > 0) {
1054
+ events.push({
1055
+ type: 'Failed Login Attempts',
1056
+ count: failedLogins.length,
1057
+ severity: failedLogins.length > 10 ? 'high' : 'medium',
1058
+ details: failedLogins.slice(0, 5).map(l => l.description),
1059
+ });
1060
+ }
1061
+
1062
+ return events;
1063
+ }
1064
+
1065
+ private detectAnomalies(logs: AuditLog[]): AnomalyReport[] {
1066
+ const anomalies: AnomalyReport[] = [];
1067
+
1068
+ // Detect unusual access patterns
1069
+ const userActivityByHour = new Map<string, Map<number, number>>();
1070
+
1071
+ for (const log of logs) {
1072
+ if (!log.userId) continue;
1073
+ const hour = new Date(log.timestamp).getHours();
1074
+ const userHours = userActivityByHour.get(log.userId) || new Map();
1075
+ userHours.set(hour, (userHours.get(hour) || 0) + 1);
1076
+ userActivityByHour.set(log.userId, userHours);
1077
+ }
1078
+
1079
+ // Check for unusual activity hours (outside 6am-11pm)
1080
+ for (const [userId, hourMap] of userActivityByHour.entries()) {
1081
+ const unusualHours = Array.from(hourMap.entries())
1082
+ .filter(([hour]) => hour < 6 || hour > 23)
1083
+ .reduce((sum, [, count]) => sum + count, 0);
1084
+
1085
+ if (unusualHours > 10) {
1086
+ anomalies.push({
1087
+ type: 'Unusual Access Hours',
1088
+ description: \`User \${userId} had \${unusualHours} activities outside normal hours\`,
1089
+ severity: 'medium',
1090
+ timestamp: new Date(),
1091
+ context: { userId, unusualHours },
1092
+ });
1093
+ }
1094
+ }
1095
+
1096
+ return anomalies;
1097
+ }
1098
+
1099
+ private analyzeAccessControl(report: ComplianceReport): any {
1100
+ return {
1101
+ status: 'compliant',
1102
+ findings: [],
1103
+ };
1104
+ }
1105
+
1106
+ private analyzeChangeManagement(report: ComplianceReport): any {
1107
+ return {
1108
+ status: 'compliant',
1109
+ findings: [],
1110
+ };
1111
+ }
1112
+
1113
+ private analyzeIncidentResponse(report: ComplianceReport): any {
1114
+ return {
1115
+ status: 'compliant',
1116
+ findings: [],
1117
+ };
1118
+ }
1119
+
1120
+ private analyzeDataProtection(report: ComplianceReport): any {
1121
+ return {
1122
+ status: 'compliant',
1123
+ findings: [],
1124
+ };
1125
+ }
1126
+ }
1127
+ `;
1128
+ }
1129
+ //# sourceMappingURL=audit-logging.js.map