@nest-omni/core 3.1.2-7 → 3.1.2-9
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.
- package/audit/audit.module.d.ts +10 -0
- package/audit/audit.module.js +254 -0
- package/audit/controllers/audit.controller.d.ts +20 -0
- package/audit/controllers/audit.controller.js +142 -0
- package/audit/controllers/index.d.ts +1 -0
- package/audit/controllers/index.js +17 -0
- package/audit/decorators/audit-controller.decorator.d.ts +5 -0
- package/audit/decorators/audit-controller.decorator.js +17 -0
- package/audit/decorators/audit-operation.decorator.d.ts +7 -0
- package/audit/decorators/audit-operation.decorator.js +25 -0
- package/audit/decorators/entity-audit.decorator.d.ts +10 -0
- package/audit/decorators/entity-audit.decorator.js +70 -0
- package/audit/decorators/index.d.ts +3 -0
- package/audit/decorators/index.js +19 -0
- package/audit/dto/audit-log-query.dto.d.ts +14 -0
- package/audit/dto/audit-log-query.dto.js +95 -0
- package/audit/dto/begin-transaction.dto.d.ts +3 -0
- package/audit/dto/begin-transaction.dto.js +22 -0
- package/audit/dto/compare-entities.dto.d.ts +6 -0
- package/audit/dto/compare-entities.dto.js +44 -0
- package/audit/dto/index.d.ts +5 -0
- package/audit/dto/index.js +21 -0
- package/audit/dto/pre-check-restore.dto.d.ts +5 -0
- package/audit/dto/pre-check-restore.dto.js +32 -0
- package/audit/dto/restore-entity.dto.d.ts +9 -0
- package/audit/dto/restore-entity.dto.js +53 -0
- package/audit/entities/entity-audit-log.entity.d.ts +23 -0
- package/audit/entities/entity-audit-log.entity.js +110 -0
- package/audit/entities/entity-transaction.entity.d.ts +21 -0
- package/audit/entities/entity-transaction.entity.js +80 -0
- package/audit/entities/index.d.ts +4 -0
- package/audit/entities/index.js +20 -0
- package/audit/entities/manual-operation-log.entity.d.ts +13 -0
- package/audit/entities/manual-operation-log.entity.js +65 -0
- package/audit/entities/operation-template.entity.d.ts +11 -0
- package/audit/entities/operation-template.entity.js +65 -0
- package/audit/enums/audit.enums.d.ts +42 -0
- package/audit/enums/audit.enums.js +53 -0
- package/audit/enums/index.d.ts +1 -0
- package/audit/enums/index.js +17 -0
- package/audit/index.d.ts +12 -0
- package/audit/index.js +48 -0
- package/audit/interceptors/audit.interceptor.d.ts +12 -0
- package/audit/interceptors/audit.interceptor.js +95 -0
- package/audit/interceptors/index.d.ts +1 -0
- package/audit/interceptors/index.js +17 -0
- package/audit/interfaces/audit.interfaces.d.ts +180 -0
- package/audit/interfaces/audit.interfaces.js +2 -0
- package/audit/interfaces/index.d.ts +1 -0
- package/audit/interfaces/index.js +17 -0
- package/audit/services/audit-context.service.d.ts +10 -0
- package/audit/services/audit-context.service.js +55 -0
- package/audit/services/audit-strategy.service.d.ts +19 -0
- package/audit/services/audit-strategy.service.js +89 -0
- package/audit/services/entity-audit.service.d.ts +65 -0
- package/audit/services/entity-audit.service.js +626 -0
- package/audit/services/index.d.ts +6 -0
- package/audit/services/index.js +22 -0
- package/audit/services/multi-database.service.d.ts +10 -0
- package/audit/services/multi-database.service.js +59 -0
- package/audit/services/operation-description.service.d.ts +21 -0
- package/audit/services/operation-description.service.js +213 -0
- package/audit/services/transaction-audit.service.d.ts +22 -0
- package/audit/services/transaction-audit.service.js +201 -0
- package/audit/subscribers/entity-audit.subscriber.d.ts +14 -0
- package/audit/subscribers/entity-audit.subscriber.js +136 -0
- package/audit/subscribers/index.d.ts +1 -0
- package/audit/subscribers/index.js +17 -0
- package/common/utils.d.ts +1 -0
- package/common/utils.js +6 -0
- package/http-client/config/http-client.config.d.ts +6 -0
- package/http-client/config/http-client.config.js +87 -0
- package/http-client/config/index.d.ts +1 -0
- package/http-client/config/index.js +17 -0
- package/http-client/decorators/http-client.decorators.d.ts +72 -0
- package/http-client/decorators/http-client.decorators.js +204 -0
- package/http-client/decorators/index.d.ts +1 -0
- package/http-client/decorators/index.js +17 -0
- package/http-client/entities/http-log.entity.d.ts +98 -0
- package/http-client/entities/http-log.entity.js +143 -0
- package/http-client/entities/index.d.ts +1 -0
- package/http-client/entities/index.js +17 -0
- package/http-client/errors/http-client.errors.d.ts +56 -0
- package/http-client/errors/http-client.errors.js +149 -0
- package/http-client/errors/index.d.ts +1 -0
- package/http-client/errors/index.js +17 -0
- package/http-client/examples/advanced-usage.example.d.ts +23 -0
- package/http-client/examples/advanced-usage.example.js +332 -0
- package/http-client/examples/auth-with-waiting-lock.example.d.ts +17 -0
- package/http-client/examples/auth-with-waiting-lock.example.js +336 -0
- package/http-client/examples/basic-usage.example.d.ts +53 -0
- package/http-client/examples/basic-usage.example.js +161 -0
- package/http-client/examples/index.d.ts +3 -0
- package/http-client/examples/index.js +19 -0
- package/http-client/examples/multi-api-configuration.example.d.ts +98 -0
- package/http-client/examples/multi-api-configuration.example.js +353 -0
- package/http-client/http-client.module.d.ts +11 -0
- package/http-client/http-client.module.js +257 -0
- package/http-client/index.d.ts +10 -0
- package/http-client/index.js +27 -0
- package/http-client/interfaces/api-client-config.interface.d.ts +152 -0
- package/http-client/interfaces/api-client-config.interface.js +12 -0
- package/http-client/interfaces/http-client-config.interface.d.ts +123 -0
- package/http-client/interfaces/http-client-config.interface.js +2 -0
- package/http-client/services/api-client-registry.service.d.ts +41 -0
- package/http-client/services/api-client-registry.service.js +412 -0
- package/http-client/services/cache.service.d.ts +24 -0
- package/http-client/services/cache.service.js +264 -0
- package/http-client/services/circuit-breaker.service.d.ts +33 -0
- package/http-client/services/circuit-breaker.service.js +180 -0
- package/http-client/services/http-client.service.d.ts +60 -0
- package/http-client/services/http-client.service.js +504 -0
- package/http-client/services/http-log-query.service.d.ts +76 -0
- package/http-client/services/http-log-query.service.js +590 -0
- package/http-client/services/http-replay.service.d.ts +58 -0
- package/http-client/services/http-replay.service.js +266 -0
- package/http-client/services/index.d.ts +7 -0
- package/http-client/services/index.js +23 -0
- package/http-client/services/log-cleanup.service.d.ts +64 -0
- package/http-client/services/log-cleanup.service.js +268 -0
- package/http-client/services/logging.service.d.ts +36 -0
- package/http-client/services/logging.service.js +445 -0
- package/http-client/utils/call-stack-extractor.util.d.ts +29 -0
- package/http-client/utils/call-stack-extractor.util.js +138 -0
- package/http-client/utils/context-extractor.util.d.ts +44 -0
- package/http-client/utils/context-extractor.util.js +173 -0
- package/http-client/utils/curl-generator.util.d.ts +9 -0
- package/http-client/utils/curl-generator.util.js +169 -0
- package/http-client/utils/index.d.ts +4 -0
- package/http-client/utils/index.js +20 -0
- package/http-client/utils/request-id.util.d.ts +4 -0
- package/http-client/utils/request-id.util.js +34 -0
- package/http-client/utils/retry-recorder.util.d.ts +30 -0
- package/http-client/utils/retry-recorder.util.js +143 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/setup/bootstrap.setup.js +5 -1
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var HttpLogQueryService_1;
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.HttpLogQueryService = void 0;
|
|
23
|
+
const common_1 = require("@nestjs/common");
|
|
24
|
+
const typeorm_1 = require("typeorm");
|
|
25
|
+
const http_log_entity_1 = require("../entities/http-log.entity");
|
|
26
|
+
const curl_generator_util_1 = require("../utils/curl-generator.util");
|
|
27
|
+
let HttpLogQueryService = HttpLogQueryService_1 = class HttpLogQueryService {
|
|
28
|
+
constructor(dataSource) {
|
|
29
|
+
this.dataSource = dataSource;
|
|
30
|
+
this.logger = new common_1.Logger(HttpLogQueryService_1.name);
|
|
31
|
+
this.logRepository = null;
|
|
32
|
+
this.initializeRepository();
|
|
33
|
+
}
|
|
34
|
+
findLogs() {
|
|
35
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
36
|
+
if (!this.logRepository) {
|
|
37
|
+
throw new Error('HTTP log repository not available');
|
|
38
|
+
}
|
|
39
|
+
const { page = 1, limit = 50, sortBy = 'createdAt', sortOrder = 'DESC', includeDetails = false, } = options;
|
|
40
|
+
const queryBuilder = this.buildQuery(options, includeDetails);
|
|
41
|
+
const offset = (page - 1) * limit;
|
|
42
|
+
queryBuilder.skip(offset).take(limit);
|
|
43
|
+
queryBuilder.orderBy(`log.${sortBy}`, sortOrder);
|
|
44
|
+
const [logs, total] = yield queryBuilder.getManyAndCount();
|
|
45
|
+
return {
|
|
46
|
+
logs,
|
|
47
|
+
total,
|
|
48
|
+
page,
|
|
49
|
+
limit,
|
|
50
|
+
totalPages: Math.ceil(total / limit),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
findLogById(id_1) {
|
|
55
|
+
return __awaiter(this, arguments, void 0, function* (id, includeDetails = false) {
|
|
56
|
+
if (!this.logRepository) {
|
|
57
|
+
throw new Error('HTTP log repository not available');
|
|
58
|
+
}
|
|
59
|
+
const queryBuilder = this.logRepository.createQueryBuilder('log');
|
|
60
|
+
if (includeDetails) {
|
|
61
|
+
queryBuilder.leftJoinAndSelect('log.retryRecords', 'retryRecords');
|
|
62
|
+
}
|
|
63
|
+
return queryBuilder.where('log.id = :id', { id }).getOne();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
findLogByRequestId(requestId) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
if (!this.logRepository) {
|
|
69
|
+
throw new Error('HTTP log repository not available');
|
|
70
|
+
}
|
|
71
|
+
return this.logRepository.findOne({
|
|
72
|
+
where: { requestId },
|
|
73
|
+
order: { createdAt: 'DESC' },
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
findErrorLogs() {
|
|
78
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
79
|
+
const queryOptions = Object.assign(Object.assign({}, options), { success: false });
|
|
80
|
+
return this.findLogs(queryOptions);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
findSlowRequests() {
|
|
84
|
+
return __awaiter(this, arguments, void 0, function* (minResponseTime = 5000, options = {}) {
|
|
85
|
+
if (!this.logRepository) {
|
|
86
|
+
throw new Error('HTTP log repository not available');
|
|
87
|
+
}
|
|
88
|
+
const { page = 1, limit = 50 } = options;
|
|
89
|
+
const offset = (page - 1) * limit;
|
|
90
|
+
const [logs, total] = yield this.logRepository
|
|
91
|
+
.createQueryBuilder('log')
|
|
92
|
+
.where('log.responseTime >= :minResponseTime', { minResponseTime })
|
|
93
|
+
.orderBy('log.responseTime', 'DESC')
|
|
94
|
+
.skip(offset)
|
|
95
|
+
.take(limit)
|
|
96
|
+
.getManyAndCount();
|
|
97
|
+
return { logs, total };
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
findRetryLogs() {
|
|
101
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
102
|
+
if (!this.logRepository) {
|
|
103
|
+
throw new Error('HTTP log repository not available');
|
|
104
|
+
}
|
|
105
|
+
const { minRetries = 1, page = 1, limit = 50 } = options;
|
|
106
|
+
const offset = (page - 1) * limit;
|
|
107
|
+
const queryBuilder = this.logRepository
|
|
108
|
+
.createQueryBuilder('log')
|
|
109
|
+
.where('log.attemptCount > :minRetries', { minRetries: minRetries - 1 })
|
|
110
|
+
.orderBy('log.attemptCount', 'DESC')
|
|
111
|
+
.addOrderBy('log.createdAt', 'DESC')
|
|
112
|
+
.skip(offset)
|
|
113
|
+
.take(limit);
|
|
114
|
+
const [logs, total] = yield queryBuilder.getManyAndCount();
|
|
115
|
+
return { logs, total };
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
getStats() {
|
|
119
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
120
|
+
if (!this.logRepository) {
|
|
121
|
+
throw new Error('HTTP log repository not available');
|
|
122
|
+
}
|
|
123
|
+
const { startDate, endDate, userId, serviceName } = options;
|
|
124
|
+
const baseQuery = this.logRepository.createQueryBuilder('log');
|
|
125
|
+
if (startDate) {
|
|
126
|
+
baseQuery.andWhere('log.createdAt >= :startDate', { startDate });
|
|
127
|
+
}
|
|
128
|
+
if (endDate) {
|
|
129
|
+
baseQuery.andWhere('log.createdAt <= :endDate', { endDate });
|
|
130
|
+
}
|
|
131
|
+
if (userId) {
|
|
132
|
+
baseQuery.andWhere('log.userId = :userId', { userId });
|
|
133
|
+
}
|
|
134
|
+
if (serviceName) {
|
|
135
|
+
baseQuery.andWhere('log.serviceName = :serviceName', { serviceName });
|
|
136
|
+
}
|
|
137
|
+
const [total, successful, failed] = yield Promise.all([
|
|
138
|
+
baseQuery.getCount(),
|
|
139
|
+
baseQuery
|
|
140
|
+
.clone()
|
|
141
|
+
.andWhere('log.success = :success', { success: true })
|
|
142
|
+
.getCount(),
|
|
143
|
+
baseQuery
|
|
144
|
+
.clone()
|
|
145
|
+
.andWhere('log.success = :success', { success: false })
|
|
146
|
+
.getCount(),
|
|
147
|
+
]);
|
|
148
|
+
const responseTimeStats = yield baseQuery
|
|
149
|
+
.select('AVG(log.responseTime)', 'avg')
|
|
150
|
+
.addSelect('MIN(log.responseTime)', 'min')
|
|
151
|
+
.addSelect('MAX(log.responseTime)', 'max')
|
|
152
|
+
.getRawOne();
|
|
153
|
+
const httpMethodStats = yield baseQuery
|
|
154
|
+
.select('log.method', 'method')
|
|
155
|
+
.addSelect('COUNT(*)', 'count')
|
|
156
|
+
.groupBy('log.method')
|
|
157
|
+
.getRawMany();
|
|
158
|
+
const statusStats = yield baseQuery
|
|
159
|
+
.select('log.statusCode', 'statusCode')
|
|
160
|
+
.addSelect('COUNT(*)', 'count')
|
|
161
|
+
.groupBy('log.statusCode')
|
|
162
|
+
.getRawMany();
|
|
163
|
+
const serviceStats = yield baseQuery
|
|
164
|
+
.select('log.serviceName', 'serviceName')
|
|
165
|
+
.addSelect('COUNT(*)', 'count')
|
|
166
|
+
.groupBy('log.serviceName')
|
|
167
|
+
.getRawMany();
|
|
168
|
+
const operationStats = yield baseQuery
|
|
169
|
+
.select('log.operationName', 'operationName')
|
|
170
|
+
.addSelect('COUNT(*)', 'count')
|
|
171
|
+
.groupBy('log.operationName')
|
|
172
|
+
.getRawMany();
|
|
173
|
+
const sourceStats = yield baseQuery
|
|
174
|
+
.select('log.source', 'source')
|
|
175
|
+
.addSelect('COUNT(*)', 'count')
|
|
176
|
+
.groupBy('log.source')
|
|
177
|
+
.getRawMany();
|
|
178
|
+
const errorStats = yield baseQuery
|
|
179
|
+
.clone()
|
|
180
|
+
.where('log.success = :success', { success: false })
|
|
181
|
+
.select('log.errorCode', 'errorCode')
|
|
182
|
+
.addSelect('COUNT(*)', 'count')
|
|
183
|
+
.groupBy('log.errorCode')
|
|
184
|
+
.getRawMany();
|
|
185
|
+
const retryStats = yield baseQuery
|
|
186
|
+
.select('AVG(log.attemptCount)', 'avgAttempts')
|
|
187
|
+
.addSelect('MAX(log.attemptCount)', 'maxAttempts')
|
|
188
|
+
.addSelect('SUM(log.attemptCount - 1)', 'totalRetries')
|
|
189
|
+
.addSelect('COUNT(*)', 'totalRequests')
|
|
190
|
+
.getRawOne();
|
|
191
|
+
const cacheStats = yield baseQuery
|
|
192
|
+
.select('SUM(CASE WHEN log.cacheHit = true THEN 1 ELSE 0 END)', 'cacheHits')
|
|
193
|
+
.addSelect('SUM(CASE WHEN log.cacheHit = false OR log.cacheHit IS NULL THEN 1 ELSE 0 END)', 'cacheMisses')
|
|
194
|
+
.addSelect('COUNT(*)', 'total')
|
|
195
|
+
.getRawOne();
|
|
196
|
+
const percentiles = yield this.calculatePercentiles(baseQuery);
|
|
197
|
+
return {
|
|
198
|
+
totalRequests: total,
|
|
199
|
+
successfulRequests: successful,
|
|
200
|
+
failedRequests: failed,
|
|
201
|
+
successRate: total > 0 ? (successful / total) * 100 : 0,
|
|
202
|
+
averageResponseTime: (responseTimeStats === null || responseTimeStats === void 0 ? void 0 : responseTimeStats.avg) || 0,
|
|
203
|
+
minResponseTime: (responseTimeStats === null || responseTimeStats === void 0 ? void 0 : responseTimeStats.min) || 0,
|
|
204
|
+
maxResponseTime: (responseTimeStats === null || responseTimeStats === void 0 ? void 0 : responseTimeStats.max) || 0,
|
|
205
|
+
p50ResponseTime: percentiles.p50,
|
|
206
|
+
p95ResponseTime: percentiles.p95,
|
|
207
|
+
p99ResponseTime: percentiles.p99,
|
|
208
|
+
requestsByMethod: this.formatStats(httpMethodStats, 'method', 'count'),
|
|
209
|
+
requestsByStatus: this.formatStats(statusStats, 'statusCode', 'count'),
|
|
210
|
+
requestsByService: this.formatStats(serviceStats, 'serviceName', 'count'),
|
|
211
|
+
requestsBySource: this.formatStats(sourceStats, 'source', 'count'),
|
|
212
|
+
errorsByCode: this.formatStats(errorStats, 'errorCode', 'count'),
|
|
213
|
+
retryStats: {
|
|
214
|
+
totalRetries: (retryStats === null || retryStats === void 0 ? void 0 : retryStats.totalRetries) || 0,
|
|
215
|
+
averageRetries: (retryStats === null || retryStats === void 0 ? void 0 : retryStats.avgAttempts)
|
|
216
|
+
? parseFloat(retryStats.avgAttempts) - 1
|
|
217
|
+
: 0,
|
|
218
|
+
maxRetries: (retryStats === null || retryStats === void 0 ? void 0 : retryStats.maxAttempts)
|
|
219
|
+
? parseInt(retryStats.maxAttempts) - 1
|
|
220
|
+
: 0,
|
|
221
|
+
retryRate: total > 0 ? (((retryStats === null || retryStats === void 0 ? void 0 : retryStats.totalRetries) || 0) / total) * 100 : 0,
|
|
222
|
+
},
|
|
223
|
+
cacheStats: {
|
|
224
|
+
cacheHits: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheHits) || 0,
|
|
225
|
+
cacheMisses: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheMisses) || 0,
|
|
226
|
+
cacheHitRate: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.total) > 0
|
|
227
|
+
? (((cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheHits) || 0) / cacheStats.total) * 100
|
|
228
|
+
: 0,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
deleteOldLogs() {
|
|
234
|
+
return __awaiter(this, arguments, void 0, function* (daysToKeep = 30) {
|
|
235
|
+
if (!this.logRepository) {
|
|
236
|
+
throw new Error('HTTP log repository not available');
|
|
237
|
+
}
|
|
238
|
+
const cutoffDate = new Date();
|
|
239
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
|
|
240
|
+
const result = yield this.logRepository
|
|
241
|
+
.createQueryBuilder()
|
|
242
|
+
.delete()
|
|
243
|
+
.where('createdAt < :cutoffDate', { cutoffDate })
|
|
244
|
+
.execute();
|
|
245
|
+
this.logger.log(`Deleted ${result.affected} old HTTP logs (older than ${daysToKeep} days)`);
|
|
246
|
+
return result.affected || 0;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
clearAllLogs() {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
if (!this.logRepository) {
|
|
252
|
+
throw new Error('HTTP log repository not available');
|
|
253
|
+
}
|
|
254
|
+
yield this.logRepository.clear();
|
|
255
|
+
this.logger.warn('Cleared all HTTP logs');
|
|
256
|
+
return 0;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
generateCurlFromLog(logId) {
|
|
260
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
261
|
+
if (!this.logRepository) {
|
|
262
|
+
throw new Error('HTTP log repository not available');
|
|
263
|
+
}
|
|
264
|
+
const log = yield this.logRepository.findOne({
|
|
265
|
+
where: { id: logId },
|
|
266
|
+
});
|
|
267
|
+
if (!log) {
|
|
268
|
+
this.logger.warn(`Log with ID ${logId} not found`);
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
const sanitizedHeaders = this.sanitizeHeadersForCurl(log.headers || {});
|
|
273
|
+
const requestConfig = {
|
|
274
|
+
method: log.method,
|
|
275
|
+
url: log.url,
|
|
276
|
+
headers: sanitizedHeaders,
|
|
277
|
+
data: log.body,
|
|
278
|
+
params: log.params,
|
|
279
|
+
};
|
|
280
|
+
const curlCommand = curl_generator_util_1.CurlGenerator.generateCurlCommand(requestConfig);
|
|
281
|
+
this.logger.log(`Generated curl command for log ${logId}`);
|
|
282
|
+
return curlCommand;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
this.logger.error(`Failed to generate curl command for log ${logId}`, error);
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
generateCurlFromRequestId(requestId) {
|
|
291
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
292
|
+
if (!this.logRepository) {
|
|
293
|
+
throw new Error('HTTP log repository not available');
|
|
294
|
+
}
|
|
295
|
+
const log = yield this.logRepository.findOne({
|
|
296
|
+
where: { requestId },
|
|
297
|
+
order: { createdAt: 'DESC' },
|
|
298
|
+
});
|
|
299
|
+
if (!log) {
|
|
300
|
+
this.logger.warn(`Log with requestId ${requestId} not found`);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
return this.generateCurlFromLog(log.id);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
generateReplayConfigFromLog(logId) {
|
|
307
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
+
if (!this.logRepository) {
|
|
309
|
+
throw new Error('HTTP log repository not available');
|
|
310
|
+
}
|
|
311
|
+
const log = yield this.logRepository.findOne({
|
|
312
|
+
where: { id: logId },
|
|
313
|
+
});
|
|
314
|
+
if (!log) {
|
|
315
|
+
this.logger.warn(`Log with ID ${logId} not found`);
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const replayConfig = {
|
|
320
|
+
method: log.method,
|
|
321
|
+
url: log.url,
|
|
322
|
+
headers: log.headers || {},
|
|
323
|
+
data: log.body
|
|
324
|
+
? typeof log.body === 'string'
|
|
325
|
+
? JSON.parse(log.body)
|
|
326
|
+
: log.body
|
|
327
|
+
: undefined,
|
|
328
|
+
params: log.params || {},
|
|
329
|
+
metadata: {
|
|
330
|
+
originalLogId: log.id,
|
|
331
|
+
originalRequestId: log.requestId,
|
|
332
|
+
replayTimestamp: new Date().toISOString(),
|
|
333
|
+
replayedBy: 'http-log-query-service',
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
this.logger.log(`Generated replay config for log ${logId}`);
|
|
337
|
+
return replayConfig;
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
this.logger.error(`Failed to generate replay config for log ${logId}`, error);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
generateReplayConfigFromRequestId(requestId) {
|
|
346
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
347
|
+
if (!this.logRepository) {
|
|
348
|
+
throw new Error('HTTP log repository not available');
|
|
349
|
+
}
|
|
350
|
+
const log = yield this.logRepository.findOne({
|
|
351
|
+
where: { requestId },
|
|
352
|
+
order: { createdAt: 'DESC' },
|
|
353
|
+
});
|
|
354
|
+
if (!log) {
|
|
355
|
+
this.logger.warn(`Log with requestId ${requestId} not found`);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
return this.generateReplayConfigFromLog(log.id);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
generateBatchCurlCommands() {
|
|
362
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
363
|
+
if (!this.logRepository) {
|
|
364
|
+
throw new Error('HTTP log repository not available');
|
|
365
|
+
}
|
|
366
|
+
const limitedOptions = Object.assign(Object.assign({}, options), { limit: Math.min(options.limit || 10, 50) });
|
|
367
|
+
const result = yield this.findLogs(limitedOptions);
|
|
368
|
+
const curlCommands = [];
|
|
369
|
+
for (const log of result.logs) {
|
|
370
|
+
try {
|
|
371
|
+
const curlCommand = yield this.generateCurlFromLog(log.id);
|
|
372
|
+
if (curlCommand) {
|
|
373
|
+
curlCommands.push({
|
|
374
|
+
logId: log.id,
|
|
375
|
+
requestId: log.requestId,
|
|
376
|
+
curlCommand,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
this.logger.warn(`Failed to generate curl for log ${log.id}`, error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
this.logger.log(`Generated ${curlCommands.length} curl commands`);
|
|
385
|
+
return curlCommands;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
getLogReplayInfo(logId) {
|
|
389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
390
|
+
if (!this.logRepository) {
|
|
391
|
+
throw new Error('HTTP log repository not available');
|
|
392
|
+
}
|
|
393
|
+
const log = yield this.findLogById(logId, true);
|
|
394
|
+
if (!log) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
const [curlCommand, replayConfig] = yield Promise.all([
|
|
398
|
+
this.generateCurlFromLog(logId),
|
|
399
|
+
this.generateReplayConfigFromLog(logId),
|
|
400
|
+
]);
|
|
401
|
+
return {
|
|
402
|
+
log,
|
|
403
|
+
curlCommand,
|
|
404
|
+
replayConfig,
|
|
405
|
+
};
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
getLogReplayInfoByRequestId(requestId) {
|
|
409
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
410
|
+
const log = yield this.findLogByRequestId(requestId);
|
|
411
|
+
if (!log) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
return this.getLogReplayInfo(log.id);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
initializeRepository() {
|
|
418
|
+
if (this.dataSource) {
|
|
419
|
+
try {
|
|
420
|
+
this.logRepository = this.dataSource.getRepository(http_log_entity_1.HttpLogEntity);
|
|
421
|
+
this.logger.log('HTTP log repository initialized');
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
this.logger.error('Failed to initialize HTTP log repository', error);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
calculatePercentiles(queryBuilder) {
|
|
429
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
430
|
+
try {
|
|
431
|
+
const results = yield queryBuilder
|
|
432
|
+
.select('log.responseTime', 'responseTime')
|
|
433
|
+
.where('log.responseTime IS NOT NULL')
|
|
434
|
+
.orderBy('log.responseTime', 'ASC')
|
|
435
|
+
.getRawMany();
|
|
436
|
+
if (results.length === 0) {
|
|
437
|
+
return { p50: 0, p95: 0, p99: 0 };
|
|
438
|
+
}
|
|
439
|
+
const responseTimes = results
|
|
440
|
+
.map((r) => r.responseTime)
|
|
441
|
+
.sort((a, b) => a - b);
|
|
442
|
+
const total = responseTimes.length;
|
|
443
|
+
const getPercentile = (p) => {
|
|
444
|
+
const index = Math.ceil((p / 100) * total) - 1;
|
|
445
|
+
return responseTimes[Math.max(0, index)];
|
|
446
|
+
};
|
|
447
|
+
return {
|
|
448
|
+
p50: getPercentile(50),
|
|
449
|
+
p95: getPercentile(95),
|
|
450
|
+
p99: getPercentile(99),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
this.logger.warn('Failed to calculate percentiles', error);
|
|
455
|
+
return { p50: 0, p95: 0, p99: 0 };
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
formatStats(stats, keyField, valueField) {
|
|
460
|
+
return stats.reduce((acc, item) => {
|
|
461
|
+
const key = item[keyField] || 'unknown';
|
|
462
|
+
const value = parseInt(item[valueField]) || 0;
|
|
463
|
+
acc[key] = value;
|
|
464
|
+
return acc;
|
|
465
|
+
}, {});
|
|
466
|
+
}
|
|
467
|
+
buildQuery(options, includeDetails = false) {
|
|
468
|
+
if (!this.logRepository) {
|
|
469
|
+
throw new Error('HTTP log repository not available');
|
|
470
|
+
}
|
|
471
|
+
const queryBuilder = this.logRepository.createQueryBuilder('log');
|
|
472
|
+
if (includeDetails) {
|
|
473
|
+
queryBuilder.leftJoinAndSelect('log.retryRecords', 'retryRecords');
|
|
474
|
+
}
|
|
475
|
+
if (options.requestId) {
|
|
476
|
+
queryBuilder.andWhere('log.requestId = :requestId', {
|
|
477
|
+
requestId: options.requestId,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (options.userId) {
|
|
481
|
+
queryBuilder.andWhere('log.userId = :userId', { userId: options.userId });
|
|
482
|
+
}
|
|
483
|
+
if (options.method) {
|
|
484
|
+
if (Array.isArray(options.method)) {
|
|
485
|
+
queryBuilder.andWhere('log.method IN (:...methods)', {
|
|
486
|
+
methods: options.method,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
queryBuilder.andWhere('log.method = :method', {
|
|
491
|
+
method: options.method,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (options.url) {
|
|
496
|
+
queryBuilder.andWhere('log.url LIKE :url', { url: `%${options.url}%` });
|
|
497
|
+
}
|
|
498
|
+
if (options.statusCode) {
|
|
499
|
+
if (Array.isArray(options.statusCode)) {
|
|
500
|
+
queryBuilder.andWhere('log.statusCode IN (:...statusCodes)', {
|
|
501
|
+
statusCodes: options.statusCode,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
queryBuilder.andWhere('log.statusCode = :statusCode', {
|
|
506
|
+
statusCode: options.statusCode,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (options.success !== undefined) {
|
|
511
|
+
queryBuilder.andWhere('log.success = :success', {
|
|
512
|
+
success: options.success,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
if (options.serviceName) {
|
|
516
|
+
queryBuilder.andWhere('log.serviceName = :serviceName', {
|
|
517
|
+
serviceName: options.serviceName,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (options.operationName) {
|
|
521
|
+
queryBuilder.andWhere('log.operationName = :operationName', {
|
|
522
|
+
operationName: options.operationName,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (options.clientIp) {
|
|
526
|
+
queryBuilder.andWhere('log.clientIp = :clientIp', {
|
|
527
|
+
clientIp: options.clientIp,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
if (options.source) {
|
|
531
|
+
queryBuilder.andWhere('log.source = :source', { source: options.source });
|
|
532
|
+
}
|
|
533
|
+
if (options.tags && options.tags.length > 0) {
|
|
534
|
+
queryBuilder.andWhere('JSON_CONTAINS(log.tags, :tags)', {
|
|
535
|
+
tags: JSON.stringify(options.tags),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
if (options.errorCode) {
|
|
539
|
+
queryBuilder.andWhere('log.errorCode = :errorCode', {
|
|
540
|
+
errorCode: options.errorCode,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
if (options.minResponseTime) {
|
|
544
|
+
queryBuilder.andWhere('log.responseTime >= :minResponseTime', {
|
|
545
|
+
minResponseTime: options.minResponseTime,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
if (options.maxResponseTime) {
|
|
549
|
+
queryBuilder.andWhere('log.responseTime <= :maxResponseTime', {
|
|
550
|
+
maxResponseTime: options.maxResponseTime,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (options.startDate) {
|
|
554
|
+
queryBuilder.andWhere('log.createdAt >= :startDate', {
|
|
555
|
+
startDate: options.startDate,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (options.endDate) {
|
|
559
|
+
queryBuilder.andWhere('log.createdAt <= :endDate', {
|
|
560
|
+
endDate: options.endDate,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
if (options.keyword) {
|
|
564
|
+
queryBuilder.andWhere('(log.url LIKE :keyword OR log.errorMessage LIKE :keyword OR log.body LIKE :keyword)', { keyword: `%${options.keyword}%` });
|
|
565
|
+
}
|
|
566
|
+
return queryBuilder;
|
|
567
|
+
}
|
|
568
|
+
sanitizeHeadersForCurl(headers) {
|
|
569
|
+
const sensitiveHeaders = [
|
|
570
|
+
'authorization',
|
|
571
|
+
'apikey',
|
|
572
|
+
'x-api-key',
|
|
573
|
+
'x-auth-token',
|
|
574
|
+
'cookie',
|
|
575
|
+
'set-cookie',
|
|
576
|
+
];
|
|
577
|
+
const sanitized = Object.assign({}, headers);
|
|
578
|
+
for (const header of sensitiveHeaders) {
|
|
579
|
+
if (sanitized[header]) {
|
|
580
|
+
sanitized[header] = '***REDACTED***';
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return sanitized;
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
exports.HttpLogQueryService = HttpLogQueryService;
|
|
587
|
+
exports.HttpLogQueryService = HttpLogQueryService = HttpLogQueryService_1 = __decorate([
|
|
588
|
+
(0, common_1.Injectable)(),
|
|
589
|
+
__metadata("design:paramtypes", [typeorm_1.DataSource])
|
|
590
|
+
], HttpLogQueryService);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
import { HttpClientService } from './http-client.service';
|
|
3
|
+
import { HttpLogQueryService } from './http-log-query.service';
|
|
4
|
+
import { HttpLogEntity } from '../entities/http-log.entity';
|
|
5
|
+
export interface HttpReplayOptions {
|
|
6
|
+
overrideHeaders?: boolean;
|
|
7
|
+
overrideBody?: boolean;
|
|
8
|
+
overrideParams?: boolean;
|
|
9
|
+
additionalHeaders?: Record<string, string>;
|
|
10
|
+
metadata?: Record<string, any>;
|
|
11
|
+
logReplay?: boolean;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpReplayResult {
|
|
15
|
+
originalLog: HttpLogEntity;
|
|
16
|
+
requestConfig: AxiosRequestConfig;
|
|
17
|
+
response: any;
|
|
18
|
+
responseTime: number;
|
|
19
|
+
success: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
replayTimestamp: string;
|
|
22
|
+
replayId: string;
|
|
23
|
+
}
|
|
24
|
+
export interface BatchReplayResult {
|
|
25
|
+
totalRequests: number;
|
|
26
|
+
successfulRequests: number;
|
|
27
|
+
failedRequests: number;
|
|
28
|
+
successRate: number;
|
|
29
|
+
totalResponseTime: number;
|
|
30
|
+
averageResponseTime: number;
|
|
31
|
+
results: HttpReplayResult[];
|
|
32
|
+
}
|
|
33
|
+
export declare class HttpReplayService {
|
|
34
|
+
private readonly httpClient;
|
|
35
|
+
private readonly logQueryService;
|
|
36
|
+
private readonly logger;
|
|
37
|
+
constructor(httpClient: HttpClientService, logQueryService: HttpLogQueryService);
|
|
38
|
+
replayByLogId(logId: string, options?: HttpReplayOptions): Promise<HttpReplayResult>;
|
|
39
|
+
replayByRequestId(requestId: string, options?: HttpReplayOptions): Promise<HttpReplayResult>;
|
|
40
|
+
batchReplay(logIds: string[], options?: HttpReplayOptions, concurrency?: number): Promise<BatchReplayResult>;
|
|
41
|
+
batchReplayByQuery(queryOptions?: any, replayOptions?: HttpReplayOptions, concurrency?: number): Promise<BatchReplayResult>;
|
|
42
|
+
replayAndCompare(logId: string, options?: HttpReplayOptions): Promise<{
|
|
43
|
+
original: HttpLogEntity;
|
|
44
|
+
replay: HttpReplayResult;
|
|
45
|
+
comparison: {
|
|
46
|
+
statusMatch: boolean;
|
|
47
|
+
responseTimeDifference: number;
|
|
48
|
+
headersMatch: boolean;
|
|
49
|
+
bodyMatch: boolean;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
private buildReplayConfig;
|
|
53
|
+
private executeReplayRequest;
|
|
54
|
+
private generateReplayId;
|
|
55
|
+
private chunkArray;
|
|
56
|
+
private compareHeaders;
|
|
57
|
+
private compareBodies;
|
|
58
|
+
}
|