@push.rocks/smartproxy 10.2.0 → 11.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 (60) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/common/port80-adapter.d.ts +11 -0
  3. package/dist_ts/common/port80-adapter.js +61 -0
  4. package/dist_ts/examples/forwarding-example.d.ts +1 -0
  5. package/dist_ts/examples/forwarding-example.js +96 -0
  6. package/dist_ts/index.d.ts +1 -0
  7. package/dist_ts/index.js +3 -1
  8. package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
  9. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
  10. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
  11. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
  12. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
  13. package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
  14. package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
  15. package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
  16. package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
  17. package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
  18. package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
  19. package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
  20. package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
  21. package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
  22. package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
  23. package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
  24. package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
  25. package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
  26. package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
  27. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
  28. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
  29. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
  30. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
  31. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
  32. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
  33. package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
  34. package/dist_ts/smartproxy/forwarding/index.js +23 -0
  35. package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
  36. package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
  37. package/package.json +2 -2
  38. package/readme.md +158 -8
  39. package/readme.plan.md +471 -42
  40. package/ts/00_commitinfo_data.ts +1 -1
  41. package/ts/common/port80-adapter.ts +87 -0
  42. package/ts/examples/forwarding-example.ts +128 -0
  43. package/ts/index.ts +3 -0
  44. package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
  45. package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
  46. package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
  47. package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
  48. package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
  49. package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
  50. package/ts/smartproxy/classes.smartproxy.ts +68 -15
  51. package/ts/smartproxy/forwarding/domain-config.ts +28 -0
  52. package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
  53. package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
  54. package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
  55. package/ts/smartproxy/forwarding/http.handler.ts +140 -0
  56. package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
  57. package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
  58. package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
  59. package/ts/smartproxy/forwarding/index.ts +52 -0
  60. package/ts/smartproxy/types/forwarding.types.ts +162 -0
@@ -1,5 +1,7 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.js';
3
+ import type { ForwardingType, IForwardConfig, IForwardingHandler } from './types/forwarding.types.js';
4
+ import { ForwardingHandlerFactory } from './forwarding/forwarding.factory.js';
3
5
 
4
6
  /**
5
7
  * Manages domain configurations and target selection
@@ -7,7 +9,10 @@ import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.
7
9
  export class DomainConfigManager {
8
10
  // Track round-robin indices for domain configs
9
11
  private domainTargetIndices: Map<IDomainConfig, number> = new Map();
10
-
12
+
13
+ // Cache forwarding handlers for each domain config
14
+ private forwardingHandlers: Map<IDomainConfig, IForwardingHandler> = new Map();
15
+
11
16
  constructor(private settings: ISmartProxyOptions) {}
12
17
 
13
18
  /**
@@ -15,7 +20,7 @@ export class DomainConfigManager {
15
20
  */
16
21
  public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void {
17
22
  this.settings.domainConfigs = newDomainConfigs;
18
-
23
+
19
24
  // Reset target indices for removed configs
20
25
  const currentConfigSet = new Set(newDomainConfigs);
21
26
  for (const [config] of this.domainTargetIndices) {
@@ -23,6 +28,31 @@ export class DomainConfigManager {
23
28
  this.domainTargetIndices.delete(config);
24
29
  }
25
30
  }
31
+
32
+ // Clear handlers for removed configs and create handlers for new configs
33
+ const handlersToRemove: IDomainConfig[] = [];
34
+ for (const [config] of this.forwardingHandlers) {
35
+ if (!currentConfigSet.has(config)) {
36
+ handlersToRemove.push(config);
37
+ }
38
+ }
39
+
40
+ // Remove handlers that are no longer needed
41
+ for (const config of handlersToRemove) {
42
+ this.forwardingHandlers.delete(config);
43
+ }
44
+
45
+ // Create handlers for new configs
46
+ for (const config of newDomainConfigs) {
47
+ if (!this.forwardingHandlers.has(config)) {
48
+ try {
49
+ const handler = this.createForwardingHandler(config);
50
+ this.forwardingHandlers.set(config, handler);
51
+ } catch (err) {
52
+ console.log(`Error creating forwarding handler for domain ${config.domains.join(', ')}: ${err}`);
53
+ }
54
+ }
55
+ }
26
56
  }
27
57
 
28
58
  /**
@@ -48,10 +78,12 @@ export class DomainConfigManager {
48
78
  */
49
79
  public findDomainConfigForPort(port: number): IDomainConfig | undefined {
50
80
  return this.settings.domainConfigs.find(
51
- (domain) =>
52
- domain.portRanges &&
53
- domain.portRanges.length > 0 &&
54
- this.isPortInRanges(port, domain.portRanges)
81
+ (domain) => {
82
+ const portRanges = domain.forwarding?.advanced?.portRanges;
83
+ return portRanges &&
84
+ portRanges.length > 0 &&
85
+ this.isPortInRanges(port, portRanges);
86
+ }
55
87
  );
56
88
  }
57
89
 
@@ -66,48 +98,102 @@ export class DomainConfigManager {
66
98
  * Get target IP with round-robin support
67
99
  */
68
100
  public getTargetIP(domainConfig: IDomainConfig): string {
69
- if (domainConfig.targetIPs && domainConfig.targetIPs.length > 0) {
101
+ const targetHosts = Array.isArray(domainConfig.forwarding.target.host)
102
+ ? domainConfig.forwarding.target.host
103
+ : [domainConfig.forwarding.target.host];
104
+
105
+ if (targetHosts.length > 0) {
70
106
  const currentIndex = this.domainTargetIndices.get(domainConfig) || 0;
71
- const ip = domainConfig.targetIPs[currentIndex % domainConfig.targetIPs.length];
107
+ const ip = targetHosts[currentIndex % targetHosts.length];
72
108
  this.domainTargetIndices.set(domainConfig, currentIndex + 1);
73
109
  return ip;
74
110
  }
75
-
111
+
76
112
  return this.settings.targetIP || 'localhost';
77
113
  }
114
+
115
+ /**
116
+ * Get target host with round-robin support (for tests)
117
+ * This is just an alias for getTargetIP for easier test compatibility
118
+ */
119
+ public getTargetHost(domainConfig: IDomainConfig): string {
120
+ return this.getTargetIP(domainConfig);
121
+ }
122
+
123
+ /**
124
+ * Get target port from domain config
125
+ */
126
+ public getTargetPort(domainConfig: IDomainConfig, defaultPort: number): number {
127
+ return domainConfig.forwarding.target.port || defaultPort;
128
+ }
78
129
 
79
130
  /**
80
131
  * Checks if a domain should use NetworkProxy
81
132
  */
82
133
  public shouldUseNetworkProxy(domainConfig: IDomainConfig): boolean {
83
- return !!domainConfig.useNetworkProxy;
134
+ const forwardingType = this.getForwardingType(domainConfig);
135
+ return forwardingType === 'https-terminate-to-http' ||
136
+ forwardingType === 'https-terminate-to-https';
84
137
  }
85
-
138
+
86
139
  /**
87
140
  * Gets the NetworkProxy port for a domain
88
141
  */
89
142
  public getNetworkProxyPort(domainConfig: IDomainConfig): number | undefined {
90
- return domainConfig.useNetworkProxy
91
- ? (domainConfig.networkProxyPort || this.settings.networkProxyPort)
92
- : undefined;
143
+ // First check if we should use NetworkProxy at all
144
+ if (!this.shouldUseNetworkProxy(domainConfig)) {
145
+ return undefined;
146
+ }
147
+
148
+ return domainConfig.forwarding.advanced?.networkProxyPort || this.settings.networkProxyPort;
93
149
  }
94
150
 
95
151
  /**
96
152
  * Get effective allowed and blocked IPs for a domain
153
+ *
154
+ * This method combines domain-specific security rules from the forwarding configuration
155
+ * with global security defaults when necessary.
97
156
  */
98
157
  public getEffectiveIPRules(domainConfig: IDomainConfig): {
99
158
  allowedIPs: string[],
100
159
  blockedIPs: string[]
101
160
  } {
161
+ // Start with empty arrays
162
+ const allowedIPs: string[] = [];
163
+ const blockedIPs: string[] = [];
164
+
165
+ // Add IPs from forwarding security settings if available
166
+ if (domainConfig.forwarding?.security?.allowedIps) {
167
+ allowedIPs.push(...domainConfig.forwarding.security.allowedIps);
168
+ } else {
169
+ // If no allowed IPs are specified in forwarding config and global defaults exist, use them
170
+ if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
171
+ allowedIPs.push(...this.settings.defaultAllowedIPs);
172
+ } else {
173
+ // Default to allow all if no specific rules
174
+ allowedIPs.push('*');
175
+ }
176
+ }
177
+
178
+ // Add blocked IPs from forwarding security settings if available
179
+ if (domainConfig.forwarding?.security?.blockedIps) {
180
+ blockedIPs.push(...domainConfig.forwarding.security.blockedIps);
181
+ }
182
+
183
+ // Always add global blocked IPs, even if domain has its own rules
184
+ // This ensures that global blocks take precedence
185
+ if (this.settings.defaultBlockedIPs && this.settings.defaultBlockedIPs.length > 0) {
186
+ // Add only unique IPs that aren't already in the list
187
+ for (const ip of this.settings.defaultBlockedIPs) {
188
+ if (!blockedIPs.includes(ip)) {
189
+ blockedIPs.push(ip);
190
+ }
191
+ }
192
+ }
193
+
102
194
  return {
103
- allowedIPs: [
104
- ...domainConfig.allowedIPs,
105
- ...(this.settings.defaultAllowedIPs || [])
106
- ],
107
- blockedIPs: [
108
- ...(domainConfig.blockedIPs || []),
109
- ...(this.settings.defaultBlockedIPs || [])
110
- ]
195
+ allowedIPs,
196
+ blockedIPs
111
197
  };
112
198
  }
113
199
 
@@ -115,9 +201,97 @@ export class DomainConfigManager {
115
201
  * Get connection timeout for a domain
116
202
  */
117
203
  public getConnectionTimeout(domainConfig?: IDomainConfig): number {
118
- if (domainConfig?.connectionTimeout) {
119
- return domainConfig.connectionTimeout;
204
+ if (domainConfig?.forwarding.advanced?.timeout) {
205
+ return domainConfig.forwarding.advanced.timeout;
120
206
  }
207
+
121
208
  return this.settings.maxConnectionLifetime || 86400000; // 24 hours default
122
209
  }
210
+
211
+ /**
212
+ * Creates a forwarding handler for a domain configuration
213
+ */
214
+ private createForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
215
+ // Create a new handler using the factory
216
+ const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding);
217
+
218
+ // Initialize the handler
219
+ handler.initialize().catch(err => {
220
+ console.log(`Error initializing forwarding handler for ${domainConfig.domains.join(', ')}: ${err}`);
221
+ });
222
+
223
+ return handler;
224
+ }
225
+
226
+ /**
227
+ * Gets a forwarding handler for a domain config
228
+ * If no handler exists, creates one
229
+ */
230
+ public getForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
231
+ // If we already have a handler, return it
232
+ if (this.forwardingHandlers.has(domainConfig)) {
233
+ return this.forwardingHandlers.get(domainConfig)!;
234
+ }
235
+
236
+ // Otherwise create a new handler
237
+ const handler = this.createForwardingHandler(domainConfig);
238
+ this.forwardingHandlers.set(domainConfig, handler);
239
+
240
+ return handler;
241
+ }
242
+
243
+ /**
244
+ * Gets the forwarding type for a domain config
245
+ */
246
+ public getForwardingType(domainConfig?: IDomainConfig): ForwardingType | undefined {
247
+ if (!domainConfig?.forwarding) return undefined;
248
+ return domainConfig.forwarding.type;
249
+ }
250
+
251
+ /**
252
+ * Checks if the forwarding type requires TLS termination
253
+ */
254
+ public requiresTlsTermination(domainConfig?: IDomainConfig): boolean {
255
+ if (!domainConfig) return false;
256
+
257
+ const forwardingType = this.getForwardingType(domainConfig);
258
+ return forwardingType === 'https-terminate-to-http' ||
259
+ forwardingType === 'https-terminate-to-https';
260
+ }
261
+
262
+ /**
263
+ * Checks if the forwarding type supports HTTP
264
+ */
265
+ public supportsHttp(domainConfig?: IDomainConfig): boolean {
266
+ if (!domainConfig) return false;
267
+
268
+ const forwardingType = this.getForwardingType(domainConfig);
269
+
270
+ // HTTP-only always supports HTTP
271
+ if (forwardingType === 'http-only') return true;
272
+
273
+ // For termination types, check the HTTP settings
274
+ if (forwardingType === 'https-terminate-to-http' ||
275
+ forwardingType === 'https-terminate-to-https') {
276
+ // HTTP is supported by default for termination types
277
+ return domainConfig.forwarding?.http?.enabled !== false;
278
+ }
279
+
280
+ // HTTPS-passthrough doesn't support HTTP
281
+ return false;
282
+ }
283
+
284
+ /**
285
+ * Checks if HTTP requests should be redirected to HTTPS
286
+ */
287
+ public shouldRedirectToHttps(domainConfig?: IDomainConfig): boolean {
288
+ if (!domainConfig?.forwarding) return false;
289
+
290
+ // Only check for redirect if HTTP is enabled
291
+ if (this.supportsHttp(domainConfig)) {
292
+ return !!domainConfig.forwarding.http?.redirectToHttps;
293
+ }
294
+
295
+ return false;
296
+ }
123
297
  }
@@ -1,23 +1,15 @@
1
1
  import * as plugins from '../plugins.js';
2
+ import type { IForwardConfig } from './forwarding/index.js';
2
3
 
3
4
  /**
4
5
  * Provision object for static or HTTP-01 certificate
5
6
  */
6
7
  export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
7
8
 
8
- /** Domain configuration with per-domain allowed port ranges */
9
+ /** Domain configuration with forwarding configuration */
9
10
  export interface IDomainConfig {
10
11
  domains: string[]; // Glob patterns for domain(s)
11
- allowedIPs: string[]; // Glob patterns for allowed IPs
12
- blockedIPs?: string[]; // Glob patterns for blocked IPs
13
- targetIPs?: string[]; // If multiple targetIPs are given, use round robin.
14
- portRanges?: Array<{ from: number; to: number }>; // Optional port ranges
15
- // Allow domain-specific timeout override
16
- connectionTimeout?: number; // Connection timeout override (ms)
17
-
18
- // NetworkProxy integration options for this specific domain
19
- useNetworkProxy?: boolean; // Whether to use NetworkProxy for this domain
20
- networkProxyPort?: number; // Override default NetworkProxy port for this domain
12
+ forwarding: IForwardConfig; // Unified forwarding configuration
21
13
  }
22
14
 
23
15
  /** Port proxy settings including global allowed port ranges */
@@ -84,8 +84,10 @@ export class PortRangeManager {
84
84
  } | undefined {
85
85
  for (let i = 0; i < this.settings.domainConfigs.length; i++) {
86
86
  const domain = this.settings.domainConfigs[i];
87
- if (domain.portRanges) {
88
- for (const range of domain.portRanges) {
87
+ // Get port ranges from forwarding.advanced if available
88
+ const portRanges = domain.forwarding?.advanced?.portRanges;
89
+ if (portRanges && portRanges.length > 0) {
90
+ for (const range of portRanges) {
89
91
  if (port >= range.from && port <= range.to) {
90
92
  return { domainIndex: i, range };
91
93
  }
@@ -129,17 +131,20 @@ export class PortRangeManager {
129
131
 
130
132
  // Add domain-specific port ranges
131
133
  for (const domain of this.settings.domainConfigs) {
132
- if (domain.portRanges) {
133
- for (const range of domain.portRanges) {
134
+ // Get port ranges from forwarding.advanced
135
+ const portRanges = domain.forwarding?.advanced?.portRanges;
136
+ if (portRanges && portRanges.length > 0) {
137
+ for (const range of portRanges) {
134
138
  for (let port = range.from; port <= range.to; port++) {
135
139
  ports.add(port);
136
140
  }
137
141
  }
138
142
  }
139
-
140
- // Add domain-specific NetworkProxy port if configured
141
- if (domain.useNetworkProxy && domain.networkProxyPort) {
142
- ports.add(domain.networkProxyPort);
143
+
144
+ // Add domain-specific NetworkProxy port if configured in forwarding.advanced
145
+ const networkProxyPort = domain.forwarding?.advanced?.networkProxyPort;
146
+ if (networkProxyPort) {
147
+ ports.add(networkProxyPort);
143
148
  }
144
149
  }
145
150
 
@@ -170,8 +175,10 @@ export class PortRangeManager {
170
175
 
171
176
  // Track domain-specific port ranges
172
177
  for (const domain of this.settings.domainConfigs) {
173
- if (domain.portRanges) {
174
- for (const range of domain.portRanges) {
178
+ // Get port ranges from forwarding.advanced
179
+ const portRanges = domain.forwarding?.advanced?.portRanges;
180
+ if (portRanges && portRanges.length > 0) {
181
+ for (const range of portRanges) {
175
182
  for (let port = range.from; port <= range.to; port++) {
176
183
  if (!portMappings.has(port)) {
177
184
  portMappings.set(port, []);
@@ -63,45 +63,69 @@ export class SecurityManager {
63
63
  }
64
64
 
65
65
  /**
66
- * Check if an IP is allowed using glob patterns
66
+ * Check if an IP is authorized using forwarding security rules
67
+ *
68
+ * This method is used to determine if an IP is allowed to connect, based on security
69
+ * rules configured in the forwarding configuration. The allowed and blocked IPs are
70
+ * typically derived from domain.forwarding.security.allowedIps and blockedIps through
71
+ * DomainConfigManager.getEffectiveIPRules().
72
+ *
73
+ * @param ip - The IP address to check
74
+ * @param allowedIPs - Array of allowed IP patterns from forwarding.security.allowedIps
75
+ * @param blockedIPs - Array of blocked IP patterns from forwarding.security.blockedIps
76
+ * @returns true if IP is authorized, false if blocked
67
77
  */
68
78
  public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean {
69
79
  // Skip IP validation if allowedIPs is empty
70
80
  if (!ip || (allowedIPs.length === 0 && blockedIPs.length === 0)) {
71
81
  return true;
72
82
  }
73
-
74
- // First check if IP is blocked
83
+
84
+ // First check if IP is blocked - blocked IPs take precedence
75
85
  if (blockedIPs.length > 0 && this.isGlobIPMatch(ip, blockedIPs)) {
76
86
  return false;
77
87
  }
78
-
88
+
79
89
  // Then check if IP is allowed
80
90
  return this.isGlobIPMatch(ip, allowedIPs);
81
91
  }
82
92
 
83
93
  /**
84
- * Check if the IP matches any of the glob patterns
94
+ * Check if the IP matches any of the glob patterns from security configuration
95
+ *
96
+ * This method checks IP addresses against glob patterns and handles IPv4/IPv6 normalization.
97
+ * It's used to implement IP filtering based on the forwarding.security configuration.
98
+ *
99
+ * @param ip - The IP address to check
100
+ * @param patterns - Array of glob patterns from forwarding.security.allowedIps or blockedIps
101
+ * @returns true if IP matches any pattern, false otherwise
85
102
  */
86
103
  private isGlobIPMatch(ip: string, patterns: string[]): boolean {
87
104
  if (!ip || !patterns || patterns.length === 0) return false;
88
105
 
106
+ // Handle IPv4/IPv6 normalization for proper matching
89
107
  const normalizeIP = (ip: string): string[] => {
90
108
  if (!ip) return [];
109
+ // Handle IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
91
110
  if (ip.startsWith('::ffff:')) {
92
111
  const ipv4 = ip.slice(7);
93
112
  return [ip, ipv4];
94
113
  }
114
+ // Handle IPv4 addresses by also checking IPv4-mapped form
95
115
  if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
96
116
  return [ip, `::ffff:${ip}`];
97
117
  }
98
118
  return [ip];
99
119
  };
100
120
 
121
+ // Normalize the IP being checked
101
122
  const normalizedIPVariants = normalizeIP(ip);
102
123
  if (normalizedIPVariants.length === 0) return false;
103
124
 
125
+ // Normalize the pattern IPs for consistent comparison
104
126
  const expandedPatterns = patterns.flatMap(normalizeIP);
127
+
128
+ // Check for any match between normalized IP variants and patterns
105
129
  return normalizedIPVariants.some((ipVariant) =>
106
130
  expandedPatterns.some((pattern) => plugins.minimatch(ipVariant, pattern))
107
131
  );
@@ -61,9 +61,9 @@ export class TimeoutManager {
61
61
  * Calculate effective max lifetime based on connection type
62
62
  */
63
63
  public getEffectiveMaxLifetime(record: IConnectionRecord): number {
64
- // Use domain-specific timeout if available
65
- const baseTimeout = record.domainConfig?.connectionTimeout ||
66
- this.settings.maxConnectionLifetime ||
64
+ // Use domain-specific timeout from forwarding.advanced if available
65
+ const baseTimeout = record.domainConfig?.forwarding?.advanced?.timeout ||
66
+ this.settings.maxConnectionLifetime ||
67
67
  86400000; // 24 hours default
68
68
 
69
69
  // For immortal keep-alive connections, use an extremely long lifetime
@@ -12,6 +12,8 @@ import { Port80Handler } from '../port80handler/classes.port80handler.js';
12
12
  import { CertProvisioner } from './classes.pp.certprovisioner.js';
13
13
  import type { ICertificateData } from '../common/types.js';
14
14
  import { buildPort80Handler } from '../common/acmeFactory.js';
15
+ import type { ForwardingType } from './types/forwarding.types.js';
16
+ import { createPort80HandlerOptions } from '../common/port80-adapter.js';
15
17
 
16
18
  import type { ISmartProxyOptions, IDomainConfig } from './classes.pp.interfaces.js';
17
19
  export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
@@ -156,11 +158,44 @@ export class SmartProxy extends plugins.EventEmitter {
156
158
  return;
157
159
  }
158
160
 
161
+ // Process domain configs
162
+ // Note: ensureForwardingConfig is no longer needed since forwarding is now required
163
+
164
+ // Initialize domain config manager with the processed configs
165
+ this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs);
166
+
159
167
  // Initialize Port80Handler if enabled
160
168
  await this.initializePort80Handler();
169
+
161
170
  // Initialize CertProvisioner for unified certificate workflows
162
171
  if (this.port80Handler) {
163
172
  const acme = this.settings.acme!;
173
+
174
+ // Convert domain forwards to use the new forwarding system if possible
175
+ const domainForwards = acme.domainForwards?.map(f => {
176
+ // If the domain has a forwarding config in domainConfigs, use that
177
+ const domainConfig = this.settings.domainConfigs.find(
178
+ dc => dc.domains.some(d => d === f.domain)
179
+ );
180
+
181
+ if (domainConfig?.forwarding) {
182
+ return {
183
+ domain: f.domain,
184
+ forwardConfig: f.forwardConfig,
185
+ acmeForwardConfig: f.acmeForwardConfig,
186
+ sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
187
+ };
188
+ }
189
+
190
+ // Otherwise use the existing configuration
191
+ return {
192
+ domain: f.domain,
193
+ forwardConfig: f.forwardConfig,
194
+ acmeForwardConfig: f.acmeForwardConfig,
195
+ sslRedirect: f.sslRedirect || false
196
+ };
197
+ }) || [];
198
+
164
199
  this.certProvisioner = new CertProvisioner(
165
200
  this.settings.domainConfigs,
166
201
  this.port80Handler,
@@ -169,13 +204,9 @@ export class SmartProxy extends plugins.EventEmitter {
169
204
  acme.renewThresholdDays!,
170
205
  acme.renewCheckIntervalHours!,
171
206
  acme.autoRenew!,
172
- acme.domainForwards?.map(f => ({
173
- domain: f.domain,
174
- forwardConfig: f.forwardConfig,
175
- acmeForwardConfig: f.acmeForwardConfig,
176
- sslRedirect: f.sslRedirect || false
177
- })) || []
207
+ domainForwards
178
208
  );
209
+
179
210
  this.certProvisioner.on('certificate', (certData) => {
180
211
  this.emit('certificate', {
181
212
  domain: certData.domain,
@@ -186,6 +217,7 @@ export class SmartProxy extends plugins.EventEmitter {
186
217
  isRenewal: certData.isRenewal
187
218
  });
188
219
  });
220
+
189
221
  await this.certProvisioner.start();
190
222
  console.log('CertProvisioner started');
191
223
  }
@@ -378,21 +410,41 @@ export class SmartProxy extends plugins.EventEmitter {
378
410
  */
379
411
  public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
380
412
  console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
381
-
413
+
382
414
  // Update domain configs in DomainConfigManager
383
415
  this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
384
-
416
+
385
417
  // If NetworkProxy is initialized, resync the configurations
386
418
  if (this.networkProxyBridge.getNetworkProxy()) {
387
419
  await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
388
420
  }
389
-
390
- // If Port80Handler is running, provision certificates per new domain
421
+
422
+ // If Port80Handler is running, provision certificates based on forwarding type
391
423
  if (this.port80Handler && this.settings.acme?.enabled) {
392
424
  for (const domainConfig of newDomainConfigs) {
425
+ // Skip certificate provisioning for http-only or passthrough configs that don't need certs
426
+ const forwardingType = domainConfig.forwarding.type;
427
+ const needsCertificate =
428
+ forwardingType === 'https-terminate-to-http' ||
429
+ forwardingType === 'https-terminate-to-https';
430
+
431
+ // Skip certificate provisioning if ACME is explicitly disabled for this domain
432
+ const acmeDisabled = domainConfig.forwarding.acme?.enabled === false;
433
+
434
+ if (!needsCertificate || acmeDisabled) {
435
+ if (this.settings.enableDetailedLogging) {
436
+ console.log(`Skipping certificate provisioning for ${domainConfig.domains.join(', ')} (${forwardingType})`);
437
+ }
438
+ continue;
439
+ }
440
+
393
441
  for (const domain of domainConfig.domains) {
394
442
  const isWildcard = domain.includes('*');
395
443
  let provision: string | plugins.tsclass.network.ICert = 'http01';
444
+
445
+ // Check for ACME forwarding configuration in the domain
446
+ const forwardAcmeChallenges = domainConfig.forwarding.acme?.forwardChallenges;
447
+
396
448
  if (this.settings.certProvisionFunction) {
397
449
  try {
398
450
  provision = await this.settings.certProvisionFunction(domain);
@@ -403,16 +455,17 @@ export class SmartProxy extends plugins.EventEmitter {
403
455
  console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
404
456
  continue;
405
457
  }
458
+
406
459
  if (provision === 'http01') {
407
460
  if (isWildcard) {
408
461
  console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
409
462
  continue;
410
463
  }
411
- this.port80Handler.addDomain({
412
- domainName: domain,
413
- sslRedirect: true,
414
- acmeMaintenance: true
415
- });
464
+
465
+ // Create Port80Handler options from the forwarding configuration
466
+ const port80Config = createPort80HandlerOptions(domain, domainConfig.forwarding);
467
+
468
+ this.port80Handler.addDomain(port80Config);
416
469
  console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
417
470
  } else {
418
471
  // Static certificate (e.g., DNS-01 provisioned) supports wildcards
@@ -0,0 +1,28 @@
1
+ import type { IForwardConfig } from '../types/forwarding.types.js';
2
+
3
+ /**
4
+ * Domain configuration with unified forwarding configuration
5
+ */
6
+ export interface IDomainConfig {
7
+ // Core properties - domain patterns
8
+ domains: string[];
9
+
10
+ // Unified forwarding configuration
11
+ forwarding: IForwardConfig;
12
+ }
13
+
14
+ /**
15
+ * Helper function to create a domain configuration
16
+ */
17
+ export function createDomainConfig(
18
+ domains: string | string[],
19
+ forwarding: IForwardConfig
20
+ ): IDomainConfig {
21
+ // Normalize domains to an array
22
+ const domainArray = Array.isArray(domains) ? domains : [domains];
23
+
24
+ return {
25
+ domains: domainArray,
26
+ forwarding
27
+ };
28
+ }