request-scope-api 1.0.0
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/README.md +275 -0
- package/dist/cjs/adapters/adapter.interface.js +9 -0
- package/dist/cjs/adapters/adapter.interface.js.map +1 -0
- package/dist/cjs/adapters/mongo.adapter.js +188 -0
- package/dist/cjs/adapters/mongo.adapter.js.map +1 -0
- package/dist/cjs/adapters/mysql.adapter.js +243 -0
- package/dist/cjs/adapters/mysql.adapter.js.map +1 -0
- package/dist/cjs/adapters/pg.adapter.js +334 -0
- package/dist/cjs/adapters/pg.adapter.js.map +1 -0
- package/dist/cjs/capture.js +310 -0
- package/dist/cjs/capture.js.map +1 -0
- package/dist/cjs/config.js +122 -0
- package/dist/cjs/config.js.map +1 -0
- package/dist/cjs/dashboard/api.js +173 -0
- package/dist/cjs/dashboard/api.js.map +1 -0
- package/dist/cjs/dashboard/router.js +96 -0
- package/dist/cjs/dashboard/router.js.map +1 -0
- package/dist/cjs/index.js +49 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/masker.js +73 -0
- package/dist/cjs/masker.js.map +1 -0
- package/dist/cjs/middleware.js +198 -0
- package/dist/cjs/middleware.js.map +1 -0
- package/dist/cjs/queue.js +114 -0
- package/dist/cjs/queue.js.map +1 -0
- package/dist/cjs/retention.js +64 -0
- package/dist/cjs/retention.js.map +1 -0
- package/dist/cjs/types.js +9 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/dashboard/assets/index-C0TqFHk6.css +1 -0
- package/dist/dashboard/assets/index-MCuAZo4Q.js +67 -0
- package/dist/dashboard/index.html +13 -0
- package/dist/esm/adapters/adapter.interface.js +8 -0
- package/dist/esm/adapters/adapter.interface.js.map +1 -0
- package/dist/esm/adapters/mongo.adapter.js +184 -0
- package/dist/esm/adapters/mongo.adapter.js.map +1 -0
- package/dist/esm/adapters/mysql.adapter.js +236 -0
- package/dist/esm/adapters/mysql.adapter.js.map +1 -0
- package/dist/esm/adapters/pg.adapter.js +330 -0
- package/dist/esm/adapters/pg.adapter.js.map +1 -0
- package/dist/esm/capture.js +304 -0
- package/dist/esm/capture.js.map +1 -0
- package/dist/esm/config.js +117 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/dashboard/api.js +168 -0
- package/dist/esm/dashboard/api.js.map +1 -0
- package/dist/esm/dashboard/router.js +90 -0
- package/dist/esm/dashboard/router.js.map +1 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/masker.js +70 -0
- package/dist/esm/masker.js.map +1 -0
- package/dist/esm/middleware.js +193 -0
- package/dist/esm/middleware.js.map +1 -0
- package/dist/esm/queue.js +110 -0
- package/dist/esm/queue.js.map +1 -0
- package/dist/esm/retention.js +60 -0
- package/dist/esm/retention.js.map +1 -0
- package/dist/esm/types.js +8 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/adapters/adapter.interface.d.ts +7 -0
- package/dist/types/adapters/mongo.adapter.d.ts +25 -0
- package/dist/types/adapters/mysql.adapter.d.ts +24 -0
- package/dist/types/adapters/pg.adapter.d.ts +29 -0
- package/dist/types/capture.d.ts +88 -0
- package/dist/types/config.d.ts +38 -0
- package/dist/types/dashboard/api.d.ts +49 -0
- package/dist/types/dashboard/router.d.ts +28 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/masker.d.ts +15 -0
- package/dist/types/middleware.d.ts +67 -0
- package/dist/types/queue.d.ts +49 -0
- package/dist/types/retention.d.ts +30 -0
- package/dist/types/types.d.ts +101 -0
- package/package.json +48 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>RequestScope Dashboard</title>
|
|
7
|
+
<script type="module" crossorigin src="/requestscope/assets/index-MCuAZo4Q.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/requestscope/assets/index-C0TqFHk6.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StorageAdapter interface — the contract all storage adapters must implement.
|
|
3
|
+
*
|
|
4
|
+
* Adapters should import from this file so they stay decoupled from the
|
|
5
|
+
* top-level types module while still satisfying the shared contract.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=adapter.interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.interface.js","sourceRoot":"","sources":["../../../src/adapters/adapter.interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoAdapter — MongoDB implementation of the StorageAdapter interface.
|
|
3
|
+
*
|
|
4
|
+
* Collection: `requestscope_records`
|
|
5
|
+
* `_id` is set to the record's UUID string for direct look-ups.
|
|
6
|
+
* A descending index on `timestamp` is created at initialization for sort
|
|
7
|
+
* performance and retention queries.
|
|
8
|
+
*/
|
|
9
|
+
import { MongoClient } from 'mongodb';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helper: map statusCodeGroup → {$gte, $lt} range
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
function statusGroupRange(group) {
|
|
14
|
+
const base = parseInt(group[0], 10) * 100;
|
|
15
|
+
return { $gte: base, $lt: base + 100 };
|
|
16
|
+
}
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Helper: convert a BSON document back to a RequestRecord
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
function documentToRecord(doc) {
|
|
21
|
+
return {
|
|
22
|
+
id: doc._id,
|
|
23
|
+
method: doc.method,
|
|
24
|
+
url: doc.url,
|
|
25
|
+
route: doc.route,
|
|
26
|
+
queryParams: doc.queryParams,
|
|
27
|
+
pathParams: doc.pathParams,
|
|
28
|
+
requestHeaders: doc.requestHeaders,
|
|
29
|
+
requestBody: doc.requestBody,
|
|
30
|
+
requestBodySize: doc.requestBodySize,
|
|
31
|
+
clientIp: doc.clientIp,
|
|
32
|
+
userAgent: doc.userAgent,
|
|
33
|
+
statusCode: doc.statusCode,
|
|
34
|
+
responseHeaders: doc.responseHeaders,
|
|
35
|
+
responseBody: doc.responseBody,
|
|
36
|
+
responseBodySize: doc.responseBodySize,
|
|
37
|
+
responseTime: doc.responseTime,
|
|
38
|
+
errorMessage: doc.errorMessage,
|
|
39
|
+
errorStack: doc.errorStack,
|
|
40
|
+
errorStatusCode: doc.errorStatusCode,
|
|
41
|
+
timestamp: doc.timestamp,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// MongoAdapter
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
export class MongoAdapter {
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.client = null;
|
|
51
|
+
this.collection = null;
|
|
52
|
+
}
|
|
53
|
+
// -------------------------------------------------------------------------
|
|
54
|
+
// initialize()
|
|
55
|
+
// -------------------------------------------------------------------------
|
|
56
|
+
async initialize() {
|
|
57
|
+
const uri = this.config.uri;
|
|
58
|
+
if (!uri) {
|
|
59
|
+
process.stderr.write('[MongoAdapter] storage.uri is required for MongoDB\n');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
this.client = new MongoClient(uri);
|
|
64
|
+
await this.client.connect();
|
|
65
|
+
const db = this.client.db();
|
|
66
|
+
// Create the collection if it does not exist (MongoDB creates it
|
|
67
|
+
// implicitly on first write, but we also create the index here).
|
|
68
|
+
const collections = await db
|
|
69
|
+
.listCollections({ name: 'requestscope_records' }, { nameOnly: true })
|
|
70
|
+
.toArray();
|
|
71
|
+
if (collections.length === 0) {
|
|
72
|
+
await db.createCollection('requestscope_records');
|
|
73
|
+
}
|
|
74
|
+
this.collection = db.collection('requestscope_records');
|
|
75
|
+
// Descending index on timestamp for sort performance and retention queries.
|
|
76
|
+
await this.collection.createIndex({ timestamp: -1 }, { background: true });
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
process.stderr.write(`[MongoAdapter] Connection error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
80
|
+
// Do not rethrow — fault-isolated per design.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// -------------------------------------------------------------------------
|
|
84
|
+
// insert()
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
async insert(records) {
|
|
87
|
+
if (!this.collection || records.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
const docs = records.map((r) => ({
|
|
90
|
+
_id: r.id,
|
|
91
|
+
method: r.method,
|
|
92
|
+
url: r.url,
|
|
93
|
+
route: r.route,
|
|
94
|
+
queryParams: r.queryParams,
|
|
95
|
+
pathParams: r.pathParams,
|
|
96
|
+
requestHeaders: r.requestHeaders,
|
|
97
|
+
requestBody: r.requestBody,
|
|
98
|
+
requestBodySize: r.requestBodySize,
|
|
99
|
+
clientIp: r.clientIp,
|
|
100
|
+
userAgent: r.userAgent,
|
|
101
|
+
statusCode: r.statusCode,
|
|
102
|
+
responseHeaders: r.responseHeaders,
|
|
103
|
+
responseBody: r.responseBody,
|
|
104
|
+
responseBodySize: r.responseBodySize,
|
|
105
|
+
responseTime: r.responseTime,
|
|
106
|
+
errorMessage: r.errorMessage,
|
|
107
|
+
errorStack: r.errorStack,
|
|
108
|
+
errorStatusCode: r.errorStatusCode,
|
|
109
|
+
timestamp: r.timestamp,
|
|
110
|
+
}));
|
|
111
|
+
await this.collection.insertMany(docs, { ordered: false });
|
|
112
|
+
}
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
// query()
|
|
115
|
+
// -------------------------------------------------------------------------
|
|
116
|
+
async query(filters, pagination) {
|
|
117
|
+
if (!this.collection)
|
|
118
|
+
return { records: [], total: 0 };
|
|
119
|
+
// Build MongoDB filter document (AND logic — all conditions applied).
|
|
120
|
+
const mongoFilter = {};
|
|
121
|
+
if (filters.id !== undefined) {
|
|
122
|
+
mongoFilter._id = filters.id;
|
|
123
|
+
}
|
|
124
|
+
if (filters.search !== undefined && filters.search !== '') {
|
|
125
|
+
mongoFilter.url = { $regex: filters.search, $options: 'i' };
|
|
126
|
+
}
|
|
127
|
+
// Date range — lexicographic ISO 8601 comparison is valid for UTC strings.
|
|
128
|
+
if (filters.startDate !== undefined || filters.endDate !== undefined) {
|
|
129
|
+
const tsFilter = {};
|
|
130
|
+
if (filters.startDate !== undefined)
|
|
131
|
+
tsFilter.$gte = filters.startDate;
|
|
132
|
+
if (filters.endDate !== undefined)
|
|
133
|
+
tsFilter.$lte = filters.endDate;
|
|
134
|
+
mongoFilter.timestamp = tsFilter;
|
|
135
|
+
}
|
|
136
|
+
// statusCode (exact match) takes precedence over statusCodeGroup.
|
|
137
|
+
if (filters.statusCode !== undefined) {
|
|
138
|
+
mongoFilter.statusCode = filters.statusCode;
|
|
139
|
+
}
|
|
140
|
+
else if (filters.statusCodeGroup !== undefined) {
|
|
141
|
+
mongoFilter.statusCode = statusGroupRange(filters.statusCodeGroup);
|
|
142
|
+
}
|
|
143
|
+
if (filters.method !== undefined && filters.method !== '') {
|
|
144
|
+
mongoFilter.method = { $regex: filters.method, $options: 'i' };
|
|
145
|
+
}
|
|
146
|
+
// Sort mapping.
|
|
147
|
+
const sortField = filters.sortBy === 'responseTime'
|
|
148
|
+
? 'responseTime'
|
|
149
|
+
: filters.sortBy === 'statusCode'
|
|
150
|
+
? 'statusCode'
|
|
151
|
+
: 'timestamp';
|
|
152
|
+
const sortDirection = filters.sortOrder === 'asc' ? 1 : -1;
|
|
153
|
+
// Pagination.
|
|
154
|
+
const { page, pageSize } = pagination;
|
|
155
|
+
const skip = (page - 1) * pageSize;
|
|
156
|
+
// Execute count and find in parallel.
|
|
157
|
+
const [total, docs] = await Promise.all([
|
|
158
|
+
this.collection.countDocuments(mongoFilter),
|
|
159
|
+
this.collection
|
|
160
|
+
.find(mongoFilter)
|
|
161
|
+
.sort({ [sortField]: sortDirection })
|
|
162
|
+
.skip(skip)
|
|
163
|
+
.limit(pageSize)
|
|
164
|
+
.toArray(),
|
|
165
|
+
]);
|
|
166
|
+
return {
|
|
167
|
+
records: docs.map(documentToRecord),
|
|
168
|
+
total,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// -------------------------------------------------------------------------
|
|
172
|
+
// deleteOlderThan()
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
async deleteOlderThan(date) {
|
|
175
|
+
if (!this.collection)
|
|
176
|
+
return 0;
|
|
177
|
+
const cutoff = date.toISOString();
|
|
178
|
+
const result = await this.collection.deleteMany({
|
|
179
|
+
timestamp: { $lt: cutoff },
|
|
180
|
+
});
|
|
181
|
+
return result.deletedCount;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=mongo.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo.adapter.js","sourceRoot":"","sources":["../../../src/adapters/mongo.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQLAdapter — StorageAdapter implementation for MySQL using mysql2/promise.
|
|
3
|
+
*
|
|
4
|
+
* Compiles under `strict: true` with zero errors.
|
|
5
|
+
* Requirements: 1.6, 1.7, 6.1, 6.3, 6.5, 7.4
|
|
6
|
+
*/
|
|
7
|
+
import mysql from 'mysql2/promise';
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Column name mapping: JS camelCase → SQL snake_case
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const SORT_COLUMN_MAP = {
|
|
12
|
+
timestamp: 'timestamp',
|
|
13
|
+
responseTime: 'response_time',
|
|
14
|
+
statusCode: 'status_code',
|
|
15
|
+
};
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// DDL
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const DDL = `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS requestscope_records (
|
|
21
|
+
id VARCHAR(36) NOT NULL PRIMARY KEY,
|
|
22
|
+
method VARCHAR(10) NOT NULL,
|
|
23
|
+
url TEXT NOT NULL,
|
|
24
|
+
route VARCHAR(500) NULL,
|
|
25
|
+
query_params JSON NOT NULL,
|
|
26
|
+
path_params JSON NOT NULL,
|
|
27
|
+
request_headers JSON NOT NULL,
|
|
28
|
+
request_body MEDIUMTEXT NOT NULL,
|
|
29
|
+
request_body_size INT UNSIGNED NOT NULL,
|
|
30
|
+
client_ip VARCHAR(45) NOT NULL,
|
|
31
|
+
user_agent TEXT NOT NULL,
|
|
32
|
+
status_code SMALLINT NOT NULL,
|
|
33
|
+
response_headers JSON NOT NULL,
|
|
34
|
+
response_body MEDIUMTEXT NOT NULL,
|
|
35
|
+
response_body_size INT UNSIGNED NOT NULL,
|
|
36
|
+
response_time FLOAT NOT NULL,
|
|
37
|
+
error_message TEXT NULL,
|
|
38
|
+
error_stack TEXT NULL,
|
|
39
|
+
error_status_code SMALLINT NULL,
|
|
40
|
+
timestamp VARCHAR(30) NOT NULL,
|
|
41
|
+
INDEX idx_timestamp (timestamp),
|
|
42
|
+
INDEX idx_status_code (status_code),
|
|
43
|
+
INDEX idx_method (method)
|
|
44
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
45
|
+
`.trim();
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Helpers
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/**
|
|
50
|
+
* Parse a JSON column value. MySQL drivers may return it as a string or
|
|
51
|
+
* as an already-parsed object depending on the version/config.
|
|
52
|
+
*/
|
|
53
|
+
function parseJson(value) {
|
|
54
|
+
if (value === null || value === undefined)
|
|
55
|
+
return {};
|
|
56
|
+
if (typeof value === 'object')
|
|
57
|
+
return value;
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(value);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
/** Map a database row back to a `RequestRecord`. */
|
|
69
|
+
function rowToRecord(row) {
|
|
70
|
+
return {
|
|
71
|
+
id: row.id,
|
|
72
|
+
method: row.method,
|
|
73
|
+
url: row.url,
|
|
74
|
+
route: row.route,
|
|
75
|
+
queryParams: parseJson(row.query_params),
|
|
76
|
+
pathParams: parseJson(row.path_params),
|
|
77
|
+
requestHeaders: parseJson(row.request_headers),
|
|
78
|
+
requestBody: row.request_body,
|
|
79
|
+
requestBodySize: row.request_body_size,
|
|
80
|
+
clientIp: row.client_ip,
|
|
81
|
+
userAgent: row.user_agent,
|
|
82
|
+
statusCode: row.status_code,
|
|
83
|
+
responseHeaders: parseJson(row.response_headers),
|
|
84
|
+
responseBody: row.response_body,
|
|
85
|
+
responseBodySize: row.response_body_size,
|
|
86
|
+
responseTime: row.response_time,
|
|
87
|
+
errorMessage: row.error_message,
|
|
88
|
+
errorStack: row.error_stack,
|
|
89
|
+
errorStatusCode: row.error_status_code,
|
|
90
|
+
timestamp: row.timestamp,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Resolve the `statusCodeGroup` filter to a `[min, max)` pair. */
|
|
94
|
+
function statusGroupRange(group) {
|
|
95
|
+
const base = parseInt(group[0], 10) * 100;
|
|
96
|
+
return [base, base + 100];
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// MySQLAdapter
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
export class MySQLAdapter {
|
|
102
|
+
constructor(config) {
|
|
103
|
+
this.pool = null;
|
|
104
|
+
this.config = config;
|
|
105
|
+
}
|
|
106
|
+
// -------------------------------------------------------------------------
|
|
107
|
+
// initialize
|
|
108
|
+
// -------------------------------------------------------------------------
|
|
109
|
+
async initialize() {
|
|
110
|
+
const options = {
|
|
111
|
+
host: this.config.host ?? 'localhost',
|
|
112
|
+
port: this.config.port ?? 3306,
|
|
113
|
+
database: this.config.database,
|
|
114
|
+
user: this.config.username,
|
|
115
|
+
password: this.config.password,
|
|
116
|
+
connectionLimit: this.config.poolSize ?? 5,
|
|
117
|
+
ssl: this.config.ssl ? { rejectUnauthorized: true } : undefined,
|
|
118
|
+
// Return JSON columns as strings so we control the parsing
|
|
119
|
+
typeCast: false,
|
|
120
|
+
};
|
|
121
|
+
this.pool = mysql.createPool(options);
|
|
122
|
+
await this.pool.execute(DDL);
|
|
123
|
+
}
|
|
124
|
+
// -------------------------------------------------------------------------
|
|
125
|
+
// insert
|
|
126
|
+
// -------------------------------------------------------------------------
|
|
127
|
+
async insert(records) {
|
|
128
|
+
if (records.length === 0)
|
|
129
|
+
return;
|
|
130
|
+
const pool = this.getPool();
|
|
131
|
+
// Build: INSERT INTO ... VALUES (?, ...), (?, ...), ...
|
|
132
|
+
const columnList = [
|
|
133
|
+
'id', 'method', 'url', 'route',
|
|
134
|
+
'query_params', 'path_params', 'request_headers',
|
|
135
|
+
'request_body', 'request_body_size',
|
|
136
|
+
'client_ip', 'user_agent',
|
|
137
|
+
'status_code', 'response_headers',
|
|
138
|
+
'response_body', 'response_body_size',
|
|
139
|
+
'response_time',
|
|
140
|
+
'error_message', 'error_stack', 'error_status_code',
|
|
141
|
+
'timestamp',
|
|
142
|
+
];
|
|
143
|
+
const placeholderRow = `(${columnList.map(() => '?').join(', ')})`;
|
|
144
|
+
const placeholders = records.map(() => placeholderRow).join(', ');
|
|
145
|
+
const sql = `INSERT INTO requestscope_records (${columnList.join(', ')}) VALUES ${placeholders}`;
|
|
146
|
+
const values = [];
|
|
147
|
+
for (const r of records) {
|
|
148
|
+
values.push(r.id, r.method, r.url, r.route, JSON.stringify(r.queryParams), JSON.stringify(r.pathParams), JSON.stringify(r.requestHeaders), r.requestBody, r.requestBodySize, r.clientIp, r.userAgent, r.statusCode, JSON.stringify(r.responseHeaders), r.responseBody, r.responseBodySize, r.responseTime, r.errorMessage, r.errorStack, r.errorStatusCode, r.timestamp);
|
|
149
|
+
}
|
|
150
|
+
await pool.execute(sql, values);
|
|
151
|
+
}
|
|
152
|
+
// -------------------------------------------------------------------------
|
|
153
|
+
// query
|
|
154
|
+
// -------------------------------------------------------------------------
|
|
155
|
+
async query(filters, pagination) {
|
|
156
|
+
const pool = this.getPool();
|
|
157
|
+
const conditions = [];
|
|
158
|
+
const params = [];
|
|
159
|
+
// id — exact match
|
|
160
|
+
if (filters.id !== undefined) {
|
|
161
|
+
conditions.push('id = ?');
|
|
162
|
+
params.push(filters.id);
|
|
163
|
+
}
|
|
164
|
+
// search — url LIKE ?
|
|
165
|
+
if (filters.search !== undefined) {
|
|
166
|
+
conditions.push('url LIKE ?');
|
|
167
|
+
params.push(`%${filters.search}%`);
|
|
168
|
+
}
|
|
169
|
+
// startDate — timestamp >= ?
|
|
170
|
+
if (filters.startDate !== undefined) {
|
|
171
|
+
conditions.push('timestamp >= ?');
|
|
172
|
+
params.push(filters.startDate);
|
|
173
|
+
}
|
|
174
|
+
// endDate — timestamp <= ?
|
|
175
|
+
if (filters.endDate !== undefined) {
|
|
176
|
+
conditions.push('timestamp <= ?');
|
|
177
|
+
params.push(filters.endDate);
|
|
178
|
+
}
|
|
179
|
+
// statusCode (exact, takes precedence over group)
|
|
180
|
+
if (filters.statusCode !== undefined) {
|
|
181
|
+
conditions.push('status_code = ?');
|
|
182
|
+
params.push(filters.statusCode);
|
|
183
|
+
}
|
|
184
|
+
else if (filters.statusCodeGroup !== undefined) {
|
|
185
|
+
const [min, max] = statusGroupRange(filters.statusCodeGroup);
|
|
186
|
+
conditions.push('status_code >= ? AND status_code < ?');
|
|
187
|
+
params.push(min, max);
|
|
188
|
+
}
|
|
189
|
+
// method — case-insensitive
|
|
190
|
+
if (filters.method !== undefined) {
|
|
191
|
+
conditions.push('LOWER(method) = LOWER(?)');
|
|
192
|
+
params.push(filters.method);
|
|
193
|
+
}
|
|
194
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
195
|
+
// Sort
|
|
196
|
+
const sortColRaw = filters.sortBy ?? 'timestamp';
|
|
197
|
+
const sortCol = SORT_COLUMN_MAP[sortColRaw] ?? 'timestamp';
|
|
198
|
+
const sortDir = (filters.sortOrder ?? 'desc').toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
|
|
199
|
+
const orderClause = `ORDER BY ${sortCol} ${sortDir}`;
|
|
200
|
+
// COUNT query (reuses same params)
|
|
201
|
+
const countSql = `SELECT COUNT(*) AS total FROM requestscope_records ${whereClause}`;
|
|
202
|
+
const [countRows] = await pool.execute(countSql, params);
|
|
203
|
+
const total = Number(countRows[0]?.total ?? 0);
|
|
204
|
+
// Page slice
|
|
205
|
+
const { page, pageSize } = pagination;
|
|
206
|
+
const offset = (Math.max(1, page) - 1) * pageSize;
|
|
207
|
+
const dataSql = `
|
|
208
|
+
SELECT * FROM requestscope_records
|
|
209
|
+
${whereClause}
|
|
210
|
+
${orderClause}
|
|
211
|
+
LIMIT ? OFFSET ?
|
|
212
|
+
`.trim();
|
|
213
|
+
const [rows] = await pool.execute(dataSql, [...params, pageSize, offset]);
|
|
214
|
+
const records = rows.map(rowToRecord);
|
|
215
|
+
return { records, total };
|
|
216
|
+
}
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
// deleteOlderThan
|
|
219
|
+
// -------------------------------------------------------------------------
|
|
220
|
+
async deleteOlderThan(date) {
|
|
221
|
+
const pool = this.getPool();
|
|
222
|
+
const iso = date.toISOString();
|
|
223
|
+
const [result] = await pool.execute('DELETE FROM requestscope_records WHERE timestamp < ?', [iso]);
|
|
224
|
+
return result.affectedRows;
|
|
225
|
+
}
|
|
226
|
+
// -------------------------------------------------------------------------
|
|
227
|
+
// Private helpers
|
|
228
|
+
// -------------------------------------------------------------------------
|
|
229
|
+
getPool() {
|
|
230
|
+
if (this.pool === null) {
|
|
231
|
+
throw new Error('MySQLAdapter: call initialize() before using the adapter');
|
|
232
|
+
}
|
|
233
|
+
return this.pool;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=mysql.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.adapter.js","sourceRoot":"","sources":["../../../src/adapters/mysql.adapter.ts"],"names":[],"mappings":"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"}
|