@push.rocks/smartproxy 13.1.3 → 15.0.1
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/domain-config-manager.d.ts +15 -0
- package/dist_ts/proxies/smart-proxy/domain-config-manager.js +140 -9
- 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 +11 -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 +250 -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/domain-config-manager.ts +157 -14
- 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 +92 -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 +312 -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,26 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
105
112
|
this.securityManager,
|
|
106
113
|
this.timeoutManager
|
|
107
114
|
);
|
|
115
|
+
|
|
116
|
+
// Create the new route manager first
|
|
117
|
+
this.routeManager = new RouteManager(this.settings);
|
|
118
|
+
|
|
119
|
+
// Create domain config manager and port range manager
|
|
108
120
|
this.domainConfigManager = new DomainConfigManager(this.settings);
|
|
121
|
+
|
|
122
|
+
// Share the route manager with the domain config manager
|
|
123
|
+
if (typeof this.domainConfigManager.setRouteManager === 'function') {
|
|
124
|
+
this.domainConfigManager.setRouteManager(this.routeManager);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.portRangeManager = new PortRangeManager(this.settings);
|
|
128
|
+
|
|
129
|
+
// Create other required components
|
|
109
130
|
this.tlsManager = new TlsManager(this.settings);
|
|
110
131
|
this.networkProxyBridge = new NetworkProxyBridge(this.settings);
|
|
111
|
-
this.portRangeManager = new PortRangeManager(this.settings);
|
|
112
132
|
|
|
113
|
-
// Initialize connection handler
|
|
114
|
-
this.
|
|
133
|
+
// Initialize connection handler with route support
|
|
134
|
+
this.routeConnectionHandler = new RouteConnectionHandler(
|
|
115
135
|
this.settings,
|
|
116
136
|
this.connectionManager,
|
|
117
137
|
this.securityManager,
|
|
@@ -119,12 +139,12 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
119
139
|
this.tlsManager,
|
|
120
140
|
this.networkProxyBridge,
|
|
121
141
|
this.timeoutManager,
|
|
122
|
-
this.
|
|
142
|
+
this.routeManager
|
|
123
143
|
);
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
/**
|
|
127
|
-
* The settings for the
|
|
147
|
+
* The settings for the SmartProxy
|
|
128
148
|
*/
|
|
129
149
|
public settings: ISmartProxyOptions;
|
|
130
150
|
|
|
@@ -142,8 +162,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
142
162
|
// Build and start the Port80Handler
|
|
143
163
|
this.port80Handler = buildPort80Handler({
|
|
144
164
|
...config,
|
|
145
|
-
httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort
|
|
165
|
+
httpsRedirectPort: config.httpsRedirectPort || (isLegacyOptions(this.settings) ? this.settings.fromPort : 443)
|
|
146
166
|
});
|
|
167
|
+
|
|
147
168
|
// Share Port80Handler with NetworkProxyBridge before start
|
|
148
169
|
this.networkProxyBridge.setPort80Handler(this.port80Handler);
|
|
149
170
|
await this.port80Handler.start();
|
|
@@ -154,7 +175,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
154
175
|
}
|
|
155
176
|
|
|
156
177
|
/**
|
|
157
|
-
* Start the proxy server
|
|
178
|
+
* Start the proxy server with support for both configuration types
|
|
158
179
|
*/
|
|
159
180
|
public async start() {
|
|
160
181
|
// Don't start if already shutting down
|
|
@@ -163,11 +184,17 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
163
184
|
return;
|
|
164
185
|
}
|
|
165
186
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
// Initialize domain config based on configuration type
|
|
188
|
+
if (isLegacyOptions(this.settings)) {
|
|
189
|
+
// Initialize domain config manager with the legacy domain configs
|
|
190
|
+
this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs || []);
|
|
191
|
+
} else if (isRoutedOptions(this.settings)) {
|
|
192
|
+
// For pure route-based configuration, the domain config is already initialized
|
|
193
|
+
// in the constructor, but we might need to regenerate it
|
|
194
|
+
if (typeof this.domainConfigManager.generateDomainConfigsFromRoutes === 'function') {
|
|
195
|
+
this.domainConfigManager.generateDomainConfigsFromRoutes();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
171
198
|
|
|
172
199
|
// Initialize Port80Handler if enabled
|
|
173
200
|
await this.initializePort80Handler();
|
|
@@ -176,20 +203,39 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
176
203
|
if (this.port80Handler) {
|
|
177
204
|
const acme = this.settings.acme!;
|
|
178
205
|
|
|
179
|
-
//
|
|
206
|
+
// Setup domain forwards based on configuration type
|
|
180
207
|
const domainForwards = acme.domainForwards?.map(f => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
208
|
+
if (isLegacyOptions(this.settings)) {
|
|
209
|
+
// If using legacy mode, check if domain config exists
|
|
210
|
+
const domainConfig = this.settings.domainConfigs.find(
|
|
211
|
+
dc => dc.domains.some(d => d === f.domain)
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (domainConfig?.forwarding) {
|
|
215
|
+
return {
|
|
216
|
+
domain: f.domain,
|
|
217
|
+
forwardConfig: f.forwardConfig,
|
|
218
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
219
|
+
sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// In route mode, look for matching route
|
|
224
|
+
const route = this.routeManager.findMatchingRoute({
|
|
225
|
+
port: 443,
|
|
188
226
|
domain: f.domain,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
227
|
+
clientIp: '127.0.0.1' // Dummy IP for finding routes
|
|
228
|
+
})?.route;
|
|
229
|
+
|
|
230
|
+
if (route && route.action.type === 'forward' && route.action.tls) {
|
|
231
|
+
// If we found a matching route with TLS settings
|
|
232
|
+
return {
|
|
233
|
+
domain: f.domain,
|
|
234
|
+
forwardConfig: f.forwardConfig,
|
|
235
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
236
|
+
sslRedirect: f.sslRedirect || false
|
|
237
|
+
};
|
|
238
|
+
}
|
|
193
239
|
}
|
|
194
240
|
|
|
195
241
|
// Otherwise use the existing configuration
|
|
@@ -201,17 +247,38 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
201
247
|
};
|
|
202
248
|
}) || [];
|
|
203
249
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
250
|
+
// Create CertProvisioner with appropriate parameters
|
|
251
|
+
if (isLegacyOptions(this.settings)) {
|
|
252
|
+
this.certProvisioner = new CertProvisioner(
|
|
253
|
+
this.settings.domainConfigs,
|
|
254
|
+
this.port80Handler,
|
|
255
|
+
this.networkProxyBridge,
|
|
256
|
+
this.settings.certProvisionFunction,
|
|
257
|
+
acme.renewThresholdDays!,
|
|
258
|
+
acme.renewCheckIntervalHours!,
|
|
259
|
+
acme.autoRenew!,
|
|
260
|
+
domainForwards
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
// For route-based configuration, we need to adapt the interface
|
|
264
|
+
// Convert routes to domain configs for CertProvisioner
|
|
265
|
+
const domainConfigs: IDomainConfig[] = this.extractDomainConfigsFromRoutes(
|
|
266
|
+
(this.settings as IRoutedSmartProxyOptions).routes
|
|
267
|
+
);
|
|
214
268
|
|
|
269
|
+
this.certProvisioner = new CertProvisioner(
|
|
270
|
+
domainConfigs,
|
|
271
|
+
this.port80Handler,
|
|
272
|
+
this.networkProxyBridge,
|
|
273
|
+
this.settings.certProvisionFunction,
|
|
274
|
+
acme.renewThresholdDays!,
|
|
275
|
+
acme.renewCheckIntervalHours!,
|
|
276
|
+
acme.autoRenew!,
|
|
277
|
+
domainForwards
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Register certificate event handler
|
|
215
282
|
this.certProvisioner.on('certificate', (certData) => {
|
|
216
283
|
this.emit('certificate', {
|
|
217
284
|
domain: certData.domain,
|
|
@@ -228,25 +295,22 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
228
295
|
}
|
|
229
296
|
|
|
230
297
|
// Initialize and start NetworkProxy if needed
|
|
231
|
-
if (
|
|
232
|
-
this.settings.useNetworkProxy &&
|
|
233
|
-
this.settings.useNetworkProxy.length > 0
|
|
234
|
-
) {
|
|
298
|
+
if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
|
|
235
299
|
await this.networkProxyBridge.initialize();
|
|
236
300
|
await this.networkProxyBridge.start();
|
|
237
301
|
}
|
|
238
302
|
|
|
239
|
-
// Validate
|
|
240
|
-
const configWarnings = this.
|
|
303
|
+
// Validate the route configuration
|
|
304
|
+
const configWarnings = this.routeManager.validateConfiguration();
|
|
241
305
|
if (configWarnings.length > 0) {
|
|
242
|
-
console.log("
|
|
306
|
+
console.log("Route configuration warnings:");
|
|
243
307
|
for (const warning of configWarnings) {
|
|
244
308
|
console.log(` - ${warning}`);
|
|
245
309
|
}
|
|
246
310
|
}
|
|
247
311
|
|
|
248
|
-
// Get listening ports from
|
|
249
|
-
const listeningPorts = this.
|
|
312
|
+
// Get listening ports from RouteManager
|
|
313
|
+
const listeningPorts = this.routeManager.getListeningPorts();
|
|
250
314
|
|
|
251
315
|
// Create servers for each port
|
|
252
316
|
for (const port of listeningPorts) {
|
|
@@ -258,8 +322,8 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
258
322
|
return;
|
|
259
323
|
}
|
|
260
324
|
|
|
261
|
-
// Delegate to connection handler
|
|
262
|
-
this.
|
|
325
|
+
// Delegate to route connection handler
|
|
326
|
+
this.routeConnectionHandler.handleConnection(socket);
|
|
263
327
|
}).on('error', (err: Error) => {
|
|
264
328
|
console.log(`Server Error on port ${port}: ${err.message}`);
|
|
265
329
|
});
|
|
@@ -268,7 +332,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
268
332
|
const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
|
|
269
333
|
console.log(
|
|
270
334
|
`SmartProxy -> OK: Now listening on port ${port}${
|
|
271
|
-
this.settings.sniEnabled && !isNetworkProxyPort ?
|
|
335
|
+
isLegacyOptions(this.settings) && this.settings.sniEnabled && !isNetworkProxyPort ?
|
|
336
|
+
' (SNI passthrough enabled)' :
|
|
337
|
+
''
|
|
272
338
|
}${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}`
|
|
273
339
|
);
|
|
274
340
|
});
|
|
@@ -348,12 +414,70 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
348
414
|
}
|
|
349
415
|
}
|
|
350
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Extract domain configurations from routes for certificate provisioning
|
|
419
|
+
*/
|
|
420
|
+
private extractDomainConfigsFromRoutes(routes: IRouteConfig[]): IDomainConfig[] {
|
|
421
|
+
const domainConfigs: IDomainConfig[] = [];
|
|
422
|
+
|
|
423
|
+
for (const route of routes) {
|
|
424
|
+
// Skip routes without domain specs
|
|
425
|
+
if (!route.match.domains) continue;
|
|
426
|
+
|
|
427
|
+
// Skip non-forward routes
|
|
428
|
+
if (route.action.type !== 'forward') continue;
|
|
429
|
+
|
|
430
|
+
// Only process routes that need TLS termination (those with certificates)
|
|
431
|
+
if (!route.action.tls ||
|
|
432
|
+
route.action.tls.mode === 'passthrough' ||
|
|
433
|
+
!route.action.target) continue;
|
|
434
|
+
|
|
435
|
+
const domains = Array.isArray(route.match.domains)
|
|
436
|
+
? route.match.domains
|
|
437
|
+
: [route.match.domains];
|
|
438
|
+
|
|
439
|
+
// Determine forwarding type based on TLS mode
|
|
440
|
+
const forwardingType = route.action.tls.mode === 'terminate'
|
|
441
|
+
? 'https-terminate-to-http'
|
|
442
|
+
: 'https-terminate-to-https';
|
|
443
|
+
|
|
444
|
+
// Create a forwarding config
|
|
445
|
+
const forwarding = {
|
|
446
|
+
type: forwardingType as any,
|
|
447
|
+
target: {
|
|
448
|
+
host: Array.isArray(route.action.target.host)
|
|
449
|
+
? route.action.target.host[0]
|
|
450
|
+
: route.action.target.host,
|
|
451
|
+
port: route.action.target.port
|
|
452
|
+
},
|
|
453
|
+
// Add TLS settings
|
|
454
|
+
https: {
|
|
455
|
+
customCert: route.action.tls.certificate !== 'auto'
|
|
456
|
+
? route.action.tls.certificate
|
|
457
|
+
: undefined
|
|
458
|
+
},
|
|
459
|
+
// Add security settings if present
|
|
460
|
+
security: route.action.security,
|
|
461
|
+
// Add advanced settings if present
|
|
462
|
+
advanced: route.action.advanced
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
domainConfigs.push({
|
|
466
|
+
domains,
|
|
467
|
+
forwarding
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return domainConfigs;
|
|
472
|
+
}
|
|
473
|
+
|
|
351
474
|
/**
|
|
352
475
|
* Stop the proxy server
|
|
353
476
|
*/
|
|
354
477
|
public async stop() {
|
|
355
478
|
console.log('SmartProxy shutting down...');
|
|
356
479
|
this.isShuttingDown = true;
|
|
480
|
+
|
|
357
481
|
// Stop CertProvisioner if active
|
|
358
482
|
if (this.certProvisioner) {
|
|
359
483
|
await this.certProvisioner.stop();
|
|
@@ -411,14 +535,17 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
411
535
|
}
|
|
412
536
|
|
|
413
537
|
/**
|
|
414
|
-
* Updates the domain configurations for the proxy
|
|
538
|
+
* Updates the domain configurations for the proxy (legacy support)
|
|
415
539
|
*/
|
|
416
540
|
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
|
|
417
541
|
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
|
|
418
542
|
|
|
419
|
-
// Update domain configs in DomainConfigManager
|
|
543
|
+
// Update domain configs in DomainConfigManager (legacy)
|
|
420
544
|
this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
|
|
421
545
|
|
|
546
|
+
// Also update the RouteManager with these domain configs
|
|
547
|
+
this.routeManager.updateFromDomainConfigs(newDomainConfigs);
|
|
548
|
+
|
|
422
549
|
// If NetworkProxy is initialized, resync the configurations
|
|
423
550
|
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
424
551
|
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
@@ -428,7 +555,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
428
555
|
if (this.port80Handler && this.settings.acme?.enabled) {
|
|
429
556
|
for (const domainConfig of newDomainConfigs) {
|
|
430
557
|
// Skip certificate provisioning for http-only or passthrough configs that don't need certs
|
|
431
|
-
const forwardingType =
|
|
558
|
+
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
432
559
|
const needsCertificate =
|
|
433
560
|
forwardingType === 'https-terminate-to-http' ||
|
|
434
561
|
forwardingType === 'https-terminate-to-https';
|
|
@@ -490,6 +617,95 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
490
617
|
}
|
|
491
618
|
}
|
|
492
619
|
|
|
620
|
+
/**
|
|
621
|
+
* Update routes with new configuration (new API)
|
|
622
|
+
*/
|
|
623
|
+
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
|
624
|
+
console.log(`Updating routes (${newRoutes.length} routes)`);
|
|
625
|
+
|
|
626
|
+
// Update routes in RouteManager
|
|
627
|
+
this.routeManager.updateRoutes(newRoutes);
|
|
628
|
+
|
|
629
|
+
// If NetworkProxy is initialized, resync the configurations
|
|
630
|
+
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
631
|
+
// Create equivalent domain configs for NetworkProxy
|
|
632
|
+
const domainConfigs = this.extractDomainConfigsFromRoutes(newRoutes);
|
|
633
|
+
|
|
634
|
+
// Update domain configs in DomainConfigManager for sync
|
|
635
|
+
this.domainConfigManager.updateDomainConfigs(domainConfigs);
|
|
636
|
+
|
|
637
|
+
// Sync with NetworkProxy
|
|
638
|
+
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// If Port80Handler is running, provision certificates based on routes
|
|
642
|
+
if (this.port80Handler && this.settings.acme?.enabled) {
|
|
643
|
+
for (const route of newRoutes) {
|
|
644
|
+
// Skip routes without domains
|
|
645
|
+
if (!route.match.domains) continue;
|
|
646
|
+
|
|
647
|
+
// Skip non-forward routes
|
|
648
|
+
if (route.action.type !== 'forward') continue;
|
|
649
|
+
|
|
650
|
+
// Skip routes without TLS termination
|
|
651
|
+
if (!route.action.tls ||
|
|
652
|
+
route.action.tls.mode === 'passthrough' ||
|
|
653
|
+
!route.action.target) continue;
|
|
654
|
+
|
|
655
|
+
// Skip certificate provisioning if certificate is not auto
|
|
656
|
+
if (route.action.tls.certificate !== 'auto') continue;
|
|
657
|
+
|
|
658
|
+
const domains = Array.isArray(route.match.domains)
|
|
659
|
+
? route.match.domains
|
|
660
|
+
: [route.match.domains];
|
|
661
|
+
|
|
662
|
+
for (const domain of domains) {
|
|
663
|
+
const isWildcard = domain.includes('*');
|
|
664
|
+
let provision: string | plugins.tsclass.network.ICert = 'http01';
|
|
665
|
+
|
|
666
|
+
if (this.settings.certProvisionFunction) {
|
|
667
|
+
try {
|
|
668
|
+
provision = await this.settings.certProvisionFunction(domain);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
console.log(`certProvider error for ${domain}: ${err}`);
|
|
671
|
+
}
|
|
672
|
+
} else if (isWildcard) {
|
|
673
|
+
console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (provision === 'http01') {
|
|
678
|
+
if (isWildcard) {
|
|
679
|
+
console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Register domain with Port80Handler
|
|
684
|
+
this.port80Handler.addDomain({
|
|
685
|
+
domainName: domain,
|
|
686
|
+
sslRedirect: true,
|
|
687
|
+
acmeMaintenance: true
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
|
|
691
|
+
} else {
|
|
692
|
+
// Handle static certificate (e.g., DNS-01 provisioned)
|
|
693
|
+
const certObj = provision as plugins.tsclass.network.ICert;
|
|
694
|
+
const certData: ICertificateData = {
|
|
695
|
+
domain: certObj.domainName,
|
|
696
|
+
certificate: certObj.publicKey,
|
|
697
|
+
privateKey: certObj.privateKey,
|
|
698
|
+
expiryDate: new Date(certObj.validUntil)
|
|
699
|
+
};
|
|
700
|
+
this.networkProxyBridge.applyExternalCertificate(certData);
|
|
701
|
+
console.log(`Applied static certificate for ${domain} from certProvider`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
console.log('Provisioned certificates for new routes');
|
|
707
|
+
}
|
|
708
|
+
}
|
|
493
709
|
|
|
494
710
|
/**
|
|
495
711
|
* Request a certificate for a specific domain
|
|
@@ -583,7 +799,8 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
583
799
|
networkProxyConnections,
|
|
584
800
|
terminationStats,
|
|
585
801
|
acmeEnabled: !!this.port80Handler,
|
|
586
|
-
port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null
|
|
802
|
+
port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null,
|
|
803
|
+
routes: this.routeManager.getListeningPorts().length
|
|
587
804
|
};
|
|
588
805
|
}
|
|
589
806
|
|
|
@@ -591,18 +808,44 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
591
808
|
* Get a list of eligible domains for ACME certificates
|
|
592
809
|
*/
|
|
593
810
|
public getEligibleDomainsForCertificates(): string[] {
|
|
594
|
-
// Collect all non-wildcard domains from domain configs
|
|
595
811
|
const domains: string[] = [];
|
|
596
812
|
|
|
597
|
-
|
|
813
|
+
// Get domains from routes
|
|
814
|
+
const routes = isRoutedOptions(this.settings) ? this.settings.routes : [];
|
|
815
|
+
|
|
816
|
+
for (const route of routes) {
|
|
817
|
+
if (!route.match.domains) continue;
|
|
818
|
+
|
|
819
|
+
// Skip routes without TLS termination or auto certificates
|
|
820
|
+
if (route.action.type !== 'forward' ||
|
|
821
|
+
!route.action.tls ||
|
|
822
|
+
route.action.tls.mode === 'passthrough' ||
|
|
823
|
+
route.action.tls.certificate !== 'auto') continue;
|
|
824
|
+
|
|
825
|
+
const routeDomains = Array.isArray(route.match.domains)
|
|
826
|
+
? route.match.domains
|
|
827
|
+
: [route.match.domains];
|
|
828
|
+
|
|
598
829
|
// Skip domains that can't be used with ACME
|
|
599
|
-
const eligibleDomains =
|
|
830
|
+
const eligibleDomains = routeDomains.filter(domain =>
|
|
600
831
|
!domain.includes('*') && this.isValidDomain(domain)
|
|
601
832
|
);
|
|
602
833
|
|
|
603
834
|
domains.push(...eligibleDomains);
|
|
604
835
|
}
|
|
605
836
|
|
|
837
|
+
// For legacy mode, also get domains from domain configs
|
|
838
|
+
if (isLegacyOptions(this.settings)) {
|
|
839
|
+
for (const config of this.settings.domainConfigs) {
|
|
840
|
+
// Skip domains that can't be used with ACME
|
|
841
|
+
const eligibleDomains = config.domains.filter(domain =>
|
|
842
|
+
!domain.includes('*') && this.isValidDomain(domain)
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
domains.push(...eligibleDomains);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
606
849
|
return domains;
|
|
607
850
|
}
|
|
608
851
|
|