@push.rocks/smartproxy 15.0.2 → 16.0.3

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 (160) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/index.d.ts +10 -4
  3. package/dist_ts/certificate/index.js +5 -7
  4. package/dist_ts/certificate/models/certificate-types.d.ts +35 -15
  5. package/dist_ts/certificate/providers/cert-provisioner.d.ts +41 -15
  6. package/dist_ts/certificate/providers/cert-provisioner.js +201 -41
  7. package/dist_ts/core/models/index.d.ts +2 -0
  8. package/dist_ts/core/models/index.js +3 -1
  9. package/dist_ts/core/models/route-context.d.ts +62 -0
  10. package/dist_ts/core/models/route-context.js +43 -0
  11. package/dist_ts/core/models/socket-augmentation.d.ts +12 -0
  12. package/dist_ts/core/models/socket-augmentation.js +18 -0
  13. package/dist_ts/core/utils/event-system.d.ts +200 -0
  14. package/dist_ts/core/utils/event-system.js +224 -0
  15. package/dist_ts/core/utils/index.d.ts +7 -0
  16. package/dist_ts/core/utils/index.js +8 -1
  17. package/dist_ts/core/utils/route-manager.d.ts +118 -0
  18. package/dist_ts/core/utils/route-manager.js +383 -0
  19. package/dist_ts/core/utils/route-utils.d.ts +94 -0
  20. package/dist_ts/core/utils/route-utils.js +264 -0
  21. package/dist_ts/core/utils/security-utils.d.ts +111 -0
  22. package/dist_ts/core/utils/security-utils.js +212 -0
  23. package/dist_ts/core/utils/shared-security-manager.d.ts +110 -0
  24. package/dist_ts/core/utils/shared-security-manager.js +252 -0
  25. package/dist_ts/core/utils/template-utils.d.ts +37 -0
  26. package/dist_ts/core/utils/template-utils.js +104 -0
  27. package/dist_ts/core/utils/websocket-utils.d.ts +23 -0
  28. package/dist_ts/core/utils/websocket-utils.js +86 -0
  29. package/dist_ts/forwarding/config/forwarding-types.d.ts +40 -76
  30. package/dist_ts/forwarding/config/forwarding-types.js +19 -18
  31. package/dist_ts/forwarding/config/index.d.ts +4 -2
  32. package/dist_ts/forwarding/config/index.js +5 -3
  33. package/dist_ts/forwarding/handlers/base-handler.js +3 -1
  34. package/dist_ts/forwarding/index.d.ts +5 -6
  35. package/dist_ts/forwarding/index.js +3 -3
  36. package/dist_ts/http/models/http-types.js +1 -1
  37. package/dist_ts/http/port80/acme-interfaces.d.ts +30 -0
  38. package/dist_ts/http/port80/acme-interfaces.js +46 -1
  39. package/dist_ts/http/port80/port80-handler.d.ts +17 -2
  40. package/dist_ts/http/port80/port80-handler.js +49 -11
  41. package/dist_ts/http/router/index.d.ts +5 -1
  42. package/dist_ts/http/router/index.js +4 -2
  43. package/dist_ts/http/router/route-router.d.ts +108 -0
  44. package/dist_ts/http/router/route-router.js +393 -0
  45. package/dist_ts/index.d.ts +8 -2
  46. package/dist_ts/index.js +10 -3
  47. package/dist_ts/proxies/index.d.ts +7 -2
  48. package/dist_ts/proxies/index.js +10 -4
  49. package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +21 -0
  50. package/dist_ts/proxies/network-proxy/certificate-manager.js +92 -1
  51. package/dist_ts/proxies/network-proxy/context-creator.d.ts +34 -0
  52. package/dist_ts/proxies/network-proxy/context-creator.js +108 -0
  53. package/dist_ts/proxies/network-proxy/function-cache.d.ts +90 -0
  54. package/dist_ts/proxies/network-proxy/function-cache.js +198 -0
  55. package/dist_ts/proxies/network-proxy/http-request-handler.d.ts +40 -0
  56. package/dist_ts/proxies/network-proxy/http-request-handler.js +256 -0
  57. package/dist_ts/proxies/network-proxy/http2-request-handler.d.ts +24 -0
  58. package/dist_ts/proxies/network-proxy/http2-request-handler.js +201 -0
  59. package/dist_ts/proxies/network-proxy/models/types.d.ts +73 -1
  60. package/dist_ts/proxies/network-proxy/models/types.js +242 -1
  61. package/dist_ts/proxies/network-proxy/network-proxy.d.ts +23 -20
  62. package/dist_ts/proxies/network-proxy/network-proxy.js +147 -60
  63. package/dist_ts/proxies/network-proxy/request-handler.d.ts +38 -5
  64. package/dist_ts/proxies/network-proxy/request-handler.js +584 -198
  65. package/dist_ts/proxies/network-proxy/security-manager.d.ts +65 -0
  66. package/dist_ts/proxies/network-proxy/security-manager.js +255 -0
  67. package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +13 -2
  68. package/dist_ts/proxies/network-proxy/websocket-handler.js +238 -20
  69. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  70. package/dist_ts/proxies/smart-proxy/index.js +3 -3
  71. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -66
  72. package/dist_ts/proxies/smart-proxy/models/interfaces.js +5 -4
  73. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +173 -6
  74. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +20 -7
  75. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +49 -108
  76. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +81 -0
  77. package/dist_ts/proxies/smart-proxy/port-manager.js +166 -0
  78. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +7 -5
  79. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +155 -160
  80. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +9 -0
  81. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +11 -0
  82. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +5 -125
  83. package/dist_ts/proxies/smart-proxy/route-helpers.js +8 -195
  84. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +14 -11
  85. package/dist_ts/proxies/smart-proxy/route-manager.js +81 -124
  86. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +101 -12
  87. package/dist_ts/proxies/smart-proxy/smart-proxy.js +178 -306
  88. package/dist_ts/proxies/smart-proxy/timeout-manager.js +3 -3
  89. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +12 -0
  90. package/dist_ts/proxies/smart-proxy/utils/index.js +19 -0
  91. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +240 -0
  92. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +451 -0
  93. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +51 -0
  94. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +124 -0
  95. package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +131 -0
  96. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +217 -0
  97. package/dist_ts/proxies/smart-proxy/utils/route-utils.d.ts +79 -0
  98. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +266 -0
  99. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +73 -0
  100. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +264 -0
  101. package/package.json +1 -1
  102. package/readme.md +241 -125
  103. package/readme.plan.md +73 -286
  104. package/ts/00_commitinfo_data.ts +1 -1
  105. package/ts/certificate/index.ts +17 -9
  106. package/ts/certificate/models/certificate-types.ts +37 -16
  107. package/ts/certificate/providers/cert-provisioner.ts +247 -54
  108. package/ts/core/models/index.ts +2 -0
  109. package/ts/core/models/route-context.ts +113 -0
  110. package/ts/core/models/socket-augmentation.ts +33 -0
  111. package/ts/core/utils/event-system.ts +376 -0
  112. package/ts/core/utils/index.ts +7 -0
  113. package/ts/core/utils/route-manager.ts +489 -0
  114. package/ts/core/utils/route-utils.ts +312 -0
  115. package/ts/core/utils/security-utils.ts +309 -0
  116. package/ts/core/utils/shared-security-manager.ts +333 -0
  117. package/ts/core/utils/template-utils.ts +124 -0
  118. package/ts/core/utils/websocket-utils.ts +81 -0
  119. package/ts/forwarding/config/forwarding-types.ts +79 -107
  120. package/ts/forwarding/config/index.ts +4 -2
  121. package/ts/forwarding/handlers/base-handler.ts +4 -2
  122. package/ts/forwarding/index.ts +3 -2
  123. package/ts/http/models/http-types.ts +0 -1
  124. package/ts/http/port80/acme-interfaces.ts +84 -0
  125. package/ts/http/port80/port80-handler.ts +61 -15
  126. package/ts/http/router/index.ts +8 -1
  127. package/ts/http/router/route-router.ts +482 -0
  128. package/ts/index.ts +14 -2
  129. package/ts/proxies/index.ts +12 -3
  130. package/ts/proxies/network-proxy/certificate-manager.ts +114 -10
  131. package/ts/proxies/network-proxy/context-creator.ts +145 -0
  132. package/ts/proxies/network-proxy/function-cache.ts +259 -0
  133. package/ts/proxies/network-proxy/http-request-handler.ts +330 -0
  134. package/ts/proxies/network-proxy/http2-request-handler.ts +255 -0
  135. package/ts/proxies/network-proxy/models/types.ts +312 -1
  136. package/ts/proxies/network-proxy/network-proxy.ts +195 -86
  137. package/ts/proxies/network-proxy/request-handler.ts +698 -246
  138. package/ts/proxies/network-proxy/security-manager.ts +298 -0
  139. package/ts/proxies/network-proxy/websocket-handler.ts +276 -33
  140. package/ts/proxies/smart-proxy/index.ts +2 -12
  141. package/ts/proxies/smart-proxy/models/interfaces.ts +13 -67
  142. package/ts/proxies/smart-proxy/models/route-types.ts +223 -25
  143. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +57 -123
  144. package/ts/proxies/smart-proxy/port-manager.ts +195 -0
  145. package/ts/proxies/smart-proxy/route-connection-handler.ts +191 -225
  146. package/ts/proxies/smart-proxy/route-manager.ts +101 -144
  147. package/ts/proxies/smart-proxy/smart-proxy.ts +206 -377
  148. package/ts/proxies/smart-proxy/timeout-manager.ts +2 -2
  149. package/ts/proxies/smart-proxy/utils/index.ts +40 -0
  150. package/ts/proxies/smart-proxy/utils/route-helpers.ts +621 -0
  151. package/ts/proxies/smart-proxy/utils/route-migration-utils.ts +165 -0
  152. package/ts/proxies/smart-proxy/utils/route-patterns.ts +309 -0
  153. package/ts/proxies/smart-proxy/utils/route-utils.ts +330 -0
  154. package/ts/proxies/smart-proxy/utils/route-validators.ts +288 -0
  155. package/ts/forwarding/config/domain-config.ts +0 -28
  156. package/ts/forwarding/config/domain-manager.ts +0 -283
  157. package/ts/proxies/smart-proxy/connection-handler.ts +0 -1240
  158. package/ts/proxies/smart-proxy/domain-config-manager.ts +0 -441
  159. package/ts/proxies/smart-proxy/port-range-manager.ts +0 -211
  160. package/ts/proxies/smart-proxy/route-helpers.ts +0 -344
@@ -3,11 +3,10 @@ import * as plugins from '../../plugins.js';
3
3
  // Importing required components
4
4
  import { ConnectionManager } from './connection-manager.js';
5
5
  import { SecurityManager } from './security-manager.js';
6
- import { DomainConfigManager } from './domain-config-manager.js';
7
6
  import { TlsManager } from './tls-manager.js';
8
7
  import { NetworkProxyBridge } from './network-proxy-bridge.js';
9
8
  import { TimeoutManager } from './timeout-manager.js';
10
- import { PortRangeManager } from './port-range-manager.js';
9
+ import { PortManager } from './port-manager.js';
11
10
  import { RouteManager } from './route-manager.js';
12
11
  import { RouteConnectionHandler } from './route-connection-handler.js';
13
12
 
@@ -19,31 +18,39 @@ import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
19
18
  import { createPort80HandlerOptions } from '../../common/port80-adapter.js';
20
19
 
21
20
  // Import types and utilities
22
- import type {
23
- ISmartProxyOptions,
24
- IRoutedSmartProxyOptions,
25
- IDomainConfig
21
+ import type {
22
+ ISmartProxyOptions,
23
+ IRoutedSmartProxyOptions
26
24
  } from './models/interfaces.js';
27
25
  import { isRoutedOptions, isLegacyOptions } from './models/interfaces.js';
28
26
  import type { IRouteConfig } from './models/route-types.js';
29
27
 
30
28
  /**
31
- * SmartProxy - Unified route-based API
29
+ * SmartProxy - Pure route-based API
30
+ *
31
+ * SmartProxy is a unified proxy system that works with routes to define connection handling behavior.
32
+ * Each route contains matching criteria (ports, domains, etc.) and an action to take (forward, redirect, block).
33
+ *
34
+ * Configuration is provided through a set of routes, with each route defining:
35
+ * - What to match (ports, domains, paths, client IPs)
36
+ * - What to do with matching traffic (forward, redirect, block)
37
+ * - How to handle TLS (passthrough, terminate, terminate-and-reencrypt)
38
+ * - Security settings (IP restrictions, connection limits)
39
+ * - Advanced options (timeout, headers, etc.)
32
40
  */
33
41
  export class SmartProxy extends plugins.EventEmitter {
34
- private netServers: plugins.net.Server[] = [];
42
+ // Port manager handles dynamic listener management
43
+ private portManager: PortManager;
35
44
  private connectionLogger: NodeJS.Timeout | null = null;
36
45
  private isShuttingDown: boolean = false;
37
46
 
38
47
  // Component managers
39
48
  private connectionManager: ConnectionManager;
40
49
  private securityManager: SecurityManager;
41
- private domainConfigManager: DomainConfigManager;
42
50
  private tlsManager: TlsManager;
43
51
  private networkProxyBridge: NetworkProxyBridge;
44
52
  private timeoutManager: TimeoutManager;
45
- private portRangeManager: PortRangeManager;
46
- private routeManager: RouteManager;
53
+ public routeManager: RouteManager; // Made public for route management
47
54
  private routeConnectionHandler: RouteConnectionHandler;
48
55
 
49
56
  // Port80Handler for ACME certificate management
@@ -52,7 +59,35 @@ export class SmartProxy extends plugins.EventEmitter {
52
59
  private certProvisioner?: CertProvisioner;
53
60
 
54
61
  /**
55
- * Constructor that supports both legacy and route-based configuration
62
+ * Constructor for SmartProxy
63
+ *
64
+ * @param settingsArg Configuration options containing routes and other settings
65
+ * Routes define how traffic is matched and handled, with each route having:
66
+ * - match: criteria for matching traffic (ports, domains, paths, IPs)
67
+ * - action: what to do with matched traffic (forward, redirect, block)
68
+ *
69
+ * Example:
70
+ * ```ts
71
+ * const proxy = new SmartProxy({
72
+ * routes: [
73
+ * {
74
+ * match: {
75
+ * ports: 443,
76
+ * domains: ['example.com', '*.example.com']
77
+ * },
78
+ * action: {
79
+ * type: 'forward',
80
+ * target: { host: '10.0.0.1', port: 8443 },
81
+ * tls: { mode: 'passthrough' }
82
+ * }
83
+ * }
84
+ * ],
85
+ * defaults: {
86
+ * target: { host: 'localhost', port: 8080 },
87
+ * security: { allowedIps: ['*'] }
88
+ * }
89
+ * });
90
+ * ```
56
91
  */
57
92
  constructor(settingsArg: ISmartProxyOptions) {
58
93
  super();
@@ -98,9 +133,9 @@ export class SmartProxy extends plugins.EventEmitter {
98
133
  autoRenew: true,
99
134
  certificateStore: './certs',
100
135
  skipConfiguredCerts: false,
101
- httpsRedirectPort: this.settings.fromPort || 443,
136
+ httpsRedirectPort: 443,
102
137
  renewCheckIntervalHours: 24,
103
- domainForwards: []
138
+ routeForwards: []
104
139
  };
105
140
  }
106
141
 
@@ -113,18 +148,9 @@ export class SmartProxy extends plugins.EventEmitter {
113
148
  this.timeoutManager
114
149
  );
115
150
 
116
- // Create the new route manager first
151
+ // Create the route manager
117
152
  this.routeManager = new RouteManager(this.settings);
118
153
 
119
- // Create domain config manager and port range manager
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
154
 
129
155
  // Create other required components
130
156
  this.tlsManager = new TlsManager(this.settings);
@@ -135,12 +161,14 @@ export class SmartProxy extends plugins.EventEmitter {
135
161
  this.settings,
136
162
  this.connectionManager,
137
163
  this.securityManager,
138
- this.domainConfigManager,
139
164
  this.tlsManager,
140
165
  this.networkProxyBridge,
141
166
  this.timeoutManager,
142
167
  this.routeManager
143
168
  );
169
+
170
+ // Initialize port manager
171
+ this.portManager = new PortManager(this.settings, this.routeConnectionHandler);
144
172
  }
145
173
 
146
174
  /**
@@ -162,7 +190,7 @@ export class SmartProxy extends plugins.EventEmitter {
162
190
  // Build and start the Port80Handler
163
191
  this.port80Handler = buildPort80Handler({
164
192
  ...config,
165
- httpsRedirectPort: config.httpsRedirectPort || (isLegacyOptions(this.settings) ? this.settings.fromPort : 443)
193
+ httpsRedirectPort: config.httpsRedirectPort || 443
166
194
  });
167
195
 
168
196
  // Share Port80Handler with NetworkProxyBridge before start
@@ -184,17 +212,7 @@ export class SmartProxy extends plugins.EventEmitter {
184
212
  return;
185
213
  }
186
214
 
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
- }
215
+ // Pure route-based configuration - no domain configs needed
198
216
 
199
217
  // Initialize Port80Handler if enabled
200
218
  await this.initializePort80Handler();
@@ -203,80 +221,22 @@ export class SmartProxy extends plugins.EventEmitter {
203
221
  if (this.port80Handler) {
204
222
  const acme = this.settings.acme!;
205
223
 
206
- // Setup domain forwards based on configuration type
207
- const domainForwards = acme.domainForwards?.map(f => {
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,
226
- domain: f.domain,
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
- }
239
- }
240
-
241
- // Otherwise use the existing configuration
242
- return {
243
- domain: f.domain,
244
- forwardConfig: f.forwardConfig,
245
- acmeForwardConfig: f.acmeForwardConfig,
246
- sslRedirect: f.sslRedirect || false
247
- };
248
- }) || [];
224
+ // Setup route forwards
225
+ const routeForwards = acme.routeForwards?.map(f => f) || [];
249
226
 
250
227
  // 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
- );
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
- }
228
+ // No longer need to support multiple configuration types
229
+ // Just pass the routes directly
230
+ this.certProvisioner = new CertProvisioner(
231
+ this.settings.routes,
232
+ this.port80Handler,
233
+ this.networkProxyBridge,
234
+ this.settings.certProvisionFunction,
235
+ acme.renewThresholdDays!,
236
+ acme.renewCheckIntervalHours!,
237
+ acme.autoRenew!,
238
+ routeForwards
239
+ );
280
240
 
281
241
  // Register certificate event handler
282
242
  this.certProvisioner.on('certificate', (certData) => {
@@ -312,35 +272,8 @@ export class SmartProxy extends plugins.EventEmitter {
312
272
  // Get listening ports from RouteManager
313
273
  const listeningPorts = this.routeManager.getListeningPorts();
314
274
 
315
- // Create servers for each port
316
- for (const port of listeningPorts) {
317
- const server = plugins.net.createServer((socket) => {
318
- // Check if shutting down
319
- if (this.isShuttingDown) {
320
- socket.end();
321
- socket.destroy();
322
- return;
323
- }
324
-
325
- // Delegate to route connection handler
326
- this.routeConnectionHandler.handleConnection(socket);
327
- }).on('error', (err: Error) => {
328
- console.log(`Server Error on port ${port}: ${err.message}`);
329
- });
330
-
331
- server.listen(port, () => {
332
- const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
333
- console.log(
334
- `SmartProxy -> OK: Now listening on port ${port}${
335
- isLegacyOptions(this.settings) && this.settings.sniEnabled && !isNetworkProxyPort ?
336
- ' (SNI passthrough enabled)' :
337
- ''
338
- }${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}`
339
- );
340
- });
341
-
342
- this.netServers.push(server);
343
- }
275
+ // Start port listeners using the PortManager
276
+ await this.portManager.addPorts(listeningPorts);
344
277
 
345
278
  // Set up periodic connection logging and inactivity checks
346
279
  this.connectionLogger = setInterval(() => {
@@ -416,60 +349,9 @@ export class SmartProxy extends plugins.EventEmitter {
416
349
 
417
350
  /**
418
351
  * Extract domain configurations from routes for certificate provisioning
352
+ *
353
+ * Note: This method has been removed as we now work directly with routes
419
354
  */
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
355
 
474
356
  /**
475
357
  * Stop the proxy server
@@ -477,6 +359,7 @@ export class SmartProxy extends plugins.EventEmitter {
477
359
  public async stop() {
478
360
  console.log('SmartProxy shutting down...');
479
361
  this.isShuttingDown = true;
362
+ this.portManager.setShuttingDown(true);
480
363
 
481
364
  // Stop CertProvisioner if active
482
365
  if (this.certProvisioner) {
@@ -495,31 +378,14 @@ export class SmartProxy extends plugins.EventEmitter {
495
378
  }
496
379
  }
497
380
 
498
- // Stop accepting new connections
499
- const closeServerPromises: Promise<void>[] = this.netServers.map(
500
- (server) =>
501
- new Promise<void>((resolve) => {
502
- if (!server.listening) {
503
- resolve();
504
- return;
505
- }
506
- server.close((err) => {
507
- if (err) {
508
- console.log(`Error closing server: ${err.message}`);
509
- }
510
- resolve();
511
- });
512
- })
513
- );
514
-
515
381
  // Stop the connection logger
516
382
  if (this.connectionLogger) {
517
383
  clearInterval(this.connectionLogger);
518
384
  this.connectionLogger = null;
519
385
  }
520
386
 
521
- // Wait for servers to close
522
- await Promise.all(closeServerPromises);
387
+ // Stop all port listeners
388
+ await this.portManager.closeAll();
523
389
  console.log('All servers closed. Cleaning up active connections...');
524
390
 
525
391
  // Clean up all active connections
@@ -528,195 +394,130 @@ export class SmartProxy extends plugins.EventEmitter {
528
394
  // Stop NetworkProxy
529
395
  await this.networkProxyBridge.stop();
530
396
 
531
- // Clear all servers
532
- this.netServers = [];
533
397
 
534
398
  console.log('SmartProxy shutdown complete.');
535
399
  }
536
400
 
537
401
  /**
538
- * Updates the domain configurations for the proxy (legacy support)
402
+ * Updates the domain configurations for the proxy
403
+ *
404
+ * Note: This legacy method has been removed. Use updateRoutes instead.
539
405
  */
540
- public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
541
- console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
406
+ public async updateDomainConfigs(): Promise<void> {
407
+ console.warn('Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.');
408
+ throw new Error('updateDomainConfigs() is deprecated - use updateRoutes() instead');
409
+ }
410
+
411
+ /**
412
+ * Update routes with new configuration
413
+ *
414
+ * This method replaces the current route configuration with the provided routes.
415
+ * It also provisions certificates for routes that require TLS termination and have
416
+ * `certificate: 'auto'` set in their TLS configuration.
417
+ *
418
+ * @param newRoutes Array of route configurations to use
419
+ *
420
+ * Example:
421
+ * ```ts
422
+ * proxy.updateRoutes([
423
+ * {
424
+ * match: { ports: 443, domains: 'secure.example.com' },
425
+ * action: {
426
+ * type: 'forward',
427
+ * target: { host: '10.0.0.1', port: 8443 },
428
+ * tls: { mode: 'terminate', certificate: 'auto' }
429
+ * }
430
+ * }
431
+ * ]);
432
+ * ```
433
+ */
434
+ public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
435
+ console.log(`Updating routes (${newRoutes.length} routes)`);
436
+
437
+ // Update routes in RouteManager
438
+ this.routeManager.updateRoutes(newRoutes);
542
439
 
543
- // Update domain configs in DomainConfigManager (legacy)
544
- this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
440
+ // Get the new set of required ports
441
+ const requiredPorts = this.routeManager.getListeningPorts();
545
442
 
546
- // Also update the RouteManager with these domain configs
547
- this.routeManager.updateFromDomainConfigs(newDomainConfigs);
443
+ // Update port listeners to match the new configuration
444
+ await this.portManager.updatePorts(requiredPorts);
548
445
 
549
446
  // If NetworkProxy is initialized, resync the configurations
550
447
  if (this.networkProxyBridge.getNetworkProxy()) {
551
- await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
448
+ await this.networkProxyBridge.syncRoutesToNetworkProxy(newRoutes);
552
449
  }
553
450
 
554
- // If Port80Handler is running, provision certificates based on forwarding type
451
+ // If Port80Handler is running, provision certificates based on routes
555
452
  if (this.port80Handler && this.settings.acme?.enabled) {
556
- for (const domainConfig of newDomainConfigs) {
557
- // Skip certificate provisioning for http-only or passthrough configs that don't need certs
558
- const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
559
- const needsCertificate =
560
- forwardingType === 'https-terminate-to-http' ||
561
- forwardingType === 'https-terminate-to-https';
562
-
563
- // Skip certificate provisioning if ACME is explicitly disabled for this domain
564
- const acmeDisabled = domainConfig.forwarding.acme?.enabled === false;
565
-
566
- if (!needsCertificate || acmeDisabled) {
567
- if (this.settings.enableDetailedLogging) {
568
- console.log(`Skipping certificate provisioning for ${domainConfig.domains.join(', ')} (${forwardingType})`);
569
- }
570
- continue;
571
- }
453
+ // Register all eligible domains from routes
454
+ this.port80Handler.addDomainsFromRoutes(newRoutes);
572
455
 
573
- for (const domain of domainConfig.domains) {
574
- const isWildcard = domain.includes('*');
575
- let provision: string | plugins.tsclass.network.ICert = 'http01';
456
+ // Handle static certificates from certProvisionFunction if available
457
+ if (this.settings.certProvisionFunction) {
458
+ for (const route of newRoutes) {
459
+ // Skip routes without domains
460
+ if (!route.match.domains) continue;
576
461
 
577
- // Check for ACME forwarding configuration in the domain
578
- const forwardAcmeChallenges = domainConfig.forwarding.acme?.forwardChallenges;
462
+ // Skip non-forward routes
463
+ if (route.action.type !== 'forward') continue;
579
464
 
580
- if (this.settings.certProvisionFunction) {
581
- try {
582
- provision = await this.settings.certProvisionFunction(domain);
583
- } catch (err) {
584
- console.log(`certProvider error for ${domain}: ${err}`);
585
- }
586
- } else if (isWildcard) {
587
- console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
588
- continue;
589
- }
465
+ // Skip routes without TLS termination
466
+ if (!route.action.tls ||
467
+ route.action.tls.mode === 'passthrough' ||
468
+ !route.action.target) continue;
590
469
 
591
- if (provision === 'http01') {
592
- if (isWildcard) {
593
- console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
594
- continue;
595
- }
470
+ // Skip certificate provisioning if certificate is not auto
471
+ if (route.action.tls.certificate !== 'auto') continue;
596
472
 
597
- // Create Port80Handler options from the forwarding configuration
598
- const port80Config = createPort80HandlerOptions(domain, domainConfig.forwarding);
473
+ const domains = Array.isArray(route.match.domains)
474
+ ? route.match.domains
475
+ : [route.match.domains];
599
476
 
600
- this.port80Handler.addDomain(port80Config);
601
- console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
602
- } else {
603
- // Static certificate (e.g., DNS-01 provisioned) supports wildcards
604
- const certObj = provision as plugins.tsclass.network.ICert;
605
- const certData: ICertificateData = {
606
- domain: certObj.domainName,
607
- certificate: certObj.publicKey,
608
- privateKey: certObj.privateKey,
609
- expiryDate: new Date(certObj.validUntil)
610
- };
611
- this.networkProxyBridge.applyExternalCertificate(certData);
612
- console.log(`Applied static certificate for ${domain} from certProvider`);
613
- }
614
- }
615
- }
616
- console.log('Provisioned certificates for new domains');
617
- }
618
- }
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) {
477
+ for (const domain of domains) {
667
478
  try {
668
- provision = await this.settings.certProvisionFunction(domain);
479
+ const provision = await this.settings.certProvisionFunction(domain);
480
+
481
+ // Skip http01 as those are handled by Port80Handler
482
+ if (provision !== 'http01') {
483
+ // Handle static certificate (e.g., DNS-01 provisioned)
484
+ const certObj = provision as plugins.tsclass.network.ICert;
485
+ const certData: ICertificateData = {
486
+ domain: certObj.domainName,
487
+ certificate: certObj.publicKey,
488
+ privateKey: certObj.privateKey,
489
+ expiryDate: new Date(certObj.validUntil),
490
+ routeReference: {
491
+ routeName: route.name
492
+ }
493
+ };
494
+ this.networkProxyBridge.applyExternalCertificate(certData);
495
+ console.log(`Applied static certificate for ${domain} from certProvider`);
496
+ }
669
497
  } catch (err) {
670
498
  console.log(`certProvider error for ${domain}: ${err}`);
671
499
  }
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
500
  }
703
501
  }
704
502
  }
705
-
503
+
706
504
  console.log('Provisioned certificates for new routes');
707
505
  }
708
506
  }
709
507
 
710
508
  /**
711
509
  * Request a certificate for a specific domain
510
+ *
511
+ * @param domain The domain to request a certificate for
512
+ * @param routeName Optional route name to associate with the certificate
712
513
  */
713
- public async requestCertificate(domain: string): Promise<boolean> {
514
+ public async requestCertificate(domain: string, routeName?: string): Promise<boolean> {
714
515
  // Validate domain format
715
516
  if (!this.isValidDomain(domain)) {
716
517
  console.log(`Invalid domain format: ${domain}`);
717
518
  return false;
718
519
  }
719
-
520
+
720
521
  // Use Port80Handler if available
721
522
  if (this.port80Handler) {
722
523
  try {
@@ -726,15 +527,16 @@ export class SmartProxy extends plugins.EventEmitter {
726
527
  console.log(`Certificate already exists for ${domain}, valid until ${cert.expiryDate.toISOString()}`);
727
528
  return true;
728
529
  }
729
-
530
+
730
531
  // Register domain for certificate issuance
731
532
  this.port80Handler.addDomain({
732
- domainName: domain,
533
+ domain,
733
534
  sslRedirect: true,
734
- acmeMaintenance: true
535
+ acmeMaintenance: true,
536
+ routeReference: routeName ? { routeName } : undefined
735
537
  });
736
-
737
- console.log(`Domain ${domain} registered for certificate issuance`);
538
+
539
+ console.log(`Domain ${domain} registered for certificate issuance` + (routeName ? ` for route '${routeName}'` : ''));
738
540
  return true;
739
541
  } catch (err) {
740
542
  console.log(`Error registering domain with Port80Handler: ${err}`);
@@ -771,6 +573,41 @@ export class SmartProxy extends plugins.EventEmitter {
771
573
  return true;
772
574
  }
773
575
 
576
+ /**
577
+ * Add a new listening port without changing the route configuration
578
+ *
579
+ * This allows you to add a port listener without updating routes.
580
+ * Useful for preparing to listen on a port before adding routes for it.
581
+ *
582
+ * @param port The port to start listening on
583
+ * @returns Promise that resolves when the port is listening
584
+ */
585
+ public async addListeningPort(port: number): Promise<void> {
586
+ return this.portManager.addPort(port);
587
+ }
588
+
589
+ /**
590
+ * Stop listening on a specific port without changing the route configuration
591
+ *
592
+ * This allows you to stop a port listener without updating routes.
593
+ * Useful for temporary maintenance or port changes.
594
+ *
595
+ * @param port The port to stop listening on
596
+ * @returns Promise that resolves when the port is closed
597
+ */
598
+ public async removeListeningPort(port: number): Promise<void> {
599
+ return this.portManager.removePort(port);
600
+ }
601
+
602
+ /**
603
+ * Get a list of all ports currently being listened on
604
+ *
605
+ * @returns Array of port numbers
606
+ */
607
+ public getListeningPorts(): number[] {
608
+ return this.portManager.getListeningPorts();
609
+ }
610
+
774
611
  /**
775
612
  * Get statistics about current connections
776
613
  */
@@ -800,7 +637,9 @@ export class SmartProxy extends plugins.EventEmitter {
800
637
  terminationStats,
801
638
  acmeEnabled: !!this.port80Handler,
802
639
  port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null,
803
- routes: this.routeManager.getListeningPorts().length
640
+ routes: this.routeManager.getListeningPorts().length,
641
+ listeningPorts: this.portManager.getListeningPorts(),
642
+ activePorts: this.portManager.getListeningPorts().length
804
643
  };
805
644
  }
806
645
 
@@ -834,17 +673,7 @@ export class SmartProxy extends plugins.EventEmitter {
834
673
  domains.push(...eligibleDomains);
835
674
  }
836
675
 
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
- }
676
+ // Legacy mode is no longer supported
848
677
 
849
678
  return domains;
850
679
  }