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
package/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# request-scope-api
|
|
2
|
+
|
|
3
|
+
Zero-friction API request observability for Express.js applications.
|
|
4
|
+
|
|
5
|
+
request-scope-api captures HTTP requests and responses, stores them in your database, and provides a dashboard for inspection. It's designed to be production-ready with minimal configuration and zero performance impact on your application.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install request-scope-api
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### MongoDB
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import express from 'express';
|
|
19
|
+
import request-scope-api from 'request-scope-api';
|
|
20
|
+
|
|
21
|
+
const app = express();
|
|
22
|
+
|
|
23
|
+
app.use(request-scope-api({
|
|
24
|
+
storage: {
|
|
25
|
+
type: 'mongodb',
|
|
26
|
+
uri: 'mongodb://localhost:27017/myapp'
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
app.listen(3000);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### MySQL
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import express from 'express';
|
|
37
|
+
import request-scope-api from 'request-scope-api';
|
|
38
|
+
|
|
39
|
+
const app = express();
|
|
40
|
+
|
|
41
|
+
app.use(request-scope-api({
|
|
42
|
+
storage: {
|
|
43
|
+
type: 'mysql',
|
|
44
|
+
host: 'localhost',
|
|
45
|
+
port: 3306,
|
|
46
|
+
database: 'myapp',
|
|
47
|
+
username: 'user',
|
|
48
|
+
password: 'pass'
|
|
49
|
+
}
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
app.listen(3000);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### PostgreSQL
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import express from 'express';
|
|
59
|
+
import request-scope-api from 'request-scope-api';
|
|
60
|
+
|
|
61
|
+
const app = express();
|
|
62
|
+
|
|
63
|
+
app.use(request-scope-api({
|
|
64
|
+
storage: {
|
|
65
|
+
type: 'postgresql',
|
|
66
|
+
host: 'localhost',
|
|
67
|
+
port: 5432,
|
|
68
|
+
database: 'myapp',
|
|
69
|
+
username: 'user',
|
|
70
|
+
password: 'pass'
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
app.listen(3000);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Dashboard
|
|
78
|
+
|
|
79
|
+
Mount the dashboard at a specific path with optional authentication:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
import request-scope-api from 'request-scope-api';
|
|
83
|
+
|
|
84
|
+
const app = express();
|
|
85
|
+
|
|
86
|
+
// Capture middleware
|
|
87
|
+
app.use(request-scope-api({
|
|
88
|
+
storage: {
|
|
89
|
+
type: 'mongodb',
|
|
90
|
+
uri: 'mongodb://localhost:27017/myapp'
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
### request-scope-apiConfig
|
|
98
|
+
|
|
99
|
+
| Option | Type | Default | Description |
|
|
100
|
+
|--------|------|---------|-------------|
|
|
101
|
+
| `storage` | `StorageConfig` | **required** | Database storage configuration |
|
|
102
|
+
| `retentionDays` | `number` | `30` | Number of days to retain records (1-365) |
|
|
103
|
+
| `ignore` | `string[]` | `[]` | URL paths to skip (no capture) |
|
|
104
|
+
| `maskFields` | `string[]` | `[]` | Additional field names to mask (merged with built-in defaults) |
|
|
105
|
+
| `dashboard` | `DashboardConfig` | `undefined` | Dashboard configuration |
|
|
106
|
+
|
|
107
|
+
### StorageConfig
|
|
108
|
+
|
|
109
|
+
| Option | Type | Required For | Description |
|
|
110
|
+
|--------|------|---------------|-------------|
|
|
111
|
+
| `type` | `'mongodb' \| 'mysql' \| 'postgresql'` | All | Storage backend type |
|
|
112
|
+
| `uri` | `string` | MongoDB | MongoDB connection URI |
|
|
113
|
+
| `host` | `string` | MySQL, PostgreSQL | Database host |
|
|
114
|
+
| `port` | `number` | MySQL, PostgreSQL | Database port |
|
|
115
|
+
| `database` | `string` | MySQL, PostgreSQL | Database name |
|
|
116
|
+
| `username` | `string` | MySQL, PostgreSQL | Database username |
|
|
117
|
+
| `password` | `string` | MySQL, PostgreSQL | Database password |
|
|
118
|
+
| `poolSize` | `number` | All | Connection pool size (default: 5) |
|
|
119
|
+
| `ssl` | `boolean` | All | Enable SSL/TLS (default: false) |
|
|
120
|
+
|
|
121
|
+
### DashboardConfig
|
|
122
|
+
|
|
123
|
+
| Option | Type | Default | Description |
|
|
124
|
+
|--------|------|---------|-------------|
|
|
125
|
+
| `auth` | `AuthConfig` | `undefined` | Authentication configuration |
|
|
126
|
+
| `mountPath` | `string` | `"/request-scope-api"` | Mount path for the dashboard |
|
|
127
|
+
|
|
128
|
+
### AuthConfig
|
|
129
|
+
|
|
130
|
+
| Option | Type | Description |
|
|
131
|
+
|--------|------|-------------|
|
|
132
|
+
| `username` | `string` | Basic auth username |
|
|
133
|
+
| `password` | `string` | Basic auth password |
|
|
134
|
+
|
|
135
|
+
## Database-Specific Setup
|
|
136
|
+
|
|
137
|
+
### MongoDB
|
|
138
|
+
|
|
139
|
+
- **URI Format**: `mongodb://[username:password@]host[:port][/database][?options]`
|
|
140
|
+
- **Required Fields**: `uri`
|
|
141
|
+
- **Optional**: `poolSize`, `ssl`
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
```javascript
|
|
145
|
+
storage: {
|
|
146
|
+
type: 'mongodb',
|
|
147
|
+
uri: 'mongodb://user:pass@localhost:27017/myapp?authSource=admin'
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### MySQL
|
|
152
|
+
|
|
153
|
+
- **Required Fields**: `host`, `port`, `database`, `username`, `password`
|
|
154
|
+
- **Optional**: `poolSize`, `ssl`
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
```javascript
|
|
158
|
+
storage: {
|
|
159
|
+
type: 'mysql',
|
|
160
|
+
host: 'localhost',
|
|
161
|
+
port: 3306,
|
|
162
|
+
database: 'myapp',
|
|
163
|
+
username: 'user',
|
|
164
|
+
password: 'pass',
|
|
165
|
+
poolSize: 10,
|
|
166
|
+
ssl: true
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### PostgreSQL
|
|
171
|
+
|
|
172
|
+
- **Required Fields**: `host`, `port`, `database`, `username`, `password`
|
|
173
|
+
- **Optional**: `poolSize`, `ssl`
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
```javascript
|
|
177
|
+
storage: {
|
|
178
|
+
type: 'postgresql',
|
|
179
|
+
host: 'localhost',
|
|
180
|
+
port: 5432,
|
|
181
|
+
database: 'myapp',
|
|
182
|
+
username: 'user',
|
|
183
|
+
password: 'pass',
|
|
184
|
+
poolSize: 10,
|
|
185
|
+
ssl: true
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Features
|
|
190
|
+
|
|
191
|
+
- **Zero Configuration**: Works out of the box with sensible defaults
|
|
192
|
+
- **Multiple Storage Backends**: MongoDB, MySQL, PostgreSQL support
|
|
193
|
+
- **Automatic Retention**: Configurable data retention with automatic cleanup
|
|
194
|
+
- **Sensitive Field Masking**: Built-in masking for passwords, tokens, and API keys
|
|
195
|
+
- **Request/Response Body Capture**: Captures full request and response bodies (with size limits)
|
|
196
|
+
- **Error Tracking**: Captures error messages and stack traces
|
|
197
|
+
- **Performance Metrics**: Records response time and body sizes
|
|
198
|
+
- **Dashboard UI**: Web interface for browsing and filtering requests
|
|
199
|
+
- **Basic Auth**: Optional authentication for dashboard access
|
|
200
|
+
|
|
201
|
+
## Sensitive Field Masking
|
|
202
|
+
|
|
203
|
+
request-scope-api automatically masks sensitive fields in request headers, request body, and response headers. Built-in sensitive field names (case-insensitive):
|
|
204
|
+
|
|
205
|
+
- `password`
|
|
206
|
+
- `token`
|
|
207
|
+
- `authorization`
|
|
208
|
+
- `apiKey`
|
|
209
|
+
- `secret`
|
|
210
|
+
|
|
211
|
+
Add custom fields to mask via the `maskFields` configuration:
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
app.use(request-scope-api({
|
|
215
|
+
storage: { /* ... */ },
|
|
216
|
+
maskFields: ['creditCard', 'ssn', 'apiKey']
|
|
217
|
+
}));
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Ignoring Paths
|
|
221
|
+
|
|
222
|
+
Skip capture for specific URL paths:
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
app.use(request-scope-api({
|
|
226
|
+
storage: { /* ... */ },
|
|
227
|
+
ignore: ['/health', '/metrics', '/favicon.ico']
|
|
228
|
+
}));
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Error Handling
|
|
232
|
+
|
|
233
|
+
To capture error data, use the provided error middleware:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
import request-scope-api, { errorHandler } from 'request-scope-api';
|
|
237
|
+
|
|
238
|
+
app.use(request-scope-api({ /* ... */ }));
|
|
239
|
+
app.use(errorHandler);
|
|
240
|
+
|
|
241
|
+
// Your routes
|
|
242
|
+
app.get('/api/users', async (req, res, next) => {
|
|
243
|
+
try {
|
|
244
|
+
// ...
|
|
245
|
+
} catch (err) {
|
|
246
|
+
next(err); // Error will be captured
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## API Endpoints
|
|
252
|
+
|
|
253
|
+
The dashboard provides the following API endpoints:
|
|
254
|
+
|
|
255
|
+
- `GET /api/records` - List records with filtering, sorting, and pagination
|
|
256
|
+
- `GET /api/records/:id` - Get a single record by ID
|
|
257
|
+
|
|
258
|
+
### Query Parameters (GET /api/records)
|
|
259
|
+
|
|
260
|
+
| Parameter | Type | Description |
|
|
261
|
+
|-----------|------|-------------|
|
|
262
|
+
| `search` | `string` | Case-insensitive substring match on URL |
|
|
263
|
+
| `startDate` | `string` | ISO 8601 timestamp (inclusive lower bound) |
|
|
264
|
+
| `endDate` | `string` | ISO 8601 timestamp (inclusive upper bound) |
|
|
265
|
+
| `statusCodeGroup` | `string` | Status code group: `2xx`, `3xx`, `4xx`, `5xx` |
|
|
266
|
+
| `statusCode` | `number` | Exact status code match |
|
|
267
|
+
| `method` | `string` | Exact HTTP method match (case-insensitive) |
|
|
268
|
+
| `sortBy` | `string` | Sort field: `timestamp`, `responseTime`, `statusCode` |
|
|
269
|
+
| `sortOrder` | `string` | Sort order: `asc`, `desc` |
|
|
270
|
+
| `page` | `number` | Page number (default: 1) |
|
|
271
|
+
| `pageSize` | `number` | Page size (default: 25) |
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* StorageAdapter interface — the contract all storage adapters must implement.
|
|
4
|
+
*
|
|
5
|
+
* Adapters should import from this file so they stay decoupled from the
|
|
6
|
+
* top-level types module while still satisfying the shared contract.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# 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,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MongoAdapter — MongoDB implementation of the StorageAdapter interface.
|
|
4
|
+
*
|
|
5
|
+
* Collection: `requestscope_records`
|
|
6
|
+
* `_id` is set to the record's UUID string for direct look-ups.
|
|
7
|
+
* A descending index on `timestamp` is created at initialization for sort
|
|
8
|
+
* performance and retention queries.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.MongoAdapter = void 0;
|
|
12
|
+
const mongodb_1 = require("mongodb");
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Helper: map statusCodeGroup → {$gte, $lt} range
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function statusGroupRange(group) {
|
|
17
|
+
const base = parseInt(group[0], 10) * 100;
|
|
18
|
+
return { $gte: base, $lt: base + 100 };
|
|
19
|
+
}
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Helper: convert a BSON document back to a RequestRecord
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function documentToRecord(doc) {
|
|
24
|
+
return {
|
|
25
|
+
id: doc._id,
|
|
26
|
+
method: doc.method,
|
|
27
|
+
url: doc.url,
|
|
28
|
+
route: doc.route,
|
|
29
|
+
queryParams: doc.queryParams,
|
|
30
|
+
pathParams: doc.pathParams,
|
|
31
|
+
requestHeaders: doc.requestHeaders,
|
|
32
|
+
requestBody: doc.requestBody,
|
|
33
|
+
requestBodySize: doc.requestBodySize,
|
|
34
|
+
clientIp: doc.clientIp,
|
|
35
|
+
userAgent: doc.userAgent,
|
|
36
|
+
statusCode: doc.statusCode,
|
|
37
|
+
responseHeaders: doc.responseHeaders,
|
|
38
|
+
responseBody: doc.responseBody,
|
|
39
|
+
responseBodySize: doc.responseBodySize,
|
|
40
|
+
responseTime: doc.responseTime,
|
|
41
|
+
errorMessage: doc.errorMessage,
|
|
42
|
+
errorStack: doc.errorStack,
|
|
43
|
+
errorStatusCode: doc.errorStatusCode,
|
|
44
|
+
timestamp: doc.timestamp,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// MongoAdapter
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
class MongoAdapter {
|
|
51
|
+
constructor(config) {
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.client = null;
|
|
54
|
+
this.collection = null;
|
|
55
|
+
}
|
|
56
|
+
// -------------------------------------------------------------------------
|
|
57
|
+
// initialize()
|
|
58
|
+
// -------------------------------------------------------------------------
|
|
59
|
+
async initialize() {
|
|
60
|
+
const uri = this.config.uri;
|
|
61
|
+
if (!uri) {
|
|
62
|
+
process.stderr.write('[MongoAdapter] storage.uri is required for MongoDB\n');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
this.client = new mongodb_1.MongoClient(uri);
|
|
67
|
+
await this.client.connect();
|
|
68
|
+
const db = this.client.db();
|
|
69
|
+
// Create the collection if it does not exist (MongoDB creates it
|
|
70
|
+
// implicitly on first write, but we also create the index here).
|
|
71
|
+
const collections = await db
|
|
72
|
+
.listCollections({ name: 'requestscope_records' }, { nameOnly: true })
|
|
73
|
+
.toArray();
|
|
74
|
+
if (collections.length === 0) {
|
|
75
|
+
await db.createCollection('requestscope_records');
|
|
76
|
+
}
|
|
77
|
+
this.collection = db.collection('requestscope_records');
|
|
78
|
+
// Descending index on timestamp for sort performance and retention queries.
|
|
79
|
+
await this.collection.createIndex({ timestamp: -1 }, { background: true });
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
process.stderr.write(`[MongoAdapter] Connection error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
83
|
+
// Do not rethrow — fault-isolated per design.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// -------------------------------------------------------------------------
|
|
87
|
+
// insert()
|
|
88
|
+
// -------------------------------------------------------------------------
|
|
89
|
+
async insert(records) {
|
|
90
|
+
if (!this.collection || records.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const docs = records.map((r) => ({
|
|
93
|
+
_id: r.id,
|
|
94
|
+
method: r.method,
|
|
95
|
+
url: r.url,
|
|
96
|
+
route: r.route,
|
|
97
|
+
queryParams: r.queryParams,
|
|
98
|
+
pathParams: r.pathParams,
|
|
99
|
+
requestHeaders: r.requestHeaders,
|
|
100
|
+
requestBody: r.requestBody,
|
|
101
|
+
requestBodySize: r.requestBodySize,
|
|
102
|
+
clientIp: r.clientIp,
|
|
103
|
+
userAgent: r.userAgent,
|
|
104
|
+
statusCode: r.statusCode,
|
|
105
|
+
responseHeaders: r.responseHeaders,
|
|
106
|
+
responseBody: r.responseBody,
|
|
107
|
+
responseBodySize: r.responseBodySize,
|
|
108
|
+
responseTime: r.responseTime,
|
|
109
|
+
errorMessage: r.errorMessage,
|
|
110
|
+
errorStack: r.errorStack,
|
|
111
|
+
errorStatusCode: r.errorStatusCode,
|
|
112
|
+
timestamp: r.timestamp,
|
|
113
|
+
}));
|
|
114
|
+
await this.collection.insertMany(docs, { ordered: false });
|
|
115
|
+
}
|
|
116
|
+
// -------------------------------------------------------------------------
|
|
117
|
+
// query()
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
async query(filters, pagination) {
|
|
120
|
+
if (!this.collection)
|
|
121
|
+
return { records: [], total: 0 };
|
|
122
|
+
// Build MongoDB filter document (AND logic — all conditions applied).
|
|
123
|
+
const mongoFilter = {};
|
|
124
|
+
if (filters.id !== undefined) {
|
|
125
|
+
mongoFilter._id = filters.id;
|
|
126
|
+
}
|
|
127
|
+
if (filters.search !== undefined && filters.search !== '') {
|
|
128
|
+
mongoFilter.url = { $regex: filters.search, $options: 'i' };
|
|
129
|
+
}
|
|
130
|
+
// Date range — lexicographic ISO 8601 comparison is valid for UTC strings.
|
|
131
|
+
if (filters.startDate !== undefined || filters.endDate !== undefined) {
|
|
132
|
+
const tsFilter = {};
|
|
133
|
+
if (filters.startDate !== undefined)
|
|
134
|
+
tsFilter.$gte = filters.startDate;
|
|
135
|
+
if (filters.endDate !== undefined)
|
|
136
|
+
tsFilter.$lte = filters.endDate;
|
|
137
|
+
mongoFilter.timestamp = tsFilter;
|
|
138
|
+
}
|
|
139
|
+
// statusCode (exact match) takes precedence over statusCodeGroup.
|
|
140
|
+
if (filters.statusCode !== undefined) {
|
|
141
|
+
mongoFilter.statusCode = filters.statusCode;
|
|
142
|
+
}
|
|
143
|
+
else if (filters.statusCodeGroup !== undefined) {
|
|
144
|
+
mongoFilter.statusCode = statusGroupRange(filters.statusCodeGroup);
|
|
145
|
+
}
|
|
146
|
+
if (filters.method !== undefined && filters.method !== '') {
|
|
147
|
+
mongoFilter.method = { $regex: filters.method, $options: 'i' };
|
|
148
|
+
}
|
|
149
|
+
// Sort mapping.
|
|
150
|
+
const sortField = filters.sortBy === 'responseTime'
|
|
151
|
+
? 'responseTime'
|
|
152
|
+
: filters.sortBy === 'statusCode'
|
|
153
|
+
? 'statusCode'
|
|
154
|
+
: 'timestamp';
|
|
155
|
+
const sortDirection = filters.sortOrder === 'asc' ? 1 : -1;
|
|
156
|
+
// Pagination.
|
|
157
|
+
const { page, pageSize } = pagination;
|
|
158
|
+
const skip = (page - 1) * pageSize;
|
|
159
|
+
// Execute count and find in parallel.
|
|
160
|
+
const [total, docs] = await Promise.all([
|
|
161
|
+
this.collection.countDocuments(mongoFilter),
|
|
162
|
+
this.collection
|
|
163
|
+
.find(mongoFilter)
|
|
164
|
+
.sort({ [sortField]: sortDirection })
|
|
165
|
+
.skip(skip)
|
|
166
|
+
.limit(pageSize)
|
|
167
|
+
.toArray(),
|
|
168
|
+
]);
|
|
169
|
+
return {
|
|
170
|
+
records: docs.map(documentToRecord),
|
|
171
|
+
total,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// -------------------------------------------------------------------------
|
|
175
|
+
// deleteOlderThan()
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
async deleteOlderThan(date) {
|
|
178
|
+
if (!this.collection)
|
|
179
|
+
return 0;
|
|
180
|
+
const cutoff = date.toISOString();
|
|
181
|
+
const result = await this.collection.deleteMany({
|
|
182
|
+
timestamp: { $lt: cutoff },
|
|
183
|
+
});
|
|
184
|
+
return result.deletedCount;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.MongoAdapter = MongoAdapter;
|
|
188
|
+
//# 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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"}
|