@push.rocks/smartproxy 3.25.4 → 3.28.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 +1 -1
- package/dist_ts/classes.port80handler.d.ts +96 -14
- package/dist_ts/classes.port80handler.js +330 -61
- package/dist_ts/classes.router.d.ts +85 -7
- package/dist_ts/classes.router.js +241 -15
- package/dist_ts/plugins.d.ts +4 -2
- package/dist_ts/plugins.js +5 -3
- package/package.json +1 -1
- package/readme.md +108 -18
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.port80handler.ts +413 -68
- package/ts/classes.router.ts +335 -17
- package/ts/plugins.ts +5 -2
|
@@ -1,19 +1,109 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* Events emitted by the ACME Certificate Manager
|
|
4
|
+
*/
|
|
5
|
+
export var CertManagerEvents;
|
|
6
|
+
(function (CertManagerEvents) {
|
|
7
|
+
CertManagerEvents["CERTIFICATE_ISSUED"] = "certificate-issued";
|
|
8
|
+
CertManagerEvents["CERTIFICATE_RENEWED"] = "certificate-renewed";
|
|
9
|
+
CertManagerEvents["CERTIFICATE_FAILED"] = "certificate-failed";
|
|
10
|
+
CertManagerEvents["CERTIFICATE_EXPIRING"] = "certificate-expiring";
|
|
11
|
+
CertManagerEvents["MANAGER_STARTED"] = "manager-started";
|
|
12
|
+
CertManagerEvents["MANAGER_STOPPED"] = "manager-stopped";
|
|
13
|
+
})(CertManagerEvents || (CertManagerEvents = {}));
|
|
14
|
+
/**
|
|
15
|
+
* Improved ACME Certificate Manager with event emission and external certificate management
|
|
16
|
+
*/
|
|
17
|
+
export class AcmeCertManager extends plugins.EventEmitter {
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new ACME Certificate Manager
|
|
20
|
+
* @param options Configuration options
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
super();
|
|
24
|
+
this.server = null;
|
|
5
25
|
this.acmeClient = null;
|
|
6
26
|
this.accountKey = null;
|
|
27
|
+
this.renewalTimer = null;
|
|
28
|
+
this.isShuttingDown = false;
|
|
7
29
|
this.domainCertificates = new Map();
|
|
8
|
-
//
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
|
|
30
|
+
// Default options
|
|
31
|
+
this.options = {
|
|
32
|
+
port: options.port ?? 80,
|
|
33
|
+
contactEmail: options.contactEmail ?? 'admin@example.com',
|
|
34
|
+
useProduction: options.useProduction ?? false, // Safer default: staging
|
|
35
|
+
renewThresholdDays: options.renewThresholdDays ?? 30,
|
|
36
|
+
httpsRedirectPort: options.httpsRedirectPort ?? 443,
|
|
37
|
+
renewCheckIntervalHours: options.renewCheckIntervalHours ?? 24,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Starts the HTTP server for ACME challenges
|
|
42
|
+
*/
|
|
43
|
+
async start() {
|
|
44
|
+
if (this.server) {
|
|
45
|
+
throw new Error('Server is already running');
|
|
46
|
+
}
|
|
47
|
+
if (this.isShuttingDown) {
|
|
48
|
+
throw new Error('Server is shutting down');
|
|
49
|
+
}
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
try {
|
|
52
|
+
this.server = plugins.http.createServer((req, res) => this.handleRequest(req, res));
|
|
53
|
+
this.server.on('error', (error) => {
|
|
54
|
+
if (error.code === 'EACCES') {
|
|
55
|
+
reject(new Error(`Permission denied to bind to port ${this.options.port}. Try running with elevated privileges or use a port > 1024.`));
|
|
56
|
+
}
|
|
57
|
+
else if (error.code === 'EADDRINUSE') {
|
|
58
|
+
reject(new Error(`Port ${this.options.port} is already in use.`));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
reject(error);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
this.server.listen(this.options.port, () => {
|
|
65
|
+
console.log(`AcmeCertManager is listening on port ${this.options.port}`);
|
|
66
|
+
this.startRenewalTimer();
|
|
67
|
+
this.emit(CertManagerEvents.MANAGER_STARTED, this.options.port);
|
|
68
|
+
resolve();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
reject(error);
|
|
73
|
+
}
|
|
12
74
|
});
|
|
13
75
|
}
|
|
14
76
|
/**
|
|
15
|
-
*
|
|
16
|
-
|
|
77
|
+
* Stops the HTTP server and renewal timer
|
|
78
|
+
*/
|
|
79
|
+
async stop() {
|
|
80
|
+
if (!this.server) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.isShuttingDown = true;
|
|
84
|
+
// Stop the renewal timer
|
|
85
|
+
if (this.renewalTimer) {
|
|
86
|
+
clearInterval(this.renewalTimer);
|
|
87
|
+
this.renewalTimer = null;
|
|
88
|
+
}
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
if (this.server) {
|
|
91
|
+
this.server.close(() => {
|
|
92
|
+
this.server = null;
|
|
93
|
+
this.isShuttingDown = false;
|
|
94
|
+
this.emit(CertManagerEvents.MANAGER_STOPPED);
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.isShuttingDown = false;
|
|
100
|
+
resolve();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Adds a domain to be managed for certificates
|
|
106
|
+
* @param domain The domain to add
|
|
17
107
|
*/
|
|
18
108
|
addDomain(domain) {
|
|
19
109
|
if (!this.domainCertificates.has(domain)) {
|
|
@@ -22,8 +112,8 @@ export class Port80Handler {
|
|
|
22
112
|
}
|
|
23
113
|
}
|
|
24
114
|
/**
|
|
25
|
-
* Removes a domain from management
|
|
26
|
-
* @param domain The domain to remove
|
|
115
|
+
* Removes a domain from management
|
|
116
|
+
* @param domain The domain to remove
|
|
27
117
|
*/
|
|
28
118
|
removeDomain(domain) {
|
|
29
119
|
if (this.domainCertificates.delete(domain)) {
|
|
@@ -31,32 +121,91 @@ export class Port80Handler {
|
|
|
31
121
|
}
|
|
32
122
|
}
|
|
33
123
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
124
|
+
* Sets a certificate for a domain directly (for externally obtained certificates)
|
|
125
|
+
* @param domain The domain for the certificate
|
|
126
|
+
* @param certificate The certificate (PEM format)
|
|
127
|
+
* @param privateKey The private key (PEM format)
|
|
128
|
+
* @param expiryDate Optional expiry date
|
|
129
|
+
*/
|
|
130
|
+
setCertificate(domain, certificate, privateKey, expiryDate) {
|
|
131
|
+
let domainInfo = this.domainCertificates.get(domain);
|
|
132
|
+
if (!domainInfo) {
|
|
133
|
+
domainInfo = { certObtained: false, obtainingInProgress: false };
|
|
134
|
+
this.domainCertificates.set(domain, domainInfo);
|
|
135
|
+
}
|
|
136
|
+
domainInfo.certificate = certificate;
|
|
137
|
+
domainInfo.privateKey = privateKey;
|
|
138
|
+
domainInfo.certObtained = true;
|
|
139
|
+
domainInfo.obtainingInProgress = false;
|
|
140
|
+
if (expiryDate) {
|
|
141
|
+
domainInfo.expiryDate = expiryDate;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Try to extract expiry date from certificate
|
|
145
|
+
try {
|
|
146
|
+
// This is a simplistic approach - in a real implementation, use a proper
|
|
147
|
+
// certificate parsing library like node-forge or x509
|
|
148
|
+
const matches = certificate.match(/Not After\s*:\s*(.*?)(?:\n|$)/i);
|
|
149
|
+
if (matches && matches[1]) {
|
|
150
|
+
domainInfo.expiryDate = new Date(matches[1]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.warn(`Failed to extract expiry date from certificate for ${domain}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
console.log(`Certificate set for ${domain}`);
|
|
158
|
+
// Emit certificate event
|
|
159
|
+
this.emitCertificateEvent(CertManagerEvents.CERTIFICATE_ISSUED, {
|
|
160
|
+
domain,
|
|
161
|
+
certificate,
|
|
162
|
+
privateKey,
|
|
163
|
+
expiryDate: domainInfo.expiryDate || new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days default
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Gets the certificate for a domain if it exists
|
|
168
|
+
* @param domain The domain to get the certificate for
|
|
169
|
+
*/
|
|
170
|
+
getCertificate(domain) {
|
|
171
|
+
const domainInfo = this.domainCertificates.get(domain);
|
|
172
|
+
if (!domainInfo || !domainInfo.certObtained || !domainInfo.certificate || !domainInfo.privateKey) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
domain,
|
|
177
|
+
certificate: domainInfo.certificate,
|
|
178
|
+
privateKey: domainInfo.privateKey,
|
|
179
|
+
expiryDate: domainInfo.expiryDate || new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days default
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Lazy initialization of the ACME client
|
|
184
|
+
* @returns An ACME client instance
|
|
36
185
|
*/
|
|
37
186
|
async getAcmeClient() {
|
|
38
187
|
if (this.acmeClient) {
|
|
39
188
|
return this.acmeClient;
|
|
40
189
|
}
|
|
41
|
-
// Generate a new account key
|
|
42
|
-
this.accountKey = (await acme.forge.createPrivateKey()).toString();
|
|
43
|
-
this.acmeClient = new acme.Client({
|
|
44
|
-
directoryUrl:
|
|
45
|
-
|
|
46
|
-
|
|
190
|
+
// Generate a new account key
|
|
191
|
+
this.accountKey = (await plugins.acme.forge.createPrivateKey()).toString();
|
|
192
|
+
this.acmeClient = new plugins.acme.Client({
|
|
193
|
+
directoryUrl: this.options.useProduction
|
|
194
|
+
? plugins.acme.directory.letsencrypt.production
|
|
195
|
+
: plugins.acme.directory.letsencrypt.staging,
|
|
47
196
|
accountKey: this.accountKey,
|
|
48
197
|
});
|
|
49
|
-
// Create a new account
|
|
198
|
+
// Create a new account
|
|
50
199
|
await this.acmeClient.createAccount({
|
|
51
200
|
termsOfServiceAgreed: true,
|
|
52
|
-
contact: [
|
|
201
|
+
contact: [`mailto:${this.options.contactEmail}`],
|
|
53
202
|
});
|
|
54
203
|
return this.acmeClient;
|
|
55
204
|
}
|
|
56
205
|
/**
|
|
57
|
-
* Handles incoming HTTP requests
|
|
58
|
-
*
|
|
59
|
-
*
|
|
206
|
+
* Handles incoming HTTP requests
|
|
207
|
+
* @param req The HTTP request
|
|
208
|
+
* @param res The HTTP response
|
|
60
209
|
*/
|
|
61
210
|
handleRequest(req, res) {
|
|
62
211
|
const hostHeader = req.headers.host;
|
|
@@ -67,7 +216,7 @@ export class Port80Handler {
|
|
|
67
216
|
}
|
|
68
217
|
// Extract domain (ignoring any port in the Host header)
|
|
69
218
|
const domain = hostHeader.split(':')[0];
|
|
70
|
-
// If the request is for an ACME HTTP-01 challenge, handle it
|
|
219
|
+
// If the request is for an ACME HTTP-01 challenge, handle it
|
|
71
220
|
if (req.url && req.url.startsWith('/.well-known/acme-challenge/')) {
|
|
72
221
|
this.handleAcmeChallenge(req, res, domain);
|
|
73
222
|
return;
|
|
@@ -78,18 +227,20 @@ export class Port80Handler {
|
|
|
78
227
|
return;
|
|
79
228
|
}
|
|
80
229
|
const domainInfo = this.domainCertificates.get(domain);
|
|
81
|
-
// If certificate exists, redirect to HTTPS
|
|
230
|
+
// If certificate exists, redirect to HTTPS
|
|
82
231
|
if (domainInfo.certObtained) {
|
|
83
|
-
const
|
|
232
|
+
const httpsPort = this.options.httpsRedirectPort;
|
|
233
|
+
const portSuffix = httpsPort === 443 ? '' : `:${httpsPort}`;
|
|
234
|
+
const redirectUrl = `https://${domain}${portSuffix}${req.url || '/'}`;
|
|
84
235
|
res.statusCode = 301;
|
|
85
236
|
res.setHeader('Location', redirectUrl);
|
|
86
237
|
res.end(`Redirecting to ${redirectUrl}`);
|
|
87
238
|
}
|
|
88
239
|
else {
|
|
89
|
-
// Trigger certificate issuance if not already running
|
|
240
|
+
// Trigger certificate issuance if not already running
|
|
90
241
|
if (!domainInfo.obtainingInProgress) {
|
|
91
|
-
domainInfo.obtainingInProgress = true;
|
|
92
242
|
this.obtainCertificate(domain).catch(err => {
|
|
243
|
+
this.emit(CertManagerEvents.CERTIFICATE_FAILED, { domain, error: err.message });
|
|
93
244
|
console.error(`Error obtaining certificate for ${domain}:`, err);
|
|
94
245
|
});
|
|
95
246
|
}
|
|
@@ -98,7 +249,10 @@ export class Port80Handler {
|
|
|
98
249
|
}
|
|
99
250
|
}
|
|
100
251
|
/**
|
|
101
|
-
* Serves the ACME HTTP-01 challenge response
|
|
252
|
+
* Serves the ACME HTTP-01 challenge response
|
|
253
|
+
* @param req The HTTP request
|
|
254
|
+
* @param res The HTTP response
|
|
255
|
+
* @param domain The domain for the challenge
|
|
102
256
|
*/
|
|
103
257
|
handleAcmeChallenge(req, res, domain) {
|
|
104
258
|
const domainInfo = this.domainCertificates.get(domain);
|
|
@@ -107,7 +261,7 @@ export class Port80Handler {
|
|
|
107
261
|
res.end('Domain not configured');
|
|
108
262
|
return;
|
|
109
263
|
}
|
|
110
|
-
// The token is the last part of the URL
|
|
264
|
+
// The token is the last part of the URL
|
|
111
265
|
const urlParts = req.url?.split('/');
|
|
112
266
|
const token = urlParts ? urlParts[urlParts.length - 1] : '';
|
|
113
267
|
if (domainInfo.challengeToken === token && domainInfo.challengeKeyAuthorization) {
|
|
@@ -122,65 +276,180 @@ export class Port80Handler {
|
|
|
122
276
|
}
|
|
123
277
|
}
|
|
124
278
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
279
|
+
* Obtains a certificate for a domain using ACME HTTP-01 challenge
|
|
280
|
+
* @param domain The domain to obtain a certificate for
|
|
281
|
+
* @param isRenewal Whether this is a renewal attempt
|
|
127
282
|
*/
|
|
128
|
-
async obtainCertificate(domain) {
|
|
283
|
+
async obtainCertificate(domain, isRenewal = false) {
|
|
284
|
+
// Get the domain info
|
|
285
|
+
const domainInfo = this.domainCertificates.get(domain);
|
|
286
|
+
if (!domainInfo) {
|
|
287
|
+
throw new Error(`Domain not found: ${domain}`);
|
|
288
|
+
}
|
|
289
|
+
// Prevent concurrent certificate issuance
|
|
290
|
+
if (domainInfo.obtainingInProgress) {
|
|
291
|
+
console.log(`Certificate issuance already in progress for ${domain}`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
domainInfo.obtainingInProgress = true;
|
|
295
|
+
domainInfo.lastRenewalAttempt = new Date();
|
|
129
296
|
try {
|
|
130
297
|
const client = await this.getAcmeClient();
|
|
131
|
-
// Create a new order for the domain
|
|
298
|
+
// Create a new order for the domain
|
|
132
299
|
const order = await client.createOrder({
|
|
133
300
|
identifiers: [{ type: 'dns', value: domain }],
|
|
134
301
|
});
|
|
135
|
-
// Get the authorizations for the order
|
|
302
|
+
// Get the authorizations for the order
|
|
136
303
|
const authorizations = await client.getAuthorizations(order);
|
|
137
304
|
for (const authz of authorizations) {
|
|
138
305
|
const challenge = authz.challenges.find(ch => ch.type === 'http-01');
|
|
139
306
|
if (!challenge) {
|
|
140
307
|
throw new Error('HTTP-01 challenge not found');
|
|
141
308
|
}
|
|
142
|
-
// Get the key authorization for the challenge
|
|
309
|
+
// Get the key authorization for the challenge
|
|
143
310
|
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge);
|
|
144
|
-
|
|
311
|
+
// Store the challenge data
|
|
145
312
|
domainInfo.challengeToken = challenge.token;
|
|
146
313
|
domainInfo.challengeKeyAuthorization = keyAuthorization;
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
314
|
+
// ACME client type definition workaround - use compatible approach
|
|
315
|
+
// First check if challenge verification is needed
|
|
316
|
+
const authzUrl = authz.url;
|
|
317
|
+
try {
|
|
318
|
+
// Check if authzUrl exists and perform verification
|
|
319
|
+
if (authzUrl) {
|
|
320
|
+
await client.verifyChallenge(authz, challenge);
|
|
321
|
+
}
|
|
322
|
+
// Complete the challenge
|
|
323
|
+
await client.completeChallenge(challenge);
|
|
324
|
+
// Wait for validation
|
|
325
|
+
await client.waitForValidStatus(challenge);
|
|
326
|
+
console.log(`HTTP-01 challenge completed for ${domain}`);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
console.error(`Challenge error for ${domain}:`, error);
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
156
332
|
}
|
|
157
|
-
// Generate a CSR and
|
|
158
|
-
|
|
159
|
-
const [csrBuffer, privateKeyBuffer] = await acme.forge.createCsr({
|
|
333
|
+
// Generate a CSR and private key
|
|
334
|
+
const [csrBuffer, privateKeyBuffer] = await plugins.acme.forge.createCsr({
|
|
160
335
|
commonName: domain,
|
|
161
336
|
});
|
|
162
337
|
const csr = csrBuffer.toString();
|
|
163
338
|
const privateKey = privateKeyBuffer.toString();
|
|
164
|
-
// Finalize the order
|
|
339
|
+
// Finalize the order with our CSR
|
|
165
340
|
await client.finalizeOrder(order, csr);
|
|
341
|
+
// Get the certificate with the full chain
|
|
166
342
|
const certificate = await client.getCertificate(order);
|
|
167
|
-
|
|
343
|
+
// Store the certificate and key
|
|
168
344
|
domainInfo.certificate = certificate;
|
|
169
345
|
domainInfo.privateKey = privateKey;
|
|
170
346
|
domainInfo.certObtained = true;
|
|
171
|
-
|
|
347
|
+
// Clear challenge data
|
|
172
348
|
delete domainInfo.challengeToken;
|
|
173
349
|
delete domainInfo.challengeKeyAuthorization;
|
|
174
|
-
|
|
175
|
-
|
|
350
|
+
// Extract expiry date from certificate
|
|
351
|
+
try {
|
|
352
|
+
const matches = certificate.match(/Not After\s*:\s*(.*?)(?:\n|$)/i);
|
|
353
|
+
if (matches && matches[1]) {
|
|
354
|
+
domainInfo.expiryDate = new Date(matches[1]);
|
|
355
|
+
console.log(`Certificate for ${domain} will expire on ${domainInfo.expiryDate.toISOString()}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
console.warn(`Failed to extract expiry date from certificate for ${domain}`);
|
|
360
|
+
}
|
|
361
|
+
console.log(`Certificate ${isRenewal ? 'renewed' : 'obtained'} for ${domain}`);
|
|
362
|
+
// Emit the appropriate event
|
|
363
|
+
const eventType = isRenewal
|
|
364
|
+
? CertManagerEvents.CERTIFICATE_RENEWED
|
|
365
|
+
: CertManagerEvents.CERTIFICATE_ISSUED;
|
|
366
|
+
this.emitCertificateEvent(eventType, {
|
|
367
|
+
domain,
|
|
368
|
+
certificate,
|
|
369
|
+
privateKey,
|
|
370
|
+
expiryDate: domainInfo.expiryDate || new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days default
|
|
371
|
+
});
|
|
176
372
|
}
|
|
177
373
|
catch (error) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
374
|
+
// Check for rate limit errors
|
|
375
|
+
if (error.message && (error.message.includes('rateLimited') ||
|
|
376
|
+
error.message.includes('too many certificates') ||
|
|
377
|
+
error.message.includes('rate limit'))) {
|
|
378
|
+
console.error(`Rate limit reached for ${domain}. Waiting before retry.`);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
console.error(`Error during certificate issuance for ${domain}:`, error);
|
|
382
|
+
}
|
|
383
|
+
// Emit failure event
|
|
384
|
+
this.emit(CertManagerEvents.CERTIFICATE_FAILED, {
|
|
385
|
+
domain,
|
|
386
|
+
error: error.message || 'Unknown error',
|
|
387
|
+
isRenewal
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
finally {
|
|
391
|
+
// Reset flag whether successful or not
|
|
392
|
+
domainInfo.obtainingInProgress = false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Starts the certificate renewal timer
|
|
397
|
+
*/
|
|
398
|
+
startRenewalTimer() {
|
|
399
|
+
if (this.renewalTimer) {
|
|
400
|
+
clearInterval(this.renewalTimer);
|
|
401
|
+
}
|
|
402
|
+
// Convert hours to milliseconds
|
|
403
|
+
const checkInterval = this.options.renewCheckIntervalHours * 60 * 60 * 1000;
|
|
404
|
+
this.renewalTimer = setInterval(() => this.checkForRenewals(), checkInterval);
|
|
405
|
+
// Prevent the timer from keeping the process alive
|
|
406
|
+
if (this.renewalTimer.unref) {
|
|
407
|
+
this.renewalTimer.unref();
|
|
408
|
+
}
|
|
409
|
+
console.log(`Certificate renewal check scheduled every ${this.options.renewCheckIntervalHours} hours`);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Checks for certificates that need renewal
|
|
413
|
+
*/
|
|
414
|
+
checkForRenewals() {
|
|
415
|
+
if (this.isShuttingDown) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
console.log('Checking for certificates that need renewal...');
|
|
419
|
+
const now = new Date();
|
|
420
|
+
const renewThresholdMs = this.options.renewThresholdDays * 24 * 60 * 60 * 1000;
|
|
421
|
+
for (const [domain, domainInfo] of this.domainCertificates.entries()) {
|
|
422
|
+
// Skip domains without certificates or already in renewal
|
|
423
|
+
if (!domainInfo.certObtained || domainInfo.obtainingInProgress) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
// Skip domains without expiry dates
|
|
427
|
+
if (!domainInfo.expiryDate) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const timeUntilExpiry = domainInfo.expiryDate.getTime() - now.getTime();
|
|
431
|
+
// Check if certificate is near expiry
|
|
432
|
+
if (timeUntilExpiry <= renewThresholdMs) {
|
|
433
|
+
console.log(`Certificate for ${domain} expires soon, renewing...`);
|
|
434
|
+
this.emit(CertManagerEvents.CERTIFICATE_EXPIRING, {
|
|
435
|
+
domain,
|
|
436
|
+
expiryDate: domainInfo.expiryDate,
|
|
437
|
+
daysRemaining: Math.ceil(timeUntilExpiry / (24 * 60 * 60 * 1000))
|
|
438
|
+
});
|
|
439
|
+
// Start renewal process
|
|
440
|
+
this.obtainCertificate(domain, true).catch(err => {
|
|
441
|
+
console.error(`Error renewing certificate for ${domain}:`, err);
|
|
442
|
+
});
|
|
182
443
|
}
|
|
183
444
|
}
|
|
184
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* Emits a certificate event with the certificate data
|
|
448
|
+
* @param eventType The event type to emit
|
|
449
|
+
* @param data The certificate data
|
|
450
|
+
*/
|
|
451
|
+
emitCertificateEvent(eventType, data) {
|
|
452
|
+
this.emit(eventType, data);
|
|
453
|
+
}
|
|
185
454
|
}
|
|
186
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wb3J0ODBoYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5wb3J0ODBoYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sYUFBYSxDQUFDO0FBV3BDLE1BQU0sT0FBTyxhQUFhO0lBTXhCO1FBSFEsZUFBVSxHQUF1QixJQUFJLENBQUM7UUFDdEMsZUFBVSxHQUFrQixJQUFJLENBQUM7UUFHdkMsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksR0FBRyxFQUE4QixDQUFDO1FBRWhFLDhDQUE4QztRQUM5QyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUU7WUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDO1FBQ3ZELENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNJLFNBQVMsQ0FBQyxNQUFjO1FBQzdCLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7WUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDekYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7T0FHRztJQUNJ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
|
|
455
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wb3J0ODBoYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5wb3J0ODBoYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBc0N4Qzs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGlCQU9YO0FBUEQsV0FBWSxpQkFBaUI7SUFDM0IsOERBQXlDLENBQUE7SUFDekMsZ0VBQTJDLENBQUE7SUFDM0MsOERBQXlDLENBQUE7SUFDekMsa0VBQTZDLENBQUE7SUFDN0Msd0RBQW1DLENBQUE7SUFDbkMsd0RBQW1DLENBQUE7QUFDckMsQ0FBQyxFQVBXLGlCQUFpQixLQUFqQixpQkFBaUIsUUFPNUI7QUFFRDs7R0FFRztBQUNILE1BQU0sT0FBTyxlQUFnQixTQUFRLE9BQU8sQ0FBQyxZQUFZO0lBU3ZEOzs7T0FHRztJQUNILFlBQVksVUFBbUMsRUFBRTtRQUMvQyxLQUFLLEVBQUUsQ0FBQztRQVpGLFdBQU0sR0FBK0IsSUFBSSxDQUFDO1FBQzFDLGVBQVUsR0FBK0IsSUFBSSxDQUFDO1FBQzlDLGVBQVUsR0FBa0IsSUFBSSxDQUFDO1FBQ2pDLGlCQUFZLEdBQTBCLElBQUksQ0FBQztRQUMzQyxtQkFBYyxHQUFZLEtBQUssQ0FBQztRQVN0QyxJQUFJLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxHQUFHLEVBQThCLENBQUM7UUFFaEUsa0JBQWtCO1FBQ2xCLElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQ3hCLFlBQVksRUFBRSxPQUFPLENBQUMsWUFBWSxJQUFJLG1CQUFtQjtZQUN6RCxhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWEsSUFBSSxLQUFLLEVBQUUseUJBQXlCO1lBQ3hFLGtCQUFrQixFQUFFLE9BQU8sQ0FBQyxrQkFBa0IsSUFBSSxFQUFFO1lBQ3BELGlCQUFpQixFQUFF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
|
|
@@ -1,13 +1,91 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as tsclass from '@tsclass/tsclass';
|
|
3
|
+
/**
|
|
4
|
+
* Optional path pattern configuration that can be added to proxy configs
|
|
5
|
+
*/
|
|
6
|
+
export interface IPathPatternConfig {
|
|
7
|
+
pathPattern?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Interface for router result with additional metadata
|
|
11
|
+
*/
|
|
12
|
+
export interface IRouterResult {
|
|
13
|
+
config: tsclass.network.IReverseProxyConfig;
|
|
14
|
+
pathMatch?: string;
|
|
15
|
+
pathParams?: Record<string, string>;
|
|
16
|
+
pathRemainder?: string;
|
|
17
|
+
}
|
|
2
18
|
export declare class ProxyRouter {
|
|
3
|
-
reverseProxyConfigs
|
|
19
|
+
private reverseProxyConfigs;
|
|
20
|
+
private defaultConfig?;
|
|
21
|
+
private pathPatterns;
|
|
22
|
+
private logger;
|
|
23
|
+
constructor(configs?: tsclass.network.IReverseProxyConfig[], logger?: {
|
|
24
|
+
error: (message: string, data?: any) => void;
|
|
25
|
+
warn: (message: string, data?: any) => void;
|
|
26
|
+
info: (message: string, data?: any) => void;
|
|
27
|
+
debug: (message: string, data?: any) => void;
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Sets a new set of reverse configs to be routed to
|
|
31
|
+
* @param reverseCandidatesArg Array of reverse proxy configurations
|
|
32
|
+
*/
|
|
33
|
+
setNewProxyConfigs(reverseCandidatesArg: tsclass.network.IReverseProxyConfig[]): void;
|
|
34
|
+
/**
|
|
35
|
+
* Routes a request based on hostname and path
|
|
36
|
+
* @param req The incoming HTTP request
|
|
37
|
+
* @returns The matching proxy config or undefined if no match found
|
|
38
|
+
*/
|
|
39
|
+
routeReq(req: http.IncomingMessage): tsclass.network.IReverseProxyConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Routes a request with detailed matching information
|
|
42
|
+
* @param req The incoming HTTP request
|
|
43
|
+
* @returns Detailed routing result including matched config and path information
|
|
44
|
+
*/
|
|
45
|
+
routeReqWithDetails(req: http.IncomingMessage): IRouterResult | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Find a config for a specific host and path
|
|
48
|
+
*/
|
|
49
|
+
private findConfigForHost;
|
|
50
|
+
/**
|
|
51
|
+
* Matches a URL path against a pattern
|
|
52
|
+
* Supports:
|
|
53
|
+
* - Exact matches: /users/profile
|
|
54
|
+
* - Wildcards: /api/* (matches any path starting with /api/)
|
|
55
|
+
* - Path parameters: /users/:id (captures id as a parameter)
|
|
56
|
+
*
|
|
57
|
+
* @param path The URL path to match
|
|
58
|
+
* @param pattern The pattern to match against
|
|
59
|
+
* @returns Match result with params and remainder, or null if no match
|
|
60
|
+
*/
|
|
61
|
+
private matchPath;
|
|
62
|
+
/**
|
|
63
|
+
* Gets all currently active proxy configurations
|
|
64
|
+
* @returns Array of all active configurations
|
|
65
|
+
*/
|
|
66
|
+
getProxyConfigs(): tsclass.network.IReverseProxyConfig[];
|
|
67
|
+
/**
|
|
68
|
+
* Gets all hostnames that this router is configured to handle
|
|
69
|
+
* @returns Array of hostnames
|
|
70
|
+
*/
|
|
71
|
+
getHostnames(): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Adds a single new proxy configuration
|
|
74
|
+
* @param config The configuration to add
|
|
75
|
+
* @param pathPattern Optional path pattern for route matching
|
|
76
|
+
*/
|
|
77
|
+
addProxyConfig(config: tsclass.network.IReverseProxyConfig, pathPattern?: string): void;
|
|
4
78
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @param
|
|
79
|
+
* Sets a path pattern for an existing config
|
|
80
|
+
* @param config The existing configuration
|
|
81
|
+
* @param pathPattern The path pattern to set
|
|
82
|
+
* @returns Boolean indicating if the config was found and updated
|
|
7
83
|
*/
|
|
8
|
-
|
|
84
|
+
setPathPattern(config: tsclass.network.IReverseProxyConfig, pathPattern: string): boolean;
|
|
9
85
|
/**
|
|
10
|
-
*
|
|
86
|
+
* Removes a proxy configuration by hostname
|
|
87
|
+
* @param hostname The hostname to remove
|
|
88
|
+
* @returns Boolean indicating whether any configs were removed
|
|
11
89
|
*/
|
|
12
|
-
|
|
90
|
+
removeProxyConfig(hostname: string): boolean;
|
|
13
91
|
}
|