@push.rocks/smartproxy 7.1.2 → 10.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 (45) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/classes.router.d.ts +9 -10
  3. package/dist_ts/classes.router.js +3 -5
  4. package/dist_ts/common/acmeFactory.d.ts +9 -0
  5. package/dist_ts/common/acmeFactory.js +20 -0
  6. package/dist_ts/common/eventUtils.d.ts +15 -0
  7. package/dist_ts/common/eventUtils.js +19 -0
  8. package/dist_ts/common/types.d.ts +82 -0
  9. package/dist_ts/common/types.js +17 -0
  10. package/dist_ts/networkproxy/classes.np.certificatemanager.js +23 -19
  11. package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +1 -0
  12. package/dist_ts/networkproxy/classes.np.networkproxy.js +5 -1
  13. package/dist_ts/networkproxy/classes.np.types.d.ts +5 -10
  14. package/dist_ts/networkproxy/classes.np.types.js +1 -1
  15. package/dist_ts/plugins.d.ts +6 -3
  16. package/dist_ts/plugins.js +7 -4
  17. package/dist_ts/port80handler/classes.port80handler.d.ts +14 -111
  18. package/dist_ts/port80handler/classes.port80handler.js +94 -373
  19. package/dist_ts/smartproxy/classes.pp.certprovisioner.d.ts +54 -0
  20. package/dist_ts/smartproxy/classes.pp.certprovisioner.js +166 -0
  21. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +11 -33
  22. package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +5 -0
  23. package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +21 -11
  24. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +1 -11
  25. package/dist_ts/smartproxy/classes.smartproxy.d.ts +3 -5
  26. package/dist_ts/smartproxy/classes.smartproxy.js +94 -180
  27. package/package.json +12 -10
  28. package/readme.hints.md +64 -1
  29. package/readme.md +253 -408
  30. package/readme.plan.md +29 -0
  31. package/ts/00_commitinfo_data.ts +1 -1
  32. package/ts/classes.router.ts +13 -15
  33. package/ts/common/acmeFactory.ts +23 -0
  34. package/ts/common/eventUtils.ts +34 -0
  35. package/ts/common/types.ts +89 -0
  36. package/ts/networkproxy/classes.np.certificatemanager.ts +23 -19
  37. package/ts/networkproxy/classes.np.networkproxy.ts +4 -0
  38. package/ts/networkproxy/classes.np.types.ts +6 -10
  39. package/ts/plugins.ts +17 -4
  40. package/ts/port80handler/classes.port80handler.ts +108 -509
  41. package/ts/smartproxy/classes.pp.certprovisioner.ts +188 -0
  42. package/ts/smartproxy/classes.pp.interfaces.ts +13 -36
  43. package/ts/smartproxy/classes.pp.networkproxybridge.ts +22 -10
  44. package/ts/smartproxy/classes.pp.portrangemanager.ts +0 -10
  45. package/ts/smartproxy/classes.smartproxy.ts +103 -195
@@ -8,14 +8,15 @@ import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
8
8
  import { TimeoutManager } from './classes.pp.timeoutmanager.js';
9
9
  import { PortRangeManager } from './classes.pp.portrangemanager.js';
10
10
  import { ConnectionHandler } from './classes.pp.connectionhandler.js';
11
- import { Port80Handler, Port80HandlerEvents } from '../port80handler/classes.port80handler.js';
12
- import * as path from 'path';
13
- import * as fs from 'fs';
11
+ import { Port80Handler } from '../port80handler/classes.port80handler.js';
12
+ import { CertProvisioner } from './classes.pp.certprovisioner.js';
13
+ import type { ICertificateData } from '../common/types.js';
14
+ import { buildPort80Handler } from '../common/acmeFactory.js';
14
15
 
15
16
  /**
16
17
  * SmartProxy - Main class that coordinates all components
17
18
  */
18
- export class SmartProxy {
19
+ export class SmartProxy extends plugins.EventEmitter {
19
20
  private netServers: plugins.net.Server[] = [];
20
21
  private connectionLogger: NodeJS.Timeout | null = null;
21
22
  private isShuttingDown: boolean = false;
@@ -32,8 +33,11 @@ export class SmartProxy {
32
33
 
33
34
  // Port80Handler for ACME certificate management
34
35
  private port80Handler: Port80Handler | null = null;
36
+ // CertProvisioner for unified certificate workflows
37
+ private certProvisioner?: CertProvisioner;
35
38
 
36
39
  constructor(settingsArg: IPortProxySettings) {
40
+ super();
37
41
  // Set reasonable defaults for all settings
38
42
  this.settings = {
39
43
  ...settingsArg,
@@ -62,41 +66,25 @@ export class SmartProxy {
62
66
  keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
63
67
  extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
64
68
  networkProxyPort: settingsArg.networkProxyPort || 8443,
65
- port80HandlerConfig: settingsArg.port80HandlerConfig || {},
69
+ acme: settingsArg.acme || {},
66
70
  globalPortRanges: settingsArg.globalPortRanges || [],
67
71
  };
68
72
 
69
- // Set port80HandlerConfig defaults, using legacy acme config if available
70
- if (!this.settings.port80HandlerConfig || Object.keys(this.settings.port80HandlerConfig).length === 0) {
71
- if (this.settings.acme) {
72
- // Migrate from legacy acme config
73
- this.settings.port80HandlerConfig = {
74
- enabled: this.settings.acme.enabled,
75
- port: this.settings.acme.port || 80,
76
- contactEmail: this.settings.acme.contactEmail || 'admin@example.com',
77
- useProduction: this.settings.acme.useProduction || false,
78
- renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
79
- autoRenew: this.settings.acme.autoRenew !== false, // Default to true
80
- certificateStore: this.settings.acme.certificateStore || './certs',
81
- skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
82
- httpsRedirectPort: this.settings.fromPort,
83
- renewCheckIntervalHours: 24
84
- };
85
- } else {
86
- // Set defaults if no config provided
87
- this.settings.port80HandlerConfig = {
88
- enabled: false,
89
- port: 80,
90
- contactEmail: 'admin@example.com',
91
- useProduction: false,
92
- renewThresholdDays: 30,
93
- autoRenew: true,
94
- certificateStore: './certs',
95
- skipConfiguredCerts: false,
96
- httpsRedirectPort: this.settings.fromPort,
97
- renewCheckIntervalHours: 24
98
- };
99
- }
73
+ // Set default ACME options if not provided
74
+ if (!this.settings.acme || Object.keys(this.settings.acme).length === 0) {
75
+ this.settings.acme = {
76
+ enabled: false,
77
+ port: 80,
78
+ contactEmail: 'admin@example.com',
79
+ useProduction: false,
80
+ renewThresholdDays: 30,
81
+ autoRenew: true,
82
+ certificateStore: './certs',
83
+ skipConfiguredCerts: false,
84
+ httpsRedirectPort: this.settings.fromPort,
85
+ renewCheckIntervalHours: 24,
86
+ domainForwards: []
87
+ };
100
88
  }
101
89
 
102
90
  // Initialize component managers
@@ -134,89 +122,20 @@ export class SmartProxy {
134
122
  * Initialize the Port80Handler for ACME certificate management
135
123
  */
136
124
  private async initializePort80Handler(): Promise<void> {
137
- const config = this.settings.port80HandlerConfig;
138
-
139
- if (!config || !config.enabled) {
140
- console.log('Port80Handler is disabled in configuration');
125
+ const config = this.settings.acme!;
126
+ if (!config.enabled) {
127
+ console.log('ACME is disabled in configuration');
141
128
  return;
142
129
  }
143
130
 
144
131
  try {
145
- // Ensure the certificate store directory exists
146
- if (config.certificateStore) {
147
- const certStorePath = path.resolve(config.certificateStore);
148
- if (!fs.existsSync(certStorePath)) {
149
- fs.mkdirSync(certStorePath, { recursive: true });
150
- console.log(`Created certificate store directory: ${certStorePath}`);
151
- }
152
- }
153
-
154
- // Create Port80Handler with options from config
155
- this.port80Handler = new Port80Handler({
156
- port: config.port,
157
- contactEmail: config.contactEmail,
158
- useProduction: config.useProduction,
159
- renewThresholdDays: config.renewThresholdDays,
160
- httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort,
161
- renewCheckIntervalHours: config.renewCheckIntervalHours,
162
- enabled: config.enabled,
163
- autoRenew: config.autoRenew,
164
- certificateStore: config.certificateStore,
165
- skipConfiguredCerts: config.skipConfiguredCerts
166
- });
167
-
168
- // Register domain forwarding configurations
169
- if (config.domainForwards) {
170
- for (const forward of config.domainForwards) {
171
- this.port80Handler.addDomain({
172
- domainName: forward.domain,
173
- sslRedirect: true,
174
- acmeMaintenance: true,
175
- forward: forward.forwardConfig,
176
- acmeForward: forward.acmeForwardConfig
177
- });
178
-
179
- console.log(`Registered domain forwarding for ${forward.domain}`);
180
- }
181
- }
182
-
183
- // Register all non-wildcard domains from domain configs
184
- for (const domainConfig of this.settings.domainConfigs) {
185
- for (const domain of domainConfig.domains) {
186
- // Skip wildcards
187
- if (domain.includes('*')) continue;
188
-
189
- this.port80Handler.addDomain({
190
- domainName: domain,
191
- sslRedirect: true,
192
- acmeMaintenance: true
193
- });
194
-
195
- console.log(`Registered domain ${domain} with Port80Handler`);
196
- }
197
- }
198
-
199
- // Set up event listeners
200
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (certData) => {
201
- console.log(`Certificate issued for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
132
+ // Build and start the Port80Handler
133
+ this.port80Handler = buildPort80Handler({
134
+ ...config,
135
+ httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort
202
136
  });
203
-
204
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (certData) => {
205
- console.log(`Certificate renewed for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
206
- });
207
-
208
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, (failureData) => {
209
- console.log(`Certificate ${failureData.isRenewal ? 'renewal' : 'issuance'} failed for ${failureData.domain}: ${failureData.error}`);
210
- });
211
-
212
- this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, (expiryData) => {
213
- console.log(`Certificate for ${expiryData.domain} is expiring in ${expiryData.daysRemaining} days`);
214
- });
215
-
216
- // Share Port80Handler with NetworkProxyBridge
137
+ // Share Port80Handler with NetworkProxyBridge before start
217
138
  this.networkProxyBridge.setPort80Handler(this.port80Handler);
218
-
219
- // Start Port80Handler
220
139
  await this.port80Handler.start();
221
140
  console.log(`Port80Handler started on port ${config.port}`);
222
141
  } catch (err) {
@@ -236,6 +155,37 @@ export class SmartProxy {
236
155
 
237
156
  // Initialize Port80Handler if enabled
238
157
  await this.initializePort80Handler();
158
+ // Initialize CertProvisioner for unified certificate workflows
159
+ if (this.port80Handler) {
160
+ const acme = this.settings.acme!;
161
+ this.certProvisioner = new CertProvisioner(
162
+ this.settings.domainConfigs,
163
+ this.port80Handler,
164
+ this.networkProxyBridge,
165
+ this.settings.certProvider,
166
+ acme.renewThresholdDays!,
167
+ acme.renewCheckIntervalHours!,
168
+ acme.autoRenew!,
169
+ acme.domainForwards?.map(f => ({
170
+ domain: f.domain,
171
+ forwardConfig: f.forwardConfig,
172
+ acmeForwardConfig: f.acmeForwardConfig,
173
+ sslRedirect: f.sslRedirect || false
174
+ })) || []
175
+ );
176
+ this.certProvisioner.on('certificate', (certData) => {
177
+ this.emit('certificate', {
178
+ domain: certData.domain,
179
+ publicKey: certData.certificate,
180
+ privateKey: certData.privateKey,
181
+ expiryDate: certData.expiryDate,
182
+ source: certData.source,
183
+ isRenewal: certData.isRenewal
184
+ });
185
+ });
186
+ await this.certProvisioner.start();
187
+ console.log('CertProvisioner started');
188
+ }
239
189
 
240
190
  // Initialize and start NetworkProxy if needed
241
191
  if (
@@ -364,6 +314,11 @@ export class SmartProxy {
364
314
  public async stop() {
365
315
  console.log('PortProxy shutting down...');
366
316
  this.isShuttingDown = true;
317
+ // Stop CertProvisioner if active
318
+ if (this.certProvisioner) {
319
+ await this.certProvisioner.stop();
320
+ console.log('CertProvisioner stopped');
321
+ }
367
322
 
368
323
  // Stop the Port80Handler if running
369
324
  if (this.port80Handler) {
@@ -429,91 +384,43 @@ export class SmartProxy {
429
384
  await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
430
385
  }
431
386
 
432
- // If Port80Handler is running, register non-wildcard domains
433
- if (this.port80Handler && this.settings.port80HandlerConfig?.enabled) {
387
+ // If Port80Handler is running, provision certificates per new domain
388
+ if (this.port80Handler && this.settings.acme?.enabled) {
434
389
  for (const domainConfig of newDomainConfigs) {
435
390
  for (const domain of domainConfig.domains) {
436
- // Skip wildcards
437
391
  if (domain.includes('*')) continue;
438
-
439
- this.port80Handler.addDomain({
440
- domainName: domain,
441
- sslRedirect: true,
442
- acmeMaintenance: true
443
- });
392
+ let provision = 'http01' as string | plugins.tsclass.network.ICert;
393
+ if (this.settings.certProvider) {
394
+ try {
395
+ provision = await this.settings.certProvider(domain);
396
+ } catch (err) {
397
+ console.log(`certProvider error for ${domain}: ${err}`);
398
+ }
399
+ }
400
+ if (provision === 'http01') {
401
+ this.port80Handler.addDomain({
402
+ domainName: domain,
403
+ sslRedirect: true,
404
+ acmeMaintenance: true
405
+ });
406
+ console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
407
+ } else {
408
+ const certObj = provision as plugins.tsclass.network.ICert;
409
+ const certData: ICertificateData = {
410
+ domain: certObj.domainName,
411
+ certificate: certObj.publicKey,
412
+ privateKey: certObj.privateKey,
413
+ expiryDate: new Date(certObj.validUntil)
414
+ };
415
+ this.networkProxyBridge.applyExternalCertificate(certData);
416
+ console.log(`Applied static certificate for ${domain} from certProvider`);
417
+ }
444
418
  }
445
419
  }
446
-
447
- console.log('Registered non-wildcard domains with Port80Handler');
420
+ console.log('Provisioned certificates for new domains');
448
421
  }
449
422
  }
450
423
 
451
- /**
452
- * Updates the Port80Handler configuration
453
- */
454
- public async updatePort80HandlerConfig(config: IPortProxySettings['port80HandlerConfig']): Promise<void> {
455
- if (!config) return;
456
-
457
- console.log('Updating Port80Handler configuration');
458
-
459
- // Update the settings
460
- this.settings.port80HandlerConfig = {
461
- ...this.settings.port80HandlerConfig,
462
- ...config
463
- };
464
-
465
- // Check if we need to restart Port80Handler
466
- let needsRestart = false;
467
-
468
- // Restart if enabled state changed
469
- if (this.port80Handler && config.enabled === false) {
470
- needsRestart = true;
471
- } else if (!this.port80Handler && config.enabled === true) {
472
- needsRestart = true;
473
- } else if (this.port80Handler && (
474
- config.port !== undefined ||
475
- config.contactEmail !== undefined ||
476
- config.useProduction !== undefined ||
477
- config.renewThresholdDays !== undefined ||
478
- config.renewCheckIntervalHours !== undefined
479
- )) {
480
- // Restart if critical settings changed
481
- needsRestart = true;
482
- }
483
-
484
- if (needsRestart) {
485
- // Stop if running
486
- if (this.port80Handler) {
487
- try {
488
- await this.port80Handler.stop();
489
- this.port80Handler = null;
490
- console.log('Stopped Port80Handler for configuration update');
491
- } catch (err) {
492
- console.log(`Error stopping Port80Handler: ${err}`);
493
- }
494
- }
495
-
496
- // Start with new config if enabled
497
- if (this.settings.port80HandlerConfig.enabled) {
498
- await this.initializePort80Handler();
499
- console.log('Restarted Port80Handler with new configuration');
500
- }
501
- } else if (this.port80Handler) {
502
- // Just update domain forwards if they changed
503
- if (config.domainForwards) {
504
- for (const forward of config.domainForwards) {
505
- this.port80Handler.addDomain({
506
- domainName: forward.domain,
507
- sslRedirect: true,
508
- acmeMaintenance: true,
509
- forward: forward.forwardConfig,
510
- acmeForward: forward.acmeForwardConfig
511
- });
512
- }
513
- console.log('Updated domain forwards in Port80Handler');
514
- }
515
- }
516
- }
517
424
 
518
425
  /**
519
426
  * Request a certificate for a specific domain
@@ -607,7 +514,7 @@ export class SmartProxy {
607
514
  networkProxyConnections,
608
515
  terminationStats,
609
516
  acmeEnabled: !!this.port80Handler,
610
- port80HandlerPort: this.port80Handler ? this.settings.port80HandlerConfig?.port : null
517
+ port80HandlerPort: this.port80Handler ? this.settings.acme?.port : null
611
518
  };
612
519
  }
613
520
 
@@ -658,7 +565,7 @@ export class SmartProxy {
658
565
  status: 'valid',
659
566
  expiryDate: expiryDate.toISOString(),
660
567
  daysRemaining,
661
- renewalNeeded: daysRemaining <= this.settings.port80HandlerConfig.renewThresholdDays
568
+ renewalNeeded: daysRemaining <= (this.settings.acme?.renewThresholdDays ?? 0)
662
569
  };
663
570
  } else {
664
571
  certificateStatus[domain] = {
@@ -668,11 +575,12 @@ export class SmartProxy {
668
575
  }
669
576
  }
670
577
 
578
+ const acme = this.settings.acme!;
671
579
  return {
672
580
  enabled: true,
673
- port: this.settings.port80HandlerConfig.port,
674
- useProduction: this.settings.port80HandlerConfig.useProduction,
675
- autoRenew: this.settings.port80HandlerConfig.autoRenew,
581
+ port: acme.port!,
582
+ useProduction: acme.useProduction!,
583
+ autoRenew: acme.autoRenew!,
676
584
  certificates: certificateStatus
677
585
  };
678
586
  }