paygate-mcp 7.4.0 → 7.6.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 +92 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +198 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
101
101
|
- **Key Activity Timeline** — `GET /keys/activity?key=...` returns a unified chronological feed of audit events and usage events for a specific key — newest first, optional `?since=` and `?limit=` filters, alias support
|
|
102
102
|
- **Credit Reservations** — `POST /keys/reserve` holds credits, `POST /keys/reserve/commit` deducts held credits, `POST /keys/reserve/release` frees the hold, `GET /keys/reserve` lists active reservations — prevents overcommit, configurable TTL (10s–1h), max 50 per key, auto-expiry, audit trail
|
|
103
103
|
- **Request Log** — `GET /requests` queryable log of every tool call with timing, credits charged, status (allowed/denied), deny reason, key, and request ID — filter by key/tool/status/since, pagination, summary statistics (totals + avg duration), 5000-entry ring buffer
|
|
104
|
+
- **Tool Stats** — `GET /tools/stats` per-tool analytics: call counts, success rate, avg/p95 latency, credits consumed, deny reason breakdown, top 10 consumers — optional `?tool=` for detailed single-tool view, `?since=` filter
|
|
105
|
+
- **Request Log Export** — `GET /requests/export` exports the full request log as JSON or CSV with Content-Disposition headers — filter by key/tool/status/since/until, combined time-window queries, no pagination limit
|
|
104
106
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
105
107
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
106
108
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2143,6 +2145,96 @@ curl "http://localhost:3402/requests?tool=my_tool&status=allowed&limit=10" \
|
|
|
2143
2145
|
|
|
2144
2146
|
5000-entry ring buffer. Summary statistics are computed on filtered results. Deny reasons: `insufficient_credits`, `rate_limited`, `invalid_api_key`, `key_suspended`, `api_key_expired`, `tool_not_allowed`, `quota_exceeded`.
|
|
2145
2147
|
|
|
2148
|
+
### Tool Stats
|
|
2149
|
+
|
|
2150
|
+
Per-tool analytics derived from the request log:
|
|
2151
|
+
|
|
2152
|
+
```bash
|
|
2153
|
+
# Overview of all tools
|
|
2154
|
+
curl "http://localhost:3402/tools/stats" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2155
|
+
|
|
2156
|
+
# Detailed stats for a specific tool
|
|
2157
|
+
curl "http://localhost:3402/tools/stats?tool=my_tool" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2158
|
+
|
|
2159
|
+
# Filter by time range
|
|
2160
|
+
curl "http://localhost:3402/tools/stats?since=2025-03-01T00:00:00Z" \
|
|
2161
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2162
|
+
```
|
|
2163
|
+
|
|
2164
|
+
**Response (overview):**
|
|
2165
|
+
|
|
2166
|
+
```json
|
|
2167
|
+
{
|
|
2168
|
+
"totalTools": 3,
|
|
2169
|
+
"totalCalls": 150,
|
|
2170
|
+
"tools": [
|
|
2171
|
+
{
|
|
2172
|
+
"tool": "my_tool",
|
|
2173
|
+
"totalCalls": 100,
|
|
2174
|
+
"allowed": 95,
|
|
2175
|
+
"denied": 5,
|
|
2176
|
+
"successRate": 95,
|
|
2177
|
+
"totalCredits": 475,
|
|
2178
|
+
"avgDurationMs": 42
|
|
2179
|
+
}
|
|
2180
|
+
]
|
|
2181
|
+
}
|
|
2182
|
+
```
|
|
2183
|
+
|
|
2184
|
+
**Response (detailed `?tool=my_tool`):**
|
|
2185
|
+
|
|
2186
|
+
```json
|
|
2187
|
+
{
|
|
2188
|
+
"tool": "my_tool",
|
|
2189
|
+
"totalCalls": 100,
|
|
2190
|
+
"allowed": 95,
|
|
2191
|
+
"denied": 5,
|
|
2192
|
+
"successRate": 95,
|
|
2193
|
+
"totalCredits": 475,
|
|
2194
|
+
"avgDurationMs": 42,
|
|
2195
|
+
"p95DurationMs": 120,
|
|
2196
|
+
"denyReasons": {
|
|
2197
|
+
"insufficient_credits": 3,
|
|
2198
|
+
"rate_limited": 2
|
|
2199
|
+
},
|
|
2200
|
+
"topConsumers": [
|
|
2201
|
+
{ "key": "pg_abc1...2345", "calls": 50, "credits": 250 },
|
|
2202
|
+
{ "key": "pg_xyz9...8765", "calls": 30, "credits": 150 }
|
|
2203
|
+
]
|
|
2204
|
+
}
|
|
2205
|
+
```
|
|
2206
|
+
|
|
2207
|
+
Top consumers limited to 10. Tools sorted by call count in overview. Data sourced from request log (5000-entry ring buffer).
|
|
2208
|
+
|
|
2209
|
+
### Request Log Export
|
|
2210
|
+
|
|
2211
|
+
Export the request log as JSON or CSV for offline analysis:
|
|
2212
|
+
|
|
2213
|
+
```bash
|
|
2214
|
+
# Export as JSON (default)
|
|
2215
|
+
curl "http://localhost:3402/requests/export" \
|
|
2216
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" -o paygate-requests.json
|
|
2217
|
+
|
|
2218
|
+
# Export as CSV
|
|
2219
|
+
curl "http://localhost:3402/requests/export?format=csv" \
|
|
2220
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" -o paygate-requests.csv
|
|
2221
|
+
|
|
2222
|
+
# Export with filters
|
|
2223
|
+
curl "http://localhost:3402/requests/export?tool=my_tool&status=denied&since=2025-03-01T00:00:00Z&until=2025-03-31T23:59:59Z" \
|
|
2224
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2225
|
+
```
|
|
2226
|
+
|
|
2227
|
+
| Parameter | Description |
|
|
2228
|
+
|-----------|-------------|
|
|
2229
|
+
| `format` | `json` (default) or `csv` |
|
|
2230
|
+
| `key` | Filter by API key (partial match) |
|
|
2231
|
+
| `tool` | Filter by tool name (exact match) |
|
|
2232
|
+
| `status` | `allowed` or `denied` |
|
|
2233
|
+
| `since` | ISO 8601 start timestamp |
|
|
2234
|
+
| `until` | ISO 8601 end timestamp |
|
|
2235
|
+
|
|
2236
|
+
Both formats include Content-Disposition headers for automatic file download. Unlike `/requests`, the export endpoint returns **all** matching entries (no pagination limit). CSV includes proper quoting for values with commas.
|
|
2237
|
+
|
|
2146
2238
|
### IP Allowlisting
|
|
2147
2239
|
|
|
2148
2240
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -315,6 +315,10 @@ export declare class PayGateServer {
|
|
|
315
315
|
*/
|
|
316
316
|
gracefulStop(timeoutMs?: number): Promise<void>;
|
|
317
317
|
private handleRequestLog;
|
|
318
|
+
private handleToolStats;
|
|
319
|
+
private handleRequestLogExport;
|
|
320
|
+
/** Calculate percentile from an array of numbers. */
|
|
321
|
+
private percentile;
|
|
318
322
|
}
|
|
319
323
|
export {};
|
|
320
324
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAgB,eAAe,EAA0B,MAAM,MAAM,CAAC;AAI7E,OAAO,EAAE,aAAa,EAAkB,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAU7F,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAqD,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAS,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA6B,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAqB,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKrD,0EAA0E;AAC1E,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAsBvF;AAyCD,yCAAyC;AACzC,KAAK,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;AAa5C,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,aAAa,CAAqC;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAQ;IAC5C,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;IAC5C,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,sCAAsC;IACtC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAS;IACzB,wEAAwE;IACxE,OAAO,CAAC,eAAe,CAAS;IAChC,mDAAmD;IACnD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,kDAAkD;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,gDAAgD;IAChD,OAAO,CAAC,iBAAiB,CAAqF;IAC9G,8CAA8C;IAC9C,OAAO,CAAC,wBAAwB,CAA+C;IAC/E,8BAA8B;IAC9B,OAAO,CAAC,gBAAgB,CAOhB;IACR,2CAA2C;IAC3C,OAAO,CAAC,aAAa,CAA+C;IACpE,4CAA4C;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,kCAAkC;IAClC,OAAO,CAAC,kBAAkB,CAOX;IACf,+CAA+C;IAC/C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,qDAAqD;IACrD,OAAO,CAAC,UAAU,CAUV;IACR,gCAAgC;IAChC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IAC7C,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAK;IACrB,sEAAsE;IACtE,OAAO,CAAC,UAAU,CAAuB;IAEzC,0DAA0D;IAC1D,OAAO,KAAK,OAAO,GAElB;gBAGC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,EAC1D,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,OAAO,CAAC,EAAE,mBAAmB,EAAE,EAC/B,QAAQ,CAAC,EAAE,MAAM;IAsMnB;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAK1B,KAAK,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YA0C5C,aAAa;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -678,6 +678,12 @@ class PayGateServer {
|
|
|
678
678
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
679
679
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
680
680
|
return;
|
|
681
|
+
case '/requests/export':
|
|
682
|
+
if (req.method === 'GET')
|
|
683
|
+
return this.handleRequestLogExport(req, res);
|
|
684
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
685
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
686
|
+
return;
|
|
681
687
|
// ─── Registry / Discovery endpoints ──────────────────────────────
|
|
682
688
|
case '/.well-known/mcp-payment':
|
|
683
689
|
return this.handlePaymentMetadata(req, res);
|
|
@@ -687,6 +693,12 @@ class PayGateServer {
|
|
|
687
693
|
return this.handleMetrics(req, res);
|
|
688
694
|
case '/analytics':
|
|
689
695
|
return this.handleAnalytics(req, res);
|
|
696
|
+
case '/tools/stats':
|
|
697
|
+
if (req.method === 'GET')
|
|
698
|
+
return this.handleToolStats(req, res);
|
|
699
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
700
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
701
|
+
return;
|
|
690
702
|
case '/alerts':
|
|
691
703
|
if (req.method === 'GET')
|
|
692
704
|
return this.handleGetAlerts(req, res);
|
|
@@ -1296,6 +1308,8 @@ class PayGateServer {
|
|
|
1296
1308
|
keyActivity: 'GET /keys/activity?key=... — Unified activity timeline for a key (requires X-Admin-Key)',
|
|
1297
1309
|
creditReservations: 'POST /keys/reserve to hold credits, POST /keys/reserve/commit to deduct, POST /keys/reserve/release to release, GET /keys/reserve to list (requires X-Admin-Key)',
|
|
1298
1310
|
requestLog: 'GET /requests — Queryable log of tool call requests with timing, credits, status (requires X-Admin-Key)',
|
|
1311
|
+
requestLogExport: 'GET /requests/export — Export request log as JSON or CSV with filters (requires X-Admin-Key)',
|
|
1312
|
+
toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
|
|
1299
1313
|
...(this.oauth ? {
|
|
1300
1314
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1301
1315
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -6340,6 +6354,190 @@ class PayGateServer {
|
|
|
6340
6354
|
requests: page,
|
|
6341
6355
|
}));
|
|
6342
6356
|
}
|
|
6357
|
+
// ─── /tools/stats — Per-tool analytics from request log ────────────────────
|
|
6358
|
+
handleToolStats(req, res) {
|
|
6359
|
+
if (!this.checkAdmin(req, res))
|
|
6360
|
+
return;
|
|
6361
|
+
const urlParts = req.url?.split('?') || [];
|
|
6362
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6363
|
+
const sinceFilter = params.get('since');
|
|
6364
|
+
const toolFilter = params.get('tool');
|
|
6365
|
+
let entries = this.requestLog;
|
|
6366
|
+
// Filter by since
|
|
6367
|
+
if (sinceFilter) {
|
|
6368
|
+
const sinceTime = new Date(sinceFilter).getTime();
|
|
6369
|
+
if (!isNaN(sinceTime)) {
|
|
6370
|
+
entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
// If specific tool requested, return detailed stats for just that tool
|
|
6374
|
+
if (toolFilter) {
|
|
6375
|
+
const toolEntries = entries.filter(e => e.tool === toolFilter);
|
|
6376
|
+
const allowed = toolEntries.filter(e => e.status === 'allowed');
|
|
6377
|
+
const denied = toolEntries.filter(e => e.status === 'denied');
|
|
6378
|
+
const totalCredits = toolEntries.reduce((sum, e) => sum + e.credits, 0);
|
|
6379
|
+
const avgDurationMs = toolEntries.length > 0
|
|
6380
|
+
? Math.round(toolEntries.reduce((sum, e) => sum + e.durationMs, 0) / toolEntries.length)
|
|
6381
|
+
: 0;
|
|
6382
|
+
const p95 = this.percentile(toolEntries.map(e => e.durationMs), 95);
|
|
6383
|
+
// Deny reason breakdown
|
|
6384
|
+
const denyReasons = {};
|
|
6385
|
+
for (const e of denied) {
|
|
6386
|
+
const reason = e.denyReason || 'unknown';
|
|
6387
|
+
denyReasons[reason] = (denyReasons[reason] || 0) + 1;
|
|
6388
|
+
}
|
|
6389
|
+
// Top consumers by call count
|
|
6390
|
+
const consumerCalls = {};
|
|
6391
|
+
const consumerCredits = {};
|
|
6392
|
+
for (const e of toolEntries) {
|
|
6393
|
+
consumerCalls[e.key] = (consumerCalls[e.key] || 0) + 1;
|
|
6394
|
+
consumerCredits[e.key] = (consumerCredits[e.key] || 0) + e.credits;
|
|
6395
|
+
}
|
|
6396
|
+
const topConsumers = Object.entries(consumerCalls)
|
|
6397
|
+
.sort((a, b) => b[1] - a[1])
|
|
6398
|
+
.slice(0, 10)
|
|
6399
|
+
.map(([key, calls]) => ({ key, calls, credits: consumerCredits[key] || 0 }));
|
|
6400
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6401
|
+
res.end(JSON.stringify({
|
|
6402
|
+
tool: toolFilter,
|
|
6403
|
+
totalCalls: toolEntries.length,
|
|
6404
|
+
allowed: allowed.length,
|
|
6405
|
+
denied: denied.length,
|
|
6406
|
+
successRate: toolEntries.length > 0
|
|
6407
|
+
? Math.round((allowed.length / toolEntries.length) * 10000) / 100
|
|
6408
|
+
: 0,
|
|
6409
|
+
totalCredits,
|
|
6410
|
+
avgDurationMs,
|
|
6411
|
+
p95DurationMs: p95,
|
|
6412
|
+
denyReasons,
|
|
6413
|
+
topConsumers,
|
|
6414
|
+
}));
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
// Aggregate stats per tool
|
|
6418
|
+
const toolMap = {};
|
|
6419
|
+
for (const e of entries) {
|
|
6420
|
+
if (!toolMap[e.tool]) {
|
|
6421
|
+
toolMap[e.tool] = { calls: 0, allowed: 0, denied: 0, credits: 0, totalDurationMs: 0 };
|
|
6422
|
+
}
|
|
6423
|
+
const t = toolMap[e.tool];
|
|
6424
|
+
t.calls++;
|
|
6425
|
+
if (e.status === 'allowed')
|
|
6426
|
+
t.allowed++;
|
|
6427
|
+
else
|
|
6428
|
+
t.denied++;
|
|
6429
|
+
t.credits += e.credits;
|
|
6430
|
+
t.totalDurationMs += e.durationMs;
|
|
6431
|
+
}
|
|
6432
|
+
const tools = Object.entries(toolMap)
|
|
6433
|
+
.map(([tool, stats]) => ({
|
|
6434
|
+
tool,
|
|
6435
|
+
totalCalls: stats.calls,
|
|
6436
|
+
allowed: stats.allowed,
|
|
6437
|
+
denied: stats.denied,
|
|
6438
|
+
successRate: stats.calls > 0
|
|
6439
|
+
? Math.round((stats.allowed / stats.calls) * 10000) / 100
|
|
6440
|
+
: 0,
|
|
6441
|
+
totalCredits: stats.credits,
|
|
6442
|
+
avgDurationMs: stats.calls > 0 ? Math.round(stats.totalDurationMs / stats.calls) : 0,
|
|
6443
|
+
}))
|
|
6444
|
+
.sort((a, b) => b.totalCalls - a.totalCalls);
|
|
6445
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6446
|
+
res.end(JSON.stringify({
|
|
6447
|
+
totalTools: tools.length,
|
|
6448
|
+
totalCalls: entries.length,
|
|
6449
|
+
tools,
|
|
6450
|
+
}));
|
|
6451
|
+
}
|
|
6452
|
+
// ─── /requests/export — Export request log as JSON or CSV ───────────────────
|
|
6453
|
+
handleRequestLogExport(req, res) {
|
|
6454
|
+
if (!this.checkAdmin(req, res))
|
|
6455
|
+
return;
|
|
6456
|
+
const urlParts = req.url?.split('?') || [];
|
|
6457
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6458
|
+
const format = params.get('format') || 'json';
|
|
6459
|
+
const keyFilter = params.get('key');
|
|
6460
|
+
const toolFilter = params.get('tool');
|
|
6461
|
+
const statusFilter = params.get('status');
|
|
6462
|
+
const sinceFilter = params.get('since');
|
|
6463
|
+
const untilFilter = params.get('until');
|
|
6464
|
+
let filtered = this.requestLog;
|
|
6465
|
+
// Filter by key (partial match on masked key)
|
|
6466
|
+
if (keyFilter) {
|
|
6467
|
+
const kf = keyFilter.toLowerCase();
|
|
6468
|
+
filtered = filtered.filter(e => e.key.toLowerCase().includes(kf));
|
|
6469
|
+
}
|
|
6470
|
+
// Filter by tool name (exact match)
|
|
6471
|
+
if (toolFilter) {
|
|
6472
|
+
filtered = filtered.filter(e => e.tool === toolFilter);
|
|
6473
|
+
}
|
|
6474
|
+
// Filter by status
|
|
6475
|
+
if (statusFilter === 'allowed' || statusFilter === 'denied') {
|
|
6476
|
+
filtered = filtered.filter(e => e.status === statusFilter);
|
|
6477
|
+
}
|
|
6478
|
+
// Filter by since timestamp
|
|
6479
|
+
if (sinceFilter) {
|
|
6480
|
+
const sinceTime = new Date(sinceFilter).getTime();
|
|
6481
|
+
if (!isNaN(sinceTime)) {
|
|
6482
|
+
filtered = filtered.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
// Filter by until timestamp
|
|
6486
|
+
if (untilFilter) {
|
|
6487
|
+
const untilTime = new Date(untilFilter).getTime();
|
|
6488
|
+
if (!isNaN(untilTime)) {
|
|
6489
|
+
filtered = filtered.filter(e => new Date(e.timestamp).getTime() <= untilTime);
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
// Return newest first
|
|
6493
|
+
const sorted = [...filtered].reverse();
|
|
6494
|
+
if (format === 'csv') {
|
|
6495
|
+
const header = 'id,timestamp,tool,key,status,credits,durationMs,denyReason,requestId';
|
|
6496
|
+
const rows = sorted.map(e => {
|
|
6497
|
+
const escapeCsv = (v) => {
|
|
6498
|
+
if (v === undefined || v === null)
|
|
6499
|
+
return '';
|
|
6500
|
+
const s = String(v);
|
|
6501
|
+
if (s.includes(',') || s.includes('"') || s.includes('\n')) {
|
|
6502
|
+
return '"' + s.replace(/"/g, '""') + '"';
|
|
6503
|
+
}
|
|
6504
|
+
return s;
|
|
6505
|
+
};
|
|
6506
|
+
return [
|
|
6507
|
+
e.id,
|
|
6508
|
+
e.timestamp,
|
|
6509
|
+
escapeCsv(e.tool),
|
|
6510
|
+
escapeCsv(e.key),
|
|
6511
|
+
e.status,
|
|
6512
|
+
e.credits,
|
|
6513
|
+
e.durationMs,
|
|
6514
|
+
escapeCsv(e.denyReason),
|
|
6515
|
+
escapeCsv(e.requestId),
|
|
6516
|
+
].join(',');
|
|
6517
|
+
});
|
|
6518
|
+
const csv = [header, ...rows].join('\n');
|
|
6519
|
+
res.writeHead(200, {
|
|
6520
|
+
'Content-Type': 'text/csv',
|
|
6521
|
+
'Content-Disposition': 'attachment; filename="paygate-requests.csv"',
|
|
6522
|
+
});
|
|
6523
|
+
res.end(csv);
|
|
6524
|
+
}
|
|
6525
|
+
else {
|
|
6526
|
+
res.writeHead(200, {
|
|
6527
|
+
'Content-Type': 'application/json',
|
|
6528
|
+
'Content-Disposition': 'attachment; filename="paygate-requests.json"',
|
|
6529
|
+
});
|
|
6530
|
+
res.end(JSON.stringify({ count: sorted.length, requests: sorted }, null, 2));
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
/** Calculate percentile from an array of numbers. */
|
|
6534
|
+
percentile(values, p) {
|
|
6535
|
+
if (values.length === 0)
|
|
6536
|
+
return 0;
|
|
6537
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
6538
|
+
const idx = Math.ceil((p / 100) * sorted.length) - 1;
|
|
6539
|
+
return sorted[Math.max(0, idx)];
|
|
6540
|
+
}
|
|
6343
6541
|
}
|
|
6344
6542
|
exports.PayGateServer = PayGateServer;
|
|
6345
6543
|
//# sourceMappingURL=server.js.map
|