@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.
Files changed (32) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +15 -0
  3. package/dist_ts/proxies/smart-proxy/domain-config-manager.js +140 -9
  4. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -3
  5. package/dist_ts/proxies/smart-proxy/index.js +9 -5
  6. package/dist_ts/proxies/smart-proxy/models/index.d.ts +2 -0
  7. package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
  8. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +82 -15
  9. package/dist_ts/proxies/smart-proxy/models/interfaces.js +11 -1
  10. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +133 -0
  11. package/dist_ts/proxies/smart-proxy/models/route-types.js +2 -0
  12. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +55 -0
  13. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +804 -0
  14. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +127 -0
  15. package/dist_ts/proxies/smart-proxy/route-helpers.js +196 -0
  16. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +103 -0
  17. package/dist_ts/proxies/smart-proxy/route-manager.js +483 -0
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -8
  19. package/dist_ts/proxies/smart-proxy/smart-proxy.js +250 -46
  20. package/package.json +2 -2
  21. package/readme.md +675 -446
  22. package/readme.plan.md +311 -250
  23. package/ts/00_commitinfo_data.ts +2 -2
  24. package/ts/proxies/smart-proxy/domain-config-manager.ts +157 -14
  25. package/ts/proxies/smart-proxy/index.ts +20 -4
  26. package/ts/proxies/smart-proxy/models/index.ts +4 -0
  27. package/ts/proxies/smart-proxy/models/interfaces.ts +92 -13
  28. package/ts/proxies/smart-proxy/models/route-types.ts +184 -0
  29. package/ts/proxies/smart-proxy/route-connection-handler.ts +1117 -0
  30. package/ts/proxies/smart-proxy/route-helpers.ts +344 -0
  31. package/ts/proxies/smart-proxy/route-manager.ts +587 -0
  32. 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 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,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.connectionHandler = new ConnectionHandler(
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.portRangeManager
142
+ this.routeManager
123
143
  );
124
144
  }
125
145
 
126
146
  /**
127
- * The settings for the port proxy
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
- // 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);
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
- // Convert domain forwards to use the new forwarding system if possible
206
+ // Setup domain forwards based on configuration type
180
207
  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 {
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
- forwardConfig: f.forwardConfig,
190
- acmeForwardConfig: f.acmeForwardConfig,
191
- sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
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
- 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
- );
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 port configuration
240
- const configWarnings = this.portRangeManager.validateConfiguration();
303
+ // Validate the route configuration
304
+ const configWarnings = this.routeManager.validateConfiguration();
241
305
  if (configWarnings.length > 0) {
242
- console.log("Port configuration warnings:");
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 PortRangeManager
249
- const listeningPorts = this.portRangeManager.getListeningPorts();
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.connectionHandler.handleConnection(socket);
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 ? ' (SNI passthrough enabled)' : ''
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 = domainConfig.forwarding.type;
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
- for (const config of this.settings.domainConfigs) {
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 = config.domains.filter(domain =>
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