@push.rocks/smartproxy 18.0.2 → 18.2.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 (53) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/certificate-manager.d.ts +150 -0
  3. package/dist_ts/certificate/certificate-manager.js +505 -0
  4. package/dist_ts/certificate/events/simplified-events.d.ts +56 -0
  5. package/dist_ts/certificate/events/simplified-events.js +13 -0
  6. package/dist_ts/certificate/models/certificate-errors.d.ts +69 -0
  7. package/dist_ts/certificate/models/certificate-errors.js +141 -0
  8. package/dist_ts/certificate/models/certificate-strategy.d.ts +60 -0
  9. package/dist_ts/certificate/models/certificate-strategy.js +73 -0
  10. package/dist_ts/certificate/simplified-certificate-manager.d.ts +150 -0
  11. package/dist_ts/certificate/simplified-certificate-manager.js +501 -0
  12. package/dist_ts/http/index.d.ts +1 -9
  13. package/dist_ts/http/index.js +5 -11
  14. package/dist_ts/plugins.d.ts +3 -1
  15. package/dist_ts/plugins.js +4 -2
  16. package/dist_ts/proxies/network-proxy/network-proxy.js +3 -1
  17. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +48 -0
  18. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +76 -0
  19. package/dist_ts/proxies/network-proxy/websocket-handler.js +41 -4
  20. package/dist_ts/proxies/smart-proxy/cert-store.d.ts +10 -0
  21. package/dist_ts/proxies/smart-proxy/cert-store.js +70 -0
  22. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +116 -0
  23. package/dist_ts/proxies/smart-proxy/certificate-manager.js +401 -0
  24. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +168 -0
  25. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +642 -0
  26. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +26 -0
  27. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  28. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +65 -0
  29. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +31 -0
  30. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +102 -0
  31. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +73 -0
  32. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +10 -44
  33. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +66 -202
  34. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -0
  35. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +62 -2
  36. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +41 -0
  37. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +132 -0
  38. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +18 -13
  39. package/dist_ts/proxies/smart-proxy/smart-proxy.js +79 -196
  40. package/package.json +7 -5
  41. package/readme.md +224 -10
  42. package/readme.plan.md +1405 -617
  43. package/ts/00_commitinfo_data.ts +1 -1
  44. package/ts/http/index.ts +5 -12
  45. package/ts/plugins.ts +4 -1
  46. package/ts/proxies/network-proxy/network-proxy.ts +3 -0
  47. package/ts/proxies/network-proxy/websocket-handler.ts +38 -3
  48. package/ts/proxies/smart-proxy/cert-store.ts +86 -0
  49. package/ts/proxies/smart-proxy/certificate-manager.ts +506 -0
  50. package/ts/proxies/smart-proxy/models/route-types.ts +33 -3
  51. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +86 -239
  52. package/ts/proxies/smart-proxy/route-connection-handler.ts +74 -1
  53. package/ts/proxies/smart-proxy/smart-proxy.ts +105 -222
@@ -11,12 +11,8 @@ import { RouteManager } from './route-manager.js';
11
11
  import { RouteConnectionHandler } from './route-connection-handler.js';
12
12
  import { NFTablesManager } from './nftables-manager.js';
13
13
 
14
- // External dependencies
15
- import { Port80Handler } from '../../http/port80/port80-handler.js';
16
- import { CertProvisioner } from '../../certificate/providers/cert-provisioner.js';
17
- import type { ICertificateData } from '../../certificate/models/certificate-types.js';
18
- import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
19
- import { createPort80HandlerOptions } from '../../common/port80-adapter.js';
14
+ // Certificate manager
15
+ import { SmartCertManager, type ICertStatus } from './certificate-manager.js';
20
16
 
21
17
  // Import types and utilities
22
18
  import type {
@@ -53,10 +49,8 @@ export class SmartProxy extends plugins.EventEmitter {
53
49
  private routeConnectionHandler: RouteConnectionHandler;
54
50
  private nftablesManager: NFTablesManager;
55
51
 
56
- // Port80Handler for ACME certificate management
57
- private port80Handler: Port80Handler | null = null;
58
- // CertProvisioner for unified certificate workflows
59
- private certProvisioner?: CertProvisioner;
52
+ // Certificate manager for ACME and static certificates
53
+ private certManager: SmartCertManager | null = null;
60
54
 
61
55
  /**
62
56
  * Constructor for SmartProxy
@@ -180,29 +174,53 @@ export class SmartProxy extends plugins.EventEmitter {
180
174
  public settings: ISmartProxyOptions;
181
175
 
182
176
  /**
183
- * Initialize the Port80Handler for ACME certificate management
177
+ * Initialize certificate manager
184
178
  */
185
- private async initializePort80Handler(): Promise<void> {
186
- const config = this.settings.acme!;
187
- if (!config.enabled) {
188
- console.log('ACME is disabled in configuration');
179
+ private async initializeCertificateManager(): Promise<void> {
180
+ // Extract global ACME options if any routes use auto certificates
181
+ const autoRoutes = this.settings.routes.filter(r =>
182
+ r.action.tls?.certificate === 'auto'
183
+ );
184
+
185
+ if (autoRoutes.length === 0 && !this.hasStaticCertRoutes()) {
186
+ console.log('No routes require certificate management');
189
187
  return;
190
188
  }
191
189
 
192
- try {
193
- // Build and start the Port80Handler
194
- this.port80Handler = buildPort80Handler({
195
- ...config,
196
- httpsRedirectPort: config.httpsRedirectPort || 443
197
- });
198
-
199
- // Share Port80Handler with NetworkProxyBridge before start
200
- this.networkProxyBridge.setPort80Handler(this.port80Handler);
201
- await this.port80Handler.start();
202
- console.log(`Port80Handler started on port ${config.port}`);
203
- } catch (err) {
204
- console.log(`Error initializing Port80Handler: ${err}`);
190
+ // Use the first auto route's ACME config as defaults
191
+ const defaultAcme = autoRoutes[0]?.action.tls?.acme;
192
+
193
+ this.certManager = new SmartCertManager(
194
+ this.settings.routes,
195
+ './certs', // Certificate directory
196
+ defaultAcme ? {
197
+ email: defaultAcme.email,
198
+ useProduction: defaultAcme.useProduction,
199
+ port: defaultAcme.challengePort || 80
200
+ } : undefined
201
+ );
202
+
203
+ // Connect with NetworkProxy
204
+ if (this.networkProxyBridge.getNetworkProxy()) {
205
+ this.certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
205
206
  }
207
+
208
+ // Set route update callback for ACME challenges
209
+ this.certManager.setUpdateRoutesCallback(async (routes) => {
210
+ await this.updateRoutes(routes);
211
+ });
212
+
213
+ await this.certManager.initialize();
214
+ }
215
+
216
+ /**
217
+ * Check if we have routes with static certificates
218
+ */
219
+ private hasStaticCertRoutes(): boolean {
220
+ return this.settings.routes.some(r =>
221
+ r.action.tls?.certificate &&
222
+ r.action.tls.certificate !== 'auto'
223
+ );
206
224
  }
207
225
 
208
226
  /**
@@ -215,51 +233,18 @@ export class SmartProxy extends plugins.EventEmitter {
215
233
  return;
216
234
  }
217
235
 
218
- // Pure route-based configuration - no domain configs needed
219
-
220
- // Initialize Port80Handler if enabled
221
- await this.initializePort80Handler();
222
-
223
- // Initialize CertProvisioner for unified certificate workflows
224
- if (this.port80Handler) {
225
- const acme = this.settings.acme!;
226
-
227
- // Setup route forwards
228
- const routeForwards = acme.routeForwards?.map(f => f) || [];
229
-
230
- // Create CertProvisioner with appropriate parameters
231
- // No longer need to support multiple configuration types
232
- // Just pass the routes directly
233
- this.certProvisioner = new CertProvisioner(
234
- this.settings.routes,
235
- this.port80Handler,
236
- this.networkProxyBridge,
237
- this.settings.certProvisionFunction,
238
- acme.renewThresholdDays!,
239
- acme.renewCheckIntervalHours!,
240
- acme.autoRenew!,
241
- routeForwards
242
- );
243
-
244
- // Register certificate event handler
245
- this.certProvisioner.on('certificate', (certData) => {
246
- this.emit('certificate', {
247
- domain: certData.domain,
248
- publicKey: certData.certificate,
249
- privateKey: certData.privateKey,
250
- expiryDate: certData.expiryDate,
251
- source: certData.source,
252
- isRenewal: certData.isRenewal
253
- });
254
- });
255
-
256
- await this.certProvisioner.start();
257
- console.log('CertProvisioner started');
258
- }
236
+ // Initialize certificate manager before starting servers
237
+ await this.initializeCertificateManager();
259
238
 
260
239
  // Initialize and start NetworkProxy if needed
261
240
  if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
262
241
  await this.networkProxyBridge.initialize();
242
+
243
+ // Connect NetworkProxy with certificate manager
244
+ if (this.certManager) {
245
+ this.certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
246
+ }
247
+
263
248
  await this.networkProxyBridge.start();
264
249
  }
265
250
 
@@ -371,27 +356,16 @@ export class SmartProxy extends plugins.EventEmitter {
371
356
  this.isShuttingDown = true;
372
357
  this.portManager.setShuttingDown(true);
373
358
 
374
- // Stop CertProvisioner if active
375
- if (this.certProvisioner) {
376
- await this.certProvisioner.stop();
377
- console.log('CertProvisioner stopped');
359
+ // Stop certificate manager
360
+ if (this.certManager) {
361
+ await this.certManager.stop();
362
+ console.log('Certificate manager stopped');
378
363
  }
379
364
 
380
365
  // Stop NFTablesManager
381
366
  await this.nftablesManager.stop();
382
367
  console.log('NFTablesManager stopped');
383
368
 
384
- // Stop the Port80Handler if running
385
- if (this.port80Handler) {
386
- try {
387
- await this.port80Handler.stop();
388
- console.log('Port80Handler stopped');
389
- this.port80Handler = null;
390
- } catch (err) {
391
- console.log(`Error stopping Port80Handler: ${err}`);
392
- }
393
- }
394
-
395
369
  // Stop the connection logger
396
370
  if (this.connectionLogger) {
397
371
  clearInterval(this.connectionLogger);
@@ -498,104 +472,60 @@ export class SmartProxy extends plugins.EventEmitter {
498
472
  await this.networkProxyBridge.syncRoutesToNetworkProxy(newRoutes);
499
473
  }
500
474
 
501
- // If Port80Handler is running, provision certificates based on routes
502
- if (this.port80Handler && this.settings.acme?.enabled) {
503
- // Register all eligible domains from routes
504
- this.port80Handler.addDomainsFromRoutes(newRoutes);
505
-
506
- // Handle static certificates from certProvisionFunction if available
507
- if (this.settings.certProvisionFunction) {
508
- for (const route of newRoutes) {
509
- // Skip routes without domains
510
- if (!route.match.domains) continue;
511
-
512
- // Skip non-forward routes
513
- if (route.action.type !== 'forward') continue;
514
-
515
- // Skip routes without TLS termination
516
- if (!route.action.tls ||
517
- route.action.tls.mode === 'passthrough' ||
518
- !route.action.target) continue;
519
-
520
- // Skip certificate provisioning if certificate is not auto
521
- if (route.action.tls.certificate !== 'auto') continue;
522
-
523
- const domains = Array.isArray(route.match.domains)
524
- ? route.match.domains
525
- : [route.match.domains];
526
-
527
- for (const domain of domains) {
528
- try {
529
- const provision = await this.settings.certProvisionFunction(domain);
530
-
531
- // Skip http01 as those are handled by Port80Handler
532
- if (provision !== 'http01') {
533
- // Handle static certificate (e.g., DNS-01 provisioned)
534
- const certObj = provision as plugins.tsclass.network.ICert;
535
- const certData: ICertificateData = {
536
- domain: certObj.domainName,
537
- certificate: certObj.publicKey,
538
- privateKey: certObj.privateKey,
539
- expiryDate: new Date(certObj.validUntil),
540
- routeReference: {
541
- routeName: route.name
542
- }
543
- };
544
- this.networkProxyBridge.applyExternalCertificate(certData);
545
- console.log(`Applied static certificate for ${domain} from certProvider`);
546
- }
547
- } catch (err) {
548
- console.log(`certProvider error for ${domain}: ${err}`);
549
- }
550
- }
551
- }
475
+ // Update certificate manager with new routes
476
+ if (this.certManager) {
477
+ await this.certManager.stop();
478
+
479
+ this.certManager = new SmartCertManager(
480
+ newRoutes,
481
+ './certs',
482
+ this.certManager.getAcmeOptions()
483
+ );
484
+
485
+ if (this.networkProxyBridge.getNetworkProxy()) {
486
+ this.certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
552
487
  }
553
-
554
- console.log('Provisioned certificates for new routes');
488
+
489
+ await this.certManager.initialize();
555
490
  }
556
491
  }
557
492
 
558
493
  /**
559
- * Request a certificate for a specific domain
560
- *
561
- * @param domain The domain to request a certificate for
562
- * @param routeName Optional route name to associate with the certificate
494
+ * Manually provision a certificate for a route
563
495
  */
564
- public async requestCertificate(domain: string, routeName?: string): Promise<boolean> {
565
- // Validate domain format
566
- if (!this.isValidDomain(domain)) {
567
- console.log(`Invalid domain format: ${domain}`);
568
- return false;
496
+ public async provisionCertificate(routeName: string): Promise<void> {
497
+ if (!this.certManager) {
498
+ throw new Error('Certificate manager not initialized');
569
499
  }
570
-
571
- // Use Port80Handler if available
572
- if (this.port80Handler) {
573
- try {
574
- // Check if we already have a certificate
575
- const cert = this.port80Handler.getCertificate(domain);
576
- if (cert) {
577
- console.log(`Certificate already exists for ${domain}, valid until ${cert.expiryDate.toISOString()}`);
578
- return true;
579
- }
580
-
581
- // Register domain for certificate issuance
582
- this.port80Handler.addDomain({
583
- domain,
584
- sslRedirect: true,
585
- acmeMaintenance: true,
586
- routeReference: routeName ? { routeName } : undefined
587
- });
588
-
589
- console.log(`Domain ${domain} registered for certificate issuance` + (routeName ? ` for route '${routeName}'` : ''));
590
- return true;
591
- } catch (err) {
592
- console.log(`Error registering domain with Port80Handler: ${err}`);
593
- return false;
594
- }
500
+
501
+ const route = this.settings.routes.find(r => r.name === routeName);
502
+ if (!route) {
503
+ throw new Error(`Route ${routeName} not found`);
504
+ }
505
+
506
+ await this.certManager.provisionCertificate(route);
507
+ }
508
+
509
+ /**
510
+ * Force renewal of a certificate
511
+ */
512
+ public async renewCertificate(routeName: string): Promise<void> {
513
+ if (!this.certManager) {
514
+ throw new Error('Certificate manager not initialized');
515
+ }
516
+
517
+ await this.certManager.renewCertificate(routeName);
518
+ }
519
+
520
+ /**
521
+ * Get certificate status for a route
522
+ */
523
+ public getCertificateStatus(routeName: string): ICertStatus | undefined {
524
+ if (!this.certManager) {
525
+ return undefined;
595
526
  }
596
527
 
597
- // Fall back to NetworkProxyBridge
598
- return this.networkProxyBridge.requestCertificate(domain);
528
+ return this.certManager.getCertificateStatus(routeName);
599
529
  }
600
530
 
601
531
  /**
@@ -685,8 +615,8 @@ export class SmartProxy extends plugins.EventEmitter {
685
615
  keepAliveConnections,
686
616
  networkProxyConnections,
687
617
  terminationStats,
688
- acmeEnabled: !!this.port80Handler,
689
- port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null,
618
+ acmeEnabled: !!this.certManager,
619
+ port80HandlerPort: this.certManager ? 80 : null,
690
620
  routes: this.routeManager.getListeningPorts().length,
691
621
  listeningPorts: this.portManager.getListeningPorts(),
692
622
  activePorts: this.portManager.getListeningPorts().length
@@ -735,51 +665,4 @@ export class SmartProxy extends plugins.EventEmitter {
735
665
  return this.nftablesManager.getStatus();
736
666
  }
737
667
 
738
- /**
739
- * Get status of certificates managed by Port80Handler
740
- */
741
- public getCertificateStatus(): any {
742
- if (!this.port80Handler) {
743
- return {
744
- enabled: false,
745
- message: 'Port80Handler is not enabled'
746
- };
747
- }
748
-
749
- // Get eligible domains
750
- const eligibleDomains = this.getEligibleDomainsForCertificates();
751
- const certificateStatus: Record<string, any> = {};
752
-
753
- // Check each domain
754
- for (const domain of eligibleDomains) {
755
- const cert = this.port80Handler.getCertificate(domain);
756
-
757
- if (cert) {
758
- const now = new Date();
759
- const expiryDate = cert.expiryDate;
760
- const daysRemaining = Math.floor((expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000));
761
-
762
- certificateStatus[domain] = {
763
- status: 'valid',
764
- expiryDate: expiryDate.toISOString(),
765
- daysRemaining,
766
- renewalNeeded: daysRemaining <= (this.settings.acme?.renewThresholdDays ?? 0)
767
- };
768
- } else {
769
- certificateStatus[domain] = {
770
- status: 'missing',
771
- message: 'No certificate found'
772
- };
773
- }
774
- }
775
-
776
- const acme = this.settings.acme!;
777
- return {
778
- enabled: true,
779
- port: acme.port!,
780
- useProduction: acme.useProduction!,
781
- autoRenew: acme.autoRenew!,
782
- certificates: certificateStatus
783
- };
784
- }
785
668
  }