@push.rocks/smartproxy 19.5.26 → 19.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/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +79 -0
- package/dist_ts/proxies/smart-proxy/metrics-collector.js +235 -0
- package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +55 -0
- package/dist_ts/proxies/smart-proxy/models/metrics-types.js +2 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +9 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +12 -2
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +17 -1
- package/package.json +18 -8
- package/readme.md +118 -0
- package/readme.metrics.md +591 -0
- package/ts/plugins.ts +2 -0
- package/ts/proxies/smart-proxy/metrics-collector.ts +285 -0
- package/ts/proxies/smart-proxy/models/index.ts +1 -0
- package/ts/proxies/smart-proxy/models/metrics-types.ts +54 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +13 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +27 -2
package/ts/plugins.ts
CHANGED
|
@@ -30,6 +30,7 @@ import * as smartacmeHandlers from '@push.rocks/smartacme/dist_ts/handlers/index
|
|
|
30
30
|
import * as smartlog from '@push.rocks/smartlog';
|
|
31
31
|
import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
|
|
32
32
|
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
33
|
+
import * as smartrx from '@push.rocks/smartrx';
|
|
33
34
|
|
|
34
35
|
export {
|
|
35
36
|
lik,
|
|
@@ -45,6 +46,7 @@ export {
|
|
|
45
46
|
smartlog,
|
|
46
47
|
smartlogDestinationLocal,
|
|
47
48
|
taskbuffer,
|
|
49
|
+
smartrx,
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
// third party scope
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { SmartProxy } from './smart-proxy.js';
|
|
3
|
+
import type { IProxyStats, IProxyStatsExtended } from './models/metrics-types.js';
|
|
4
|
+
import { logger } from '../../core/utils/logger.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Collects and computes metrics for SmartProxy on-demand
|
|
8
|
+
*/
|
|
9
|
+
export class MetricsCollector implements IProxyStatsExtended {
|
|
10
|
+
// RPS tracking (the only state we need to maintain)
|
|
11
|
+
private requestTimestamps: number[] = [];
|
|
12
|
+
private readonly RPS_WINDOW_SIZE = 60000; // 1 minute window
|
|
13
|
+
|
|
14
|
+
// Optional caching for performance
|
|
15
|
+
private cachedMetrics: {
|
|
16
|
+
timestamp: number;
|
|
17
|
+
connectionsByRoute?: Map<string, number>;
|
|
18
|
+
connectionsByIP?: Map<string, number>;
|
|
19
|
+
} = { timestamp: 0 };
|
|
20
|
+
|
|
21
|
+
private readonly CACHE_TTL = 1000; // 1 second cache
|
|
22
|
+
|
|
23
|
+
// RxJS subscription for connection events
|
|
24
|
+
private connectionSubscription?: plugins.smartrx.rxjs.Subscription;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private smartProxy: SmartProxy
|
|
28
|
+
) {
|
|
29
|
+
// Subscription will be set up in start() method
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the current number of active connections
|
|
34
|
+
*/
|
|
35
|
+
public getActiveConnections(): number {
|
|
36
|
+
return this.smartProxy.connectionManager.getConnectionCount();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get connection counts grouped by route name
|
|
41
|
+
*/
|
|
42
|
+
public getConnectionsByRoute(): Map<string, number> {
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
|
|
45
|
+
// Return cached value if fresh
|
|
46
|
+
if (this.cachedMetrics.connectionsByRoute &&
|
|
47
|
+
now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
|
|
48
|
+
return new Map(this.cachedMetrics.connectionsByRoute);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Compute fresh value
|
|
52
|
+
const routeCounts = new Map<string, number>();
|
|
53
|
+
const connections = this.smartProxy.connectionManager.getConnections();
|
|
54
|
+
|
|
55
|
+
if (this.smartProxy.settings?.enableDetailedLogging) {
|
|
56
|
+
logger.log('debug', `MetricsCollector: Computing route connections`, {
|
|
57
|
+
totalConnections: connections.size,
|
|
58
|
+
component: 'metrics'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const [_, record] of connections) {
|
|
63
|
+
// Try different ways to get the route name
|
|
64
|
+
const routeName = (record as any).routeName ||
|
|
65
|
+
record.routeConfig?.name ||
|
|
66
|
+
(record.routeConfig as any)?.routeName ||
|
|
67
|
+
'unknown';
|
|
68
|
+
|
|
69
|
+
if (this.smartProxy.settings?.enableDetailedLogging) {
|
|
70
|
+
logger.log('debug', `MetricsCollector: Connection route info`, {
|
|
71
|
+
connectionId: record.id,
|
|
72
|
+
routeName,
|
|
73
|
+
hasRouteConfig: !!record.routeConfig,
|
|
74
|
+
routeConfigName: record.routeConfig?.name,
|
|
75
|
+
routeConfigKeys: record.routeConfig ? Object.keys(record.routeConfig) : [],
|
|
76
|
+
component: 'metrics'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const current = routeCounts.get(routeName) || 0;
|
|
81
|
+
routeCounts.set(routeName, current + 1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Cache and return
|
|
85
|
+
this.cachedMetrics.connectionsByRoute = routeCounts;
|
|
86
|
+
this.cachedMetrics.timestamp = now;
|
|
87
|
+
return new Map(routeCounts);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get connection counts grouped by IP address
|
|
92
|
+
*/
|
|
93
|
+
public getConnectionsByIP(): Map<string, number> {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
|
|
96
|
+
// Return cached value if fresh
|
|
97
|
+
if (this.cachedMetrics.connectionsByIP &&
|
|
98
|
+
now - this.cachedMetrics.timestamp < this.CACHE_TTL) {
|
|
99
|
+
return new Map(this.cachedMetrics.connectionsByIP);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Compute fresh value
|
|
103
|
+
const ipCounts = new Map<string, number>();
|
|
104
|
+
for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
|
|
105
|
+
const ip = record.remoteIP;
|
|
106
|
+
const current = ipCounts.get(ip) || 0;
|
|
107
|
+
ipCounts.set(ip, current + 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Cache and return
|
|
111
|
+
this.cachedMetrics.connectionsByIP = ipCounts;
|
|
112
|
+
this.cachedMetrics.timestamp = now;
|
|
113
|
+
return new Map(ipCounts);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get the total number of connections since proxy start
|
|
118
|
+
*/
|
|
119
|
+
public getTotalConnections(): number {
|
|
120
|
+
// Get from termination stats
|
|
121
|
+
const stats = this.smartProxy.connectionManager.getTerminationStats();
|
|
122
|
+
let total = this.smartProxy.connectionManager.getConnectionCount(); // Add active connections
|
|
123
|
+
|
|
124
|
+
// Add all terminated connections
|
|
125
|
+
for (const reason in stats.incoming) {
|
|
126
|
+
total += stats.incoming[reason];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return total;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get the current requests per second rate
|
|
134
|
+
*/
|
|
135
|
+
public getRequestsPerSecond(): number {
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
const windowStart = now - this.RPS_WINDOW_SIZE;
|
|
138
|
+
|
|
139
|
+
// Clean old timestamps
|
|
140
|
+
this.requestTimestamps = this.requestTimestamps.filter(ts => ts > windowStart);
|
|
141
|
+
|
|
142
|
+
// Calculate RPS based on window
|
|
143
|
+
const requestsInWindow = this.requestTimestamps.length;
|
|
144
|
+
return requestsInWindow / (this.RPS_WINDOW_SIZE / 1000);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Record a new request for RPS tracking
|
|
149
|
+
*/
|
|
150
|
+
public recordRequest(): void {
|
|
151
|
+
this.requestTimestamps.push(Date.now());
|
|
152
|
+
|
|
153
|
+
// Prevent unbounded growth
|
|
154
|
+
if (this.requestTimestamps.length > 10000) {
|
|
155
|
+
this.cleanupOldRequests();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get total throughput (bytes transferred)
|
|
161
|
+
*/
|
|
162
|
+
public getThroughput(): { bytesIn: number; bytesOut: number } {
|
|
163
|
+
let bytesIn = 0;
|
|
164
|
+
let bytesOut = 0;
|
|
165
|
+
|
|
166
|
+
// Sum bytes from all active connections
|
|
167
|
+
for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
|
|
168
|
+
bytesIn += record.bytesReceived;
|
|
169
|
+
bytesOut += record.bytesSent;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { bytesIn, bytesOut };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get throughput rate (bytes per second) for last minute
|
|
177
|
+
*/
|
|
178
|
+
public getThroughputRate(): { bytesInPerSec: number; bytesOutPerSec: number } {
|
|
179
|
+
const now = Date.now();
|
|
180
|
+
let recentBytesIn = 0;
|
|
181
|
+
let recentBytesOut = 0;
|
|
182
|
+
|
|
183
|
+
// Calculate bytes transferred in last minute from active connections
|
|
184
|
+
for (const [_, record] of this.smartProxy.connectionManager.getConnections()) {
|
|
185
|
+
const connectionAge = now - record.incomingStartTime;
|
|
186
|
+
if (connectionAge < 60000) { // Connection started within last minute
|
|
187
|
+
recentBytesIn += record.bytesReceived;
|
|
188
|
+
recentBytesOut += record.bytesSent;
|
|
189
|
+
} else {
|
|
190
|
+
// For older connections, estimate rate based on average
|
|
191
|
+
const rate = connectionAge / 60000;
|
|
192
|
+
recentBytesIn += record.bytesReceived / rate;
|
|
193
|
+
recentBytesOut += record.bytesSent / rate;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
bytesInPerSec: Math.round(recentBytesIn / 60),
|
|
199
|
+
bytesOutPerSec: Math.round(recentBytesOut / 60)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get top IPs by connection count
|
|
205
|
+
*/
|
|
206
|
+
public getTopIPs(limit: number = 10): Array<{ ip: string; connections: number }> {
|
|
207
|
+
const ipCounts = this.getConnectionsByIP();
|
|
208
|
+
const sorted = Array.from(ipCounts.entries())
|
|
209
|
+
.sort((a, b) => b[1] - a[1])
|
|
210
|
+
.slice(0, limit)
|
|
211
|
+
.map(([ip, connections]) => ({ ip, connections }));
|
|
212
|
+
|
|
213
|
+
return sorted;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if an IP has reached the connection limit
|
|
218
|
+
*/
|
|
219
|
+
public isIPBlocked(ip: string, maxConnectionsPerIP: number): boolean {
|
|
220
|
+
const ipCounts = this.getConnectionsByIP();
|
|
221
|
+
const currentConnections = ipCounts.get(ip) || 0;
|
|
222
|
+
return currentConnections >= maxConnectionsPerIP;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Clean up old request timestamps
|
|
227
|
+
*/
|
|
228
|
+
private cleanupOldRequests(): void {
|
|
229
|
+
const cutoff = Date.now() - this.RPS_WINDOW_SIZE;
|
|
230
|
+
this.requestTimestamps = this.requestTimestamps.filter(ts => ts > cutoff);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Start the metrics collector and set up subscriptions
|
|
235
|
+
*/
|
|
236
|
+
public start(): void {
|
|
237
|
+
if (!this.smartProxy.routeConnectionHandler) {
|
|
238
|
+
throw new Error('MetricsCollector: RouteConnectionHandler not available');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Subscribe to the newConnectionSubject from RouteConnectionHandler
|
|
242
|
+
this.connectionSubscription = this.smartProxy.routeConnectionHandler.newConnectionSubject.subscribe({
|
|
243
|
+
next: (record) => {
|
|
244
|
+
this.recordRequest();
|
|
245
|
+
|
|
246
|
+
// Optional: Log connection details
|
|
247
|
+
if (this.smartProxy.settings?.enableDetailedLogging) {
|
|
248
|
+
logger.log('debug', `MetricsCollector: New connection recorded`, {
|
|
249
|
+
connectionId: record.id,
|
|
250
|
+
remoteIP: record.remoteIP,
|
|
251
|
+
routeName: record.routeConfig?.name || 'unknown',
|
|
252
|
+
component: 'metrics'
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
error: (err) => {
|
|
257
|
+
logger.log('error', `MetricsCollector: Error in connection subscription`, {
|
|
258
|
+
error: err.message,
|
|
259
|
+
component: 'metrics'
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
logger.log('debug', 'MetricsCollector started', { component: 'metrics' });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Stop the metrics collector and clean up resources
|
|
269
|
+
*/
|
|
270
|
+
public stop(): void {
|
|
271
|
+
if (this.connectionSubscription) {
|
|
272
|
+
this.connectionSubscription.unsubscribe();
|
|
273
|
+
this.connectionSubscription = undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
logger.log('debug', 'MetricsCollector stopped', { component: 'metrics' });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Alias for stop() for backward compatibility
|
|
281
|
+
*/
|
|
282
|
+
public destroy(): void {
|
|
283
|
+
this.stop();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for proxy statistics and metrics
|
|
3
|
+
*/
|
|
4
|
+
export interface IProxyStats {
|
|
5
|
+
/**
|
|
6
|
+
* Get the current number of active connections
|
|
7
|
+
*/
|
|
8
|
+
getActiveConnections(): number;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get connection counts grouped by route name
|
|
12
|
+
*/
|
|
13
|
+
getConnectionsByRoute(): Map<string, number>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get connection counts grouped by IP address
|
|
17
|
+
*/
|
|
18
|
+
getConnectionsByIP(): Map<string, number>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the total number of connections since proxy start
|
|
22
|
+
*/
|
|
23
|
+
getTotalConnections(): number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the current requests per second rate
|
|
27
|
+
*/
|
|
28
|
+
getRequestsPerSecond(): number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get total throughput (bytes transferred)
|
|
32
|
+
*/
|
|
33
|
+
getThroughput(): { bytesIn: number; bytesOut: number };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extended interface for additional metrics helpers
|
|
38
|
+
*/
|
|
39
|
+
export interface IProxyStatsExtended extends IProxyStats {
|
|
40
|
+
/**
|
|
41
|
+
* Get throughput rate (bytes per second) for last minute
|
|
42
|
+
*/
|
|
43
|
+
getThroughputRate(): { bytesInPerSec: number; bytesOutPerSec: number };
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get top IPs by connection count
|
|
47
|
+
*/
|
|
48
|
+
getTopIPs(limit?: number): Array<{ ip: string; connections: number }>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if an IP has reached the connection limit
|
|
52
|
+
*/
|
|
53
|
+
isIPBlocked(ip: string, maxConnectionsPerIP: number): boolean;
|
|
54
|
+
}
|
|
@@ -23,6 +23,9 @@ export class RouteConnectionHandler {
|
|
|
23
23
|
|
|
24
24
|
// Cache for route contexts to avoid recreation
|
|
25
25
|
private routeContextCache: Map<string, IRouteContext> = new Map();
|
|
26
|
+
|
|
27
|
+
// RxJS Subject for new connections
|
|
28
|
+
public newConnectionSubject = new plugins.smartrx.rxjs.Subject<IConnectionRecord>();
|
|
26
29
|
|
|
27
30
|
constructor(
|
|
28
31
|
settings: ISmartProxyOptions,
|
|
@@ -35,6 +38,7 @@ export class RouteConnectionHandler {
|
|
|
35
38
|
) {
|
|
36
39
|
this.settings = settings;
|
|
37
40
|
}
|
|
41
|
+
|
|
38
42
|
|
|
39
43
|
/**
|
|
40
44
|
* Create a route context object for port and host mapping functions
|
|
@@ -110,6 +114,9 @@ export class RouteConnectionHandler {
|
|
|
110
114
|
// Connection was rejected due to limit - socket already destroyed by connection manager
|
|
111
115
|
return;
|
|
112
116
|
}
|
|
117
|
+
|
|
118
|
+
// Emit new connection event
|
|
119
|
+
this.newConnectionSubject.next(record);
|
|
113
120
|
const connectionId = record.id;
|
|
114
121
|
|
|
115
122
|
// Apply socket optimizations (apply to underlying socket)
|
|
@@ -640,6 +647,9 @@ export class RouteConnectionHandler {
|
|
|
640
647
|
): void {
|
|
641
648
|
const connectionId = record.id;
|
|
642
649
|
const action = route.action as IRouteAction;
|
|
650
|
+
|
|
651
|
+
// Store the route config in the connection record for metrics and other uses
|
|
652
|
+
record.routeConfig = route;
|
|
643
653
|
|
|
644
654
|
// Check if this route uses NFTables for forwarding
|
|
645
655
|
if (action.forwardingEngine === 'nftables') {
|
|
@@ -957,6 +967,9 @@ export class RouteConnectionHandler {
|
|
|
957
967
|
): Promise<void> {
|
|
958
968
|
const connectionId = record.id;
|
|
959
969
|
|
|
970
|
+
// Store the route config in the connection record for metrics and other uses
|
|
971
|
+
record.routeConfig = route;
|
|
972
|
+
|
|
960
973
|
if (!route.action.socketHandler) {
|
|
961
974
|
logger.log('error', 'socket-handler action missing socketHandler function', {
|
|
962
975
|
connectionId,
|
|
@@ -27,6 +27,10 @@ import { Mutex } from './utils/mutex.js';
|
|
|
27
27
|
// Import ACME state manager
|
|
28
28
|
import { AcmeStateManager } from './acme-state-manager.js';
|
|
29
29
|
|
|
30
|
+
// Import metrics collector
|
|
31
|
+
import { MetricsCollector } from './metrics-collector.js';
|
|
32
|
+
import type { IProxyStats } from './models/metrics-types.js';
|
|
33
|
+
|
|
30
34
|
/**
|
|
31
35
|
* SmartProxy - Pure route-based API
|
|
32
36
|
*
|
|
@@ -47,13 +51,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
47
51
|
private isShuttingDown: boolean = false;
|
|
48
52
|
|
|
49
53
|
// Component managers
|
|
50
|
-
|
|
54
|
+
public connectionManager: ConnectionManager;
|
|
51
55
|
private securityManager: SecurityManager;
|
|
52
56
|
private tlsManager: TlsManager;
|
|
53
57
|
private httpProxyBridge: HttpProxyBridge;
|
|
54
58
|
private timeoutManager: TimeoutManager;
|
|
55
59
|
public routeManager: RouteManager; // Made public for route management
|
|
56
|
-
|
|
60
|
+
public routeConnectionHandler: RouteConnectionHandler; // Made public for metrics
|
|
57
61
|
private nftablesManager: NFTablesManager;
|
|
58
62
|
|
|
59
63
|
// Certificate manager for ACME and static certificates
|
|
@@ -64,6 +68,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
64
68
|
private routeUpdateLock: any = null; // Will be initialized as AsyncMutex
|
|
65
69
|
private acmeStateManager: AcmeStateManager;
|
|
66
70
|
|
|
71
|
+
// Metrics collector
|
|
72
|
+
private metricsCollector: MetricsCollector;
|
|
73
|
+
|
|
67
74
|
// Track port usage across route updates
|
|
68
75
|
private portUsageMap: Map<number, Set<string>> = new Map();
|
|
69
76
|
|
|
@@ -204,6 +211,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
204
211
|
|
|
205
212
|
// Initialize ACME state manager
|
|
206
213
|
this.acmeStateManager = new AcmeStateManager();
|
|
214
|
+
|
|
215
|
+
// Initialize metrics collector with reference to this SmartProxy instance
|
|
216
|
+
this.metricsCollector = new MetricsCollector(this);
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
/**
|
|
@@ -383,6 +393,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
383
393
|
logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' });
|
|
384
394
|
await this.certManager.provisionAllCertificates();
|
|
385
395
|
}
|
|
396
|
+
|
|
397
|
+
// Start the metrics collector now that all components are initialized
|
|
398
|
+
this.metricsCollector.start();
|
|
386
399
|
|
|
387
400
|
// Set up periodic connection logging and inactivity checks
|
|
388
401
|
this.connectionLogger = setInterval(() => {
|
|
@@ -508,6 +521,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
508
521
|
|
|
509
522
|
// Clear ACME state manager
|
|
510
523
|
this.acmeStateManager.clear();
|
|
524
|
+
|
|
525
|
+
// Stop metrics collector
|
|
526
|
+
this.metricsCollector.stop();
|
|
511
527
|
|
|
512
528
|
logger.log('info', 'SmartProxy shutdown complete.');
|
|
513
529
|
}
|
|
@@ -905,6 +921,15 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
905
921
|
return this.certManager.getCertificateStatus(routeName);
|
|
906
922
|
}
|
|
907
923
|
|
|
924
|
+
/**
|
|
925
|
+
* Get proxy statistics and metrics
|
|
926
|
+
*
|
|
927
|
+
* @returns IProxyStats interface with various metrics methods
|
|
928
|
+
*/
|
|
929
|
+
public getStats(): IProxyStats {
|
|
930
|
+
return this.metricsCollector;
|
|
931
|
+
}
|
|
932
|
+
|
|
908
933
|
/**
|
|
909
934
|
* Validates if a domain name is valid for certificate issuance
|
|
910
935
|
*/
|