@push.rocks/smartproxy 21.1.7 → 22.4.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 (103) hide show
  1. package/changelog.md +81 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
  4. package/dist_ts/core/utils/shared-security-manager.js +66 -1
  5. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
  6. package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
  7. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
  8. package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
  9. package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
  10. package/dist_ts/proxies/http-proxy/index.js +6 -2
  11. package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
  12. package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
  13. package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
  14. package/dist_ts/proxies/nftables-proxy/index.js +2 -1
  15. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
  16. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
  17. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
  18. package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
  19. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
  20. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
  21. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
  22. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
  23. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
  24. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
  25. package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
  26. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
  27. package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
  28. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
  29. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +0 -1
  30. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
  31. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
  32. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
  33. package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
  34. package/dist_ts/proxies/smart-proxy/smart-proxy.js +1 -2
  35. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
  36. package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
  37. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
  38. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
  39. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
  40. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
  41. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
  42. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
  43. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
  44. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
  45. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
  46. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
  47. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
  48. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
  49. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
  50. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
  51. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
  52. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
  53. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
  54. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
  55. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
  56. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
  57. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
  58. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
  59. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
  60. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
  61. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
  62. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
  63. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
  64. package/npmextra.json +12 -6
  65. package/package.json +34 -24
  66. package/readme.hints.md +184 -1
  67. package/readme.md +235 -172
  68. package/ts/00_commitinfo_data.ts +1 -1
  69. package/ts/core/utils/shared-security-manager.ts +98 -13
  70. package/ts/proxies/http-proxy/default-certificates.ts +150 -0
  71. package/ts/proxies/http-proxy/http-proxy.ts +9 -15
  72. package/ts/proxies/http-proxy/index.ts +6 -1
  73. package/ts/proxies/http-proxy/security-manager.ts +141 -161
  74. package/ts/proxies/nftables-proxy/index.ts +1 -0
  75. package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
  76. package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
  77. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
  78. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
  79. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
  80. package/ts/proxies/smart-proxy/certificate-manager.ts +3 -2
  81. package/ts/proxies/smart-proxy/connection-manager.ts +21 -8
  82. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +39 -13
  83. package/ts/proxies/smart-proxy/models/interfaces.ts +0 -1
  84. package/ts/proxies/smart-proxy/route-connection-handler.ts +88 -16
  85. package/ts/proxies/smart-proxy/security-manager.ts +98 -86
  86. package/ts/proxies/smart-proxy/smart-proxy.ts +0 -2
  87. package/ts/proxies/smart-proxy/tls-manager.ts +1 -37
  88. package/ts/proxies/smart-proxy/utils/index.ts +3 -5
  89. package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
  90. package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
  91. package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
  92. package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
  93. package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
  94. package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
  95. package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
  96. package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
  97. package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
  98. package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
  99. package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
  100. package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
  101. package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
  102. package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
  103. package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { IRouteConfig, IRouteMatch } from '../models/route-types.js';
9
- import { validateRouteConfig } from './route-validators.js';
9
+ import { validateRouteConfig } from './route-validator.js';
10
10
 
11
11
  /**
12
12
  * Merge two route configurations
@@ -1,5 +1,5 @@
1
1
  import { logger } from '../../../core/utils/logger.js';
2
- import type { IRouteConfig } from '../models/route-types.js';
2
+ import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../models/route-types.js';
3
3
 
4
4
  /**
5
5
  * Validates route configurations for correctness and safety
@@ -439,8 +439,8 @@ export class RouteValidator {
439
439
  * Validate IPv6 address
440
440
  */
441
441
  private static isValidIPv6(ip: string): boolean {
442
- // Simple IPv6 validation
443
- const ipv6Pattern = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|::[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{1,4}){0,6}|::1|::)$/;
442
+ // IPv6 validation including IPv6-mapped IPv4 addresses (::ffff:x.x.x.x)
443
+ const ipv6Pattern = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|::[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{1,4}){0,6}|::1|::|::ffff:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i;
444
444
  return ipv6Pattern.test(ip);
445
445
  }
446
446
 
@@ -454,7 +454,7 @@ export class RouteValidator {
454
454
  errors: routeErrors,
455
455
  component: 'route-validator'
456
456
  });
457
-
457
+
458
458
  for (const error of routeErrors) {
459
459
  logger.log('error', ` - ${error}`, {
460
460
  route: routeName,
@@ -463,4 +463,274 @@ export class RouteValidator {
463
463
  }
464
464
  }
465
465
  }
466
+ }
467
+
468
+ // ============================================================================
469
+ // Functional API (for backwards compatibility with route-validators.ts)
470
+ // ============================================================================
471
+
472
+ /**
473
+ * Validates a port range or port number
474
+ * @param port Port number, port range, or port function
475
+ * @returns True if valid, false otherwise
476
+ */
477
+ export function isValidPort(port: any): boolean {
478
+ if (typeof port === 'number') {
479
+ return port > 0 && port < 65536;
480
+ } else if (Array.isArray(port)) {
481
+ return port.every(p =>
482
+ (typeof p === 'number' && p > 0 && p < 65536) ||
483
+ (typeof p === 'object' && 'from' in p && 'to' in p &&
484
+ p.from > 0 && p.from < 65536 && p.to > 0 && p.to < 65536)
485
+ );
486
+ } else if (typeof port === 'function') {
487
+ return true;
488
+ } else if (typeof port === 'object' && 'from' in port && 'to' in port) {
489
+ return port.from > 0 && port.from < 65536 && port.to > 0 && port.to < 65536;
490
+ }
491
+ return false;
492
+ }
493
+
494
+ /**
495
+ * Validates a domain string - supports wildcards, localhost, and IP addresses
496
+ * @param domain Domain string to validate
497
+ * @returns True if valid, false otherwise
498
+ */
499
+ export function isValidDomain(domain: string): boolean {
500
+ if (!domain || typeof domain !== 'string') return false;
501
+ if (domain === '*') return true;
502
+ if (domain === 'localhost') return true;
503
+
504
+ const domainPatterns = [
505
+ // Standard domain with optional wildcard subdomain (*.example.com)
506
+ /^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/,
507
+ // Wildcard prefix without dot (*example.com)
508
+ /^\*[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?))*$/,
509
+ // IP address
510
+ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
511
+ // IPv6 address
512
+ /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/
513
+ ];
514
+
515
+ return domainPatterns.some(pattern => pattern.test(domain));
516
+ }
517
+
518
+ /**
519
+ * Validates a route match configuration
520
+ * @param match Route match configuration to validate
521
+ * @returns { valid: boolean, errors: string[] } Validation result
522
+ */
523
+ export function validateRouteMatch(match: IRouteMatch): { valid: boolean; errors: string[] } {
524
+ const errors: string[] = [];
525
+
526
+ if (match.ports !== undefined) {
527
+ if (!isValidPort(match.ports)) {
528
+ errors.push('Invalid port number or port range in match.ports');
529
+ }
530
+ }
531
+
532
+ if (match.domains !== undefined) {
533
+ if (typeof match.domains === 'string') {
534
+ if (!isValidDomain(match.domains)) {
535
+ errors.push(`Invalid domain format: ${match.domains}`);
536
+ }
537
+ } else if (Array.isArray(match.domains)) {
538
+ for (const domain of match.domains) {
539
+ if (!isValidDomain(domain)) {
540
+ errors.push(`Invalid domain format: ${domain}`);
541
+ }
542
+ }
543
+ } else {
544
+ errors.push('Domains must be a string or an array of strings');
545
+ }
546
+ }
547
+
548
+ if (match.path !== undefined) {
549
+ if (typeof match.path !== 'string' || !match.path.startsWith('/')) {
550
+ errors.push('Path must be a string starting with /');
551
+ }
552
+ }
553
+
554
+ return {
555
+ valid: errors.length === 0,
556
+ errors
557
+ };
558
+ }
559
+
560
+ /**
561
+ * Validates a route action configuration
562
+ * @param action Route action configuration to validate
563
+ * @returns { valid: boolean, errors: string[] } Validation result
564
+ */
565
+ export function validateRouteAction(action: IRouteAction): { valid: boolean; errors: string[] } {
566
+ const errors: string[] = [];
567
+
568
+ if (!action.type) {
569
+ errors.push('Action type is required');
570
+ } else if (!['forward', 'socket-handler'].includes(action.type)) {
571
+ errors.push(`Invalid action type: ${action.type}`);
572
+ }
573
+
574
+ if (action.type === 'forward') {
575
+ if (!action.targets || !Array.isArray(action.targets) || action.targets.length === 0) {
576
+ errors.push('Targets array is required for forward action');
577
+ } else {
578
+ action.targets.forEach((target, index) => {
579
+ if (!target.host) {
580
+ errors.push(`Target[${index}] host is required`);
581
+ } else if (typeof target.host !== 'string' &&
582
+ !Array.isArray(target.host) &&
583
+ typeof target.host !== 'function') {
584
+ errors.push(`Target[${index}] host must be a string, array of strings, or function`);
585
+ }
586
+
587
+ if (target.port === undefined) {
588
+ errors.push(`Target[${index}] port is required`);
589
+ } else if (typeof target.port !== 'number' &&
590
+ typeof target.port !== 'function' &&
591
+ target.port !== 'preserve') {
592
+ errors.push(`Target[${index}] port must be a number, 'preserve', or a function`);
593
+ } else if (typeof target.port === 'number' && !isValidPort(target.port)) {
594
+ errors.push(`Target[${index}] port must be between 1 and 65535`);
595
+ }
596
+
597
+ if (target.match) {
598
+ if (target.match.ports && !Array.isArray(target.match.ports)) {
599
+ errors.push(`Target[${index}] match.ports must be an array`);
600
+ }
601
+ if (target.match.method && !Array.isArray(target.match.method)) {
602
+ errors.push(`Target[${index}] match.method must be an array`);
603
+ }
604
+ }
605
+ });
606
+ }
607
+
608
+ if (action.tls) {
609
+ if (!['passthrough', 'terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
610
+ errors.push(`Invalid TLS mode: ${action.tls.mode}`);
611
+ }
612
+
613
+ if (['terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
614
+ if (action.tls.certificate !== 'auto' &&
615
+ (!action.tls.certificate || !action.tls.certificate.key || !action.tls.certificate.cert)) {
616
+ errors.push('Certificate must be "auto" or an object with key and cert properties');
617
+ }
618
+ }
619
+ }
620
+ }
621
+
622
+ if (action.type === 'socket-handler') {
623
+ if (!action.socketHandler) {
624
+ errors.push('Socket handler function is required for socket-handler action');
625
+ } else if (typeof action.socketHandler !== 'function') {
626
+ errors.push('Socket handler must be a function');
627
+ }
628
+ }
629
+
630
+ return {
631
+ valid: errors.length === 0,
632
+ errors
633
+ };
634
+ }
635
+
636
+ /**
637
+ * Validates a complete route configuration
638
+ * @param route Route configuration to validate
639
+ * @returns { valid: boolean, errors: string[] } Validation result
640
+ */
641
+ export function validateRouteConfig(route: IRouteConfig): { valid: boolean; errors: string[] } {
642
+ const errors: string[] = [];
643
+
644
+ if (!route.match) {
645
+ errors.push('Route match configuration is required');
646
+ }
647
+
648
+ if (!route.action) {
649
+ errors.push('Route action configuration is required');
650
+ }
651
+
652
+ if (route.match) {
653
+ const matchValidation = validateRouteMatch(route.match);
654
+ if (!matchValidation.valid) {
655
+ errors.push(...matchValidation.errors.map(err => `Match: ${err}`));
656
+ }
657
+ }
658
+
659
+ if (route.action) {
660
+ const actionValidation = validateRouteAction(route.action);
661
+ if (!actionValidation.valid) {
662
+ errors.push(...actionValidation.errors.map(err => `Action: ${err}`));
663
+ }
664
+ }
665
+
666
+ return {
667
+ valid: errors.length === 0,
668
+ errors
669
+ };
670
+ }
671
+
672
+ /**
673
+ * Validate an array of route configurations
674
+ * @param routes Array of route configurations to validate
675
+ * @returns { valid: boolean, errors: { index: number, errors: string[] }[] } Validation result
676
+ */
677
+ export function validateRoutes(routes: IRouteConfig[]): {
678
+ valid: boolean;
679
+ errors: { index: number; errors: string[] }[]
680
+ } {
681
+ const results: { index: number; errors: string[] }[] = [];
682
+
683
+ routes.forEach((route, index) => {
684
+ const validation = validateRouteConfig(route);
685
+ if (!validation.valid) {
686
+ results.push({
687
+ index,
688
+ errors: validation.errors
689
+ });
690
+ }
691
+ });
692
+
693
+ return {
694
+ valid: results.length === 0,
695
+ errors: results
696
+ };
697
+ }
698
+
699
+ /**
700
+ * Check if a route configuration has the required properties for a specific action type
701
+ * @param route Route configuration to check
702
+ * @param actionType Expected action type
703
+ * @returns True if the route has the necessary properties, false otherwise
704
+ */
705
+ export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType: string): boolean {
706
+ if (!route.action || route.action.type !== actionType) {
707
+ return false;
708
+ }
709
+
710
+ switch (actionType) {
711
+ case 'forward':
712
+ return !!route.action.targets &&
713
+ Array.isArray(route.action.targets) &&
714
+ route.action.targets.length > 0 &&
715
+ route.action.targets.every(t => t.host && t.port !== undefined);
716
+ case 'socket-handler':
717
+ return !!route.action.socketHandler && typeof route.action.socketHandler === 'function';
718
+ default:
719
+ return false;
720
+ }
721
+ }
722
+
723
+ /**
724
+ * Throws an error if the route config is invalid, returns the config if valid
725
+ * Useful for immediate validation when creating routes
726
+ * @param route Route configuration to validate
727
+ * @returns The validated route configuration
728
+ * @throws Error if the route configuration is invalid
729
+ */
730
+ export function assertValidRoute(route: IRouteConfig): IRouteConfig {
731
+ const validation = validateRouteConfig(route);
732
+ if (!validation.valid) {
733
+ throw new Error(`Invalid route configuration: ${validation.errors.join(', ')}`);
734
+ }
735
+ return route;
466
736
  }
@@ -1,244 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
6
- import { type IHttpProxyOptions, type ICertificateEntry, type ILogger, createLogger } from './models/types.js';
7
- import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
8
-
9
- /**
10
- * @deprecated This class is deprecated. Use SmartCertManager instead.
11
- *
12
- * This is a stub implementation that maintains backward compatibility
13
- * while the functionality has been moved to SmartCertManager.
14
- */
15
- export class CertificateManager {
16
- private defaultCertificates: { key: string; cert: string };
17
- private certificateCache: Map<string, ICertificateEntry> = new Map();
18
- private certificateStoreDir: string;
19
- private logger: ILogger;
20
- private httpsServer: plugins.https.Server | null = null;
21
- private initialized = false;
22
-
23
- constructor(private options: IHttpProxyOptions) {
24
- this.certificateStoreDir = path.resolve(options.acme?.certificateStore || './certs');
25
- this.logger = createLogger(options.logLevel || 'info');
26
-
27
- this.logger.warn('CertificateManager is deprecated - use SmartCertManager instead');
28
-
29
- // Initialize synchronously for backward compatibility but log warning
30
- this.initializeSync();
31
- }
32
-
33
- /**
34
- * Synchronous initialization for backward compatibility
35
- * @deprecated This uses sync filesystem operations which block the event loop
36
- */
37
- private initializeSync(): void {
38
- // Ensure certificate store directory exists
39
- try {
40
- if (!fs.existsSync(this.certificateStoreDir)) {
41
- fs.mkdirSync(this.certificateStoreDir, { recursive: true });
42
- this.logger.info(`Created certificate store directory: ${this.certificateStoreDir}`);
43
- }
44
- } catch (error) {
45
- this.logger.warn(`Failed to create certificate store directory: ${error}`);
46
- }
47
-
48
- this.loadDefaultCertificates();
49
- }
50
-
51
- /**
52
- * Async initialization - preferred method
53
- */
54
- public async initialize(): Promise<void> {
55
- if (this.initialized) return;
56
-
57
- // Ensure certificate store directory exists
58
- try {
59
- await AsyncFileSystem.ensureDir(this.certificateStoreDir);
60
- this.logger.info(`Ensured certificate store directory: ${this.certificateStoreDir}`);
61
- } catch (error) {
62
- this.logger.warn(`Failed to create certificate store directory: ${error}`);
63
- }
64
-
65
- await this.loadDefaultCertificatesAsync();
66
- this.initialized = true;
67
- }
68
-
69
- /**
70
- * Loads default certificates from the filesystem
71
- * @deprecated This uses sync filesystem operations which block the event loop
72
- */
73
- public loadDefaultCertificates(): void {
74
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
75
- const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');
76
-
77
- try {
78
- this.defaultCertificates = {
79
- key: fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8'),
80
- cert: fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8')
81
- };
82
- this.logger.info('Loaded default certificates from filesystem (sync - deprecated)');
83
- } catch (error) {
84
- this.logger.error(`Failed to load default certificates: ${error}`);
85
- this.generateSelfSignedCertificate();
86
- }
87
- }
88
-
89
- /**
90
- * Loads default certificates from the filesystem asynchronously
91
- */
92
- public async loadDefaultCertificatesAsync(): Promise<void> {
93
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
94
- const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');
95
-
96
- try {
97
- const [key, cert] = await Promise.all([
98
- AsyncFileSystem.readFile(path.join(certPath, 'key.pem')),
99
- AsyncFileSystem.readFile(path.join(certPath, 'cert.pem'))
100
- ]);
101
-
102
- this.defaultCertificates = { key, cert };
103
- this.logger.info('Loaded default certificates from filesystem (async)');
104
- } catch (error) {
105
- this.logger.error(`Failed to load default certificates: ${error}`);
106
- this.generateSelfSignedCertificate();
107
- }
108
- }
109
-
110
- /**
111
- * Generates self-signed certificates as fallback
112
- */
113
- private generateSelfSignedCertificate(): void {
114
- // Generate a self-signed certificate using forge or similar
115
- // For now, just use a placeholder
116
- const selfSignedCert = `-----BEGIN CERTIFICATE-----
117
- MIIBkTCB+wIJAKHHIgIIA0/cMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAYTAlVT
118
- MB4XDTE0MDEwMTAwMDAwMFoXDTI0MDEwMTAwMDAwMFowDTELMAkGA1UEBhMCVVMw
119
- gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMRiH0VwnOH3jCV7c6JFZWYrvuqy
120
- -----END CERTIFICATE-----`;
121
-
122
- const selfSignedKey = `-----BEGIN PRIVATE KEY-----
123
- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMRiH0VwnOH3jCV7
124
- c6JFZWYrvuqyALCLXj0pcr1iqNdHjegNXnkl5zjdaUjq4edNOKl7M1AlFiYjG2xk
125
- -----END PRIVATE KEY-----`;
126
-
127
- this.defaultCertificates = {
128
- key: selfSignedKey,
129
- cert: selfSignedCert
130
- };
131
-
132
- this.logger.warn('Using self-signed certificate as fallback');
133
- }
134
-
135
- /**
136
- * Gets the default certificates
137
- */
138
- public getDefaultCertificates(): { key: string; cert: string } {
139
- return this.defaultCertificates;
140
- }
141
-
142
- /**
143
- * @deprecated Use SmartCertManager instead
144
- */
145
- public setExternalPort80Handler(handler: any): void {
146
- this.logger.warn('setExternalPort80Handler is deprecated - use SmartCertManager instead');
147
- }
148
-
149
- /**
150
- * @deprecated Use SmartCertManager instead
151
- */
152
- public async updateRoutes(routes: IRouteConfig[]): Promise<void> {
153
- this.logger.warn('updateRoutes is deprecated - use SmartCertManager instead');
154
- }
155
-
156
- /**
157
- * Handles SNI callback to provide appropriate certificate
158
- */
159
- public handleSNI(domain: string, cb: (err: Error | null, ctx: plugins.tls.SecureContext) => void): void {
160
- const certificate = this.getCachedCertificate(domain);
161
-
162
- if (certificate) {
163
- const context = plugins.tls.createSecureContext({
164
- key: certificate.key,
165
- cert: certificate.cert
166
- });
167
- cb(null, context);
168
- return;
169
- }
170
-
171
- // Use default certificate if no domain-specific certificate found
172
- const defaultContext = plugins.tls.createSecureContext({
173
- key: this.defaultCertificates.key,
174
- cert: this.defaultCertificates.cert
175
- });
176
- cb(null, defaultContext);
177
- }
178
-
179
- /**
180
- * Updates a certificate in the cache
181
- */
182
- public updateCertificate(domain: string, cert: string, key: string): void {
183
- this.certificateCache.set(domain, {
184
- cert,
185
- key,
186
- expires: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days
187
- });
188
-
189
- this.logger.info(`Certificate updated for ${domain}`);
190
- }
191
-
192
- /**
193
- * Gets a cached certificate
194
- */
195
- private getCachedCertificate(domain: string): ICertificateEntry | null {
196
- return this.certificateCache.get(domain) || null;
197
- }
198
-
199
- /**
200
- * @deprecated Use SmartCertManager instead
201
- */
202
- public async initializePort80Handler(): Promise<any> {
203
- this.logger.warn('initializePort80Handler is deprecated - use SmartCertManager instead');
204
- return null;
205
- }
206
-
207
- /**
208
- * @deprecated Use SmartCertManager instead
209
- */
210
- public async stopPort80Handler(): Promise<void> {
211
- this.logger.warn('stopPort80Handler is deprecated - use SmartCertManager instead');
212
- }
213
-
214
- /**
215
- * @deprecated Use SmartCertManager instead
216
- */
217
- public registerDomainsWithPort80Handler(domains: string[]): void {
218
- this.logger.warn('registerDomainsWithPort80Handler is deprecated - use SmartCertManager instead');
219
- }
220
-
221
- /**
222
- * @deprecated Use SmartCertManager instead
223
- */
224
- public registerRoutesWithPort80Handler(routes: IRouteConfig[]): void {
225
- this.logger.warn('registerRoutesWithPort80Handler is deprecated - use SmartCertManager instead');
226
- }
227
-
228
- /**
229
- * Sets the HTTPS server for certificate updates
230
- */
231
- public setHttpsServer(server: plugins.https.Server): void {
232
- this.httpsServer = server;
233
- }
234
-
235
- /**
236
- * Gets statistics for metrics
237
- */
238
- public getStats() {
239
- return {
240
- cachedCertificates: this.certificateCache.size,
241
- defaultCertEnabled: true
242
- };
243
- }
244
- }