paygate-mcp 3.3.0 → 3.4.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 +100 -0
- package/dist/gate.d.ts +3 -0
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +35 -4
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +172 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +244 -0
- package/dist/plugin.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +77 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Plugin System — Extensible middleware hooks for custom billing, auth,
|
|
4
|
+
* pricing, logging, and request handling logic.
|
|
5
|
+
*
|
|
6
|
+
* Plugins register named hook functions that run at key points in the
|
|
7
|
+
* request lifecycle:
|
|
8
|
+
*
|
|
9
|
+
* Gate hooks (synchronous — hot path):
|
|
10
|
+
* - beforeGate: Short-circuit gate evaluation (deny/allow early)
|
|
11
|
+
* - afterGate: Modify gate decision after evaluation
|
|
12
|
+
* - onDeny: Notification when a tool call is denied
|
|
13
|
+
* - transformPrice: Override tool pricing dynamically
|
|
14
|
+
*
|
|
15
|
+
* Tool hooks (async — wraps proxy forward):
|
|
16
|
+
* - beforeToolCall: Modify request before forwarding to MCP server
|
|
17
|
+
* - afterToolCall: Modify response after receiving from MCP server
|
|
18
|
+
*
|
|
19
|
+
* HTTP hooks (async):
|
|
20
|
+
* - onRequest: Handle custom HTTP endpoints before normal routing
|
|
21
|
+
*
|
|
22
|
+
* Lifecycle hooks (async):
|
|
23
|
+
* - onStart: Called when the server starts
|
|
24
|
+
* - onStop: Called when the server stops
|
|
25
|
+
*
|
|
26
|
+
* Plugins run in registration order. Gate hooks are sync to avoid adding
|
|
27
|
+
* latency to the critical billing path. Tool/HTTP/lifecycle hooks may be async.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.PluginManager = void 0;
|
|
31
|
+
// ─── Plugin Manager ───────────────────────────────────────────────────────
|
|
32
|
+
class PluginManager {
|
|
33
|
+
plugins = [];
|
|
34
|
+
/** Register a plugin. Plugins run in registration order. */
|
|
35
|
+
register(plugin) {
|
|
36
|
+
if (!plugin.name) {
|
|
37
|
+
throw new Error('Plugin must have a name');
|
|
38
|
+
}
|
|
39
|
+
if (this.plugins.some(p => p.name === plugin.name)) {
|
|
40
|
+
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
41
|
+
}
|
|
42
|
+
this.plugins.push(plugin);
|
|
43
|
+
}
|
|
44
|
+
/** Unregister a plugin by name. Returns true if found and removed. */
|
|
45
|
+
unregister(name) {
|
|
46
|
+
const idx = this.plugins.findIndex(p => p.name === name);
|
|
47
|
+
if (idx === -1)
|
|
48
|
+
return false;
|
|
49
|
+
this.plugins.splice(idx, 1);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
/** List all registered plugins with their hooks. */
|
|
53
|
+
list() {
|
|
54
|
+
return this.plugins.map(p => ({
|
|
55
|
+
name: p.name,
|
|
56
|
+
version: p.version,
|
|
57
|
+
hooks: this.getPluginHooks(p),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/** Number of registered plugins. */
|
|
61
|
+
get count() {
|
|
62
|
+
return this.plugins.length;
|
|
63
|
+
}
|
|
64
|
+
// ─── Gate Hook Execution (synchronous) ────────────────────────────
|
|
65
|
+
/**
|
|
66
|
+
* Execute beforeGate hooks in order.
|
|
67
|
+
* First non-null result short-circuits the gate.
|
|
68
|
+
*/
|
|
69
|
+
executeBeforeGate(context) {
|
|
70
|
+
for (const plugin of this.plugins) {
|
|
71
|
+
if (plugin.beforeGate) {
|
|
72
|
+
try {
|
|
73
|
+
const result = plugin.beforeGate(context);
|
|
74
|
+
if (result !== null && result !== undefined)
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Plugin errors don't break the gate
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Execute afterGate hooks in order.
|
|
86
|
+
* Each plugin can modify the decision; changes cascade through.
|
|
87
|
+
*/
|
|
88
|
+
executeAfterGate(context, decision) {
|
|
89
|
+
let current = decision;
|
|
90
|
+
for (const plugin of this.plugins) {
|
|
91
|
+
if (plugin.afterGate) {
|
|
92
|
+
try {
|
|
93
|
+
current = plugin.afterGate(context, current);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Plugin errors don't break the gate
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return current;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Execute onDeny hooks (fire-and-forget).
|
|
104
|
+
*/
|
|
105
|
+
executeOnDeny(context, reason) {
|
|
106
|
+
for (const plugin of this.plugins) {
|
|
107
|
+
if (plugin.onDeny) {
|
|
108
|
+
try {
|
|
109
|
+
plugin.onDeny(context, reason);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Plugin errors don't break the flow
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Execute transformPrice hooks. First non-null result wins.
|
|
119
|
+
*/
|
|
120
|
+
executeTransformPrice(toolName, basePrice, args) {
|
|
121
|
+
for (const plugin of this.plugins) {
|
|
122
|
+
if (plugin.transformPrice) {
|
|
123
|
+
try {
|
|
124
|
+
const result = plugin.transformPrice(toolName, basePrice, args);
|
|
125
|
+
if (result !== null && result !== undefined && typeof result === 'number') {
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Plugin errors → fall through to default
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return basePrice;
|
|
135
|
+
}
|
|
136
|
+
// ─── Tool Hook Execution (async) ──────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Execute beforeToolCall hooks in order.
|
|
139
|
+
* Each can modify the request; changes cascade.
|
|
140
|
+
*/
|
|
141
|
+
async executeBeforeToolCall(context) {
|
|
142
|
+
let req = context.request;
|
|
143
|
+
for (const plugin of this.plugins) {
|
|
144
|
+
if (plugin.beforeToolCall) {
|
|
145
|
+
try {
|
|
146
|
+
req = await plugin.beforeToolCall({ ...context, request: req });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Plugin errors → skip this hook
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return req;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Execute afterToolCall hooks in order.
|
|
157
|
+
* Each can modify the response; changes cascade.
|
|
158
|
+
*/
|
|
159
|
+
async executeAfterToolCall(context, response) {
|
|
160
|
+
let res = response;
|
|
161
|
+
for (const plugin of this.plugins) {
|
|
162
|
+
if (plugin.afterToolCall) {
|
|
163
|
+
try {
|
|
164
|
+
res = await plugin.afterToolCall(context, res);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Plugin errors → skip this hook
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return res;
|
|
172
|
+
}
|
|
173
|
+
// ─── HTTP Hook Execution (async) ──────────────────────────────────
|
|
174
|
+
/**
|
|
175
|
+
* Execute onRequest hooks. First handler returning true wins.
|
|
176
|
+
*/
|
|
177
|
+
async executeOnRequest(req, res) {
|
|
178
|
+
for (const plugin of this.plugins) {
|
|
179
|
+
if (plugin.onRequest) {
|
|
180
|
+
try {
|
|
181
|
+
const handled = await plugin.onRequest(req, res);
|
|
182
|
+
if (handled)
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Plugin errors → continue to next plugin
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// ─── Lifecycle Hook Execution (async) ─────────────────────────────
|
|
193
|
+
/**
|
|
194
|
+
* Execute onStart hooks for all plugins.
|
|
195
|
+
*/
|
|
196
|
+
async executeStart() {
|
|
197
|
+
for (const plugin of this.plugins) {
|
|
198
|
+
if (plugin.onStart) {
|
|
199
|
+
await plugin.onStart();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Execute onStop hooks for all plugins (in reverse order for clean teardown).
|
|
205
|
+
*/
|
|
206
|
+
async executeStop() {
|
|
207
|
+
for (let i = this.plugins.length - 1; i >= 0; i--) {
|
|
208
|
+
const plugin = this.plugins[i];
|
|
209
|
+
if (plugin.onStop) {
|
|
210
|
+
try {
|
|
211
|
+
await plugin.onStop();
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Best-effort teardown
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// ─── Private ──────────────────────────────────────────────────────
|
|
220
|
+
getPluginHooks(plugin) {
|
|
221
|
+
const hooks = [];
|
|
222
|
+
if (plugin.onStart)
|
|
223
|
+
hooks.push('onStart');
|
|
224
|
+
if (plugin.onStop)
|
|
225
|
+
hooks.push('onStop');
|
|
226
|
+
if (plugin.beforeGate)
|
|
227
|
+
hooks.push('beforeGate');
|
|
228
|
+
if (plugin.afterGate)
|
|
229
|
+
hooks.push('afterGate');
|
|
230
|
+
if (plugin.onDeny)
|
|
231
|
+
hooks.push('onDeny');
|
|
232
|
+
if (plugin.transformPrice)
|
|
233
|
+
hooks.push('transformPrice');
|
|
234
|
+
if (plugin.beforeToolCall)
|
|
235
|
+
hooks.push('beforeToolCall');
|
|
236
|
+
if (plugin.afterToolCall)
|
|
237
|
+
hooks.push('afterToolCall');
|
|
238
|
+
if (plugin.onRequest)
|
|
239
|
+
hooks.push('onRequest');
|
|
240
|
+
return hooks;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
exports.PluginManager = PluginManager;
|
|
244
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { TeamManager } from './teams';
|
|
|
28
28
|
import { RedisSync } from './redis-sync';
|
|
29
29
|
import { ScopedTokenManager } from './tokens';
|
|
30
30
|
import { AdminKeyManager } from './admin-keys';
|
|
31
|
+
import { PluginManager, PayGatePlugin } from './plugin';
|
|
31
32
|
/** Union type for both proxy backends */
|
|
32
33
|
type ProxyBackend = McpProxy | HttpMcpProxy;
|
|
33
34
|
export declare class PayGateServer {
|
|
@@ -63,6 +64,8 @@ export declare class PayGateServer {
|
|
|
63
64
|
readonly redisSync: RedisSync | null;
|
|
64
65
|
/** Scoped token manager for short-lived delegated tokens */
|
|
65
66
|
readonly tokens: ScopedTokenManager;
|
|
67
|
+
/** Plugin manager for extensible middleware hooks */
|
|
68
|
+
readonly plugins: PluginManager;
|
|
66
69
|
/** Server start time (ms since epoch) */
|
|
67
70
|
private readonly startedAt;
|
|
68
71
|
/** Whether the server is draining (shutting down gracefully) */
|
|
@@ -74,6 +77,19 @@ export declare class PayGateServer {
|
|
|
74
77
|
constructor(config: Partial<PayGateConfig> & {
|
|
75
78
|
serverCommand: string;
|
|
76
79
|
}, adminKey?: string, statePath?: string, remoteUrl?: string, stripeWebhookSecret?: string, servers?: ServerBackendConfig[], redisUrl?: string);
|
|
80
|
+
/**
|
|
81
|
+
* Register a plugin for extensible middleware hooks.
|
|
82
|
+
* Plugins run in registration order.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* server.use({
|
|
87
|
+
* name: 'custom-pricing',
|
|
88
|
+
* transformPrice: (tool, base) => tool.startsWith('premium_') ? base * 5 : null,
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
use(plugin: PayGatePlugin): this;
|
|
77
93
|
start(): Promise<{
|
|
78
94
|
port: number;
|
|
79
95
|
adminKey: string;
|
|
@@ -156,6 +172,7 @@ export declare class PayGateServer {
|
|
|
156
172
|
private handleCreateToken;
|
|
157
173
|
private handleRevokeToken;
|
|
158
174
|
private handleListRevokedTokens;
|
|
175
|
+
private handleListPlugins;
|
|
159
176
|
private handleListAdminKeys;
|
|
160
177
|
private handleCreateAdminKey;
|
|
161
178
|
private handleRevokeAdminKey;
|
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;AAKH,OAAO,EAAE,aAAa,EAAkB,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAS7F,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,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;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -47,6 +47,7 @@ const redis_client_1 = require("./redis-client");
|
|
|
47
47
|
const redis_sync_1 = require("./redis-sync");
|
|
48
48
|
const tokens_1 = require("./tokens");
|
|
49
49
|
const admin_keys_1 = require("./admin-keys");
|
|
50
|
+
const plugin_1 = require("./plugin");
|
|
50
51
|
/** Max request body size: 1MB */
|
|
51
52
|
const MAX_BODY_SIZE = 1_048_576;
|
|
52
53
|
class PayGateServer {
|
|
@@ -82,6 +83,8 @@ class PayGateServer {
|
|
|
82
83
|
redisSync = null;
|
|
83
84
|
/** Scoped token manager for short-lived delegated tokens */
|
|
84
85
|
tokens;
|
|
86
|
+
/** Plugin manager for extensible middleware hooks */
|
|
87
|
+
plugins;
|
|
85
88
|
/** Server start time (ms since epoch) */
|
|
86
89
|
startedAt = Date.now();
|
|
87
90
|
/** Whether the server is draining (shutting down gracefully) */
|
|
@@ -186,6 +189,12 @@ class PayGateServer {
|
|
|
186
189
|
this.redisSync.atomicTopup(apiKey, amount).catch(() => { });
|
|
187
190
|
}
|
|
188
191
|
};
|
|
192
|
+
// Plugin manager for extensible middleware hooks
|
|
193
|
+
this.plugins = new plugin_1.PluginManager();
|
|
194
|
+
this.gate.pluginManager = this.plugins;
|
|
195
|
+
this.metrics.registerGauge('paygate_plugins_total', 'Number of registered plugins', () => {
|
|
196
|
+
return this.plugins.count;
|
|
197
|
+
});
|
|
189
198
|
// Scoped token manager (uses bootstrap admin key as signing secret, padded to min length)
|
|
190
199
|
const tokenSecret = this.bootstrapAdminKey.length >= 8
|
|
191
200
|
? this.bootstrapAdminKey
|
|
@@ -212,6 +221,22 @@ class PayGateServer {
|
|
|
212
221
|
};
|
|
213
222
|
}
|
|
214
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Register a plugin for extensible middleware hooks.
|
|
226
|
+
* Plugins run in registration order.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* server.use({
|
|
231
|
+
* name: 'custom-pricing',
|
|
232
|
+
* transformPrice: (tool, base) => tool.startsWith('premium_') ? base * 5 : null,
|
|
233
|
+
* });
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
use(plugin) {
|
|
237
|
+
this.plugins.register(plugin);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
215
240
|
async start() {
|
|
216
241
|
// Initialize Redis sync before starting (loads state from Redis + starts pub/sub)
|
|
217
242
|
if (this.redisSync) {
|
|
@@ -220,6 +245,10 @@ class PayGateServer {
|
|
|
220
245
|
console.log('[paygate] Redis distributed state enabled');
|
|
221
246
|
}
|
|
222
247
|
await this.handler.start();
|
|
248
|
+
// Plugin lifecycle: onStart
|
|
249
|
+
if (this.plugins.count > 0) {
|
|
250
|
+
await this.plugins.executeStart();
|
|
251
|
+
}
|
|
223
252
|
return new Promise((resolve, reject) => {
|
|
224
253
|
this.server = (0, http_1.createServer)(async (req, res) => {
|
|
225
254
|
try {
|
|
@@ -249,6 +278,12 @@ class PayGateServer {
|
|
|
249
278
|
res.end();
|
|
250
279
|
return;
|
|
251
280
|
}
|
|
281
|
+
// Plugin: onRequest — let plugins handle custom endpoints before routing
|
|
282
|
+
if (this.plugins.count > 0) {
|
|
283
|
+
const handled = await this.plugins.executeOnRequest(req, res);
|
|
284
|
+
if (handled)
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
252
287
|
const url = req.url?.split('?')[0] || '/';
|
|
253
288
|
switch (url) {
|
|
254
289
|
case '/mcp':
|
|
@@ -366,6 +401,9 @@ class PayGateServer {
|
|
|
366
401
|
break;
|
|
367
402
|
case '/admin/keys/revoke':
|
|
368
403
|
return this.handleRevokeAdminKey(req, res);
|
|
404
|
+
// ─── Plugin endpoints ──────────────────────────────────────────────
|
|
405
|
+
case '/plugins':
|
|
406
|
+
return this.handleListPlugins(req, res);
|
|
369
407
|
// ─── OAuth 2.1 endpoints ─────────────────────────────────────────
|
|
370
408
|
case '/.well-known/oauth-authorization-server':
|
|
371
409
|
return this.handleOAuthMetadata(req, res);
|
|
@@ -488,7 +526,22 @@ class PayGateServer {
|
|
|
488
526
|
}
|
|
489
527
|
return;
|
|
490
528
|
}
|
|
491
|
-
|
|
529
|
+
// Plugin: beforeToolCall — let plugins modify the request before forwarding
|
|
530
|
+
let pluginRequest = request;
|
|
531
|
+
if (this.plugins.count > 0 && request.method === 'tools/call') {
|
|
532
|
+
const toolName = request.params?.name || '';
|
|
533
|
+
const toolArgs = request.params?.arguments;
|
|
534
|
+
const pluginCtx = { apiKey, toolName, toolArgs, request };
|
|
535
|
+
pluginRequest = await this.plugins.executeBeforeToolCall(pluginCtx);
|
|
536
|
+
}
|
|
537
|
+
let response = await this.handler.handleRequest(pluginRequest, apiKey, clientIp, scopedTokenTools);
|
|
538
|
+
// Plugin: afterToolCall — let plugins modify the response
|
|
539
|
+
if (this.plugins.count > 0 && request.method === 'tools/call') {
|
|
540
|
+
const toolName = request.params?.name || '';
|
|
541
|
+
const toolArgs = request.params?.arguments;
|
|
542
|
+
const pluginCtx = { apiKey, toolName, toolArgs, request };
|
|
543
|
+
response = await this.plugins.executeAfterToolCall(pluginCtx, response);
|
|
544
|
+
}
|
|
492
545
|
// Inject pricing metadata into tools/list responses
|
|
493
546
|
if (request.method === 'tools/list' && response.result) {
|
|
494
547
|
const result = response.result;
|
|
@@ -760,6 +813,7 @@ class PayGateServer {
|
|
|
760
813
|
adminKeys: 'GET /admin/keys — List admin keys (requires X-Admin-Key, super_admin)',
|
|
761
814
|
createAdminKey: 'POST /admin/keys — Create admin key with role (requires X-Admin-Key, super_admin)',
|
|
762
815
|
revokeAdminKey: 'POST /admin/keys/revoke — Revoke an admin key (requires X-Admin-Key, super_admin)',
|
|
816
|
+
plugins: 'GET /plugins — List registered plugins (requires X-Admin-Key)',
|
|
763
817
|
...(this.oauth ? {
|
|
764
818
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
765
819
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -2484,6 +2538,20 @@ class PayGateServer {
|
|
|
2484
2538
|
}));
|
|
2485
2539
|
}
|
|
2486
2540
|
// ─── /admin/keys — Admin key management ────────────────────────────────────
|
|
2541
|
+
// ─── GET /plugins — List registered plugins ─────────────────────────────
|
|
2542
|
+
handleListPlugins(req, res) {
|
|
2543
|
+
if (req.method !== 'GET') {
|
|
2544
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
2545
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
if (!this.checkAdmin(req, res))
|
|
2549
|
+
return;
|
|
2550
|
+
const plugins = this.plugins.list();
|
|
2551
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2552
|
+
res.end(JSON.stringify({ count: plugins.length, plugins }));
|
|
2553
|
+
}
|
|
2554
|
+
// ─── Admin Key Management ────────────────────────────────────────────────
|
|
2487
2555
|
handleListAdminKeys(req, res) {
|
|
2488
2556
|
if (req.method !== 'GET') {
|
|
2489
2557
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
@@ -2634,6 +2702,10 @@ class PayGateServer {
|
|
|
2634
2702
|
});
|
|
2635
2703
|
}
|
|
2636
2704
|
async stop() {
|
|
2705
|
+
// Plugin lifecycle: onStop (reverse order)
|
|
2706
|
+
if (this.plugins.count > 0) {
|
|
2707
|
+
await this.plugins.executeStop();
|
|
2708
|
+
}
|
|
2637
2709
|
await this.handler.stop();
|
|
2638
2710
|
this.gate.destroy();
|
|
2639
2711
|
this.oauth?.destroy();
|
|
@@ -2684,6 +2756,10 @@ class PayGateServer {
|
|
|
2684
2756
|
check();
|
|
2685
2757
|
});
|
|
2686
2758
|
console.log('[paygate] Drained — tearing down resources');
|
|
2759
|
+
// Plugin lifecycle: onStop (reverse order)
|
|
2760
|
+
if (this.plugins.count > 0) {
|
|
2761
|
+
await this.plugins.executeStop();
|
|
2762
|
+
}
|
|
2687
2763
|
// Tear down resources (but skip server.close, already closed above)
|
|
2688
2764
|
await this.handler.stop();
|
|
2689
2765
|
this.gate.destroy();
|