@push.rocks/smartproxy 18.1.0 → 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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/certificate/certificate-manager.d.ts +150 -0
- package/dist_ts/certificate/certificate-manager.js +505 -0
- package/dist_ts/certificate/events/simplified-events.d.ts +56 -0
- package/dist_ts/certificate/events/simplified-events.js +13 -0
- package/dist_ts/certificate/models/certificate-errors.d.ts +69 -0
- package/dist_ts/certificate/models/certificate-errors.js +141 -0
- package/dist_ts/certificate/models/certificate-strategy.d.ts +60 -0
- package/dist_ts/certificate/models/certificate-strategy.js +73 -0
- package/dist_ts/certificate/simplified-certificate-manager.d.ts +150 -0
- package/dist_ts/certificate/simplified-certificate-manager.js +501 -0
- package/dist_ts/http/index.d.ts +1 -9
- package/dist_ts/http/index.js +5 -11
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +4 -2
- package/dist_ts/proxies/network-proxy/network-proxy.js +3 -1
- package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +48 -0
- package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +76 -0
- package/dist_ts/proxies/network-proxy/websocket-handler.js +21 -7
- package/dist_ts/proxies/smart-proxy/cert-store.d.ts +10 -0
- package/dist_ts/proxies/smart-proxy/cert-store.js +70 -0
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +116 -0
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +401 -0
- package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +168 -0
- package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +642 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +26 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +65 -0
- package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +31 -0
- package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +102 -0
- package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +73 -0
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +10 -44
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +66 -202
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +62 -2
- package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +41 -0
- package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +132 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +18 -13
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +79 -196
- package/package.json +5 -3
- package/readme.plan.md +1405 -617
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/http/index.ts +5 -12
- package/ts/plugins.ts +4 -1
- package/ts/proxies/network-proxy/network-proxy.ts +3 -0
- package/ts/proxies/network-proxy/websocket-handler.ts +18 -6
- package/ts/proxies/smart-proxy/cert-store.ts +86 -0
- package/ts/proxies/smart-proxy/certificate-manager.ts +506 -0
- package/ts/proxies/smart-proxy/models/route-types.ts +33 -3
- package/ts/proxies/smart-proxy/network-proxy-bridge.ts +86 -239
- package/ts/proxies/smart-proxy/route-connection-handler.ts +74 -1
- package/ts/proxies/smart-proxy/smart-proxy.ts +105 -222
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified certificate manager for SmartProxy
|
|
3
|
+
*/
|
|
4
|
+
import * as plugins from '../plugins.js';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import { CertificateError, CertificateErrors } from './models/certificate-errors.js';
|
|
8
|
+
import { CertificateEvent } from './events/certificate-events.js';
|
|
9
|
+
/**
|
|
10
|
+
* Unified certificate manager
|
|
11
|
+
*/
|
|
12
|
+
export class CertificateManager extends plugins.EventEmitter {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super();
|
|
15
|
+
this.certificateCache = new Map();
|
|
16
|
+
this.pendingRequests = new Map();
|
|
17
|
+
// Validate configuration
|
|
18
|
+
if (!config.certProvider) {
|
|
19
|
+
throw CertificateErrors.noCertProvider();
|
|
20
|
+
}
|
|
21
|
+
if (!config.acmeEmail) {
|
|
22
|
+
throw CertificateErrors.missingAcmeEmail();
|
|
23
|
+
}
|
|
24
|
+
// Set defaults
|
|
25
|
+
this.config = {
|
|
26
|
+
...config,
|
|
27
|
+
storageDir: config.storageDir || './certs',
|
|
28
|
+
renewBeforeDays: config.renewBeforeDays || 30,
|
|
29
|
+
defaultCertPath: config.defaultCertPath || path.join(process.cwd(), 'assets/certs/cert.pem'),
|
|
30
|
+
defaultKeyPath: config.defaultKeyPath || path.join(process.cwd(), 'assets/certs/key.pem')
|
|
31
|
+
};
|
|
32
|
+
// Ensure storage directory exists
|
|
33
|
+
this.ensureStorageDir();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the certificate manager
|
|
37
|
+
*/
|
|
38
|
+
async start() {
|
|
39
|
+
// Initialize ACME client
|
|
40
|
+
await this.initializeAcmeClient();
|
|
41
|
+
// Load stored certificates
|
|
42
|
+
await this.loadStoredCertificates();
|
|
43
|
+
// Start renewal timer
|
|
44
|
+
this.startRenewalTimer();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stop the certificate manager
|
|
48
|
+
*/
|
|
49
|
+
async stop() {
|
|
50
|
+
if (this.renewalTimer) {
|
|
51
|
+
clearInterval(this.renewalTimer);
|
|
52
|
+
this.renewalTimer = undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get a certificate for a domain (main entry point)
|
|
57
|
+
*/
|
|
58
|
+
async getCertificate(domain) {
|
|
59
|
+
// Check cache first
|
|
60
|
+
const cached = this.certificateCache.get(domain);
|
|
61
|
+
if (cached && this.isCertificateValid(cached)) {
|
|
62
|
+
return cached;
|
|
63
|
+
}
|
|
64
|
+
// Check for pending request to avoid duplicates
|
|
65
|
+
const pending = this.pendingRequests.get(domain);
|
|
66
|
+
if (pending) {
|
|
67
|
+
return pending;
|
|
68
|
+
}
|
|
69
|
+
// Start new certificate request
|
|
70
|
+
const request = this.requestCertificate(domain);
|
|
71
|
+
this.pendingRequests.set(domain, request);
|
|
72
|
+
try {
|
|
73
|
+
const cert = await request;
|
|
74
|
+
this.pendingRequests.delete(domain);
|
|
75
|
+
return cert;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.pendingRequests.delete(domain);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Request a new certificate
|
|
84
|
+
*/
|
|
85
|
+
async requestCertificate(domain) {
|
|
86
|
+
let strategyType = 'static';
|
|
87
|
+
try {
|
|
88
|
+
// Get strategy from provider
|
|
89
|
+
let strategy;
|
|
90
|
+
try {
|
|
91
|
+
strategy = await this.config.certProvider(domain);
|
|
92
|
+
}
|
|
93
|
+
catch (providerError) {
|
|
94
|
+
throw CertificateErrors.invalidCertProvider(providerError);
|
|
95
|
+
}
|
|
96
|
+
// Track strategy type for error reporting
|
|
97
|
+
strategyType = strategy.type === 'skip' ? 'static' : strategy.type;
|
|
98
|
+
let certificate;
|
|
99
|
+
switch (strategy.type) {
|
|
100
|
+
case 'acme-http':
|
|
101
|
+
certificate = await this.requestAcmeHttpCertificate(domain);
|
|
102
|
+
break;
|
|
103
|
+
case 'acme-dns':
|
|
104
|
+
certificate = await this.requestAcmeDnsCertificate(domain);
|
|
105
|
+
break;
|
|
106
|
+
case 'static':
|
|
107
|
+
certificate = {
|
|
108
|
+
domain,
|
|
109
|
+
certificate: strategy.cert,
|
|
110
|
+
privateKey: strategy.key,
|
|
111
|
+
expiresAt: strategy.expiresAt || new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
112
|
+
source: 'static'
|
|
113
|
+
};
|
|
114
|
+
break;
|
|
115
|
+
case 'skip':
|
|
116
|
+
throw CertificateErrors.certificateNotFound(domain);
|
|
117
|
+
default:
|
|
118
|
+
throw CertificateErrors.invalidCertProvider(new Error('Unknown strategy type'));
|
|
119
|
+
}
|
|
120
|
+
// Cache and store
|
|
121
|
+
this.certificateCache.set(domain, certificate);
|
|
122
|
+
await this.storeCertificate(certificate);
|
|
123
|
+
// Emit success event
|
|
124
|
+
this.emit(CertificateEvent.OBTAINED, {
|
|
125
|
+
domain,
|
|
126
|
+
type: 'new',
|
|
127
|
+
expiresAt: certificate.expiresAt,
|
|
128
|
+
source: certificate.source,
|
|
129
|
+
certificate: certificate.certificate,
|
|
130
|
+
privateKey: certificate.privateKey
|
|
131
|
+
});
|
|
132
|
+
return certificate;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const certError = error instanceof CertificateError
|
|
136
|
+
? error
|
|
137
|
+
: new CertificateError({
|
|
138
|
+
code: 'UNKNOWN_ERROR',
|
|
139
|
+
message: error.message || 'Unknown error',
|
|
140
|
+
domain,
|
|
141
|
+
cause: error
|
|
142
|
+
});
|
|
143
|
+
this.emit(CertificateEvent.FAILED, {
|
|
144
|
+
domain,
|
|
145
|
+
error: certError,
|
|
146
|
+
strategy: strategyType
|
|
147
|
+
});
|
|
148
|
+
throw certError;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Request certificate via ACME HTTP-01
|
|
153
|
+
*/
|
|
154
|
+
async requestAcmeHttpCertificate(domain) {
|
|
155
|
+
if (domain.includes('*')) {
|
|
156
|
+
throw CertificateErrors.wildcardNotSupported(domain);
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
// Create ACME order
|
|
160
|
+
const order = await this.acmeClient.createOrder({
|
|
161
|
+
identifiers: [{ type: 'dns', value: domain }]
|
|
162
|
+
});
|
|
163
|
+
// Get authorization
|
|
164
|
+
const authorization = await this.acmeClient.getAuthorization(order.authorizations[0]);
|
|
165
|
+
const challenge = authorization.challenges.find(c => c.type === 'http-01');
|
|
166
|
+
if (!challenge) {
|
|
167
|
+
throw new Error('No HTTP-01 challenge available');
|
|
168
|
+
}
|
|
169
|
+
// Prepare challenge response
|
|
170
|
+
const keyAuthorization = await this.acmeClient.getChallengeKeyAuthorization(challenge);
|
|
171
|
+
// Set up HTTP responder (this would integrate with Port80Handler)
|
|
172
|
+
await this.setupHttpChallenge(challenge.token, keyAuthorization);
|
|
173
|
+
// Notify ACME server
|
|
174
|
+
await this.acmeClient.completeChallenge(challenge);
|
|
175
|
+
await this.acmeClient.waitForValidation(challenge);
|
|
176
|
+
// Generate CSR
|
|
177
|
+
const keypair = await this.generateKeypair();
|
|
178
|
+
const csr = await this.generateCsr(domain, keypair);
|
|
179
|
+
// Finalize order
|
|
180
|
+
await this.acmeClient.finalizeOrder(order, csr);
|
|
181
|
+
const cert = await this.acmeClient.getCertificate(order);
|
|
182
|
+
// Clean up challenge
|
|
183
|
+
await this.cleanupHttpChallenge(challenge.token);
|
|
184
|
+
return {
|
|
185
|
+
domain,
|
|
186
|
+
certificate: cert,
|
|
187
|
+
privateKey: keypair.privateKey,
|
|
188
|
+
expiresAt: this.extractExpiryDate(cert),
|
|
189
|
+
source: 'acme-http'
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw CertificateErrors.acmeHttpChallengeFailed(domain, error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Request certificate via ACME DNS-01
|
|
198
|
+
*/
|
|
199
|
+
async requestAcmeDnsCertificate(domain) {
|
|
200
|
+
try {
|
|
201
|
+
// Create ACME order
|
|
202
|
+
const order = await this.acmeClient.createOrder({
|
|
203
|
+
identifiers: [{ type: 'dns', value: domain }]
|
|
204
|
+
});
|
|
205
|
+
// Get authorization
|
|
206
|
+
const authorization = await this.acmeClient.getAuthorization(order.authorizations[0]);
|
|
207
|
+
const challenge = authorization.challenges.find(c => c.type === 'dns-01');
|
|
208
|
+
if (!challenge) {
|
|
209
|
+
throw new Error('No DNS-01 challenge available');
|
|
210
|
+
}
|
|
211
|
+
// Get DNS record value
|
|
212
|
+
const keyAuthorization = await this.acmeClient.getChallengeKeyAuthorization(challenge);
|
|
213
|
+
const dnsRecord = this.acmeClient.keyAuthorizationToDns01(keyAuthorization);
|
|
214
|
+
// Note: Actual DNS record creation would be handled externally
|
|
215
|
+
console.log(`Please create DNS TXT record: _acme-challenge.${domain} = ${dnsRecord}`);
|
|
216
|
+
// In a real implementation, we'd wait for DNS propagation
|
|
217
|
+
// For now, this is a placeholder
|
|
218
|
+
await new Promise(resolve => setTimeout(resolve, 60000)); // Wait 60 seconds
|
|
219
|
+
// Notify ACME server
|
|
220
|
+
await this.acmeClient.completeChallenge(challenge);
|
|
221
|
+
await this.acmeClient.waitForValidation(challenge);
|
|
222
|
+
// Generate CSR
|
|
223
|
+
const keypair = await this.generateKeypair();
|
|
224
|
+
const csr = await this.generateCsr(domain, keypair);
|
|
225
|
+
// Finalize order
|
|
226
|
+
await this.acmeClient.finalizeOrder(order, csr);
|
|
227
|
+
const cert = await this.acmeClient.getCertificate(order);
|
|
228
|
+
return {
|
|
229
|
+
domain,
|
|
230
|
+
certificate: cert,
|
|
231
|
+
privateKey: keypair.privateKey,
|
|
232
|
+
expiresAt: this.extractExpiryDate(cert),
|
|
233
|
+
source: 'acme-dns'
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
throw CertificateErrors.acmeDnsChallengeFailed(domain, error);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Renew a certificate
|
|
242
|
+
*/
|
|
243
|
+
async renewCertificate(domain) {
|
|
244
|
+
const existing = this.certificateCache.get(domain);
|
|
245
|
+
if (!existing) {
|
|
246
|
+
throw CertificateErrors.certificateNotFound(domain);
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
// Request new certificate
|
|
250
|
+
const renewed = await this.requestCertificate(domain);
|
|
251
|
+
// Emit renewal event
|
|
252
|
+
this.emit(CertificateEvent.OBTAINED, {
|
|
253
|
+
domain,
|
|
254
|
+
type: 'renewed',
|
|
255
|
+
expiresAt: renewed.expiresAt,
|
|
256
|
+
source: renewed.source,
|
|
257
|
+
certificate: renewed.certificate,
|
|
258
|
+
privateKey: renewed.privateKey
|
|
259
|
+
});
|
|
260
|
+
return renewed;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check certificates for renewal
|
|
268
|
+
*/
|
|
269
|
+
async checkForRenewals() {
|
|
270
|
+
for (const [domain, cert] of this.certificateCache.entries()) {
|
|
271
|
+
if (this.shouldRenew(cert)) {
|
|
272
|
+
try {
|
|
273
|
+
await this.renewCertificate(domain);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.error(`Failed to renew certificate for ${domain}:`, error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (this.isExpiringSoon(cert)) {
|
|
280
|
+
this.emit(CertificateEvent.EXPIRING, {
|
|
281
|
+
domain,
|
|
282
|
+
expiresAt: cert.expiresAt,
|
|
283
|
+
daysRemaining: this.getDaysRemaining(cert)
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Initialize ACME client
|
|
290
|
+
*/
|
|
291
|
+
async initializeAcmeClient() {
|
|
292
|
+
// ACME client initialization placeholder
|
|
293
|
+
// In a real implementation, this would use an ACME library
|
|
294
|
+
console.log(`Initializing ACME client for ${this.config.acmeServer} environment`);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Load stored certificates from disk
|
|
298
|
+
*/
|
|
299
|
+
async loadStoredCertificates() {
|
|
300
|
+
try {
|
|
301
|
+
const files = await fs.readdir(this.config.storageDir);
|
|
302
|
+
const certFiles = files.filter(f => f.endsWith('.json'));
|
|
303
|
+
for (const file of certFiles) {
|
|
304
|
+
try {
|
|
305
|
+
const content = await fs.readFile(path.join(this.config.storageDir, file), 'utf-8');
|
|
306
|
+
const cert = JSON.parse(content);
|
|
307
|
+
cert.expiresAt = new Date(cert.expiresAt);
|
|
308
|
+
if (this.isCertificateValid(cert)) {
|
|
309
|
+
this.certificateCache.set(cert.domain, cert);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
console.error(`Failed to load certificate ${file}:`, error);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
console.error('Failed to load certificates:', error);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Store certificate to disk
|
|
323
|
+
*/
|
|
324
|
+
async storeCertificate(cert) {
|
|
325
|
+
const filename = `${cert.domain}.json`;
|
|
326
|
+
const filepath = path.join(this.config.storageDir, filename);
|
|
327
|
+
try {
|
|
328
|
+
await fs.writeFile(filepath, JSON.stringify(cert, null, 2));
|
|
329
|
+
// Also save individual cert and key files
|
|
330
|
+
await fs.writeFile(path.join(this.config.storageDir, `${cert.domain}.crt`), cert.certificate);
|
|
331
|
+
await fs.writeFile(path.join(this.config.storageDir, `${cert.domain}.key`), cert.privateKey);
|
|
332
|
+
// Set proper permissions on key file
|
|
333
|
+
await fs.chmod(path.join(this.config.storageDir, `${cert.domain}.key`), 0o600);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
throw CertificateErrors.storageError('write', filepath, error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Ensure storage directory exists
|
|
341
|
+
*/
|
|
342
|
+
async ensureStorageDir() {
|
|
343
|
+
try {
|
|
344
|
+
await fs.mkdir(this.config.storageDir, { recursive: true });
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
console.error('Failed to create storage directory:', error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get or create ACME account key
|
|
352
|
+
*/
|
|
353
|
+
async getOrCreateAccountKey() {
|
|
354
|
+
const keyPath = path.join(this.config.storageDir, 'account.key');
|
|
355
|
+
try {
|
|
356
|
+
return await fs.readFile(keyPath, 'utf-8');
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
// Generate new key - placeholder
|
|
360
|
+
const dummyKey = '-----BEGIN PRIVATE KEY-----\nDUMMY_KEY_FOR_TESTING\n-----END PRIVATE KEY-----';
|
|
361
|
+
await fs.writeFile(keyPath, dummyKey);
|
|
362
|
+
await fs.chmod(keyPath, 0o600);
|
|
363
|
+
return dummyKey;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Generate keypair for certificate
|
|
368
|
+
*/
|
|
369
|
+
async generateKeypair() {
|
|
370
|
+
// Keypair generation placeholder
|
|
371
|
+
return {
|
|
372
|
+
privateKey: '-----BEGIN PRIVATE KEY-----\nDUMMY_PRIVATE_KEY\n-----END PRIVATE KEY-----',
|
|
373
|
+
publicKey: '-----BEGIN PUBLIC KEY-----\nDUMMY_PUBLIC_KEY\n-----END PUBLIC KEY-----'
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Generate CSR for domain
|
|
378
|
+
*/
|
|
379
|
+
async generateCsr(domain, keypair) {
|
|
380
|
+
// CSR generation placeholder
|
|
381
|
+
return `-----BEGIN CERTIFICATE REQUEST-----\nDUMMY_CSR_FOR_${domain}\n-----END CERTIFICATE REQUEST-----`;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Extract expiry date from certificate
|
|
385
|
+
*/
|
|
386
|
+
extractExpiryDate(certPem) {
|
|
387
|
+
// Certificate expiry extraction placeholder
|
|
388
|
+
// In a real implementation, this would parse the certificate
|
|
389
|
+
return new Date(Date.now() + 90 * 24 * 60 * 60 * 1000); // 90 days from now
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Check if certificate is valid
|
|
393
|
+
*/
|
|
394
|
+
isCertificateValid(cert) {
|
|
395
|
+
return cert.expiresAt > new Date();
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Check if certificate should be renewed
|
|
399
|
+
*/
|
|
400
|
+
shouldRenew(cert) {
|
|
401
|
+
const daysRemaining = this.getDaysRemaining(cert);
|
|
402
|
+
return daysRemaining <= this.config.renewBeforeDays;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Check if certificate is expiring soon
|
|
406
|
+
*/
|
|
407
|
+
isExpiringSoon(cert) {
|
|
408
|
+
const daysRemaining = this.getDaysRemaining(cert);
|
|
409
|
+
return daysRemaining <= this.config.renewBeforeDays + 7; // Warn 7 days before renewal
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get days remaining until expiry
|
|
413
|
+
*/
|
|
414
|
+
getDaysRemaining(cert) {
|
|
415
|
+
const now = new Date();
|
|
416
|
+
const diff = cert.expiresAt.getTime() - now.getTime();
|
|
417
|
+
return Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Start renewal timer
|
|
421
|
+
*/
|
|
422
|
+
startRenewalTimer() {
|
|
423
|
+
// Check every 6 hours
|
|
424
|
+
this.renewalTimer = setInterval(() => {
|
|
425
|
+
this.checkForRenewals().catch(error => {
|
|
426
|
+
console.error('Renewal check failed:', error);
|
|
427
|
+
});
|
|
428
|
+
}, 6 * 60 * 60 * 1000);
|
|
429
|
+
// Also check immediately
|
|
430
|
+
this.checkForRenewals().catch(error => {
|
|
431
|
+
console.error('Initial renewal check failed:', error);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get default certificate for SNI fallback
|
|
436
|
+
*/
|
|
437
|
+
async getDefaultCertificate() {
|
|
438
|
+
try {
|
|
439
|
+
const [cert, key] = await Promise.all([
|
|
440
|
+
fs.readFile(this.config.defaultCertPath, 'utf-8'),
|
|
441
|
+
fs.readFile(this.config.defaultKeyPath, 'utf-8')
|
|
442
|
+
]);
|
|
443
|
+
return { cert, key };
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
throw new CertificateError({
|
|
447
|
+
code: 'DEFAULT_CERT_ERROR',
|
|
448
|
+
message: 'Failed to load default certificate',
|
|
449
|
+
solution: 'Ensure default certificate files exist at configured paths',
|
|
450
|
+
cause: error
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Set up HTTP challenge responder
|
|
456
|
+
*/
|
|
457
|
+
async setupHttpChallenge(token, keyAuthorization) {
|
|
458
|
+
// This would integrate with Port80Handler
|
|
459
|
+
// For now, it's a placeholder
|
|
460
|
+
console.log(`HTTP Challenge: /.well-known/acme-challenge/${token} = ${keyAuthorization}`);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Clean up HTTP challenge
|
|
464
|
+
*/
|
|
465
|
+
async cleanupHttpChallenge(token) {
|
|
466
|
+
// This would integrate with Port80Handler
|
|
467
|
+
// For now, it's a placeholder
|
|
468
|
+
console.log(`Cleanup HTTP Challenge: ${token}`);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Type assertion for event emitter
|
|
472
|
+
*/
|
|
473
|
+
on(event, listener) {
|
|
474
|
+
return super.on(event, listener);
|
|
475
|
+
}
|
|
476
|
+
off(event, listener) {
|
|
477
|
+
return super.off(event, listener);
|
|
478
|
+
}
|
|
479
|
+
emit(event, data) {
|
|
480
|
+
return super.emit(event, data);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Update routes (placeholder for future implementation)
|
|
484
|
+
*/
|
|
485
|
+
async updateRoutes(routes) {
|
|
486
|
+
// Process routes to extract certificate requirements
|
|
487
|
+
for (const route of routes) {
|
|
488
|
+
if (route.action.type === 'forward' &&
|
|
489
|
+
route.action.tls?.mode === 'terminate' &&
|
|
490
|
+
route.action.tls?.certificate === 'auto' &&
|
|
491
|
+
route.match.domains) {
|
|
492
|
+
const domains = Array.isArray(route.match.domains)
|
|
493
|
+
? route.match.domains
|
|
494
|
+
: [route.match.domains];
|
|
495
|
+
for (const domain of domains) {
|
|
496
|
+
// Trigger certificate retrieval for auto domains
|
|
497
|
+
this.getCertificate(domain).catch(err => {
|
|
498
|
+
console.error(`Failed to get certificate for ${domain}:`, err);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGUtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2NlcnRpZmljYXRlL2NlcnRpZmljYXRlLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVsQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNyRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQStCbEU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFPMUQsWUFBWSxNQUFnQztRQUMxQyxLQUFLLEVBQUUsQ0FBQztRQU5GLHFCQUFnQixHQUFHLElBQUksR0FBRyxFQUE2QixDQUFDO1FBR3hELG9CQUFlLEdBQUcsSUFBSSxHQUFHLEVBQXNDLENBQUM7UUFLdEUseUJBQXlCO1FBQ3pCLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDekIsTUFBTSxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUMzQyxDQUFDO1FBRUQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN0QixNQUFNLGlCQUFpQixDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDN0MsQ0FBQztRQUVELGVBQWU7UUFDZixJQUFJLENBQUMsTUFBTSxHQUFHO1lBQ1osR0FBRyxNQUFNO1lBQ1QsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVLElBQUksU0FBUztZQUMxQyxlQUFlLEVBQUUsTUFBTSxDQUFDLGVBQWUsSUFBSSxFQUFFO1lBQzdDLGVBQWUsRUFBRSxNQUFNLENBQUMsZUFBZSxJQUFJLElBQUksQ0FB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
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified certificate event system
|
|
3
|
+
*/
|
|
4
|
+
import type { CertificateError } from '../models/certificate-errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Certificate event types - only the essentials
|
|
7
|
+
*/
|
|
8
|
+
export declare enum CertificateEvent {
|
|
9
|
+
OBTAINED = "certificate:obtained",
|
|
10
|
+
FAILED = "certificate:failed",
|
|
11
|
+
EXPIRING = "certificate:expiring"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Certificate obtained event data
|
|
15
|
+
*/
|
|
16
|
+
export interface CertificateObtainedEvent {
|
|
17
|
+
domain: string;
|
|
18
|
+
type: 'new' | 'renewed';
|
|
19
|
+
expiresAt: Date;
|
|
20
|
+
source: 'acme-http' | 'acme-dns' | 'static';
|
|
21
|
+
certificate: string;
|
|
22
|
+
privateKey: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Certificate failed event data
|
|
26
|
+
*/
|
|
27
|
+
export interface CertificateFailedEvent {
|
|
28
|
+
domain: string;
|
|
29
|
+
error: CertificateError;
|
|
30
|
+
attemptNumber?: number;
|
|
31
|
+
willRetry?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Certificate expiring event data
|
|
35
|
+
*/
|
|
36
|
+
export interface CertificateExpiringEvent {
|
|
37
|
+
domain: string;
|
|
38
|
+
expiresAt: Date;
|
|
39
|
+
daysRemaining: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Combined event map for TypeScript typing
|
|
43
|
+
*/
|
|
44
|
+
export interface CertificateEventMap {
|
|
45
|
+
[CertificateEvent.OBTAINED]: CertificateObtainedEvent;
|
|
46
|
+
[CertificateEvent.FAILED]: CertificateFailedEvent;
|
|
47
|
+
[CertificateEvent.EXPIRING]: CertificateExpiringEvent;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Type-safe event emitter interface
|
|
51
|
+
*/
|
|
52
|
+
export interface ICertificateEventEmitter {
|
|
53
|
+
on<K extends keyof CertificateEventMap>(event: K, listener: (data: CertificateEventMap[K]) => void): this;
|
|
54
|
+
off<K extends keyof CertificateEventMap>(event: K, listener: (data: CertificateEventMap[K]) => void): this;
|
|
55
|
+
emit<K extends keyof CertificateEventMap>(event: K, data: CertificateEventMap[K]): boolean;
|
|
56
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified certificate event system
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Certificate event types - only the essentials
|
|
6
|
+
*/
|
|
7
|
+
export var CertificateEvent;
|
|
8
|
+
(function (CertificateEvent) {
|
|
9
|
+
CertificateEvent["OBTAINED"] = "certificate:obtained";
|
|
10
|
+
CertificateEvent["FAILED"] = "certificate:failed";
|
|
11
|
+
CertificateEvent["EXPIRING"] = "certificate:expiring";
|
|
12
|
+
})(CertificateEvent || (CertificateEvent = {}));
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2ltcGxpZmllZC1ldmVudHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jZXJ0aWZpY2F0ZS9ldmVudHMvc2ltcGxpZmllZC1ldmVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFJSDs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGdCQUlYO0FBSkQsV0FBWSxnQkFBZ0I7SUFDMUIscURBQWlDLENBQUE7SUFDakMsaURBQTZCLENBQUE7SUFDN0IscURBQWlDLENBQUE7QUFDbkMsQ0FBQyxFQUpXLGdCQUFnQixLQUFoQixnQkFBZ0IsUUFJM0IifQ==
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error types for certificate management
|
|
3
|
+
*/
|
|
4
|
+
export interface CertificateErrorDetails {
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
solution?: string;
|
|
8
|
+
domain?: string;
|
|
9
|
+
challengeType?: 'http' | 'dns';
|
|
10
|
+
details?: Record<string, any>;
|
|
11
|
+
cause?: Error;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Certificate-specific error class with structured information
|
|
15
|
+
*/
|
|
16
|
+
export declare class CertificateError extends Error {
|
|
17
|
+
readonly code: string;
|
|
18
|
+
readonly solution?: string;
|
|
19
|
+
readonly domain?: string;
|
|
20
|
+
readonly challengeType?: 'http' | 'dns';
|
|
21
|
+
readonly details?: Record<string, any>;
|
|
22
|
+
readonly cause?: Error;
|
|
23
|
+
constructor(details: CertificateErrorDetails);
|
|
24
|
+
/**
|
|
25
|
+
* Convert error to JSON for logging
|
|
26
|
+
*/
|
|
27
|
+
toJSON(): Record<string, any>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Common certificate error codes
|
|
31
|
+
*/
|
|
32
|
+
export declare const CertificateErrorCodes: {
|
|
33
|
+
readonly NO_CERT_PROVIDER: "NO_CERT_PROVIDER";
|
|
34
|
+
readonly INVALID_CERT_PROVIDER: "INVALID_CERT_PROVIDER";
|
|
35
|
+
readonly MISSING_ACME_EMAIL: "MISSING_ACME_EMAIL";
|
|
36
|
+
readonly ACME_HTTP_CHALLENGE_FAILED: "ACME_HTTP_CHALLENGE_FAILED";
|
|
37
|
+
readonly ACME_DNS_CHALLENGE_FAILED: "ACME_DNS_CHALLENGE_FAILED";
|
|
38
|
+
readonly ACME_RATE_LIMITED: "ACME_RATE_LIMITED";
|
|
39
|
+
readonly ACME_ACCOUNT_ERROR: "ACME_ACCOUNT_ERROR";
|
|
40
|
+
readonly ACME_VALIDATION_ERROR: "ACME_VALIDATION_ERROR";
|
|
41
|
+
readonly CERTIFICATE_EXPIRED: "CERTIFICATE_EXPIRED";
|
|
42
|
+
readonly CERTIFICATE_INVALID: "CERTIFICATE_INVALID";
|
|
43
|
+
readonly CERTIFICATE_NOT_FOUND: "CERTIFICATE_NOT_FOUND";
|
|
44
|
+
readonly CERTIFICATE_PARSE_ERROR: "CERTIFICATE_PARSE_ERROR";
|
|
45
|
+
readonly STORAGE_READ_ERROR: "STORAGE_READ_ERROR";
|
|
46
|
+
readonly STORAGE_WRITE_ERROR: "STORAGE_WRITE_ERROR";
|
|
47
|
+
readonly STORAGE_PERMISSION_ERROR: "STORAGE_PERMISSION_ERROR";
|
|
48
|
+
readonly NETWORK_TIMEOUT: "NETWORK_TIMEOUT";
|
|
49
|
+
readonly NETWORK_UNREACHABLE: "NETWORK_UNREACHABLE";
|
|
50
|
+
readonly DNS_RESOLUTION_FAILED: "DNS_RESOLUTION_FAILED";
|
|
51
|
+
readonly INVALID_DOMAIN: "INVALID_DOMAIN";
|
|
52
|
+
readonly WILDCARD_NOT_SUPPORTED: "WILDCARD_NOT_SUPPORTED";
|
|
53
|
+
readonly DOMAIN_NOT_CONFIGURED: "DOMAIN_NOT_CONFIGURED";
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Helper functions for creating common certificate errors
|
|
57
|
+
*/
|
|
58
|
+
export declare const CertificateErrors: {
|
|
59
|
+
noCertProvider: () => CertificateError;
|
|
60
|
+
invalidCertProvider: (error: Error) => CertificateError;
|
|
61
|
+
missingAcmeEmail: () => CertificateError;
|
|
62
|
+
acmeHttpChallengeFailed: (domain: string, details?: any) => CertificateError;
|
|
63
|
+
acmeDnsChallengeFailed: (domain: string, details?: any) => CertificateError;
|
|
64
|
+
acmeRateLimited: (domain: string, resetTime?: Date) => CertificateError;
|
|
65
|
+
certificateExpired: (domain: string, expiredAt: Date) => CertificateError;
|
|
66
|
+
certificateNotFound: (domain: string) => CertificateError;
|
|
67
|
+
wildcardNotSupported: (domain: string) => CertificateError;
|
|
68
|
+
storageError: (operation: "read" | "write", path: string, error: Error) => CertificateError;
|
|
69
|
+
};
|