@push.rocks/smartproxy 13.1.3 → 15.0.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/00_commitinfo_data.js +3 -3
- package/dist_ts/proxies/smart-proxy/index.d.ts +5 -3
- package/dist_ts/proxies/smart-proxy/index.js +9 -5
- package/dist_ts/proxies/smart-proxy/models/index.d.ts +2 -0
- package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +82 -15
- package/dist_ts/proxies/smart-proxy/models/interfaces.js +10 -1
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +133 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +2 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +55 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +804 -0
- package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +127 -0
- package/dist_ts/proxies/smart-proxy/route-helpers.js +196 -0
- package/dist_ts/proxies/smart-proxy/route-manager.d.ts +103 -0
- package/dist_ts/proxies/smart-proxy/route-manager.js +483 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -8
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +239 -46
- package/package.json +2 -2
- package/readme.md +675 -446
- package/readme.plan.md +311 -250
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/proxies/smart-proxy/index.ts +20 -4
- package/ts/proxies/smart-proxy/models/index.ts +4 -0
- package/ts/proxies/smart-proxy/models/interfaces.ts +91 -13
- package/ts/proxies/smart-proxy/models/route-types.ts +184 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +1117 -0
- package/ts/proxies/smart-proxy/route-helpers.ts +344 -0
- package/ts/proxies/smart-proxy/route-manager.ts +587 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +300 -69
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
|
|
3
|
-
// Importing
|
|
3
|
+
// Importing required components
|
|
4
4
|
import { ConnectionManager } from './connection-manager.js';
|
|
5
5
|
import { SecurityManager } from './security-manager.js';
|
|
6
6
|
import { DomainConfigManager } from './domain-config-manager.js';
|
|
@@ -8,23 +8,27 @@ import { TlsManager } from './tls-manager.js';
|
|
|
8
8
|
import { NetworkProxyBridge } from './network-proxy-bridge.js';
|
|
9
9
|
import { TimeoutManager } from './timeout-manager.js';
|
|
10
10
|
import { PortRangeManager } from './port-range-manager.js';
|
|
11
|
-
import {
|
|
11
|
+
import { RouteManager } from './route-manager.js';
|
|
12
|
+
import { RouteConnectionHandler } from './route-connection-handler.js';
|
|
12
13
|
|
|
13
|
-
// External dependencies
|
|
14
|
+
// External dependencies
|
|
14
15
|
import { Port80Handler } from '../../http/port80/port80-handler.js';
|
|
15
16
|
import { CertProvisioner } from '../../certificate/providers/cert-provisioner.js';
|
|
16
17
|
import type { ICertificateData } from '../../certificate/models/certificate-types.js';
|
|
17
18
|
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
|
|
18
|
-
import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
|
|
19
19
|
import { createPort80HandlerOptions } from '../../common/port80-adapter.js';
|
|
20
20
|
|
|
21
|
-
// Import types
|
|
22
|
-
import type {
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
// Import types and utilities
|
|
22
|
+
import type {
|
|
23
|
+
ISmartProxyOptions,
|
|
24
|
+
IRoutedSmartProxyOptions,
|
|
25
|
+
IDomainConfig
|
|
26
|
+
} from './models/interfaces.js';
|
|
27
|
+
import { isRoutedOptions, isLegacyOptions } from './models/interfaces.js';
|
|
28
|
+
import type { IRouteConfig } from './models/route-types.js';
|
|
25
29
|
|
|
26
30
|
/**
|
|
27
|
-
* SmartProxy -
|
|
31
|
+
* SmartProxy - Unified route-based API
|
|
28
32
|
*/
|
|
29
33
|
export class SmartProxy extends plugins.EventEmitter {
|
|
30
34
|
private netServers: plugins.net.Server[] = [];
|
|
@@ -34,24 +38,28 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
34
38
|
// Component managers
|
|
35
39
|
private connectionManager: ConnectionManager;
|
|
36
40
|
private securityManager: SecurityManager;
|
|
37
|
-
|
|
41
|
+
private domainConfigManager: DomainConfigManager;
|
|
38
42
|
private tlsManager: TlsManager;
|
|
39
43
|
private networkProxyBridge: NetworkProxyBridge;
|
|
40
44
|
private timeoutManager: TimeoutManager;
|
|
41
45
|
private portRangeManager: PortRangeManager;
|
|
42
|
-
private
|
|
46
|
+
private routeManager: RouteManager;
|
|
47
|
+
private routeConnectionHandler: RouteConnectionHandler;
|
|
43
48
|
|
|
44
49
|
// Port80Handler for ACME certificate management
|
|
45
50
|
private port80Handler: Port80Handler | null = null;
|
|
46
51
|
// CertProvisioner for unified certificate workflows
|
|
47
52
|
private certProvisioner?: CertProvisioner;
|
|
48
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Constructor that supports both legacy and route-based configuration
|
|
56
|
+
*/
|
|
49
57
|
constructor(settingsArg: ISmartProxyOptions) {
|
|
50
58
|
super();
|
|
59
|
+
|
|
51
60
|
// Set reasonable defaults for all settings
|
|
52
61
|
this.settings = {
|
|
53
62
|
...settingsArg,
|
|
54
|
-
targetIP: settingsArg.targetIP || 'localhost',
|
|
55
63
|
initialDataTimeout: settingsArg.initialDataTimeout || 120000,
|
|
56
64
|
socketTimeout: settingsArg.socketTimeout || 3600000,
|
|
57
65
|
inactivityCheckInterval: settingsArg.inactivityCheckInterval || 60000,
|
|
@@ -76,12 +84,11 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
76
84
|
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
|
|
77
85
|
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
|
|
78
86
|
networkProxyPort: settingsArg.networkProxyPort || 8443,
|
|
79
|
-
acme: settingsArg.acme || {},
|
|
80
|
-
globalPortRanges: settingsArg.globalPortRanges || [],
|
|
81
87
|
};
|
|
82
88
|
|
|
83
89
|
// Set default ACME options if not provided
|
|
84
|
-
|
|
90
|
+
this.settings.acme = this.settings.acme || {};
|
|
91
|
+
if (Object.keys(this.settings.acme).length === 0) {
|
|
85
92
|
this.settings.acme = {
|
|
86
93
|
enabled: false,
|
|
87
94
|
port: 80,
|
|
@@ -91,7 +98,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
91
98
|
autoRenew: true,
|
|
92
99
|
certificateStore: './certs',
|
|
93
100
|
skipConfiguredCerts: false,
|
|
94
|
-
httpsRedirectPort: this.settings.fromPort,
|
|
101
|
+
httpsRedirectPort: this.settings.fromPort || 443,
|
|
95
102
|
renewCheckIntervalHours: 24,
|
|
96
103
|
domainForwards: []
|
|
97
104
|
};
|
|
@@ -105,13 +112,20 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
105
112
|
this.securityManager,
|
|
106
113
|
this.timeoutManager
|
|
107
114
|
);
|
|
115
|
+
|
|
116
|
+
// Create domain config manager and port range manager (for backward compatibility)
|
|
108
117
|
this.domainConfigManager = new DomainConfigManager(this.settings);
|
|
118
|
+
this.portRangeManager = new PortRangeManager(this.settings);
|
|
119
|
+
|
|
120
|
+
// Create the new route manager
|
|
121
|
+
this.routeManager = new RouteManager(this.settings);
|
|
122
|
+
|
|
123
|
+
// Create other required components
|
|
109
124
|
this.tlsManager = new TlsManager(this.settings);
|
|
110
125
|
this.networkProxyBridge = new NetworkProxyBridge(this.settings);
|
|
111
|
-
this.portRangeManager = new PortRangeManager(this.settings);
|
|
112
126
|
|
|
113
|
-
// Initialize connection handler
|
|
114
|
-
this.
|
|
127
|
+
// Initialize connection handler with route support
|
|
128
|
+
this.routeConnectionHandler = new RouteConnectionHandler(
|
|
115
129
|
this.settings,
|
|
116
130
|
this.connectionManager,
|
|
117
131
|
this.securityManager,
|
|
@@ -119,12 +133,12 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
119
133
|
this.tlsManager,
|
|
120
134
|
this.networkProxyBridge,
|
|
121
135
|
this.timeoutManager,
|
|
122
|
-
this.
|
|
136
|
+
this.routeManager
|
|
123
137
|
);
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
/**
|
|
127
|
-
* The settings for the
|
|
141
|
+
* The settings for the SmartProxy
|
|
128
142
|
*/
|
|
129
143
|
public settings: ISmartProxyOptions;
|
|
130
144
|
|
|
@@ -142,8 +156,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
142
156
|
// Build and start the Port80Handler
|
|
143
157
|
this.port80Handler = buildPort80Handler({
|
|
144
158
|
...config,
|
|
145
|
-
httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort
|
|
159
|
+
httpsRedirectPort: config.httpsRedirectPort || (isLegacyOptions(this.settings) ? this.settings.fromPort : 443)
|
|
146
160
|
});
|
|
161
|
+
|
|
147
162
|
// Share Port80Handler with NetworkProxyBridge before start
|
|
148
163
|
this.networkProxyBridge.setPort80Handler(this.port80Handler);
|
|
149
164
|
await this.port80Handler.start();
|
|
@@ -154,7 +169,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
/**
|
|
157
|
-
* Start the proxy server
|
|
172
|
+
* Start the proxy server with support for both configuration types
|
|
158
173
|
*/
|
|
159
174
|
public async start() {
|
|
160
175
|
// Don't start if already shutting down
|
|
@@ -163,11 +178,11 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
163
178
|
return;
|
|
164
179
|
}
|
|
165
180
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
181
|
+
// If using legacy format, make sure domainConfigs are initialized
|
|
182
|
+
if (isLegacyOptions(this.settings)) {
|
|
183
|
+
// Initialize domain config manager with the processed configs
|
|
184
|
+
this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs);
|
|
185
|
+
}
|
|
171
186
|
|
|
172
187
|
// Initialize Port80Handler if enabled
|
|
173
188
|
await this.initializePort80Handler();
|
|
@@ -176,20 +191,39 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
176
191
|
if (this.port80Handler) {
|
|
177
192
|
const acme = this.settings.acme!;
|
|
178
193
|
|
|
179
|
-
//
|
|
194
|
+
// Setup domain forwards based on configuration type
|
|
180
195
|
const domainForwards = acme.domainForwards?.map(f => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
196
|
+
if (isLegacyOptions(this.settings)) {
|
|
197
|
+
// If using legacy mode, check if domain config exists
|
|
198
|
+
const domainConfig = this.settings.domainConfigs.find(
|
|
199
|
+
dc => dc.domains.some(d => d === f.domain)
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
if (domainConfig?.forwarding) {
|
|
203
|
+
return {
|
|
204
|
+
domain: f.domain,
|
|
205
|
+
forwardConfig: f.forwardConfig,
|
|
206
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
207
|
+
sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
// In route mode, look for matching route
|
|
212
|
+
const route = this.routeManager.findMatchingRoute({
|
|
213
|
+
port: 443,
|
|
188
214
|
domain: f.domain,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
215
|
+
clientIp: '127.0.0.1' // Dummy IP for finding routes
|
|
216
|
+
})?.route;
|
|
217
|
+
|
|
218
|
+
if (route && route.action.type === 'forward' && route.action.tls) {
|
|
219
|
+
// If we found a matching route with TLS settings
|
|
220
|
+
return {
|
|
221
|
+
domain: f.domain,
|
|
222
|
+
forwardConfig: f.forwardConfig,
|
|
223
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
224
|
+
sslRedirect: f.sslRedirect || false
|
|
225
|
+
};
|
|
226
|
+
}
|
|
193
227
|
}
|
|
194
228
|
|
|
195
229
|
// Otherwise use the existing configuration
|
|
@@ -201,17 +235,38 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
201
235
|
};
|
|
202
236
|
}) || [];
|
|
203
237
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
238
|
+
// Create CertProvisioner with appropriate parameters
|
|
239
|
+
if (isLegacyOptions(this.settings)) {
|
|
240
|
+
this.certProvisioner = new CertProvisioner(
|
|
241
|
+
this.settings.domainConfigs,
|
|
242
|
+
this.port80Handler,
|
|
243
|
+
this.networkProxyBridge,
|
|
244
|
+
this.settings.certProvisionFunction,
|
|
245
|
+
acme.renewThresholdDays!,
|
|
246
|
+
acme.renewCheckIntervalHours!,
|
|
247
|
+
acme.autoRenew!,
|
|
248
|
+
domainForwards
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
// For route-based configuration, we need to adapt the interface
|
|
252
|
+
// Convert routes to domain configs for CertProvisioner
|
|
253
|
+
const domainConfigs: IDomainConfig[] = this.extractDomainConfigsFromRoutes(
|
|
254
|
+
(this.settings as IRoutedSmartProxyOptions).routes
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
this.certProvisioner = new CertProvisioner(
|
|
258
|
+
domainConfigs,
|
|
259
|
+
this.port80Handler,
|
|
260
|
+
this.networkProxyBridge,
|
|
261
|
+
this.settings.certProvisionFunction,
|
|
262
|
+
acme.renewThresholdDays!,
|
|
263
|
+
acme.renewCheckIntervalHours!,
|
|
264
|
+
acme.autoRenew!,
|
|
265
|
+
domainForwards
|
|
266
|
+
);
|
|
267
|
+
}
|
|
214
268
|
|
|
269
|
+
// Register certificate event handler
|
|
215
270
|
this.certProvisioner.on('certificate', (certData) => {
|
|
216
271
|
this.emit('certificate', {
|
|
217
272
|
domain: certData.domain,
|
|
@@ -228,25 +283,22 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
228
283
|
}
|
|
229
284
|
|
|
230
285
|
// Initialize and start NetworkProxy if needed
|
|
231
|
-
if (
|
|
232
|
-
this.settings.useNetworkProxy &&
|
|
233
|
-
this.settings.useNetworkProxy.length > 0
|
|
234
|
-
) {
|
|
286
|
+
if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
|
|
235
287
|
await this.networkProxyBridge.initialize();
|
|
236
288
|
await this.networkProxyBridge.start();
|
|
237
289
|
}
|
|
238
290
|
|
|
239
|
-
// Validate
|
|
240
|
-
const configWarnings = this.
|
|
291
|
+
// Validate the route configuration
|
|
292
|
+
const configWarnings = this.routeManager.validateConfiguration();
|
|
241
293
|
if (configWarnings.length > 0) {
|
|
242
|
-
console.log("
|
|
294
|
+
console.log("Route configuration warnings:");
|
|
243
295
|
for (const warning of configWarnings) {
|
|
244
296
|
console.log(` - ${warning}`);
|
|
245
297
|
}
|
|
246
298
|
}
|
|
247
299
|
|
|
248
|
-
// Get listening ports from
|
|
249
|
-
const listeningPorts = this.
|
|
300
|
+
// Get listening ports from RouteManager
|
|
301
|
+
const listeningPorts = this.routeManager.getListeningPorts();
|
|
250
302
|
|
|
251
303
|
// Create servers for each port
|
|
252
304
|
for (const port of listeningPorts) {
|
|
@@ -258,8 +310,8 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
258
310
|
return;
|
|
259
311
|
}
|
|
260
312
|
|
|
261
|
-
// Delegate to connection handler
|
|
262
|
-
this.
|
|
313
|
+
// Delegate to route connection handler
|
|
314
|
+
this.routeConnectionHandler.handleConnection(socket);
|
|
263
315
|
}).on('error', (err: Error) => {
|
|
264
316
|
console.log(`Server Error on port ${port}: ${err.message}`);
|
|
265
317
|
});
|
|
@@ -268,7 +320,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
268
320
|
const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
|
|
269
321
|
console.log(
|
|
270
322
|
`SmartProxy -> OK: Now listening on port ${port}${
|
|
271
|
-
this.settings.sniEnabled && !isNetworkProxyPort ?
|
|
323
|
+
isLegacyOptions(this.settings) && this.settings.sniEnabled && !isNetworkProxyPort ?
|
|
324
|
+
' (SNI passthrough enabled)' :
|
|
325
|
+
''
|
|
272
326
|
}${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}`
|
|
273
327
|
);
|
|
274
328
|
});
|
|
@@ -348,12 +402,70 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
348
402
|
}
|
|
349
403
|
}
|
|
350
404
|
|
|
405
|
+
/**
|
|
406
|
+
* Extract domain configurations from routes for certificate provisioning
|
|
407
|
+
*/
|
|
408
|
+
private extractDomainConfigsFromRoutes(routes: IRouteConfig[]): IDomainConfig[] {
|
|
409
|
+
const domainConfigs: IDomainConfig[] = [];
|
|
410
|
+
|
|
411
|
+
for (const route of routes) {
|
|
412
|
+
// Skip routes without domain specs
|
|
413
|
+
if (!route.match.domains) continue;
|
|
414
|
+
|
|
415
|
+
// Skip non-forward routes
|
|
416
|
+
if (route.action.type !== 'forward') continue;
|
|
417
|
+
|
|
418
|
+
// Only process routes that need TLS termination (those with certificates)
|
|
419
|
+
if (!route.action.tls ||
|
|
420
|
+
route.action.tls.mode === 'passthrough' ||
|
|
421
|
+
!route.action.target) continue;
|
|
422
|
+
|
|
423
|
+
const domains = Array.isArray(route.match.domains)
|
|
424
|
+
? route.match.domains
|
|
425
|
+
: [route.match.domains];
|
|
426
|
+
|
|
427
|
+
// Determine forwarding type based on TLS mode
|
|
428
|
+
const forwardingType = route.action.tls.mode === 'terminate'
|
|
429
|
+
? 'https-terminate-to-http'
|
|
430
|
+
: 'https-terminate-to-https';
|
|
431
|
+
|
|
432
|
+
// Create a forwarding config
|
|
433
|
+
const forwarding = {
|
|
434
|
+
type: forwardingType as any,
|
|
435
|
+
target: {
|
|
436
|
+
host: Array.isArray(route.action.target.host)
|
|
437
|
+
? route.action.target.host[0]
|
|
438
|
+
: route.action.target.host,
|
|
439
|
+
port: route.action.target.port
|
|
440
|
+
},
|
|
441
|
+
// Add TLS settings
|
|
442
|
+
https: {
|
|
443
|
+
customCert: route.action.tls.certificate !== 'auto'
|
|
444
|
+
? route.action.tls.certificate
|
|
445
|
+
: undefined
|
|
446
|
+
},
|
|
447
|
+
// Add security settings if present
|
|
448
|
+
security: route.action.security,
|
|
449
|
+
// Add advanced settings if present
|
|
450
|
+
advanced: route.action.advanced
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
domainConfigs.push({
|
|
454
|
+
domains,
|
|
455
|
+
forwarding
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return domainConfigs;
|
|
460
|
+
}
|
|
461
|
+
|
|
351
462
|
/**
|
|
352
463
|
* Stop the proxy server
|
|
353
464
|
*/
|
|
354
465
|
public async stop() {
|
|
355
466
|
console.log('SmartProxy shutting down...');
|
|
356
467
|
this.isShuttingDown = true;
|
|
468
|
+
|
|
357
469
|
// Stop CertProvisioner if active
|
|
358
470
|
if (this.certProvisioner) {
|
|
359
471
|
await this.certProvisioner.stop();
|
|
@@ -411,14 +523,17 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
411
523
|
}
|
|
412
524
|
|
|
413
525
|
/**
|
|
414
|
-
* Updates the domain configurations for the proxy
|
|
526
|
+
* Updates the domain configurations for the proxy (legacy support)
|
|
415
527
|
*/
|
|
416
528
|
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
|
|
417
529
|
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
|
|
418
530
|
|
|
419
|
-
// Update domain configs in DomainConfigManager
|
|
531
|
+
// Update domain configs in DomainConfigManager (legacy)
|
|
420
532
|
this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
|
|
421
533
|
|
|
534
|
+
// Also update the RouteManager with these domain configs
|
|
535
|
+
this.routeManager.updateFromDomainConfigs(newDomainConfigs);
|
|
536
|
+
|
|
422
537
|
// If NetworkProxy is initialized, resync the configurations
|
|
423
538
|
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
424
539
|
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
@@ -428,7 +543,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
428
543
|
if (this.port80Handler && this.settings.acme?.enabled) {
|
|
429
544
|
for (const domainConfig of newDomainConfigs) {
|
|
430
545
|
// Skip certificate provisioning for http-only or passthrough configs that don't need certs
|
|
431
|
-
const forwardingType =
|
|
546
|
+
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
432
547
|
const needsCertificate =
|
|
433
548
|
forwardingType === 'https-terminate-to-http' ||
|
|
434
549
|
forwardingType === 'https-terminate-to-https';
|
|
@@ -490,6 +605,95 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
490
605
|
}
|
|
491
606
|
}
|
|
492
607
|
|
|
608
|
+
/**
|
|
609
|
+
* Update routes with new configuration (new API)
|
|
610
|
+
*/
|
|
611
|
+
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
|
612
|
+
console.log(`Updating routes (${newRoutes.length} routes)`);
|
|
613
|
+
|
|
614
|
+
// Update routes in RouteManager
|
|
615
|
+
this.routeManager.updateRoutes(newRoutes);
|
|
616
|
+
|
|
617
|
+
// If NetworkProxy is initialized, resync the configurations
|
|
618
|
+
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
619
|
+
// Create equivalent domain configs for NetworkProxy
|
|
620
|
+
const domainConfigs = this.extractDomainConfigsFromRoutes(newRoutes);
|
|
621
|
+
|
|
622
|
+
// Update domain configs in DomainConfigManager for sync
|
|
623
|
+
this.domainConfigManager.updateDomainConfigs(domainConfigs);
|
|
624
|
+
|
|
625
|
+
// Sync with NetworkProxy
|
|
626
|
+
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// If Port80Handler is running, provision certificates based on routes
|
|
630
|
+
if (this.port80Handler && this.settings.acme?.enabled) {
|
|
631
|
+
for (const route of newRoutes) {
|
|
632
|
+
// Skip routes without domains
|
|
633
|
+
if (!route.match.domains) continue;
|
|
634
|
+
|
|
635
|
+
// Skip non-forward routes
|
|
636
|
+
if (route.action.type !== 'forward') continue;
|
|
637
|
+
|
|
638
|
+
// Skip routes without TLS termination
|
|
639
|
+
if (!route.action.tls ||
|
|
640
|
+
route.action.tls.mode === 'passthrough' ||
|
|
641
|
+
!route.action.target) continue;
|
|
642
|
+
|
|
643
|
+
// Skip certificate provisioning if certificate is not auto
|
|
644
|
+
if (route.action.tls.certificate !== 'auto') continue;
|
|
645
|
+
|
|
646
|
+
const domains = Array.isArray(route.match.domains)
|
|
647
|
+
? route.match.domains
|
|
648
|
+
: [route.match.domains];
|
|
649
|
+
|
|
650
|
+
for (const domain of domains) {
|
|
651
|
+
const isWildcard = domain.includes('*');
|
|
652
|
+
let provision: string | plugins.tsclass.network.ICert = 'http01';
|
|
653
|
+
|
|
654
|
+
if (this.settings.certProvisionFunction) {
|
|
655
|
+
try {
|
|
656
|
+
provision = await this.settings.certProvisionFunction(domain);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
console.log(`certProvider error for ${domain}: ${err}`);
|
|
659
|
+
}
|
|
660
|
+
} else if (isWildcard) {
|
|
661
|
+
console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (provision === 'http01') {
|
|
666
|
+
if (isWildcard) {
|
|
667
|
+
console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Register domain with Port80Handler
|
|
672
|
+
this.port80Handler.addDomain({
|
|
673
|
+
domainName: domain,
|
|
674
|
+
sslRedirect: true,
|
|
675
|
+
acmeMaintenance: true
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
|
|
679
|
+
} else {
|
|
680
|
+
// Handle static certificate (e.g., DNS-01 provisioned)
|
|
681
|
+
const certObj = provision as plugins.tsclass.network.ICert;
|
|
682
|
+
const certData: ICertificateData = {
|
|
683
|
+
domain: certObj.domainName,
|
|
684
|
+
certificate: certObj.publicKey,
|
|
685
|
+
privateKey: certObj.privateKey,
|
|
686
|
+
expiryDate: new Date(certObj.validUntil)
|
|
687
|
+
};
|
|
688
|
+
this.networkProxyBridge.applyExternalCertificate(certData);
|
|
689
|
+
console.log(`Applied static certificate for ${domain} from certProvider`);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
console.log('Provisioned certificates for new routes');
|
|
695
|
+
}
|
|
696
|
+
}
|
|
493
697
|
|
|
494
698
|
/**
|
|
495
699
|
* Request a certificate for a specific domain
|
|
@@ -583,7 +787,8 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
583
787
|
networkProxyConnections,
|
|
584
788
|
terminationStats,
|
|
585
789
|
acmeEnabled: !!this.port80Handler,
|
|
586
|
-
port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null
|
|
790
|
+
port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null,
|
|
791
|
+
routes: this.routeManager.getListeningPorts().length
|
|
587
792
|
};
|
|
588
793
|
}
|
|
589
794
|
|
|
@@ -591,18 +796,44 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
591
796
|
* Get a list of eligible domains for ACME certificates
|
|
592
797
|
*/
|
|
593
798
|
public getEligibleDomainsForCertificates(): string[] {
|
|
594
|
-
// Collect all non-wildcard domains from domain configs
|
|
595
799
|
const domains: string[] = [];
|
|
596
800
|
|
|
597
|
-
|
|
801
|
+
// Get domains from routes
|
|
802
|
+
const routes = isRoutedOptions(this.settings) ? this.settings.routes : [];
|
|
803
|
+
|
|
804
|
+
for (const route of routes) {
|
|
805
|
+
if (!route.match.domains) continue;
|
|
806
|
+
|
|
807
|
+
// Skip routes without TLS termination or auto certificates
|
|
808
|
+
if (route.action.type !== 'forward' ||
|
|
809
|
+
!route.action.tls ||
|
|
810
|
+
route.action.tls.mode === 'passthrough' ||
|
|
811
|
+
route.action.tls.certificate !== 'auto') continue;
|
|
812
|
+
|
|
813
|
+
const routeDomains = Array.isArray(route.match.domains)
|
|
814
|
+
? route.match.domains
|
|
815
|
+
: [route.match.domains];
|
|
816
|
+
|
|
598
817
|
// Skip domains that can't be used with ACME
|
|
599
|
-
const eligibleDomains =
|
|
818
|
+
const eligibleDomains = routeDomains.filter(domain =>
|
|
600
819
|
!domain.includes('*') && this.isValidDomain(domain)
|
|
601
820
|
);
|
|
602
821
|
|
|
603
822
|
domains.push(...eligibleDomains);
|
|
604
823
|
}
|
|
605
824
|
|
|
825
|
+
// For legacy mode, also get domains from domain configs
|
|
826
|
+
if (isLegacyOptions(this.settings)) {
|
|
827
|
+
for (const config of this.settings.domainConfigs) {
|
|
828
|
+
// Skip domains that can't be used with ACME
|
|
829
|
+
const eligibleDomains = config.domains.filter(domain =>
|
|
830
|
+
!domain.includes('*') && this.isValidDomain(domain)
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
domains.push(...eligibleDomains);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
606
837
|
return domains;
|
|
607
838
|
}
|
|
608
839
|
|