@push.rocks/smartproxy 21.1.6 → 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 +89 -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 +266 -6
  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 +289 -7
  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
@@ -335,10 +335,22 @@ export class RouteValidator {
335
335
  private static isValidDomain(domain: string): boolean {
336
336
  if (!domain || typeof domain !== 'string') return false;
337
337
  if (domain === '*') return true;
338
+ if (domain === 'localhost') return true;
338
339
 
339
- // Basic domain pattern validation
340
- const domainPattern = /^(\*\.)?([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])?$/;
341
- return domainPattern.test(domain) || domain === 'localhost';
340
+ // Allow both *.domain and *domain patterns
341
+ // Also allow regular domains and subdomains
342
+ const domainPatterns = [
343
+ // Standard domain with optional wildcard subdomain (*.example.com)
344
+ /^(\*\.)?([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])?$/,
345
+ // Wildcard prefix without dot (*example.com)
346
+ /^\*[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])?))*$/,
347
+ // IP address
348
+ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
349
+ // IPv6 address
350
+ /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/
351
+ ];
352
+
353
+ return domainPatterns.some(pattern => pattern.test(domain));
342
354
  }
343
355
 
344
356
  /**
@@ -427,8 +439,8 @@ export class RouteValidator {
427
439
  * Validate IPv6 address
428
440
  */
429
441
  private static isValidIPv6(ip: string): boolean {
430
- // Simple IPv6 validation
431
- 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;
432
444
  return ipv6Pattern.test(ip);
433
445
  }
434
446
 
@@ -442,7 +454,7 @@ export class RouteValidator {
442
454
  errors: routeErrors,
443
455
  component: 'route-validator'
444
456
  });
445
-
457
+
446
458
  for (const error of routeErrors) {
447
459
  logger.log('error', ` - ${error}`, {
448
460
  route: routeName,
@@ -451,4 +463,274 @@ export class RouteValidator {
451
463
  }
452
464
  }
453
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;
454
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
- }