@push.rocks/smartproxy 15.0.2 → 16.0.2

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 (80) 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/forwarding/config/forwarding-types.d.ts +40 -76
  8. package/dist_ts/forwarding/config/forwarding-types.js +19 -18
  9. package/dist_ts/forwarding/config/index.d.ts +4 -2
  10. package/dist_ts/forwarding/config/index.js +5 -3
  11. package/dist_ts/forwarding/handlers/base-handler.js +3 -1
  12. package/dist_ts/forwarding/index.d.ts +5 -6
  13. package/dist_ts/forwarding/index.js +3 -3
  14. package/dist_ts/http/models/http-types.js +1 -1
  15. package/dist_ts/http/port80/acme-interfaces.d.ts +30 -0
  16. package/dist_ts/http/port80/acme-interfaces.js +46 -1
  17. package/dist_ts/http/port80/port80-handler.d.ts +17 -2
  18. package/dist_ts/http/port80/port80-handler.js +49 -11
  19. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -61
  20. package/dist_ts/proxies/smart-proxy/models/interfaces.js +5 -4
  21. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +118 -4
  22. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +70 -4
  23. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +193 -43
  24. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -5
  25. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +25 -146
  26. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +7 -0
  27. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +9 -0
  28. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +54 -1
  29. package/dist_ts/proxies/smart-proxy/route-helpers.js +102 -1
  30. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +3 -9
  31. package/dist_ts/proxies/smart-proxy/route-manager.js +3 -115
  32. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +72 -10
  33. package/dist_ts/proxies/smart-proxy/smart-proxy.js +135 -268
  34. package/dist_ts/proxies/smart-proxy/timeout-manager.js +3 -3
  35. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +12 -0
  36. package/dist_ts/proxies/smart-proxy/utils/index.js +19 -0
  37. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +174 -0
  38. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +332 -0
  39. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +51 -0
  40. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +124 -0
  41. package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +131 -0
  42. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +217 -0
  43. package/dist_ts/proxies/smart-proxy/utils/route-utils.d.ts +79 -0
  44. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +266 -0
  45. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +73 -0
  46. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +242 -0
  47. package/package.json +1 -1
  48. package/readme.md +139 -111
  49. package/readme.plan.md +164 -312
  50. package/ts/00_commitinfo_data.ts +1 -1
  51. package/ts/certificate/index.ts +17 -9
  52. package/ts/certificate/models/certificate-types.ts +37 -16
  53. package/ts/certificate/providers/cert-provisioner.ts +247 -54
  54. package/ts/forwarding/config/forwarding-types.ts +79 -107
  55. package/ts/forwarding/config/index.ts +4 -2
  56. package/ts/forwarding/handlers/base-handler.ts +4 -2
  57. package/ts/forwarding/index.ts +3 -2
  58. package/ts/http/models/http-types.ts +0 -1
  59. package/ts/http/port80/acme-interfaces.ts +84 -0
  60. package/ts/http/port80/port80-handler.ts +61 -15
  61. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -64
  62. package/ts/proxies/smart-proxy/models/route-types.ts +152 -22
  63. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +226 -55
  64. package/ts/proxies/smart-proxy/route-connection-handler.ts +36 -205
  65. package/ts/proxies/smart-proxy/route-helpers/index.ts +9 -0
  66. package/ts/proxies/smart-proxy/route-helpers.ts +165 -11
  67. package/ts/proxies/smart-proxy/route-manager.ts +3 -130
  68. package/ts/proxies/smart-proxy/smart-proxy.ts +157 -329
  69. package/ts/proxies/smart-proxy/timeout-manager.ts +2 -2
  70. package/ts/proxies/smart-proxy/utils/index.ts +40 -0
  71. package/ts/proxies/smart-proxy/utils/route-helpers.ts +455 -0
  72. package/ts/proxies/smart-proxy/utils/route-migration-utils.ts +165 -0
  73. package/ts/proxies/smart-proxy/utils/route-patterns.ts +309 -0
  74. package/ts/proxies/smart-proxy/utils/route-utils.ts +330 -0
  75. package/ts/proxies/smart-proxy/utils/route-validators.ts +269 -0
  76. package/ts/forwarding/config/domain-config.ts +0 -28
  77. package/ts/forwarding/config/domain-manager.ts +0 -283
  78. package/ts/proxies/smart-proxy/connection-handler.ts +0 -1240
  79. package/ts/proxies/smart-proxy/port-range-manager.ts +0 -211
  80. /package/ts/proxies/smart-proxy/{domain-config-manager.ts → domain-config-manager.ts.bak} +0 -0
@@ -1,63 +1,112 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import type { IDomainConfig } from '../../forwarding/config/domain-config.js';
3
- import type { ICertificateData, IDomainForwardConfig, IDomainOptions } from '../models/certificate-types.js';
2
+ import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
3
+ import type { ICertificateData, IRouteForwardConfig, IDomainOptions } from '../models/certificate-types.js';
4
4
  import { Port80HandlerEvents, CertProvisionerEvents } from '../events/certificate-events.js';
5
5
  import { Port80Handler } from '../../http/port80/port80-handler.js';
6
- // We need to define this interface until we migrate NetworkProxyBridge
6
+
7
+ // Interface for NetworkProxyBridge
7
8
  interface INetworkProxyBridge {
8
9
  applyExternalCertificate(certData: ICertificateData): void;
9
10
  }
10
11
 
11
- // This will be imported after NetworkProxyBridge is migrated
12
- // import type { NetworkProxyBridge } from '../../proxies/smart-proxy/network-proxy-bridge.js';
13
-
14
- // For backward compatibility
15
- export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
16
-
17
12
  /**
18
13
  * Type for static certificate provisioning
19
14
  */
20
15
  export type TCertProvisionObject = plugins.tsclass.network.ICert | 'http01' | 'dns01';
21
16
 
17
+ /**
18
+ * Interface for routes that need certificates
19
+ */
20
+ interface ICertRoute {
21
+ domain: string;
22
+ route: IRouteConfig;
23
+ tlsMode: 'terminate' | 'terminate-and-reencrypt';
24
+ }
25
+
22
26
  /**
23
27
  * CertProvisioner manages certificate provisioning and renewal workflows,
24
28
  * unifying static certificates and HTTP-01 challenges via Port80Handler.
29
+ *
30
+ * This class directly works with route configurations instead of converting to domain configs.
25
31
  */
26
32
  export class CertProvisioner extends plugins.EventEmitter {
27
- private domainConfigs: IDomainConfig[];
33
+ private routeConfigs: IRouteConfig[];
34
+ private certRoutes: ICertRoute[] = [];
28
35
  private port80Handler: Port80Handler;
29
36
  private networkProxyBridge: INetworkProxyBridge;
30
37
  private certProvisionFunction?: (domain: string) => Promise<TCertProvisionObject>;
31
- private forwardConfigs: IDomainForwardConfig[];
38
+ private routeForwards: IRouteForwardConfig[];
32
39
  private renewThresholdDays: number;
33
40
  private renewCheckIntervalHours: number;
34
41
  private autoRenew: boolean;
35
42
  private renewManager?: plugins.taskbuffer.TaskManager;
36
43
  // Track provisioning type per domain
37
- private provisionMap: Map<string, 'http01' | 'dns01' | 'static'>;
44
+ private provisionMap: Map<string, { type: 'http01' | 'dns01' | 'static', routeRef?: ICertRoute }>;
45
+
46
+ /**
47
+ * Extract routes that need certificates
48
+ * @param routes Route configurations
49
+ */
50
+ private extractCertificateRoutesFromRoutes(routes: IRouteConfig[]): ICertRoute[] {
51
+ const certRoutes: ICertRoute[] = [];
52
+
53
+ // Process all HTTPS routes that need certificates
54
+ for (const route of routes) {
55
+ // Only process routes with TLS termination that need certificates
56
+ if (route.action.type === 'forward' &&
57
+ route.action.tls &&
58
+ (route.action.tls.mode === 'terminate' || route.action.tls.mode === 'terminate-and-reencrypt') &&
59
+ route.match.domains) {
60
+
61
+ // Extract domains from the route
62
+ const domains = Array.isArray(route.match.domains)
63
+ ? route.match.domains
64
+ : [route.match.domains];
65
+
66
+ // For each domain in the route, create a certRoute entry
67
+ for (const domain of domains) {
68
+ // Skip wildcard domains that can't use ACME unless we have a certProvider
69
+ if (domain.includes('*') && (!this.certProvisionFunction || this.certProvisionFunction.length === 0)) {
70
+ console.warn(`Skipping wildcard domain that requires a certProvisionFunction: ${domain}`);
71
+ continue;
72
+ }
73
+
74
+ certRoutes.push({
75
+ domain,
76
+ route,
77
+ tlsMode: route.action.tls.mode
78
+ });
79
+ }
80
+ }
81
+ }
82
+
83
+ return certRoutes;
84
+ }
38
85
 
39
86
  /**
40
- * @param domainConfigs Array of domain configuration objects
87
+ * Constructor for CertProvisioner
88
+ *
89
+ * @param routeConfigs Array of route configurations
41
90
  * @param port80Handler HTTP-01 challenge handler instance
42
91
  * @param networkProxyBridge Bridge for applying external certificates
43
92
  * @param certProvider Optional callback returning a static cert or 'http01'
44
93
  * @param renewThresholdDays Days before expiry to trigger renewals
45
94
  * @param renewCheckIntervalHours Interval in hours to check for renewals
46
95
  * @param autoRenew Whether to automatically schedule renewals
47
- * @param forwardConfigs Domain forwarding configurations for ACME challenges
96
+ * @param routeForwards Route-specific forwarding configs for ACME challenges
48
97
  */
49
98
  constructor(
50
- domainConfigs: IDomainConfig[],
99
+ routeConfigs: IRouteConfig[],
51
100
  port80Handler: Port80Handler,
52
101
  networkProxyBridge: INetworkProxyBridge,
53
102
  certProvider?: (domain: string) => Promise<TCertProvisionObject>,
54
103
  renewThresholdDays: number = 30,
55
104
  renewCheckIntervalHours: number = 24,
56
105
  autoRenew: boolean = true,
57
- forwardConfigs: IDomainForwardConfig[] = []
106
+ routeForwards: IRouteForwardConfig[] = []
58
107
  ) {
59
108
  super();
60
- this.domainConfigs = domainConfigs;
109
+ this.routeConfigs = routeConfigs;
61
110
  this.port80Handler = port80Handler;
62
111
  this.networkProxyBridge = networkProxyBridge;
63
112
  this.certProvisionFunction = certProvider;
@@ -65,7 +114,10 @@ export class CertProvisioner extends plugins.EventEmitter {
65
114
  this.renewCheckIntervalHours = renewCheckIntervalHours;
66
115
  this.autoRenew = autoRenew;
67
116
  this.provisionMap = new Map();
68
- this.forwardConfigs = forwardConfigs;
117
+ this.routeForwards = routeForwards;
118
+
119
+ // Extract certificate routes during instantiation
120
+ this.certRoutes = this.extractCertificateRoutesFromRoutes(routeConfigs);
69
121
  }
70
122
 
71
123
  /**
@@ -75,11 +127,11 @@ export class CertProvisioner extends plugins.EventEmitter {
75
127
  // Subscribe to Port80Handler certificate events
76
128
  this.setupEventSubscriptions();
77
129
 
78
- // Apply external forwarding for ACME challenges
130
+ // Apply route forwarding for ACME challenges
79
131
  this.setupForwardingConfigs();
80
132
 
81
- // Initial provisioning for all domains
82
- await this.provisionAllDomains();
133
+ // Initial provisioning for all domains in routes
134
+ await this.provisionAllCertificates();
83
135
 
84
136
  // Schedule renewals if enabled
85
137
  if (this.autoRenew) {
@@ -91,13 +143,36 @@ export class CertProvisioner extends plugins.EventEmitter {
91
143
  * Set up event subscriptions for certificate events
92
144
  */
93
145
  private setupEventSubscriptions(): void {
94
- // We need to reimplement subscribeToPort80Handler here
95
146
  this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
96
- this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, { ...data, source: 'http01', isRenewal: false });
147
+ // Add route reference if we have it
148
+ const routeRef = this.findRouteForDomain(data.domain);
149
+ const enhancedData: ICertificateData = {
150
+ ...data,
151
+ source: 'http01',
152
+ isRenewal: false,
153
+ routeReference: routeRef ? {
154
+ routeId: routeRef.route.name,
155
+ routeName: routeRef.route.name
156
+ } : undefined
157
+ };
158
+
159
+ this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, enhancedData);
97
160
  });
98
161
 
99
162
  this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
100
- this.emit(CertProvisionerEvents.CERTIFICATE_RENEWED, { ...data, source: 'http01', isRenewal: true });
163
+ // Add route reference if we have it
164
+ const routeRef = this.findRouteForDomain(data.domain);
165
+ const enhancedData: ICertificateData = {
166
+ ...data,
167
+ source: 'http01',
168
+ isRenewal: true,
169
+ routeReference: routeRef ? {
170
+ routeId: routeRef.route.name,
171
+ routeName: routeRef.route.name
172
+ } : undefined
173
+ };
174
+
175
+ this.emit(CertProvisionerEvents.CERTIFICATE_RENEWED, enhancedData);
101
176
  });
102
177
 
103
178
  this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, (error) => {
@@ -105,38 +180,45 @@ export class CertProvisioner extends plugins.EventEmitter {
105
180
  });
106
181
  }
107
182
 
183
+ /**
184
+ * Find a route for a given domain
185
+ */
186
+ private findRouteForDomain(domain: string): ICertRoute | undefined {
187
+ return this.certRoutes.find(certRoute => certRoute.domain === domain);
188
+ }
189
+
108
190
  /**
109
191
  * Set up forwarding configurations for the Port80Handler
110
192
  */
111
193
  private setupForwardingConfigs(): void {
112
- for (const config of this.forwardConfigs) {
194
+ for (const config of this.routeForwards) {
113
195
  const domainOptions: IDomainOptions = {
114
196
  domainName: config.domain,
115
197
  sslRedirect: config.sslRedirect || false,
116
198
  acmeMaintenance: false,
117
- forward: config.forwardConfig,
118
- acmeForward: config.acmeForwardConfig
199
+ forward: config.target ? {
200
+ ip: config.target.host,
201
+ port: config.target.port
202
+ } : undefined
119
203
  };
120
204
  this.port80Handler.addDomain(domainOptions);
121
205
  }
122
206
  }
123
207
 
124
208
  /**
125
- * Provision certificates for all configured domains
209
+ * Provision certificates for all routes that need them
126
210
  */
127
- private async provisionAllDomains(): Promise<void> {
128
- const domains = this.domainConfigs.flatMap(cfg => cfg.domains);
129
-
130
- for (const domain of domains) {
131
- await this.provisionDomain(domain);
211
+ private async provisionAllCertificates(): Promise<void> {
212
+ for (const certRoute of this.certRoutes) {
213
+ await this.provisionCertificateForRoute(certRoute);
132
214
  }
133
215
  }
134
216
 
135
217
  /**
136
- * Provision a certificate for a single domain
137
- * @param domain Domain to provision
218
+ * Provision a certificate for a route
138
219
  */
139
- private async provisionDomain(domain: string): Promise<void> {
220
+ private async provisionCertificateForRoute(certRoute: ICertRoute): Promise<void> {
221
+ const { domain, route } = certRoute;
140
222
  const isWildcard = domain.includes('*');
141
223
  let provision: TCertProvisionObject = 'http01';
142
224
 
@@ -145,7 +227,7 @@ export class CertProvisioner extends plugins.EventEmitter {
145
227
  try {
146
228
  provision = await this.certProvisionFunction(domain);
147
229
  } catch (err) {
148
- console.error(`certProvider error for ${domain}:`, err);
230
+ console.error(`certProvider error for ${domain} on route ${route.name || 'unnamed'}:`, err);
149
231
  }
150
232
  } else if (isWildcard) {
151
233
  // No certProvider: cannot handle wildcard without DNS-01 support
@@ -153,6 +235,12 @@ export class CertProvisioner extends plugins.EventEmitter {
153
235
  return;
154
236
  }
155
237
 
238
+ // Store the route reference with the provision type
239
+ this.provisionMap.set(domain, {
240
+ type: provision === 'http01' || provision === 'dns01' ? provision : 'static',
241
+ routeRef: certRoute
242
+ });
243
+
156
244
  // Handle different provisioning methods
157
245
  if (provision === 'http01') {
158
246
  if (isWildcard) {
@@ -160,19 +248,21 @@ export class CertProvisioner extends plugins.EventEmitter {
160
248
  return;
161
249
  }
162
250
 
163
- this.provisionMap.set(domain, 'http01');
164
251
  this.port80Handler.addDomain({
165
252
  domainName: domain,
166
253
  sslRedirect: true,
167
- acmeMaintenance: true
254
+ acmeMaintenance: true,
255
+ routeReference: {
256
+ routeId: route.name || domain,
257
+ routeName: route.name
258
+ }
168
259
  });
169
260
  } else if (provision === 'dns01') {
170
261
  // DNS-01 challenges would be handled by the certProvisionFunction
171
- this.provisionMap.set(domain, 'dns01');
172
262
  // DNS-01 handling would go here if implemented
263
+ console.log(`DNS-01 challenge type set for ${domain}`);
173
264
  } else {
174
265
  // Static certificate (e.g., DNS-01 provisioned or user-provided)
175
- this.provisionMap.set(domain, 'static');
176
266
  const certObj = provision as plugins.tsclass.network.ICert;
177
267
  const certData: ICertificateData = {
178
268
  domain: certObj.domainName,
@@ -180,7 +270,11 @@ export class CertProvisioner extends plugins.EventEmitter {
180
270
  privateKey: certObj.privateKey,
181
271
  expiryDate: new Date(certObj.validUntil),
182
272
  source: 'static',
183
- isRenewal: false
273
+ isRenewal: false,
274
+ routeReference: {
275
+ routeId: route.name || domain,
276
+ routeName: route.name
277
+ }
184
278
  };
185
279
 
186
280
  this.networkProxyBridge.applyExternalCertificate(certData);
@@ -210,12 +304,12 @@ export class CertProvisioner extends plugins.EventEmitter {
210
304
  * Perform renewals for all domains that need it
211
305
  */
212
306
  private async performRenewals(): Promise<void> {
213
- for (const [domain, type] of this.provisionMap.entries()) {
307
+ for (const [domain, info] of this.provisionMap.entries()) {
214
308
  // Skip wildcard domains for HTTP-01 challenges
215
- if (domain.includes('*') && type === 'http01') continue;
309
+ if (domain.includes('*') && info.type === 'http01') continue;
216
310
 
217
311
  try {
218
- await this.renewDomain(domain, type);
312
+ await this.renewCertificateForDomain(domain, info.type, info.routeRef);
219
313
  } catch (err) {
220
314
  console.error(`Renewal error for ${domain}:`, err);
221
315
  }
@@ -226,8 +320,13 @@ export class CertProvisioner extends plugins.EventEmitter {
226
320
  * Renew a certificate for a specific domain
227
321
  * @param domain Domain to renew
228
322
  * @param provisionType Type of provisioning for this domain
323
+ * @param certRoute The route reference for this domain
229
324
  */
230
- private async renewDomain(domain: string, provisionType: 'http01' | 'dns01' | 'static'): Promise<void> {
325
+ private async renewCertificateForDomain(
326
+ domain: string,
327
+ provisionType: 'http01' | 'dns01' | 'static',
328
+ certRoute?: ICertRoute
329
+ ): Promise<void> {
231
330
  if (provisionType === 'http01') {
232
331
  await this.port80Handler.renewCertificate(domain);
233
332
  } else if ((provisionType === 'static' || provisionType === 'dns01') && this.certProvisionFunction) {
@@ -235,13 +334,19 @@ export class CertProvisioner extends plugins.EventEmitter {
235
334
 
236
335
  if (provision !== 'http01' && provision !== 'dns01') {
237
336
  const certObj = provision as plugins.tsclass.network.ICert;
337
+ const routeRef = certRoute?.route;
338
+
238
339
  const certData: ICertificateData = {
239
340
  domain: certObj.domainName,
240
341
  certificate: certObj.publicKey,
241
342
  privateKey: certObj.privateKey,
242
343
  expiryDate: new Date(certObj.validUntil),
243
344
  source: 'static',
244
- isRenewal: true
345
+ isRenewal: true,
346
+ routeReference: routeRef ? {
347
+ routeId: routeRef.name || domain,
348
+ routeName: routeRef.name
349
+ } : undefined
245
350
  };
246
351
 
247
352
  this.networkProxyBridge.applyExternalCertificate(certData);
@@ -261,10 +366,14 @@ export class CertProvisioner extends plugins.EventEmitter {
261
366
 
262
367
  /**
263
368
  * Request a certificate on-demand for the given domain.
369
+ * This will look for a matching route configuration and provision accordingly.
370
+ *
264
371
  * @param domain Domain name to provision
265
372
  */
266
373
  public async requestCertificate(domain: string): Promise<void> {
267
374
  const isWildcard = domain.includes('*');
375
+ // Find matching route
376
+ const certRoute = this.findRouteForDomain(domain);
268
377
 
269
378
  // Determine provisioning method
270
379
  let provision: TCertProvisionObject = 'http01';
@@ -283,7 +392,6 @@ export class CertProvisioner extends plugins.EventEmitter {
283
392
  await this.port80Handler.renewCertificate(domain);
284
393
  } else if (provision === 'dns01') {
285
394
  // DNS-01 challenges would be handled by external mechanisms
286
- // This is a placeholder for future implementation
287
395
  console.log(`DNS-01 challenge requested for ${domain}`);
288
396
  } else {
289
397
  // Static certificate (e.g., DNS-01 provisioned) supports wildcards
@@ -294,7 +402,11 @@ export class CertProvisioner extends plugins.EventEmitter {
294
402
  privateKey: certObj.privateKey,
295
403
  expiryDate: new Date(certObj.validUntil),
296
404
  source: 'static',
297
- isRenewal: false
405
+ isRenewal: false,
406
+ routeReference: certRoute ? {
407
+ routeId: certRoute.route.name || domain,
408
+ routeName: certRoute.route.name
409
+ } : undefined
298
410
  };
299
411
 
300
412
  this.networkProxyBridge.applyExternalCertificate(certData);
@@ -304,23 +416,104 @@ export class CertProvisioner extends plugins.EventEmitter {
304
416
 
305
417
  /**
306
418
  * Add a new domain for certificate provisioning
419
+ *
307
420
  * @param domain Domain to add
308
421
  * @param options Domain configuration options
309
422
  */
310
423
  public async addDomain(domain: string, options?: {
311
424
  sslRedirect?: boolean;
312
425
  acmeMaintenance?: boolean;
426
+ routeId?: string;
427
+ routeName?: string;
313
428
  }): Promise<void> {
314
429
  const domainOptions: IDomainOptions = {
315
430
  domainName: domain,
316
- sslRedirect: options?.sslRedirect || true,
317
- acmeMaintenance: options?.acmeMaintenance || true
431
+ sslRedirect: options?.sslRedirect ?? true,
432
+ acmeMaintenance: options?.acmeMaintenance ?? true,
433
+ routeReference: {
434
+ routeId: options?.routeId,
435
+ routeName: options?.routeName
436
+ }
318
437
  };
319
438
 
320
439
  this.port80Handler.addDomain(domainOptions);
321
- await this.provisionDomain(domain);
440
+
441
+ // Find matching route or create a generic one
442
+ const existingRoute = this.findRouteForDomain(domain);
443
+ if (existingRoute) {
444
+ await this.provisionCertificateForRoute(existingRoute);
445
+ } else {
446
+ // We don't have a route, just provision the domain
447
+ const isWildcard = domain.includes('*');
448
+ let provision: TCertProvisionObject = 'http01';
449
+
450
+ if (this.certProvisionFunction) {
451
+ provision = await this.certProvisionFunction(domain);
452
+ } else if (isWildcard) {
453
+ throw new Error(`Cannot request certificate for wildcard domain without certProvisionFunction: ${domain}`);
454
+ }
455
+
456
+ this.provisionMap.set(domain, {
457
+ type: provision === 'http01' || provision === 'dns01' ? provision : 'static'
458
+ });
459
+
460
+ if (provision !== 'http01' && provision !== 'dns01') {
461
+ const certObj = provision as plugins.tsclass.network.ICert;
462
+ const certData: ICertificateData = {
463
+ domain: certObj.domainName,
464
+ certificate: certObj.publicKey,
465
+ privateKey: certObj.privateKey,
466
+ expiryDate: new Date(certObj.validUntil),
467
+ source: 'static',
468
+ isRenewal: false,
469
+ routeReference: {
470
+ routeId: options?.routeId,
471
+ routeName: options?.routeName
472
+ }
473
+ };
474
+
475
+ this.networkProxyBridge.applyExternalCertificate(certData);
476
+ this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, certData);
477
+ }
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Update routes with new configurations
483
+ * This replaces all existing routes with new ones and re-provisions certificates as needed
484
+ *
485
+ * @param newRoutes New route configurations to use
486
+ */
487
+ public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
488
+ // Store the new route configs
489
+ this.routeConfigs = newRoutes;
490
+
491
+ // Extract new certificate routes
492
+ const newCertRoutes = this.extractCertificateRoutesFromRoutes(newRoutes);
493
+
494
+ // Find domains that no longer need certificates
495
+ const oldDomains = new Set(this.certRoutes.map(r => r.domain));
496
+ const newDomains = new Set(newCertRoutes.map(r => r.domain));
497
+
498
+ // Domains to remove
499
+ const domainsToRemove = [...oldDomains].filter(d => !newDomains.has(d));
500
+
501
+ // Remove obsolete domains from provision map
502
+ for (const domain of domainsToRemove) {
503
+ this.provisionMap.delete(domain);
504
+ }
505
+
506
+ // Update the cert routes
507
+ this.certRoutes = newCertRoutes;
508
+
509
+ // Provision certificates for new routes
510
+ for (const certRoute of newCertRoutes) {
511
+ if (!oldDomains.has(certRoute.domain)) {
512
+ await this.provisionCertificateForRoute(certRoute);
513
+ }
514
+ }
322
515
  }
323
516
  }
324
517
 
325
- // For backward compatibility
326
- export { CertProvisioner as CertificateProvisioner }
518
+ // Type alias for backward compatibility
519
+ export type TSmartProxyCertProvisionObject = TCertProvisionObject;