@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.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.router.d.ts +9 -10
- package/dist_ts/classes.router.js +3 -5
- package/dist_ts/common/acmeFactory.d.ts +9 -0
- package/dist_ts/common/acmeFactory.js +20 -0
- package/dist_ts/common/eventUtils.d.ts +15 -0
- package/dist_ts/common/eventUtils.js +19 -0
- package/dist_ts/common/types.d.ts +82 -0
- package/dist_ts/common/types.js +17 -0
- package/dist_ts/networkproxy/classes.np.certificatemanager.js +23 -19
- package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +1 -0
- package/dist_ts/networkproxy/classes.np.networkproxy.js +5 -1
- package/dist_ts/networkproxy/classes.np.types.d.ts +5 -10
- package/dist_ts/networkproxy/classes.np.types.js +1 -1
- package/dist_ts/plugins.d.ts +6 -3
- package/dist_ts/plugins.js +7 -4
- package/dist_ts/port80handler/classes.port80handler.d.ts +14 -111
- package/dist_ts/port80handler/classes.port80handler.js +94 -373
- package/dist_ts/smartproxy/classes.pp.certprovisioner.d.ts +54 -0
- package/dist_ts/smartproxy/classes.pp.certprovisioner.js +166 -0
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +11 -33
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +5 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +21 -11
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +1 -11
- package/dist_ts/smartproxy/classes.smartproxy.d.ts +3 -5
- package/dist_ts/smartproxy/classes.smartproxy.js +94 -180
- package/package.json +12 -10
- package/readme.hints.md +64 -1
- package/readme.md +253 -408
- package/readme.plan.md +29 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.router.ts +13 -15
- package/ts/common/acmeFactory.ts +23 -0
- package/ts/common/eventUtils.ts +34 -0
- package/ts/common/types.ts +89 -0
- package/ts/networkproxy/classes.np.certificatemanager.ts +23 -19
- package/ts/networkproxy/classes.np.networkproxy.ts +4 -0
- package/ts/networkproxy/classes.np.types.ts +6 -10
- package/ts/plugins.ts +17 -4
- package/ts/port80handler/classes.port80handler.ts +108 -509
- package/ts/smartproxy/classes.pp.certprovisioner.ts +188 -0
- package/ts/smartproxy/classes.pp.interfaces.ts +13 -36
- package/ts/smartproxy/classes.pp.networkproxybridge.ts +22 -10
- package/ts/smartproxy/classes.pp.portrangemanager.ts +0 -10
- 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
|
|
12
|
-
import
|
|
13
|
-
import
|
|
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
|
-
|
|
69
|
+
acme: settingsArg.acme || {},
|
|
66
70
|
globalPortRanges: settingsArg.globalPortRanges || [],
|
|
67
71
|
};
|
|
68
72
|
|
|
69
|
-
// Set
|
|
70
|
-
if (!this.settings.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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.
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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,
|
|
433
|
-
if (this.port80Handler && this.settings.
|
|
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.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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.
|
|
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.
|
|
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:
|
|
674
|
-
useProduction:
|
|
675
|
-
autoRenew:
|
|
581
|
+
port: acme.port!,
|
|
582
|
+
useProduction: acme.useProduction!,
|
|
583
|
+
autoRenew: acme.autoRenew!,
|
|
676
584
|
certificates: certificateStatus
|
|
677
585
|
};
|
|
678
586
|
}
|