paygate-mcp 6.6.0 → 6.8.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 +61 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +261 -1
- package/dist/server.js.map +1 -1
- package/package.json +35 -8
package/README.md
CHANGED
|
@@ -93,6 +93,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
93
93
|
- **Credit History** — `GET /keys/credit-history?key=...` returns per-key credit mutation log — tracks initial allocation, topups, transfers (in/out), auto-topups, with type/limit/since filters, balance-before/after on every entry, newest-first ordering, capped at 100 entries per key
|
|
94
94
|
- **Spending Velocity** — `GET /keys/spending-velocity?key=...` returns credit burn rate and depletion forecast — credits/calls per hour/day, estimated depletion date, top tools by spend, configurable analysis window (1h–30d)
|
|
95
95
|
- **Key Comparison** — `GET /keys/compare?keys=pg_a,pg_b` returns side-by-side comparison of 2–10 keys — credits, usage, velocity, rate limits, status, metadata (namespace/group/tags) — with not-found key reporting
|
|
96
|
+
- **Key Health Score** — `GET /keys/health?key=...` returns composite health score (0–100) with weighted component breakdown: balance health (30%), quota utilization (25%), rate limit pressure (20%), error rate (25%) — status levels (healthy/good/caution/warning/critical), key issue detection (revoked/suspended/expired/expiring/zero credits), alias support
|
|
97
|
+
- **Maintenance Mode** — `POST /maintenance` enables/disables maintenance mode with custom message — `/mcp` returns 503 to clients while admin endpoints stay operational, `GET /maintenance` checks status, `GET /health` reflects maintenance state, full audit trail
|
|
96
98
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
97
99
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
98
100
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -1837,6 +1839,65 @@ curl "http://localhost:3402/keys/compare?keys=pg_abc,pg_xyz" -H "X-Admin-Key: YO
|
|
|
1837
1839
|
|
|
1838
1840
|
Keys not found are reported in a `notFound` array. Supports aliases. Maximum 10 keys per comparison.
|
|
1839
1841
|
|
|
1842
|
+
### Key Health Score
|
|
1843
|
+
|
|
1844
|
+
`GET /keys/health?key=...` returns a composite health score (0–100) with weighted component breakdown:
|
|
1845
|
+
|
|
1846
|
+
```bash
|
|
1847
|
+
curl "http://localhost:3402/keys/health?key=pg_abc" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
1848
|
+
```
|
|
1849
|
+
|
|
1850
|
+
**Response:**
|
|
1851
|
+
|
|
1852
|
+
```json
|
|
1853
|
+
{
|
|
1854
|
+
"key": "pg_abc123...",
|
|
1855
|
+
"name": "prod-agent",
|
|
1856
|
+
"score": 72,
|
|
1857
|
+
"status": "caution",
|
|
1858
|
+
"issues": ["Key expires within 7 days", "Credits depleting rapidly"],
|
|
1859
|
+
"components": {
|
|
1860
|
+
"balance": { "score": 40, "risk": "warning", "weight": 0.30 },
|
|
1861
|
+
"quota": { "score": 85, "risk": "good", "weight": 0.25 },
|
|
1862
|
+
"rateLimit": { "score": 100, "risk": "healthy", "weight": 0.20 },
|
|
1863
|
+
"errorRate": { "score": 75, "risk": "good", "weight": 0.25 }
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
```
|
|
1867
|
+
|
|
1868
|
+
Components: **balance** (30%, hours until credit depletion), **quota** (25%, max utilization across daily/monthly limits), **rateLimit** (20%, current window usage), **errorRate** (25%, denied/total ratio). Status: healthy (≥90), good (≥75), caution (≥50), warning (≥25), critical (<25). Issues detect: revoked, suspended, expired, expiring soon, zero credits, rapid depletion. Supports aliases.
|
|
1869
|
+
|
|
1870
|
+
### Maintenance Mode
|
|
1871
|
+
|
|
1872
|
+
Put your server into maintenance mode to gracefully reject client traffic while keeping admin endpoints operational:
|
|
1873
|
+
|
|
1874
|
+
```bash
|
|
1875
|
+
# Enable maintenance mode
|
|
1876
|
+
curl -X POST http://localhost:3402/maintenance \
|
|
1877
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
1878
|
+
-d '{"enabled": true, "message": "Upgrading to v7 — back in 10 minutes"}'
|
|
1879
|
+
|
|
1880
|
+
# Check maintenance status
|
|
1881
|
+
curl http://localhost:3402/maintenance -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
1882
|
+
|
|
1883
|
+
# Disable maintenance mode
|
|
1884
|
+
curl -X POST http://localhost:3402/maintenance \
|
|
1885
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
1886
|
+
-d '{"enabled": false}'
|
|
1887
|
+
```
|
|
1888
|
+
|
|
1889
|
+
**Response (enabled):**
|
|
1890
|
+
|
|
1891
|
+
```json
|
|
1892
|
+
{
|
|
1893
|
+
"enabled": true,
|
|
1894
|
+
"message": "Upgrading to v7 — back in 10 minutes",
|
|
1895
|
+
"since": "2025-03-15T14:30:00.000Z"
|
|
1896
|
+
}
|
|
1897
|
+
```
|
|
1898
|
+
|
|
1899
|
+
When enabled, all `/mcp` requests return **503** with the custom message. Admin endpoints (`/keys`, `/maintenance`, `/audit`, etc.) remain fully operational. `GET /health` returns `{"status": "maintenance"}`. Both enable and disable actions are recorded in the audit trail (`maintenance.enabled` / `maintenance.disabled`).
|
|
1900
|
+
|
|
1840
1901
|
### IP Allowlisting
|
|
1841
1902
|
|
|
1842
1903
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/audit.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* session lifecycle, and admin operations. Ring buffer with configurable
|
|
6
6
|
* max size and age-based retention. Zero external dependencies.
|
|
7
7
|
*/
|
|
8
|
-
export type AuditEventType = 'key.created' | 'key.revoked' | 'key.suspended' | 'key.resumed' | 'key.cloned' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'webhook.replayed' | 'webhook.test' | 'webhook.pause' | 'webhook.resume' | 'key.alias_set' | 'key.expiry_warning' | 'template.created' | 'template.updated' | 'template.deleted' | 'token.created' | 'token.revoked' | 'billing.refund' | 'key.auto_topup_configured' | 'key.auto_topped_up' | 'admin_key.created' | 'admin_key.revoked' | 'group.created' | 'group.updated' | 'group.deleted' | 'group.key_assigned' | 'group.key_removed' | 'key.credits_transferred' | 'keys.exported' | 'keys.imported' | 'webhook_filter.created' | 'webhook_filter.updated' | 'webhook_filter.deleted' | 'config.reloaded' | 'config.export';
|
|
8
|
+
export type AuditEventType = 'key.created' | 'key.revoked' | 'key.suspended' | 'key.resumed' | 'key.cloned' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'webhook.replayed' | 'webhook.test' | 'webhook.pause' | 'webhook.resume' | 'key.alias_set' | 'key.expiry_warning' | 'template.created' | 'template.updated' | 'template.deleted' | 'token.created' | 'token.revoked' | 'billing.refund' | 'key.auto_topup_configured' | 'key.auto_topped_up' | 'admin_key.created' | 'admin_key.revoked' | 'group.created' | 'group.updated' | 'group.deleted' | 'group.key_assigned' | 'group.key_removed' | 'key.credits_transferred' | 'keys.exported' | 'keys.imported' | 'webhook_filter.created' | 'webhook_filter.updated' | 'webhook_filter.deleted' | 'config.reloaded' | 'config.export' | 'maintenance.enabled' | 'maintenance.disabled';
|
|
9
9
|
export interface AuditEvent {
|
|
10
10
|
/** Monotonically increasing ID */
|
|
11
11
|
id: number;
|
package/dist/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,cAAc,GAEtB,aAAa,GACb,aAAa,GACb,eAAe,GACf,aAAa,GACb,YAAY,GACZ,aAAa,GACb,WAAW,GACX,iBAAiB,GACjB,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GAEnB,YAAY,GACZ,WAAW,GAEX,iBAAiB,GACjB,mBAAmB,GAEnB,yBAAyB,GACzB,oBAAoB,GACpB,qBAAqB,GAErB,cAAc,GACd,cAAc,GACd,cAAc,GACd,mBAAmB,GACnB,kBAAkB,GAElB,mBAAmB,GACnB,yBAAyB,GAEzB,6BAA6B,GAC7B,kBAAkB,GAClB,cAAc,GACd,eAAe,GACf,gBAAgB,GAEhB,eAAe,GAEf,oBAAoB,GAEpB,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAElB,eAAe,GACf,eAAe,GAEf,gBAAgB,GAEhB,2BAA2B,GAC3B,oBAAoB,GAEpB,mBAAmB,GACnB,mBAAmB,GAEnB,eAAe,GACf,eAAe,GACf,eAAe,GACf,oBAAoB,GACpB,mBAAmB,GAEnB,yBAAyB,GAEzB,eAAe,GACf,eAAe,GAEf,wBAAwB,GACxB,wBAAwB,GACxB,wBAAwB,GAExB,iBAAiB,GACjB,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -97,6 +97,12 @@ export declare class PayGateServer {
|
|
|
97
97
|
private readonly startedAt;
|
|
98
98
|
/** Whether the server is draining (shutting down gracefully) */
|
|
99
99
|
private draining;
|
|
100
|
+
/** Whether the server is in maintenance mode (rejects /mcp with 503) */
|
|
101
|
+
private maintenanceMode;
|
|
102
|
+
/** Custom message shown during maintenance mode */
|
|
103
|
+
private maintenanceMessage;
|
|
104
|
+
/** Timestamp when maintenance mode was enabled */
|
|
105
|
+
private maintenanceSince;
|
|
100
106
|
/** Number of in-flight /mcp requests */
|
|
101
107
|
private inflight;
|
|
102
108
|
/** Config file path for hot reload (null if not using config file) */
|
|
@@ -184,6 +190,7 @@ export declare class PayGateServer {
|
|
|
184
190
|
private handleCreditHistory;
|
|
185
191
|
private handleSpendingVelocity;
|
|
186
192
|
private handleKeyComparison;
|
|
193
|
+
private handleKeyHealth;
|
|
187
194
|
private handleSetAutoTopup;
|
|
188
195
|
private handleBalance;
|
|
189
196
|
private handleLimits;
|
|
@@ -211,6 +218,8 @@ export declare class PayGateServer {
|
|
|
211
218
|
private handleGetDeadLetters;
|
|
212
219
|
private handleClearDeadLetters;
|
|
213
220
|
private handleWebhookReplay;
|
|
221
|
+
private handleGetMaintenance;
|
|
222
|
+
private handleSetMaintenance;
|
|
214
223
|
private handleConfigReload;
|
|
215
224
|
private handleWebhookStats;
|
|
216
225
|
private handleWebhookLog;
|
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
|
@@ -215,6 +215,12 @@ class PayGateServer {
|
|
|
215
215
|
startedAt = Date.now();
|
|
216
216
|
/** Whether the server is draining (shutting down gracefully) */
|
|
217
217
|
draining = false;
|
|
218
|
+
/** Whether the server is in maintenance mode (rejects /mcp with 503) */
|
|
219
|
+
maintenanceMode = false;
|
|
220
|
+
/** Custom message shown during maintenance mode */
|
|
221
|
+
maintenanceMessage = 'Server is under maintenance';
|
|
222
|
+
/** Timestamp when maintenance mode was enabled */
|
|
223
|
+
maintenanceSince = null;
|
|
218
224
|
/** Number of in-flight /mcp requests */
|
|
219
225
|
inflight = 0;
|
|
220
226
|
/** Config file path for hot reload (null if not using config file) */
|
|
@@ -487,6 +493,11 @@ class PayGateServer {
|
|
|
487
493
|
res.end(JSON.stringify({ error: 'Server is shutting down' }));
|
|
488
494
|
return;
|
|
489
495
|
}
|
|
496
|
+
if (this.maintenanceMode) {
|
|
497
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
498
|
+
res.end(JSON.stringify({ error: this.maintenanceMessage }));
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
490
501
|
return this.handleMcp(req, res);
|
|
491
502
|
case '/health':
|
|
492
503
|
return this.handleHealth(req, res);
|
|
@@ -542,6 +553,8 @@ class PayGateServer {
|
|
|
542
553
|
return this.handleSpendingVelocity(req, res);
|
|
543
554
|
case '/keys/compare':
|
|
544
555
|
return this.handleKeyComparison(req, res);
|
|
556
|
+
case '/keys/health':
|
|
557
|
+
return this.handleKeyHealth(req, res);
|
|
545
558
|
case '/keys/templates':
|
|
546
559
|
if (req.method === 'GET')
|
|
547
560
|
return this.handleListTemplates(req, res);
|
|
@@ -675,6 +688,15 @@ class PayGateServer {
|
|
|
675
688
|
return this.handleAssignKeyToGroup(req, res);
|
|
676
689
|
case '/groups/remove':
|
|
677
690
|
return this.handleRemoveKeyFromGroup(req, res);
|
|
691
|
+
// ─── Maintenance mode ──────────────────────────────────────────
|
|
692
|
+
case '/maintenance':
|
|
693
|
+
if (req.method === 'GET')
|
|
694
|
+
return this.handleGetMaintenance(req, res);
|
|
695
|
+
if (req.method === 'POST')
|
|
696
|
+
return this.handleSetMaintenance(req, res);
|
|
697
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
698
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
699
|
+
return;
|
|
678
700
|
// ─── Config endpoints ──────────────────────────────────────────
|
|
679
701
|
case '/config/reload':
|
|
680
702
|
return this.handleConfigReload(req, res);
|
|
@@ -1090,6 +1112,7 @@ class PayGateServer {
|
|
|
1090
1112
|
creditHistory: 'GET /keys/credit-history?key=... — Per-key credit mutation history (requires X-Admin-Key)',
|
|
1091
1113
|
spendingVelocity: 'GET /keys/spending-velocity?key=... — Spending rate and depletion forecast (requires X-Admin-Key)',
|
|
1092
1114
|
keyComparison: 'GET /keys/compare?keys=pg_a,pg_b — Side-by-side key comparison (requires X-Admin-Key)',
|
|
1115
|
+
keyHealth: 'GET /keys/health?key=... — Composite health score (0-100) with component breakdown (requires X-Admin-Key)',
|
|
1093
1116
|
keyTemplates: 'GET /keys/templates — List key templates + POST to create/update (requires X-Admin-Key)',
|
|
1094
1117
|
deleteTemplate: 'POST /keys/templates/delete — Delete a key template (requires X-Admin-Key)',
|
|
1095
1118
|
pricing: 'GET /pricing — Tool pricing breakdown (public)',
|
|
@@ -1131,6 +1154,7 @@ class PayGateServer {
|
|
|
1131
1154
|
removeKeyFromGroup: 'POST /groups/remove — Remove a key from a group (requires X-Admin-Key)',
|
|
1132
1155
|
configReload: 'POST /config/reload — Hot reload config from file (requires X-Admin-Key)',
|
|
1133
1156
|
configExport: 'GET /config — Export running config with sensitive values masked (requires X-Admin-Key)',
|
|
1157
|
+
maintenance: 'GET /maintenance — Check status + POST to enable/disable maintenance mode (requires X-Admin-Key)',
|
|
1134
1158
|
...(this.oauth ? {
|
|
1135
1159
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1136
1160
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -1164,7 +1188,7 @@ class PayGateServer {
|
|
|
1164
1188
|
return;
|
|
1165
1189
|
}
|
|
1166
1190
|
const uptimeMs = Date.now() - this.startedAt;
|
|
1167
|
-
const status = this.draining ? 'draining' : 'healthy';
|
|
1191
|
+
const status = this.draining ? 'draining' : this.maintenanceMode ? 'maintenance' : 'healthy';
|
|
1168
1192
|
const httpStatus = this.draining ? 503 : 200;
|
|
1169
1193
|
const health = {
|
|
1170
1194
|
status,
|
|
@@ -2922,6 +2946,173 @@ class PayGateServer {
|
|
|
2922
2946
|
keys: comparisons,
|
|
2923
2947
|
}));
|
|
2924
2948
|
}
|
|
2949
|
+
// ─── /keys/health — Composite health score ──────────────────────────────────
|
|
2950
|
+
handleKeyHealth(req, res) {
|
|
2951
|
+
if (req.method !== 'GET') {
|
|
2952
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
2953
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
if (!this.checkAdmin(req, res))
|
|
2957
|
+
return;
|
|
2958
|
+
const urlParts = req.url?.split('?') || [];
|
|
2959
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
2960
|
+
const key = params.get('key');
|
|
2961
|
+
if (!key) {
|
|
2962
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
2963
|
+
res.end(JSON.stringify({ error: 'Missing required query parameter: key' }));
|
|
2964
|
+
return;
|
|
2965
|
+
}
|
|
2966
|
+
// Use resolveKeyRaw to include expired/suspended/revoked keys in health check
|
|
2967
|
+
const record = this.gate.store.resolveKeyRaw(key);
|
|
2968
|
+
if (!record) {
|
|
2969
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
2970
|
+
res.end(JSON.stringify({ error: 'Key not found' }));
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
const actualKey = record.key;
|
|
2974
|
+
// ── Component 1: Balance health (30% weight) ──
|
|
2975
|
+
// Based on spending velocity → how many hours of usage remaining
|
|
2976
|
+
const velocity = this.creditLedger.getSpendingVelocity(actualKey, record.credits, 24);
|
|
2977
|
+
let balanceScore = 100;
|
|
2978
|
+
let balanceRisk = 'healthy';
|
|
2979
|
+
if (velocity.creditsPerHour > 0) {
|
|
2980
|
+
const hoursLeft = velocity.estimatedHoursRemaining ?? Infinity;
|
|
2981
|
+
if (hoursLeft <= 1) {
|
|
2982
|
+
balanceScore = 0;
|
|
2983
|
+
balanceRisk = 'critical';
|
|
2984
|
+
}
|
|
2985
|
+
else if (hoursLeft <= 6) {
|
|
2986
|
+
balanceScore = 20;
|
|
2987
|
+
balanceRisk = 'critical';
|
|
2988
|
+
}
|
|
2989
|
+
else if (hoursLeft <= 24) {
|
|
2990
|
+
balanceScore = 40;
|
|
2991
|
+
balanceRisk = 'warning';
|
|
2992
|
+
}
|
|
2993
|
+
else if (hoursLeft <= 72) {
|
|
2994
|
+
balanceScore = 60;
|
|
2995
|
+
balanceRisk = 'caution';
|
|
2996
|
+
}
|
|
2997
|
+
else if (hoursLeft <= 168) {
|
|
2998
|
+
balanceScore = 80;
|
|
2999
|
+
balanceRisk = 'good';
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
else if (record.credits <= 0) {
|
|
3003
|
+
balanceScore = 0;
|
|
3004
|
+
balanceRisk = 'critical';
|
|
3005
|
+
}
|
|
3006
|
+
// ── Component 2: Quota utilization (25% weight) ──
|
|
3007
|
+
let quotaScore = 100;
|
|
3008
|
+
let quotaRisk = 'healthy';
|
|
3009
|
+
const quotaConfig = record.quota || this.config.globalQuota;
|
|
3010
|
+
if (quotaConfig) {
|
|
3011
|
+
// Reset counters if day/month rolled over before reading
|
|
3012
|
+
this.gate.quotaTracker.resetIfNeeded(record);
|
|
3013
|
+
let maxUtilization = 0;
|
|
3014
|
+
if (quotaConfig.dailyCallLimit && quotaConfig.dailyCallLimit > 0) {
|
|
3015
|
+
maxUtilization = Math.max(maxUtilization, record.quotaDailyCalls / quotaConfig.dailyCallLimit);
|
|
3016
|
+
}
|
|
3017
|
+
if (quotaConfig.monthlyCallLimit && quotaConfig.monthlyCallLimit > 0) {
|
|
3018
|
+
maxUtilization = Math.max(maxUtilization, record.quotaMonthlyCalls / quotaConfig.monthlyCallLimit);
|
|
3019
|
+
}
|
|
3020
|
+
if (quotaConfig.dailyCreditLimit && quotaConfig.dailyCreditLimit > 0) {
|
|
3021
|
+
maxUtilization = Math.max(maxUtilization, record.quotaDailyCredits / quotaConfig.dailyCreditLimit);
|
|
3022
|
+
}
|
|
3023
|
+
if (quotaConfig.monthlyCreditLimit && quotaConfig.monthlyCreditLimit > 0) {
|
|
3024
|
+
maxUtilization = Math.max(maxUtilization, record.quotaMonthlyCredits / quotaConfig.monthlyCreditLimit);
|
|
3025
|
+
}
|
|
3026
|
+
quotaScore = Math.max(0, Math.round((1 - maxUtilization) * 100));
|
|
3027
|
+
if (maxUtilization >= 1)
|
|
3028
|
+
quotaRisk = 'critical';
|
|
3029
|
+
else if (maxUtilization >= 0.9)
|
|
3030
|
+
quotaRisk = 'warning';
|
|
3031
|
+
else if (maxUtilization >= 0.75)
|
|
3032
|
+
quotaRisk = 'caution';
|
|
3033
|
+
else if (maxUtilization >= 0.5)
|
|
3034
|
+
quotaRisk = 'good';
|
|
3035
|
+
}
|
|
3036
|
+
// ── Component 3: Rate limit pressure (20% weight) ──
|
|
3037
|
+
const rateStatus = this.gate.rateLimiter.getStatus(actualKey);
|
|
3038
|
+
let rateLimitScore = 100;
|
|
3039
|
+
let rateLimitRisk = 'healthy';
|
|
3040
|
+
if (rateStatus.limit > 0) {
|
|
3041
|
+
const utilization = rateStatus.used / rateStatus.limit;
|
|
3042
|
+
rateLimitScore = Math.max(0, Math.round((1 - utilization) * 100));
|
|
3043
|
+
if (utilization >= 1)
|
|
3044
|
+
rateLimitRisk = 'critical';
|
|
3045
|
+
else if (utilization >= 0.9)
|
|
3046
|
+
rateLimitRisk = 'warning';
|
|
3047
|
+
else if (utilization >= 0.75)
|
|
3048
|
+
rateLimitRisk = 'caution';
|
|
3049
|
+
else if (utilization >= 0.5)
|
|
3050
|
+
rateLimitRisk = 'good';
|
|
3051
|
+
}
|
|
3052
|
+
// ── Component 4: Error rate (25% weight) ──
|
|
3053
|
+
const keyUsage = this.gate.meter.getKeyUsage(actualKey);
|
|
3054
|
+
let errorScore = 100;
|
|
3055
|
+
let errorRisk = 'healthy';
|
|
3056
|
+
if (keyUsage.totalCalls > 0) {
|
|
3057
|
+
const errorRate = keyUsage.totalDenied / keyUsage.totalCalls;
|
|
3058
|
+
errorScore = Math.max(0, Math.round((1 - errorRate) * 100));
|
|
3059
|
+
if (errorRate >= 0.5)
|
|
3060
|
+
errorRisk = 'critical';
|
|
3061
|
+
else if (errorRate >= 0.25)
|
|
3062
|
+
errorRisk = 'warning';
|
|
3063
|
+
else if (errorRate >= 0.1)
|
|
3064
|
+
errorRisk = 'caution';
|
|
3065
|
+
else if (errorRate > 0)
|
|
3066
|
+
errorRisk = 'good';
|
|
3067
|
+
}
|
|
3068
|
+
// ── Composite score (weighted) ──
|
|
3069
|
+
const overallScore = Math.round(balanceScore * 0.30 +
|
|
3070
|
+
quotaScore * 0.25 +
|
|
3071
|
+
rateLimitScore * 0.20 +
|
|
3072
|
+
errorScore * 0.25);
|
|
3073
|
+
let overallStatus = 'healthy';
|
|
3074
|
+
if (overallScore < 25)
|
|
3075
|
+
overallStatus = 'critical';
|
|
3076
|
+
else if (overallScore < 50)
|
|
3077
|
+
overallStatus = 'warning';
|
|
3078
|
+
else if (overallScore < 75)
|
|
3079
|
+
overallStatus = 'caution';
|
|
3080
|
+
else if (overallScore < 90)
|
|
3081
|
+
overallStatus = 'good';
|
|
3082
|
+
// Check key-level issues
|
|
3083
|
+
const issues = [];
|
|
3084
|
+
if (!record.active)
|
|
3085
|
+
issues.push('Key is revoked');
|
|
3086
|
+
if (record.suspended)
|
|
3087
|
+
issues.push('Key is suspended');
|
|
3088
|
+
if (record.expiresAt && new Date(record.expiresAt).getTime() < Date.now())
|
|
3089
|
+
issues.push('Key has expired');
|
|
3090
|
+
if (record.expiresAt) {
|
|
3091
|
+
const hoursToExpiry = (new Date(record.expiresAt).getTime() - Date.now()) / 3_600_000;
|
|
3092
|
+
if (hoursToExpiry > 0 && hoursToExpiry <= 24)
|
|
3093
|
+
issues.push('Key expires within 24 hours');
|
|
3094
|
+
else if (hoursToExpiry > 0 && hoursToExpiry <= 168)
|
|
3095
|
+
issues.push('Key expires within 7 days');
|
|
3096
|
+
}
|
|
3097
|
+
if (record.credits <= 0)
|
|
3098
|
+
issues.push('Zero credits remaining');
|
|
3099
|
+
if (balanceRisk === 'critical')
|
|
3100
|
+
issues.push('Credits depleting rapidly');
|
|
3101
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3102
|
+
res.end(JSON.stringify({
|
|
3103
|
+
key: actualKey.slice(0, 10) + '...',
|
|
3104
|
+
name: record.name,
|
|
3105
|
+
score: overallScore,
|
|
3106
|
+
status: overallStatus,
|
|
3107
|
+
issues: issues.length > 0 ? issues : undefined,
|
|
3108
|
+
components: {
|
|
3109
|
+
balance: { score: balanceScore, risk: balanceRisk, weight: 0.30 },
|
|
3110
|
+
quota: { score: quotaScore, risk: quotaRisk, weight: 0.25 },
|
|
3111
|
+
rateLimit: { score: rateLimitScore, risk: rateLimitRisk, weight: 0.20 },
|
|
3112
|
+
errorRate: { score: errorScore, risk: errorRisk, weight: 0.25 },
|
|
3113
|
+
},
|
|
3114
|
+
}));
|
|
3115
|
+
}
|
|
2925
3116
|
// ─── /keys/auto-topup — Configure auto-topup ────────────────────────────────
|
|
2926
3117
|
async handleSetAutoTopup(req, res) {
|
|
2927
3118
|
if (req.method !== 'POST') {
|
|
@@ -3617,6 +3808,75 @@ class PayGateServer {
|
|
|
3617
3808
|
message: `Replayed ${replayed} dead letter entries`,
|
|
3618
3809
|
}));
|
|
3619
3810
|
}
|
|
3811
|
+
// ─── /maintenance — Maintenance mode (503 on /mcp while admin stays up) ──
|
|
3812
|
+
handleGetMaintenance(req, res) {
|
|
3813
|
+
if (req.method !== 'GET') {
|
|
3814
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
3815
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
3816
|
+
return;
|
|
3817
|
+
}
|
|
3818
|
+
if (!this.checkAdmin(req, res))
|
|
3819
|
+
return;
|
|
3820
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3821
|
+
res.end(JSON.stringify({
|
|
3822
|
+
enabled: this.maintenanceMode,
|
|
3823
|
+
message: this.maintenanceMode ? this.maintenanceMessage : undefined,
|
|
3824
|
+
since: this.maintenanceSince,
|
|
3825
|
+
}));
|
|
3826
|
+
}
|
|
3827
|
+
handleSetMaintenance(req, res) {
|
|
3828
|
+
if (req.method !== 'POST') {
|
|
3829
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
3830
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3833
|
+
if (!this.checkAdmin(req, res))
|
|
3834
|
+
return;
|
|
3835
|
+
let body = '';
|
|
3836
|
+
req.on('data', (chunk) => { body += chunk; });
|
|
3837
|
+
req.on('end', () => {
|
|
3838
|
+
let params;
|
|
3839
|
+
try {
|
|
3840
|
+
params = JSON.parse(body);
|
|
3841
|
+
}
|
|
3842
|
+
catch {
|
|
3843
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3844
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
3845
|
+
return;
|
|
3846
|
+
}
|
|
3847
|
+
if (typeof params.enabled !== 'boolean') {
|
|
3848
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3849
|
+
res.end(JSON.stringify({ error: 'Missing required field: enabled (boolean)' }));
|
|
3850
|
+
return;
|
|
3851
|
+
}
|
|
3852
|
+
const wasEnabled = this.maintenanceMode;
|
|
3853
|
+
this.maintenanceMode = params.enabled;
|
|
3854
|
+
if (params.enabled) {
|
|
3855
|
+
this.maintenanceMessage = params.message || 'Server is under maintenance';
|
|
3856
|
+
this.maintenanceSince = new Date().toISOString();
|
|
3857
|
+
if (!wasEnabled) {
|
|
3858
|
+
this.audit.log('maintenance.enabled', 'admin', `Maintenance mode enabled: ${this.maintenanceMessage}`, {
|
|
3859
|
+
message: this.maintenanceMessage,
|
|
3860
|
+
});
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
else {
|
|
3864
|
+
if (wasEnabled) {
|
|
3865
|
+
this.audit.log('maintenance.disabled', 'admin', 'Maintenance mode disabled', {
|
|
3866
|
+
since: this.maintenanceSince,
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3869
|
+
this.maintenanceMessage = 'Server is under maintenance';
|
|
3870
|
+
this.maintenanceSince = null;
|
|
3871
|
+
}
|
|
3872
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3873
|
+
res.end(JSON.stringify({
|
|
3874
|
+
enabled: this.maintenanceMode,
|
|
3875
|
+
message: this.maintenanceMode ? this.maintenanceMessage : undefined,
|
|
3876
|
+
since: this.maintenanceSince,
|
|
3877
|
+
}));
|
|
3878
|
+
});
|
|
3879
|
+
}
|
|
3620
3880
|
// ─── /config/reload — Hot reload configuration from file ─────────────────
|
|
3621
3881
|
async handleConfigReload(req, res) {
|
|
3622
3882
|
if (req.method !== 'POST') {
|