@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.
Files changed (29) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -3
  3. package/dist_ts/proxies/smart-proxy/index.js +9 -5
  4. package/dist_ts/proxies/smart-proxy/models/index.d.ts +2 -0
  5. package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
  6. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +82 -15
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.js +10 -1
  8. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +133 -0
  9. package/dist_ts/proxies/smart-proxy/models/route-types.js +2 -0
  10. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +55 -0
  11. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +804 -0
  12. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +127 -0
  13. package/dist_ts/proxies/smart-proxy/route-helpers.js +196 -0
  14. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +103 -0
  15. package/dist_ts/proxies/smart-proxy/route-manager.js +483 -0
  16. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -8
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.js +239 -46
  18. package/package.json +2 -2
  19. package/readme.md +675 -446
  20. package/readme.plan.md +311 -250
  21. package/ts/00_commitinfo_data.ts +2 -2
  22. package/ts/proxies/smart-proxy/index.ts +20 -4
  23. package/ts/proxies/smart-proxy/models/index.ts +4 -0
  24. package/ts/proxies/smart-proxy/models/interfaces.ts +91 -13
  25. package/ts/proxies/smart-proxy/models/route-types.ts +184 -0
  26. package/ts/proxies/smart-proxy/route-connection-handler.ts +1117 -0
  27. package/ts/proxies/smart-proxy/route-helpers.ts +344 -0
  28. package/ts/proxies/smart-proxy/route-manager.ts +587 -0
  29. 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 from the new structure
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 { ConnectionHandler } from './connection-handler.js';
11
+ import { RouteManager } from './route-manager.js';
12
+ import { RouteConnectionHandler } from './route-connection-handler.js';
12
13
 
13
- // External dependencies from migrated modules
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 from models
22
- import type { ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
23
- // Provide backward compatibility types
24
- export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
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 - Main class that coordinates all components
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
- public domainConfigManager: DomainConfigManager;
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 connectionHandler: ConnectionHandler;
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
- if (!this.settings.acme || Object.keys(this.settings.acme).length === 0) {
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.connectionHandler = new ConnectionHandler(
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.portRangeManager
136
+ this.routeManager
123
137
  );
124
138
  }
125
139
 
126
140
  /**
127
- * The settings for the port proxy
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
- // Process domain configs
167
- // Note: ensureForwardingConfig is no longer needed since forwarding is now required
168
-
169
- // Initialize domain config manager with the processed configs
170
- this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs);
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
- // Convert domain forwards to use the new forwarding system if possible
194
+ // Setup domain forwards based on configuration type
180
195
  const domainForwards = acme.domainForwards?.map(f => {
181
- // If the domain has a forwarding config in domainConfigs, use that
182
- const domainConfig = this.settings.domainConfigs.find(
183
- dc => dc.domains.some(d => d === f.domain)
184
- );
185
-
186
- if (domainConfig?.forwarding) {
187
- return {
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
- forwardConfig: f.forwardConfig,
190
- acmeForwardConfig: f.acmeForwardConfig,
191
- sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
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
- this.certProvisioner = new CertProvisioner(
205
- this.settings.domainConfigs,
206
- this.port80Handler,
207
- this.networkProxyBridge,
208
- this.settings.certProvisionFunction,
209
- acme.renewThresholdDays!,
210
- acme.renewCheckIntervalHours!,
211
- acme.autoRenew!,
212
- domainForwards
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 port configuration
240
- const configWarnings = this.portRangeManager.validateConfiguration();
291
+ // Validate the route configuration
292
+ const configWarnings = this.routeManager.validateConfiguration();
241
293
  if (configWarnings.length > 0) {
242
- console.log("Port configuration warnings:");
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 PortRangeManager
249
- const listeningPorts = this.portRangeManager.getListeningPorts();
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.connectionHandler.handleConnection(socket);
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 ? ' (SNI passthrough enabled)' : ''
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 = domainConfig.forwarding.type;
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
- for (const config of this.settings.domainConfigs) {
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 = config.domains.filter(domain =>
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