@push.rocks/smartproxy 18.2.0 → 19.2.2

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 (63) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/common/eventUtils.d.ts +1 -2
  3. package/dist_ts/common/eventUtils.js +2 -1
  4. package/dist_ts/core/models/common-types.d.ts +1 -1
  5. package/dist_ts/core/models/common-types.js +1 -1
  6. package/dist_ts/core/utils/event-utils.d.ts +9 -9
  7. package/dist_ts/core/utils/event-utils.js +6 -14
  8. package/dist_ts/http/models/http-types.d.ts +13 -1
  9. package/dist_ts/http/models/http-types.js +1 -1
  10. package/dist_ts/index.d.ts +4 -6
  11. package/dist_ts/index.js +4 -10
  12. package/dist_ts/proxies/index.d.ts +3 -2
  13. package/dist_ts/proxies/index.js +4 -5
  14. package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +31 -49
  15. package/dist_ts/proxies/network-proxy/certificate-manager.js +77 -374
  16. package/dist_ts/proxies/network-proxy/models/types.d.ts +12 -1
  17. package/dist_ts/proxies/network-proxy/models/types.js +1 -1
  18. package/dist_ts/proxies/network-proxy/network-proxy.d.ts +2 -7
  19. package/dist_ts/proxies/network-proxy/network-proxy.js +10 -19
  20. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +6 -0
  21. package/dist_ts/proxies/smart-proxy/certificate-manager.js +24 -5
  22. package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -1
  23. package/dist_ts/proxies/smart-proxy/models/index.js +1 -5
  24. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +30 -1
  25. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +4 -0
  26. package/dist_ts/proxies/smart-proxy/route-manager.js +7 -1
  27. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +4 -0
  28. package/dist_ts/proxies/smart-proxy/smart-proxy.js +112 -26
  29. package/package.json +1 -2
  30. package/readme.hints.md +31 -1
  31. package/readme.md +82 -6
  32. package/readme.plan.md +109 -1417
  33. package/ts/00_commitinfo_data.ts +1 -1
  34. package/ts/common/eventUtils.ts +2 -2
  35. package/ts/core/models/common-types.ts +1 -1
  36. package/ts/core/utils/event-utils.ts +12 -21
  37. package/ts/http/models/http-types.ts +8 -4
  38. package/ts/index.ts +11 -14
  39. package/ts/proxies/index.ts +7 -4
  40. package/ts/proxies/network-proxy/certificate-manager.ts +92 -417
  41. package/ts/proxies/network-proxy/models/types.ts +14 -2
  42. package/ts/proxies/network-proxy/network-proxy.ts +10 -19
  43. package/ts/proxies/smart-proxy/certificate-manager.ts +31 -4
  44. package/ts/proxies/smart-proxy/models/index.ts +2 -1
  45. package/ts/proxies/smart-proxy/models/interfaces.ts +31 -2
  46. package/ts/proxies/smart-proxy/models/route-types.ts +1 -1
  47. package/ts/proxies/smart-proxy/route-manager.ts +7 -0
  48. package/ts/proxies/smart-proxy/smart-proxy.ts +142 -25
  49. package/ts/certificate/acme/acme-factory.ts +0 -48
  50. package/ts/certificate/acme/challenge-handler.ts +0 -110
  51. package/ts/certificate/acme/index.ts +0 -3
  52. package/ts/certificate/events/certificate-events.ts +0 -36
  53. package/ts/certificate/index.ts +0 -75
  54. package/ts/certificate/models/certificate-types.ts +0 -109
  55. package/ts/certificate/providers/cert-provisioner.ts +0 -519
  56. package/ts/certificate/providers/index.ts +0 -3
  57. package/ts/certificate/storage/file-storage.ts +0 -234
  58. package/ts/certificate/storage/index.ts +0 -3
  59. package/ts/certificate/utils/certificate-helpers.ts +0 -50
  60. package/ts/http/port80/acme-interfaces.ts +0 -169
  61. package/ts/http/port80/challenge-responder.ts +0 -246
  62. package/ts/http/port80/index.ts +0 -13
  63. package/ts/http/port80/port80-handler.ts +0 -728
@@ -18,7 +18,6 @@ import { RequestHandler, type IMetricsTracker } from './request-handler.js';
18
18
  import { WebSocketHandler } from './websocket-handler.js';
19
19
  import { ProxyRouter } from '../../http/router/index.js';
20
20
  import { RouteRouter } from '../../http/router/route-router.js';
21
- import { Port80Handler } from '../../http/port80/port80-handler.js';
22
21
  import { FunctionCache } from './function-cache.js';
23
22
 
24
23
  /**
@@ -221,15 +220,10 @@ export class NetworkProxy implements IMetricsTracker {
221
220
  }
222
221
 
223
222
  /**
224
- * Sets an external Port80Handler for certificate management
225
- * This allows the NetworkProxy to use a centrally managed Port80Handler
226
- * instead of creating its own
227
- *
228
- * @param handler The Port80Handler instance to use
223
+ * @deprecated Use SmartCertManager instead
229
224
  */
230
- public setExternalPort80Handler(handler: Port80Handler): void {
231
- // Connect it to the certificate manager
232
- this.certificateManager.setExternalPort80Handler(handler);
225
+ public setExternalPort80Handler(handler: any): void {
226
+ this.logger.warn('Port80Handler is deprecated - use SmartCertManager instead');
233
227
  }
234
228
 
235
229
  /**
@@ -238,10 +232,7 @@ export class NetworkProxy implements IMetricsTracker {
238
232
  public async start(): Promise<void> {
239
233
  this.startTime = Date.now();
240
234
 
241
- // Initialize Port80Handler if enabled and not using external handler
242
- if (this.options.acme?.enabled && !this.options.useExternalPort80Handler) {
243
- await this.certificateManager.initializePort80Handler();
244
- }
235
+ // Certificate management is now handled by SmartCertManager
245
236
 
246
237
  // Create HTTP/2 server with HTTP/1 fallback
247
238
  this.httpsServer = plugins.http2.createSecureServer(
@@ -385,7 +376,7 @@ export class NetworkProxy implements IMetricsTracker {
385
376
 
386
377
  // Directly update the certificate manager with the new routes
387
378
  // This will extract domains and handle certificate provisioning
388
- this.certificateManager.updateRouteConfigs(routes);
379
+ this.certificateManager.updateRoutes(routes);
389
380
 
390
381
  // Collect all domains and certificates for configuration
391
382
  const currentHostnames = new Set<string>();
@@ -425,7 +416,7 @@ export class NetworkProxy implements IMetricsTracker {
425
416
  // Update certificate cache with any static certificates
426
417
  for (const [domain, certData] of certificateUpdates.entries()) {
427
418
  try {
428
- this.certificateManager.updateCertificateCache(
419
+ this.certificateManager.updateCertificate(
429
420
  domain,
430
421
  certData.cert,
431
422
  certData.key
@@ -547,8 +538,7 @@ export class NetworkProxy implements IMetricsTracker {
547
538
  // Close all connection pool connections
548
539
  this.connectionPool.closeAllConnections();
549
540
 
550
- // Stop Port80Handler if internally managed
551
- await this.certificateManager.stopPort80Handler();
541
+ // Certificate management cleanup is handled by SmartCertManager
552
542
 
553
543
  // Close the HTTPS server
554
544
  return new Promise((resolve) => {
@@ -566,7 +556,8 @@ export class NetworkProxy implements IMetricsTracker {
566
556
  * @returns A promise that resolves when the request is submitted (not when the certificate is issued)
567
557
  */
568
558
  public async requestCertificate(domain: string): Promise<boolean> {
569
- return this.certificateManager.requestCertificate(domain);
559
+ this.logger.warn('requestCertificate is deprecated - use SmartCertManager instead');
560
+ return false;
570
561
  }
571
562
 
572
563
  /**
@@ -587,7 +578,7 @@ export class NetworkProxy implements IMetricsTracker {
587
578
  expiryDate?: Date
588
579
  ): void {
589
580
  this.logger.info(`Updating certificate for ${domain}`);
590
- this.certificateManager.updateCertificateCache(domain, certificate, privateKey, expiryDate);
581
+ this.certificateManager.updateCertificate(domain, certificate, privateKey);
591
582
  }
592
583
 
593
584
  /**
@@ -1,6 +1,7 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { NetworkProxy } from '../network-proxy/index.js';
3
3
  import type { IRouteConfig, IRouteTls } from './models/route-types.js';
4
+ import type { IAcmeOptions } from './models/interfaces.js';
4
5
  import { CertStore } from './cert-store.js';
5
6
 
6
7
  export interface ICertStatus {
@@ -31,6 +32,9 @@ export class SmartCertManager {
31
32
  // Track certificate status by route name
32
33
  private certStatus: Map<string, ICertStatus> = new Map();
33
34
 
35
+ // Global ACME defaults from top-level configuration
36
+ private globalAcmeDefaults: IAcmeOptions | null = null;
37
+
34
38
  // Callback to update SmartProxy routes for challenges
35
39
  private updateRoutesCallback?: (routes: IRouteConfig[]) => Promise<void>;
36
40
 
@@ -50,6 +54,13 @@ export class SmartCertManager {
50
54
  this.networkProxy = networkProxy;
51
55
  }
52
56
 
57
+ /**
58
+ * Set global ACME defaults from top-level configuration
59
+ */
60
+ public setGlobalAcmeDefaults(defaults: IAcmeOptions): void {
61
+ this.globalAcmeDefaults = defaults;
62
+ }
63
+
53
64
  /**
54
65
  * Set callback for updating routes (used for challenge routes)
55
66
  */
@@ -146,7 +157,12 @@ export class SmartCertManager {
146
157
  domains: string[]
147
158
  ): Promise<void> {
148
159
  if (!this.smartAcme) {
149
- throw new Error('SmartAcme not initialized');
160
+ throw new Error(
161
+ 'SmartAcme not initialized. This usually means no ACME email was provided. ' +
162
+ 'Please ensure you have configured ACME with an email address either:\n' +
163
+ '1. In the top-level "acme" configuration\n' +
164
+ '2. In the route\'s "tls.acme" configuration'
165
+ );
150
166
  }
151
167
 
152
168
  const primaryDomain = domains[0];
@@ -161,7 +177,12 @@ export class SmartCertManager {
161
177
  return;
162
178
  }
163
179
 
164
- console.log(`Requesting ACME certificate for ${domains.join(', ')}`);
180
+ // Apply renewal threshold from global defaults or route config
181
+ const renewThreshold = route.action.tls?.acme?.renewBeforeDays ||
182
+ this.globalAcmeDefaults?.renewThresholdDays ||
183
+ 30;
184
+
185
+ console.log(`Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`);
165
186
  this.updateCertStatus(routeName, 'pending', 'acme');
166
187
 
167
188
  try {
@@ -303,7 +324,10 @@ export class SmartCertManager {
303
324
  */
304
325
  private isCertificateValid(cert: ICertificateData): boolean {
305
326
  const now = new Date();
306
- const expiryThreshold = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days
327
+
328
+ // Use renewal threshold from global defaults or fallback to 30 days
329
+ const renewThresholdDays = this.globalAcmeDefaults?.renewThresholdDays || 30;
330
+ const expiryThreshold = new Date(now.getTime() + renewThresholdDays * 24 * 60 * 60 * 1000);
307
331
 
308
332
  return cert.expiryDate > expiryThreshold;
309
333
  }
@@ -417,12 +441,15 @@ export class SmartCertManager {
417
441
  * Setup challenge handler integration with SmartProxy routing
418
442
  */
419
443
  private setupChallengeHandler(http01Handler: plugins.smartacme.handlers.Http01MemoryHandler): void {
444
+ // Use challenge port from global config or default to 80
445
+ const challengePort = this.globalAcmeDefaults?.port || 80;
446
+
420
447
  // Create a challenge route that delegates to SmartAcme's HTTP-01 handler
421
448
  const challengeRoute: IRouteConfig = {
422
449
  name: 'acme-challenge',
423
450
  priority: 1000, // High priority
424
451
  match: {
425
- ports: 80,
452
+ ports: challengePort,
426
453
  path: '/.well-known/acme-challenge/*'
427
454
  },
428
455
  action: {
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * SmartProxy models
3
3
  */
4
- export * from './interfaces.js';
4
+ // Export everything except IAcmeOptions from interfaces
5
+ export type { ISmartProxyOptions, IConnectionRecord, TSmartProxyCertProvisionObject } from './interfaces.js';
5
6
  export * from './route-types.js';
@@ -1,5 +1,19 @@
1
1
  import * as plugins from '../../../plugins.js';
2
- import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
2
+ // Certificate types removed - define IAcmeOptions locally
3
+ export interface IAcmeOptions {
4
+ enabled?: boolean;
5
+ email?: string; // Required when any route uses certificate: 'auto'
6
+ environment?: 'production' | 'staging';
7
+ accountEmail?: string; // Alias for email
8
+ port?: number; // Port for HTTP-01 challenges (default: 80)
9
+ useProduction?: boolean; // Use Let's Encrypt production (default: false)
10
+ renewThresholdDays?: number; // Days before expiry to renew (default: 30)
11
+ autoRenew?: boolean; // Enable automatic renewal (default: true)
12
+ certificateStore?: string; // Directory to store certificates (default: './certs')
13
+ skipConfiguredCerts?: boolean;
14
+ renewCheckIntervalHours?: number; // How often to check for renewals (default: 24)
15
+ routeForwards?: any[];
16
+ }
3
17
  import type { IRouteConfig } from './route-types.js';
4
18
  import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js';
5
19
 
@@ -84,7 +98,22 @@ export interface ISmartProxyOptions {
84
98
  useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy
85
99
  networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
86
100
 
87
- // ACME configuration options for SmartProxy
101
+ /**
102
+ * Global ACME configuration options for SmartProxy
103
+ *
104
+ * When set, these options will be used as defaults for all routes
105
+ * with certificate: 'auto' that don't have their own ACME configuration.
106
+ * Route-specific ACME settings will override these defaults.
107
+ *
108
+ * Example:
109
+ * ```ts
110
+ * acme: {
111
+ * email: 'ssl@example.com',
112
+ * useProduction: false,
113
+ * port: 80
114
+ * }
115
+ * ```
116
+ */
88
117
  acme?: IAcmeOptions;
89
118
 
90
119
  /**
@@ -1,5 +1,5 @@
1
1
  import * as plugins from '../../../plugins.js';
2
- import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
2
+ // Certificate types removed - use local definition
3
3
  import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js';
4
4
  import type { PortRange } from '../../../proxies/nftables-proxy/models/interfaces.js';
5
5
 
@@ -173,6 +173,13 @@ export class RouteManager extends plugins.EventEmitter {
173
173
  return this.portMap.get(port) || [];
174
174
  }
175
175
 
176
+ /**
177
+ * Get all routes
178
+ */
179
+ public getAllRoutes(): IRouteConfig[] {
180
+ return [...this.routes];
181
+ }
182
+
176
183
  /**
177
184
  * Test if a pattern matches a domain using glob matching
178
185
  */
@@ -115,21 +115,26 @@ export class SmartProxy extends plugins.EventEmitter {
115
115
  networkProxyPort: settingsArg.networkProxyPort || 8443,
116
116
  };
117
117
 
118
- // Set default ACME options if not provided
119
- this.settings.acme = this.settings.acme || {};
120
- if (Object.keys(this.settings.acme).length === 0) {
118
+ // Normalize ACME options if provided (support both email and accountEmail)
119
+ if (this.settings.acme) {
120
+ // Support both 'email' and 'accountEmail' fields
121
+ if (this.settings.acme.accountEmail && !this.settings.acme.email) {
122
+ this.settings.acme.email = this.settings.acme.accountEmail;
123
+ }
124
+
125
+ // Set reasonable defaults for commonly used fields
121
126
  this.settings.acme = {
122
- enabled: false,
123
- port: 80,
124
- accountEmail: 'admin@example.com',
125
- useProduction: false,
126
- renewThresholdDays: 30,
127
- autoRenew: true,
128
- certificateStore: './certs',
129
- skipConfiguredCerts: false,
130
- httpsRedirectPort: 443,
131
- renewCheckIntervalHours: 24,
132
- routeForwards: []
127
+ enabled: this.settings.acme.enabled !== false, // Enable by default if acme object exists
128
+ port: this.settings.acme.port || 80,
129
+ email: this.settings.acme.email,
130
+ useProduction: this.settings.acme.useProduction || false,
131
+ renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
132
+ autoRenew: this.settings.acme.autoRenew !== false, // Enable by default
133
+ certificateStore: this.settings.acme.certificateStore || './certs',
134
+ skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
135
+ renewCheckIntervalHours: this.settings.acme.renewCheckIntervalHours || 24,
136
+ routeForwards: this.settings.acme.routeForwards || [],
137
+ ...this.settings.acme // Preserve any additional fields
133
138
  };
134
139
  }
135
140
 
@@ -187,19 +192,55 @@ export class SmartProxy extends plugins.EventEmitter {
187
192
  return;
188
193
  }
189
194
 
190
- // Use the first auto route's ACME config as defaults
191
- const defaultAcme = autoRoutes[0]?.action.tls?.acme;
195
+ // Prepare ACME options with priority:
196
+ // 1. Use top-level ACME config if available
197
+ // 2. Fall back to first auto route's ACME config
198
+ // 3. Otherwise use undefined
199
+ let acmeOptions: { email?: string; useProduction?: boolean; port?: number } | undefined;
200
+
201
+ if (this.settings.acme?.email) {
202
+ // Use top-level ACME config
203
+ acmeOptions = {
204
+ email: this.settings.acme.email,
205
+ useProduction: this.settings.acme.useProduction || false,
206
+ port: this.settings.acme.port || 80
207
+ };
208
+ console.log(`Using top-level ACME configuration with email: ${acmeOptions.email}`);
209
+ } else if (autoRoutes.length > 0) {
210
+ // Check for route-level ACME config
211
+ const routeWithAcme = autoRoutes.find(r => r.action.tls?.acme?.email);
212
+ if (routeWithAcme?.action.tls?.acme) {
213
+ const routeAcme = routeWithAcme.action.tls.acme;
214
+ acmeOptions = {
215
+ email: routeAcme.email,
216
+ useProduction: routeAcme.useProduction || false,
217
+ port: routeAcme.challengePort || 80
218
+ };
219
+ console.log(`Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`);
220
+ }
221
+ }
222
+
223
+ // Validate we have required configuration
224
+ if (autoRoutes.length > 0 && !acmeOptions?.email) {
225
+ throw new Error(
226
+ 'ACME email is required for automatic certificate provisioning. ' +
227
+ 'Please provide email in either:\n' +
228
+ '1. Top-level "acme" configuration\n' +
229
+ '2. Individual route\'s "tls.acme" configuration'
230
+ );
231
+ }
192
232
 
193
233
  this.certManager = new SmartCertManager(
194
234
  this.settings.routes,
195
- './certs', // Certificate directory
196
- defaultAcme ? {
197
- email: defaultAcme.email,
198
- useProduction: defaultAcme.useProduction,
199
- port: defaultAcme.challengePort || 80
200
- } : undefined
235
+ this.settings.acme?.certificateStore || './certs',
236
+ acmeOptions
201
237
  );
202
238
 
239
+ // Pass down the global ACME config to the cert manager
240
+ if (this.settings.acme) {
241
+ this.certManager.setGlobalAcmeDefaults(this.settings.acme);
242
+ }
243
+
203
244
  // Connect with NetworkProxy
204
245
  if (this.networkProxyBridge.getNetworkProxy()) {
205
246
  this.certManager.setNetworkProxy(this.networkProxyBridge.getNetworkProxy());
@@ -250,9 +291,14 @@ export class SmartProxy extends plugins.EventEmitter {
250
291
 
251
292
  // Validate the route configuration
252
293
  const configWarnings = this.routeManager.validateConfiguration();
253
- if (configWarnings.length > 0) {
254
- console.log("Route configuration warnings:");
255
- for (const warning of configWarnings) {
294
+
295
+ // Also validate ACME configuration
296
+ const acmeWarnings = this.validateAcmeConfiguration();
297
+ const allWarnings = [...configWarnings, ...acmeWarnings];
298
+
299
+ if (allWarnings.length > 0) {
300
+ console.log("Configuration warnings:");
301
+ for (const warning of allWarnings) {
256
302
  console.log(` - ${warning}`);
257
303
  }
258
304
  }
@@ -664,5 +710,76 @@ export class SmartProxy extends plugins.EventEmitter {
664
710
  public async getNfTablesStatus(): Promise<Record<string, any>> {
665
711
  return this.nftablesManager.getStatus();
666
712
  }
713
+
714
+ /**
715
+ * Validate ACME configuration
716
+ */
717
+ private validateAcmeConfiguration(): string[] {
718
+ const warnings: string[] = [];
719
+
720
+ // Check for routes with certificate: 'auto'
721
+ const autoRoutes = this.settings.routes.filter(r =>
722
+ r.action.tls?.certificate === 'auto'
723
+ );
724
+
725
+ if (autoRoutes.length === 0) {
726
+ return warnings;
727
+ }
728
+
729
+ // Check if we have ACME email configuration
730
+ const hasTopLevelEmail = this.settings.acme?.email;
731
+ const routesWithEmail = autoRoutes.filter(r => r.action.tls?.acme?.email);
732
+
733
+ if (!hasTopLevelEmail && routesWithEmail.length === 0) {
734
+ warnings.push(
735
+ 'Routes with certificate: "auto" require ACME email configuration. ' +
736
+ 'Add email to either top-level "acme" config or individual route\'s "tls.acme" config.'
737
+ );
738
+ }
739
+
740
+ // Check for port 80 availability for challenges
741
+ if (autoRoutes.length > 0) {
742
+ const challengePort = this.settings.acme?.port || 80;
743
+ const portsInUse = this.routeManager.getListeningPorts();
744
+
745
+ if (!portsInUse.includes(challengePort)) {
746
+ warnings.push(
747
+ `Port ${challengePort} is not configured for any routes but is needed for ACME challenges. ` +
748
+ `Add a route listening on port ${challengePort} or ensure it's accessible for HTTP-01 challenges.`
749
+ );
750
+ }
751
+ }
752
+
753
+ // Check for mismatched environments
754
+ if (this.settings.acme?.useProduction) {
755
+ const stagingRoutes = autoRoutes.filter(r =>
756
+ r.action.tls?.acme?.useProduction === false
757
+ );
758
+ if (stagingRoutes.length > 0) {
759
+ warnings.push(
760
+ 'Top-level ACME uses production but some routes use staging. ' +
761
+ 'Consider aligning environments to avoid certificate issues.'
762
+ );
763
+ }
764
+ }
765
+
766
+ // Check for wildcard domains with auto certificates
767
+ for (const route of autoRoutes) {
768
+ const domains = Array.isArray(route.match.domains)
769
+ ? route.match.domains
770
+ : [route.match.domains];
771
+
772
+ const wildcardDomains = domains.filter(d => d?.includes('*'));
773
+ if (wildcardDomains.length > 0) {
774
+ warnings.push(
775
+ `Route "${route.name}" has wildcard domain(s) ${wildcardDomains.join(', ')} ` +
776
+ 'with certificate: "auto". Wildcard certificates require DNS-01 challenges, ' +
777
+ 'which are not currently supported. Use static certificates instead.'
778
+ );
779
+ }
780
+ }
781
+
782
+ return warnings;
783
+ }
667
784
 
668
785
  }
@@ -1,48 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import type { IAcmeOptions } from '../models/certificate-types.js';
4
- import { ensureCertificateDirectory } from '../utils/certificate-helpers.js';
5
- // We'll need to update this import when we move the Port80Handler
6
- import { Port80Handler } from '../../http/port80/port80-handler.js';
7
-
8
- /**
9
- * Factory to create a Port80Handler with common setup.
10
- * Ensures the certificate store directory exists and instantiates the handler.
11
- * @param options Port80Handler configuration options
12
- * @returns A new Port80Handler instance
13
- */
14
- export function buildPort80Handler(
15
- options: IAcmeOptions
16
- ): Port80Handler {
17
- if (options.certificateStore) {
18
- ensureCertificateDirectory(options.certificateStore);
19
- console.log(`Ensured certificate store directory: ${options.certificateStore}`);
20
- }
21
- return new Port80Handler(options);
22
- }
23
-
24
- /**
25
- * Creates default ACME options with sensible defaults
26
- * @param email Account email for ACME provider
27
- * @param certificateStore Path to store certificates
28
- * @param useProduction Whether to use production ACME servers
29
- * @returns Configured ACME options
30
- */
31
- export function createDefaultAcmeOptions(
32
- email: string,
33
- certificateStore: string,
34
- useProduction: boolean = false
35
- ): IAcmeOptions {
36
- return {
37
- accountEmail: email,
38
- enabled: true,
39
- port: 80,
40
- useProduction,
41
- httpsRedirectPort: 443,
42
- renewThresholdDays: 30,
43
- renewCheckIntervalHours: 24,
44
- autoRenew: true,
45
- certificateStore,
46
- skipConfiguredCerts: false
47
- };
48
- }
@@ -1,110 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type { IAcmeOptions, ICertificateData } from '../models/certificate-types.js';
3
- import { CertificateEvents } from '../events/certificate-events.js';
4
-
5
- /**
6
- * Manages ACME challenges and certificate validation
7
- */
8
- export class AcmeChallengeHandler extends plugins.EventEmitter {
9
- private options: IAcmeOptions;
10
- private client: any; // ACME client from plugins
11
- private pendingChallenges: Map<string, any>;
12
-
13
- /**
14
- * Creates a new ACME challenge handler
15
- * @param options ACME configuration options
16
- */
17
- constructor(options: IAcmeOptions) {
18
- super();
19
- this.options = options;
20
- this.pendingChallenges = new Map();
21
-
22
- // Initialize ACME client if needed
23
- // This is just a placeholder implementation since we don't use the actual
24
- // client directly in this implementation - it's handled by Port80Handler
25
- this.client = null;
26
- console.log('Created challenge handler with options:',
27
- options.accountEmail,
28
- options.useProduction ? 'production' : 'staging'
29
- );
30
- }
31
-
32
- /**
33
- * Gets or creates the ACME account key
34
- */
35
- private getAccountKey(): Buffer {
36
- // Implementation details would depend on plugin requirements
37
- // This is a simplified version
38
- if (!this.options.certificateStore) {
39
- throw new Error('Certificate store is required for ACME challenges');
40
- }
41
-
42
- // This is just a placeholder - actual implementation would check for
43
- // existing account key and create one if needed
44
- return Buffer.from('account-key-placeholder');
45
- }
46
-
47
- /**
48
- * Validates a domain using HTTP-01 challenge
49
- * @param domain Domain to validate
50
- * @param challengeToken ACME challenge token
51
- * @param keyAuthorization Key authorization for the challenge
52
- */
53
- public async handleHttpChallenge(
54
- domain: string,
55
- challengeToken: string,
56
- keyAuthorization: string
57
- ): Promise<void> {
58
- // Store challenge for response
59
- this.pendingChallenges.set(challengeToken, keyAuthorization);
60
-
61
- try {
62
- // Wait for challenge validation - this would normally be handled by the ACME client
63
- await new Promise(resolve => setTimeout(resolve, 1000));
64
- this.emit(CertificateEvents.CERTIFICATE_ISSUED, {
65
- domain,
66
- success: true
67
- });
68
- } catch (error) {
69
- this.emit(CertificateEvents.CERTIFICATE_FAILED, {
70
- domain,
71
- error: error instanceof Error ? error.message : String(error),
72
- isRenewal: false
73
- });
74
- throw error;
75
- } finally {
76
- // Clean up the challenge
77
- this.pendingChallenges.delete(challengeToken);
78
- }
79
- }
80
-
81
- /**
82
- * Responds to an HTTP-01 challenge request
83
- * @param token Challenge token from the request path
84
- * @returns The key authorization if found
85
- */
86
- public getChallengeResponse(token: string): string | null {
87
- return this.pendingChallenges.get(token) || null;
88
- }
89
-
90
- /**
91
- * Checks if a request path is an ACME challenge
92
- * @param path Request path
93
- * @returns True if this is an ACME challenge request
94
- */
95
- public isAcmeChallenge(path: string): boolean {
96
- return path.startsWith('/.well-known/acme-challenge/');
97
- }
98
-
99
- /**
100
- * Extracts the challenge token from an ACME challenge path
101
- * @param path Request path
102
- * @returns The challenge token if valid
103
- */
104
- public extractChallengeToken(path: string): string | null {
105
- if (!this.isAcmeChallenge(path)) return null;
106
-
107
- const parts = path.split('/');
108
- return parts[parts.length - 1] || null;
109
- }
110
- }
@@ -1,3 +0,0 @@
1
- /**
2
- * ACME certificate provisioning
3
- */
@@ -1,36 +0,0 @@
1
- /**
2
- * Certificate-related events emitted by certificate management components
3
- */
4
- export enum CertificateEvents {
5
- CERTIFICATE_ISSUED = 'certificate-issued',
6
- CERTIFICATE_RENEWED = 'certificate-renewed',
7
- CERTIFICATE_FAILED = 'certificate-failed',
8
- CERTIFICATE_EXPIRING = 'certificate-expiring',
9
- CERTIFICATE_APPLIED = 'certificate-applied',
10
- // Events moved from Port80Handler for compatibility
11
- MANAGER_STARTED = 'manager-started',
12
- MANAGER_STOPPED = 'manager-stopped',
13
- }
14
-
15
- /**
16
- * Port80Handler-specific events including certificate-related ones
17
- * @deprecated Use CertificateEvents and HttpEvents instead
18
- */
19
- export enum Port80HandlerEvents {
20
- CERTIFICATE_ISSUED = 'certificate-issued',
21
- CERTIFICATE_RENEWED = 'certificate-renewed',
22
- CERTIFICATE_FAILED = 'certificate-failed',
23
- CERTIFICATE_EXPIRING = 'certificate-expiring',
24
- MANAGER_STARTED = 'manager-started',
25
- MANAGER_STOPPED = 'manager-stopped',
26
- REQUEST_FORWARDED = 'request-forwarded',
27
- }
28
-
29
- /**
30
- * Certificate provider events
31
- */
32
- export enum CertProvisionerEvents {
33
- CERTIFICATE_ISSUED = 'certificate',
34
- CERTIFICATE_RENEWED = 'certificate',
35
- CERTIFICATE_FAILED = 'certificate-failed'
36
- }