paygate-mcp 8.4.0 → 8.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 +63 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +229 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
111
111
|
- **System Dashboard** — `GET /admin/dashboard` system-wide overview with key counts (active/suspended/revoked/expired), credit summary (allocated/spent/remaining), usage breakdown with deny reasons, top consumers, top tools, notification counts, and uptime
|
|
112
112
|
- **Key Lifecycle Report** — `GET /admin/lifecycle` aggregated lifecycle trends with daily creation/revocation/suspension buckets, average key lifetime, and at-risk keys (expiring, expired, zero credits)
|
|
113
113
|
- **Cost Analysis** — `GET /admin/costs` cost-centric view with per-tool and per-namespace cost breakdowns, hourly spending trends, top spenders, average cost per call, and namespace filtering
|
|
114
|
+
- **Rate Limit Analysis** — `GET /admin/rate-limits` rate limit utilization analysis with per-key and per-tool breakdown, denial trends, most throttled keys, and current window utilization
|
|
115
|
+
- **Quota Analysis** — `GET /admin/quotas` quota utilization analysis with per-key daily/monthly usage vs limits, per-tool denial breakdown, most constrained keys, and global/per-key quota source tracking
|
|
114
116
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
115
117
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
116
118
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2551,6 +2553,67 @@ curl "http://localhost:3402/admin/costs?since=2026-02-01" \
|
|
|
2551
2553
|
|
|
2552
2554
|
Returns per-tool cost breakdown (with average cost per call), per-namespace spending, hourly trend buckets (last 24 hours), and top 10 spenders ranked by credits consumed. Supports `?since=` and `?namespace=` query filters. Keys without an explicit namespace appear under `default`. Read-only.
|
|
2553
2555
|
|
|
2556
|
+
### Rate Limit Analysis
|
|
2557
|
+
|
|
2558
|
+
Analyze rate limit utilization across keys and tools:
|
|
2559
|
+
|
|
2560
|
+
```bash
|
|
2561
|
+
curl http://localhost:3402/admin/rate-limits \
|
|
2562
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2563
|
+
```
|
|
2564
|
+
|
|
2565
|
+
```json
|
|
2566
|
+
{
|
|
2567
|
+
"config": {
|
|
2568
|
+
"globalLimitPerMin": 60,
|
|
2569
|
+
"windowMs": 60000
|
|
2570
|
+
},
|
|
2571
|
+
"summary": {
|
|
2572
|
+
"totalCalls": 450,
|
|
2573
|
+
"totalRateLimited": 12,
|
|
2574
|
+
"rateLimitRate": 0.0267
|
|
2575
|
+
},
|
|
2576
|
+
"perKey": [
|
|
2577
|
+
{ "name": "ml-pipeline", "calls": 200, "rateLimited": 8, "currentWindowUsed": 45, "currentWindowRemaining": 15 },
|
|
2578
|
+
{ "name": "batch-worker", "calls": 150, "rateLimited": 4, "currentWindowUsed": 12, "currentWindowRemaining": 48 }
|
|
2579
|
+
],
|
|
2580
|
+
"perTool": [
|
|
2581
|
+
{ "tool": "generate_report", "calls": 180, "rateLimited": 10 },
|
|
2582
|
+
{ "tool": "query_data", "calls": 270, "rateLimited": 2 }
|
|
2583
|
+
],
|
|
2584
|
+
"hourlyTrends": [
|
|
2585
|
+
{ "hour": "2026-02-26T14", "calls": 52, "rateLimited": 3 },
|
|
2586
|
+
{ "hour": "2026-02-26T15", "calls": 48, "rateLimited": 1 }
|
|
2587
|
+
],
|
|
2588
|
+
"mostThrottled": [
|
|
2589
|
+
{ "name": "ml-pipeline", "rateLimited": 8, "calls": 200, "throttleRate": 0.04 }
|
|
2590
|
+
]
|
|
2591
|
+
}
|
|
2592
|
+
```
|
|
2593
|
+
|
|
2594
|
+
Returns rate limit configuration, denial summary with throttle rate, per-key breakdown with current sliding window utilization, per-tool denial counts, hourly denial trends (last 24 hours), and top 10 most throttled keys ranked by denial count. Handles unlimited rate limits (globalLimitPerMin: 0). Read-only.
|
|
2595
|
+
|
|
2596
|
+
### Quota Analysis
|
|
2597
|
+
|
|
2598
|
+
```bash
|
|
2599
|
+
curl http://localhost:3000/admin/quotas -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2600
|
+
```
|
|
2601
|
+
|
|
2602
|
+
```json
|
|
2603
|
+
{
|
|
2604
|
+
"config": { "globalQuota": { "dailyCallLimit": 100, "monthlyCallLimit": 1000, "dailyCreditLimit": 500, "monthlyCreditLimit": 5000 } },
|
|
2605
|
+
"summary": { "totalKeys": 5, "keysWithQuotas": 4, "totalQuotaDenials": 3, "quotaDenialRate": 0.02 },
|
|
2606
|
+
"perKey": [
|
|
2607
|
+
{ "name": "heavy-user", "dailyCalls": 95, "monthlyCalls": 450, "dailyCredits": 475, "monthlyCredits": 2250, "dailyCallLimit": 100, "monthlyCallLimit": 1000, "dailyCreditLimit": 500, "monthlyCreditLimit": 5000, "dailyCallUtilization": 0.95, "monthlyCallUtilization": 0.45, "source": "global" }
|
|
2608
|
+
],
|
|
2609
|
+
"perTool": [{ "tool": "summarize", "calls": 120, "quotaDenied": 2 }],
|
|
2610
|
+
"hourlyTrends": [{ "hour": "2025-01-15T14", "calls": 15, "quotaDenied": 1 }],
|
|
2611
|
+
"mostConstrained": [{ "name": "heavy-user", "dailyCalls": 95, "dailyCallLimit": 100, "dailyCallUtilization": 0.95, "monthlyCalls": 450, "monthlyCallLimit": 1000, "monthlyCallUtilization": 0.45 }]
|
|
2612
|
+
}
|
|
2613
|
+
```
|
|
2614
|
+
|
|
2615
|
+
Returns quota configuration (global or null), key counts with/without quotas, denial summary with denial rate, per-key daily/monthly call and credit usage vs limits with utilization percentages, quota source (per-key/global/none), per-tool quota denial counts, hourly denial trends (last 24 hours), and top 10 most constrained keys ranked by daily call utilization. Read-only.
|
|
2616
|
+
|
|
2554
2617
|
### IP Allowlisting
|
|
2555
2618
|
|
|
2556
2619
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -246,6 +246,8 @@ export declare class PayGateServer {
|
|
|
246
246
|
private handleSystemDashboard;
|
|
247
247
|
private handleKeyLifecycleReport;
|
|
248
248
|
private handleCostAnalysis;
|
|
249
|
+
private handleRateLimitAnalysis;
|
|
250
|
+
private handleQuotaAnalysis;
|
|
249
251
|
private handleGetNotes;
|
|
250
252
|
private handleAddNote;
|
|
251
253
|
private handleDeleteNote;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -822,6 +822,18 @@ class PayGateServer {
|
|
|
822
822
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
823
823
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
824
824
|
return;
|
|
825
|
+
case '/admin/rate-limits':
|
|
826
|
+
if (req.method === 'GET')
|
|
827
|
+
return this.handleRateLimitAnalysis(req, res);
|
|
828
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
829
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
830
|
+
return;
|
|
831
|
+
case '/admin/quotas':
|
|
832
|
+
if (req.method === 'GET')
|
|
833
|
+
return this.handleQuotaAnalysis(req, res);
|
|
834
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
835
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
836
|
+
return;
|
|
825
837
|
// ─── Plugin endpoints ──────────────────────────────────────────────
|
|
826
838
|
case '/plugins':
|
|
827
839
|
return this.handleListPlugins(req, res);
|
|
@@ -1366,6 +1378,8 @@ class PayGateServer {
|
|
|
1366
1378
|
systemDashboard: 'GET /admin/dashboard — System-wide overview with key stats, credit summary, usage breakdown, top consumers, and uptime (requires X-Admin-Key)',
|
|
1367
1379
|
keyLifecycle: 'GET /admin/lifecycle — Key lifecycle report with creation/revocation/expiry trends, average lifetime, and at-risk keys (requires X-Admin-Key)',
|
|
1368
1380
|
costAnalysis: 'GET /admin/costs — Cost analysis with per-tool, per-namespace breakdown, hourly trends, and top spenders (requires X-Admin-Key)',
|
|
1381
|
+
rateLimitAnalysis: 'GET /admin/rate-limits — Rate limit utilization analysis with per-key and per-tool breakdown, denial trends, and most throttled keys (requires X-Admin-Key)',
|
|
1382
|
+
quotaAnalysis: 'GET /admin/quotas — Quota utilization analysis with per-key and per-tool breakdown, denial trends, most constrained keys, and configuration display (requires X-Admin-Key)',
|
|
1369
1383
|
...(this.oauth ? {
|
|
1370
1384
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1371
1385
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -4783,6 +4797,221 @@ class PayGateServer {
|
|
|
4783
4797
|
topSpenders,
|
|
4784
4798
|
}));
|
|
4785
4799
|
}
|
|
4800
|
+
// ─── /admin/rate-limits — Rate limit utilization analysis ───────────────
|
|
4801
|
+
handleRateLimitAnalysis(req, res) {
|
|
4802
|
+
if (!this.checkAdmin(req, res))
|
|
4803
|
+
return;
|
|
4804
|
+
const rateLimiter = this.gate.rateLimiter;
|
|
4805
|
+
const globalLimit = rateLimiter.globalLimit;
|
|
4806
|
+
// Get all usage events to analyze rate limit denials
|
|
4807
|
+
const events = this.gate.meter.getEvents();
|
|
4808
|
+
// ── Summary ──
|
|
4809
|
+
let totalCalls = 0;
|
|
4810
|
+
let totalRateLimited = 0;
|
|
4811
|
+
for (const e of events) {
|
|
4812
|
+
totalCalls++;
|
|
4813
|
+
if (!e.allowed && e.denyReason?.includes('rate_limited')) {
|
|
4814
|
+
totalRateLimited++;
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
const rateLimitRate = totalCalls > 0
|
|
4818
|
+
? Math.round(totalRateLimited / totalCalls * 10000) / 10000
|
|
4819
|
+
: 0;
|
|
4820
|
+
// ── Per-key breakdown ──
|
|
4821
|
+
// Build a name→fullKey map from the key store for rate limiter status lookups
|
|
4822
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
4823
|
+
const nameToFullKey = new Map();
|
|
4824
|
+
for (const rec of allRecords) {
|
|
4825
|
+
nameToFullKey.set(rec.name, rec.key);
|
|
4826
|
+
}
|
|
4827
|
+
const keyMap = new Map();
|
|
4828
|
+
for (const e of events) {
|
|
4829
|
+
const name = e.keyName || e.apiKey.slice(0, 10);
|
|
4830
|
+
if (!keyMap.has(name))
|
|
4831
|
+
keyMap.set(name, { name, calls: 0, rateLimited: 0 });
|
|
4832
|
+
const k = keyMap.get(name);
|
|
4833
|
+
k.calls++;
|
|
4834
|
+
if (!e.allowed && e.denyReason?.includes('rate_limited')) {
|
|
4835
|
+
k.rateLimited++;
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
// Add current window utilization using full key from store
|
|
4839
|
+
const perKey = Array.from(keyMap.values()).map(k => {
|
|
4840
|
+
const fullKey = nameToFullKey.get(k.name) || '';
|
|
4841
|
+
const status = fullKey ? rateLimiter.getStatus(fullKey) : { used: 0, remaining: globalLimit > 0 ? globalLimit : Infinity };
|
|
4842
|
+
return {
|
|
4843
|
+
name: k.name,
|
|
4844
|
+
calls: k.calls,
|
|
4845
|
+
rateLimited: k.rateLimited,
|
|
4846
|
+
currentWindowUsed: status.used,
|
|
4847
|
+
currentWindowRemaining: status.remaining,
|
|
4848
|
+
};
|
|
4849
|
+
}).sort((a, b) => b.calls - a.calls);
|
|
4850
|
+
// ── Per-tool breakdown ──
|
|
4851
|
+
const toolMap = new Map();
|
|
4852
|
+
for (const e of events) {
|
|
4853
|
+
if (!toolMap.has(e.tool))
|
|
4854
|
+
toolMap.set(e.tool, { calls: 0, rateLimited: 0 });
|
|
4855
|
+
const t = toolMap.get(e.tool);
|
|
4856
|
+
t.calls++;
|
|
4857
|
+
if (!e.allowed && e.denyReason?.includes('rate_limited')) {
|
|
4858
|
+
t.rateLimited++;
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
const perTool = Array.from(toolMap.entries())
|
|
4862
|
+
.map(([tool, stats]) => ({ tool, ...stats }))
|
|
4863
|
+
.sort((a, b) => b.rateLimited - a.rateLimited);
|
|
4864
|
+
// ── Hourly trends (rate limit denials) ──
|
|
4865
|
+
const hourBuckets = new Map();
|
|
4866
|
+
for (const e of events) {
|
|
4867
|
+
const hour = e.timestamp.slice(0, 13); // YYYY-MM-DDTHH
|
|
4868
|
+
if (!hourBuckets.has(hour))
|
|
4869
|
+
hourBuckets.set(hour, { calls: 0, rateLimited: 0 });
|
|
4870
|
+
const h = hourBuckets.get(hour);
|
|
4871
|
+
h.calls++;
|
|
4872
|
+
if (!e.allowed && e.denyReason?.includes('rate_limited')) {
|
|
4873
|
+
h.rateLimited++;
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
const hourlyTrends = Array.from(hourBuckets.entries())
|
|
4877
|
+
.map(([hour, stats]) => ({ hour, ...stats }))
|
|
4878
|
+
.sort((a, b) => a.hour.localeCompare(b.hour))
|
|
4879
|
+
.slice(-24);
|
|
4880
|
+
// ── Most throttled keys (by rate limit denials) ──
|
|
4881
|
+
const mostThrottled = Array.from(keyMap.values())
|
|
4882
|
+
.filter(k => k.rateLimited > 0)
|
|
4883
|
+
.map(k => ({
|
|
4884
|
+
name: k.name,
|
|
4885
|
+
rateLimited: k.rateLimited,
|
|
4886
|
+
calls: k.calls,
|
|
4887
|
+
throttleRate: Math.round(k.rateLimited / k.calls * 10000) / 10000,
|
|
4888
|
+
}))
|
|
4889
|
+
.sort((a, b) => b.rateLimited - a.rateLimited)
|
|
4890
|
+
.slice(0, 10);
|
|
4891
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4892
|
+
res.end(JSON.stringify({
|
|
4893
|
+
config: {
|
|
4894
|
+
globalLimitPerMin: globalLimit,
|
|
4895
|
+
windowMs: 60000,
|
|
4896
|
+
},
|
|
4897
|
+
summary: {
|
|
4898
|
+
totalCalls,
|
|
4899
|
+
totalRateLimited,
|
|
4900
|
+
rateLimitRate,
|
|
4901
|
+
},
|
|
4902
|
+
perKey,
|
|
4903
|
+
perTool,
|
|
4904
|
+
hourlyTrends,
|
|
4905
|
+
mostThrottled,
|
|
4906
|
+
}));
|
|
4907
|
+
}
|
|
4908
|
+
// ─── /admin/quotas — Quota utilization analysis ───────────────────────────
|
|
4909
|
+
handleQuotaAnalysis(req, res) {
|
|
4910
|
+
if (!this.checkAdmin(req, res))
|
|
4911
|
+
return;
|
|
4912
|
+
const globalQuota = this.config.globalQuota || null;
|
|
4913
|
+
const allRecords = this.gate.store.getAllRecords();
|
|
4914
|
+
const events = this.gate.meter.getEvents();
|
|
4915
|
+
// ── Summary ──
|
|
4916
|
+
const totalKeys = allRecords.length;
|
|
4917
|
+
let keysWithQuotas = 0;
|
|
4918
|
+
for (const rec of allRecords) {
|
|
4919
|
+
if (rec.quota || globalQuota)
|
|
4920
|
+
keysWithQuotas++;
|
|
4921
|
+
}
|
|
4922
|
+
let totalCalls = 0;
|
|
4923
|
+
let totalQuotaDenials = 0;
|
|
4924
|
+
for (const e of events) {
|
|
4925
|
+
totalCalls++;
|
|
4926
|
+
if (!e.allowed && e.denyReason?.includes('quota_exceeded')) {
|
|
4927
|
+
totalQuotaDenials++;
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
const quotaDenialRate = totalCalls > 0
|
|
4931
|
+
? Math.round(totalQuotaDenials / totalCalls * 10000) / 10000
|
|
4932
|
+
: 0;
|
|
4933
|
+
// ── Per-key breakdown with current quota counters ──
|
|
4934
|
+
const perKey = allRecords.map(rec => {
|
|
4935
|
+
const quota = rec.quota || globalQuota;
|
|
4936
|
+
const dailyCallLimit = quota?.dailyCallLimit || 0;
|
|
4937
|
+
const monthlyCallLimit = quota?.monthlyCallLimit || 0;
|
|
4938
|
+
const dailyCreditLimit = quota?.dailyCreditLimit || 0;
|
|
4939
|
+
const monthlyCreditLimit = quota?.monthlyCreditLimit || 0;
|
|
4940
|
+
const dailyCallUtilization = dailyCallLimit > 0
|
|
4941
|
+
? Math.round(rec.quotaDailyCalls / dailyCallLimit * 10000) / 10000
|
|
4942
|
+
: 0;
|
|
4943
|
+
const monthlyCallUtilization = monthlyCallLimit > 0
|
|
4944
|
+
? Math.round(rec.quotaMonthlyCalls / monthlyCallLimit * 10000) / 10000
|
|
4945
|
+
: 0;
|
|
4946
|
+
return {
|
|
4947
|
+
name: rec.name,
|
|
4948
|
+
dailyCalls: rec.quotaDailyCalls,
|
|
4949
|
+
monthlyCalls: rec.quotaMonthlyCalls,
|
|
4950
|
+
dailyCredits: rec.quotaDailyCredits,
|
|
4951
|
+
monthlyCredits: rec.quotaMonthlyCredits,
|
|
4952
|
+
dailyCallLimit,
|
|
4953
|
+
monthlyCallLimit,
|
|
4954
|
+
dailyCreditLimit,
|
|
4955
|
+
monthlyCreditLimit,
|
|
4956
|
+
dailyCallUtilization,
|
|
4957
|
+
monthlyCallUtilization,
|
|
4958
|
+
source: rec.quota ? 'per-key' : (globalQuota ? 'global' : 'none'),
|
|
4959
|
+
};
|
|
4960
|
+
}).sort((a, b) => b.dailyCallUtilization - a.dailyCallUtilization);
|
|
4961
|
+
// ── Per-tool breakdown ──
|
|
4962
|
+
const toolMap = new Map();
|
|
4963
|
+
for (const e of events) {
|
|
4964
|
+
if (!toolMap.has(e.tool))
|
|
4965
|
+
toolMap.set(e.tool, { calls: 0, quotaDenied: 0 });
|
|
4966
|
+
const t = toolMap.get(e.tool);
|
|
4967
|
+
t.calls++;
|
|
4968
|
+
if (!e.allowed && e.denyReason?.includes('quota_exceeded')) {
|
|
4969
|
+
t.quotaDenied++;
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
const perTool = Array.from(toolMap.entries())
|
|
4973
|
+
.map(([tool, stats]) => ({ tool, ...stats }))
|
|
4974
|
+
.sort((a, b) => b.quotaDenied - a.quotaDenied);
|
|
4975
|
+
// ── Hourly trends (quota denials) ──
|
|
4976
|
+
const hourBuckets = new Map();
|
|
4977
|
+
for (const e of events) {
|
|
4978
|
+
const hour = e.timestamp.slice(0, 13); // YYYY-MM-DDTHH
|
|
4979
|
+
if (!hourBuckets.has(hour))
|
|
4980
|
+
hourBuckets.set(hour, { calls: 0, quotaDenied: 0 });
|
|
4981
|
+
const h = hourBuckets.get(hour);
|
|
4982
|
+
h.calls++;
|
|
4983
|
+
if (!e.allowed && e.denyReason?.includes('quota_exceeded')) {
|
|
4984
|
+
h.quotaDenied++;
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
const hourlyTrends = Array.from(hourBuckets.entries())
|
|
4988
|
+
.map(([hour, stats]) => ({ hour, ...stats }))
|
|
4989
|
+
.sort((a, b) => a.hour.localeCompare(b.hour))
|
|
4990
|
+
.slice(-24);
|
|
4991
|
+
// ── Most constrained keys (by daily call utilization) ──
|
|
4992
|
+
const mostConstrained = perKey
|
|
4993
|
+
.filter(k => k.dailyCallLimit > 0 || k.monthlyCallLimit > 0)
|
|
4994
|
+
.map(k => ({
|
|
4995
|
+
name: k.name,
|
|
4996
|
+
dailyCalls: k.dailyCalls,
|
|
4997
|
+
dailyCallLimit: k.dailyCallLimit,
|
|
4998
|
+
dailyCallUtilization: k.dailyCallUtilization,
|
|
4999
|
+
monthlyCalls: k.monthlyCalls,
|
|
5000
|
+
monthlyCallLimit: k.monthlyCallLimit,
|
|
5001
|
+
monthlyCallUtilization: k.monthlyCallUtilization,
|
|
5002
|
+
}))
|
|
5003
|
+
.sort((a, b) => b.dailyCallUtilization - a.dailyCallUtilization)
|
|
5004
|
+
.slice(0, 10);
|
|
5005
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5006
|
+
res.end(JSON.stringify({
|
|
5007
|
+
config: { globalQuota },
|
|
5008
|
+
summary: { totalKeys, keysWithQuotas, totalQuotaDenials, quotaDenialRate },
|
|
5009
|
+
perKey,
|
|
5010
|
+
perTool,
|
|
5011
|
+
hourlyTrends,
|
|
5012
|
+
mostConstrained,
|
|
5013
|
+
}));
|
|
5014
|
+
}
|
|
4786
5015
|
// ─── /keys/notes — Timestamped notes on API keys ─────────────────────────
|
|
4787
5016
|
handleGetNotes(req, res) {
|
|
4788
5017
|
if (!this.checkAdmin(req, res))
|