@push.rocks/smartproxy 3.34.0 → 3.37.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.
@@ -56,9 +56,21 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
56
56
  keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections
57
57
  extendedKeepAliveLifetime?: number; // Extended lifetime for keep-alive connections (ms)
58
58
 
59
- // New property for NetworkProxy integration
59
+ // NetworkProxy integration
60
60
  useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy
61
61
  networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
62
+
63
+ // ACME certificate management options
64
+ acme?: {
65
+ enabled?: boolean; // Whether to enable automatic certificate management
66
+ port?: number; // Port to listen on for ACME challenges (default: 80)
67
+ contactEmail?: string; // Email for Let's Encrypt account
68
+ useProduction?: boolean; // Whether to use Let's Encrypt production (default: false for staging)
69
+ renewThresholdDays?: number; // Days before expiry to renew certificates (default: 30)
70
+ autoRenew?: boolean; // Whether to automatically renew certificates (default: true)
71
+ certificateStore?: string; // Directory to store certificates (default: ./certs)
72
+ skipConfiguredCerts?: boolean; // Skip domains that already have certificates configured
73
+ };
62
74
  }
63
75
 
64
76
  /**
@@ -418,6 +430,18 @@ export class PortProxy {
418
430
 
419
431
  // NetworkProxy settings
420
432
  networkProxyPort: settingsArg.networkProxyPort || 8443, // Default NetworkProxy port
433
+
434
+ // ACME certificate settings with reasonable defaults
435
+ acme: settingsArg.acme || {
436
+ enabled: false,
437
+ port: 80,
438
+ contactEmail: 'admin@example.com',
439
+ useProduction: false,
440
+ renewThresholdDays: 30,
441
+ autoRenew: true,
442
+ certificateStore: './certs',
443
+ skipConfiguredCerts: false
444
+ }
421
445
  };
422
446
 
423
447
  // Initialize NetworkProxy if enabled
@@ -429,15 +453,182 @@ export class PortProxy {
429
453
  /**
430
454
  * Initialize NetworkProxy instance
431
455
  */
432
- private initializeNetworkProxy(): void {
456
+ private async initializeNetworkProxy(): Promise<void> {
433
457
  if (!this.networkProxy) {
434
- this.networkProxy = new NetworkProxy({
458
+ // Configure NetworkProxy options based on PortProxy settings
459
+ const networkProxyOptions: any = {
435
460
  port: this.settings.networkProxyPort!,
436
461
  portProxyIntegration: true,
437
462
  logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
438
- });
463
+ };
464
+
465
+ // Add ACME settings if configured
466
+ if (this.settings.acme) {
467
+ networkProxyOptions.acme = { ...this.settings.acme };
468
+ }
469
+
470
+ this.networkProxy = new NetworkProxy(networkProxyOptions);
439
471
 
440
472
  console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
473
+
474
+ // Convert and apply domain configurations to NetworkProxy
475
+ await this.syncDomainConfigsToNetworkProxy();
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Updates the domain configurations for the proxy
481
+ * @param newDomainConfigs The new domain configurations
482
+ */
483
+ public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
484
+ console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
485
+ this.settings.domainConfigs = newDomainConfigs;
486
+
487
+ // If NetworkProxy is initialized, resync the configurations
488
+ if (this.networkProxy) {
489
+ await this.syncDomainConfigsToNetworkProxy();
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Updates the ACME certificate settings
495
+ * @param acmeSettings New ACME settings
496
+ */
497
+ public async updateAcmeSettings(acmeSettings: IPortProxySettings['acme']): Promise<void> {
498
+ console.log('Updating ACME certificate settings');
499
+
500
+ // Update settings
501
+ this.settings.acme = {
502
+ ...this.settings.acme,
503
+ ...acmeSettings
504
+ };
505
+
506
+ // If NetworkProxy is initialized, update its ACME settings
507
+ if (this.networkProxy) {
508
+ try {
509
+ // Recreate NetworkProxy with new settings if ACME enabled state has changed
510
+ if (this.settings.acme.enabled !== acmeSettings.enabled) {
511
+ console.log(`ACME enabled state changed to: ${acmeSettings.enabled}`);
512
+
513
+ // Stop the current NetworkProxy
514
+ await this.networkProxy.stop();
515
+ this.networkProxy = null;
516
+
517
+ // Reinitialize with new settings
518
+ await this.initializeNetworkProxy();
519
+
520
+ // Use start() to make sure ACME gets initialized if newly enabled
521
+ await this.networkProxy.start();
522
+ } else {
523
+ // Update existing NetworkProxy with new settings
524
+ // Note: Some settings may require a restart to take effect
525
+ console.log('Updating ACME settings in NetworkProxy');
526
+
527
+ // For certificate renewals, we might want to trigger checks with the new settings
528
+ if (acmeSettings.renewThresholdDays) {
529
+ console.log(`Setting new renewal threshold to ${acmeSettings.renewThresholdDays} days`);
530
+ // This is implementation-dependent but gives an example
531
+ if (this.networkProxy.options.acme) {
532
+ this.networkProxy.options.acme.renewThresholdDays = acmeSettings.renewThresholdDays;
533
+ }
534
+ }
535
+ }
536
+ } catch (err) {
537
+ console.log(`Error updating ACME settings: ${err}`);
538
+ }
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Synchronizes PortProxy domain configurations to NetworkProxy
544
+ * This allows domains configured in PortProxy to be used by NetworkProxy
545
+ */
546
+ private async syncDomainConfigsToNetworkProxy(): Promise<void> {
547
+ if (!this.networkProxy) {
548
+ console.log('Cannot sync configurations - NetworkProxy not initialized');
549
+ return;
550
+ }
551
+
552
+ try {
553
+ // Get SSL certificates from assets
554
+ // Import fs directly since it's not in plugins
555
+ const fs = await import('fs');
556
+
557
+ let certPair;
558
+ try {
559
+ certPair = {
560
+ key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
561
+ cert: fs.readFileSync('assets/certs/cert.pem', 'utf8')
562
+ };
563
+ } catch (certError) {
564
+ console.log(`Warning: Could not read default certificates: ${certError}`);
565
+ console.log('Using empty certificate placeholders - ACME will generate proper certificates if enabled');
566
+
567
+ // Use empty placeholders - NetworkProxy will use its internal defaults
568
+ // or ACME will generate proper ones if enabled
569
+ certPair = {
570
+ key: '',
571
+ cert: ''
572
+ };
573
+ }
574
+
575
+ // Convert domain configs to NetworkProxy configs
576
+ const proxyConfigs = this.networkProxy.convertPortProxyConfigs(
577
+ this.settings.domainConfigs,
578
+ certPair
579
+ );
580
+
581
+ // Log ACME-eligible domains if ACME is enabled
582
+ if (this.settings.acme?.enabled) {
583
+ const acmeEligibleDomains = proxyConfigs
584
+ .filter(config => !config.hostName.includes('*')) // Exclude wildcards
585
+ .map(config => config.hostName);
586
+
587
+ if (acmeEligibleDomains.length > 0) {
588
+ console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
589
+ } else {
590
+ console.log('No domains eligible for ACME certificates found in configuration');
591
+ }
592
+ }
593
+
594
+ // Update NetworkProxy with the converted configs
595
+ this.networkProxy.updateProxyConfigs(proxyConfigs).then(() => {
596
+ console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`);
597
+ }).catch(err => {
598
+ console.log(`Error synchronizing configurations: ${err.message}`);
599
+ });
600
+ } catch (err) {
601
+ console.log(`Failed to sync configurations: ${err}`);
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Requests a certificate for a specific domain
607
+ * @param domain The domain to request a certificate for
608
+ * @returns Promise that resolves to true if the request was successful, false otherwise
609
+ */
610
+ public async requestCertificate(domain: string): Promise<boolean> {
611
+ if (!this.networkProxy) {
612
+ console.log('Cannot request certificate - NetworkProxy not initialized');
613
+ return false;
614
+ }
615
+
616
+ if (!this.settings.acme?.enabled) {
617
+ console.log('Cannot request certificate - ACME is not enabled');
618
+ return false;
619
+ }
620
+
621
+ try {
622
+ const result = await this.networkProxy.requestCertificate(domain);
623
+ if (result) {
624
+ console.log(`Certificate request for ${domain} submitted successfully`);
625
+ } else {
626
+ console.log(`Certificate request for ${domain} failed`);
627
+ }
628
+ return result;
629
+ } catch (err) {
630
+ console.log(`Error requesting certificate: ${err}`);
631
+ return false;
441
632
  }
442
633
  }
443
634
 
@@ -1278,10 +1469,27 @@ export class PortProxy {
1278
1469
  return;
1279
1470
  }
1280
1471
 
1472
+ // Initialize NetworkProxy if needed (useNetworkProxy is set but networkProxy isn't initialized)
1473
+ if (this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0 && !this.networkProxy) {
1474
+ await this.initializeNetworkProxy();
1475
+ }
1476
+
1281
1477
  // Start NetworkProxy if configured
1282
1478
  if (this.networkProxy) {
1283
1479
  await this.networkProxy.start();
1284
1480
  console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);
1481
+
1482
+ // Log ACME status
1483
+ if (this.settings.acme?.enabled) {
1484
+ console.log(`ACME certificate management is enabled (${this.settings.acme.useProduction ? 'Production' : 'Staging'} mode)`);
1485
+ console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
1486
+
1487
+ // Register domains for ACME certificates if enabled
1488
+ if (this.networkProxy.options.acme?.enabled) {
1489
+ console.log('Registering domains with ACME certificate manager...');
1490
+ // The NetworkProxy will handle this internally via registerDomainsWithAcmeManager()
1491
+ }
1492
+ }
1285
1493
  }
1286
1494
 
1287
1495
  // Define a unified connection handler for all listening ports.
@@ -2036,11 +2244,17 @@ export class PortProxy {
2036
2244
  }
2037
2245
  }
2038
2246
 
2039
- // Stop NetworkProxy if it was started
2247
+ // Stop NetworkProxy if it was started (which also stops ACME manager)
2040
2248
  if (this.networkProxy) {
2041
2249
  try {
2250
+ console.log('Stopping NetworkProxy...');
2042
2251
  await this.networkProxy.stop();
2043
2252
  console.log('NetworkProxy stopped successfully');
2253
+
2254
+ // Log ACME shutdown if it was enabled
2255
+ if (this.settings.acme?.enabled) {
2256
+ console.log('ACME certificate manager stopped');
2257
+ }
2044
2258
  } catch (err) {
2045
2259
  console.log(`Error stopping NetworkProxy: ${err}`);
2046
2260
  }