@push.rocks/smartproxy 21.1.7 → 22.6.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/changelog.md +109 -0
- package/dist_rust/rustproxy +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
- package/dist_ts/core/utils/shared-security-manager.js +66 -1
- package/dist_ts/index.d.ts +1 -5
- package/dist_ts/index.js +3 -9
- package/dist_ts/protocols/common/fragment-handler.js +5 -1
- package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
- package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
- package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
- package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
- package/dist_ts/proxies/http-proxy/index.js +6 -2
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
- package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
- package/dist_ts/proxies/index.d.ts +1 -5
- package/dist_ts/proxies/index.js +2 -6
- package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
- package/dist_ts/proxies/nftables-proxy/index.js +2 -1
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
- package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
- package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
- package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
- package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
- package/dist_ts/proxies/smart-proxy/index.js +7 -13
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
- package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
- package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
- package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -622
- package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
- package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
- package/dist_ts/routing/index.d.ts +1 -1
- package/dist_ts/routing/index.js +3 -3
- package/dist_ts/routing/models/http-types.d.ts +119 -4
- package/dist_ts/routing/models/http-types.js +93 -5
- package/npmextra.json +12 -6
- package/package.json +34 -24
- package/readme.hints.md +184 -1
- package/readme.md +580 -266
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/shared-security-manager.ts +98 -13
- package/ts/index.ts +4 -12
- package/ts/protocols/common/fragment-handler.ts +4 -0
- package/ts/proxies/index.ts +1 -9
- package/ts/proxies/nftables-proxy/index.ts +1 -0
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
- package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
- package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
- package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
- package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
- package/ts/proxies/smart-proxy/index.ts +6 -13
- package/ts/proxies/smart-proxy/models/interfaces.ts +6 -5
- package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +282 -800
- package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
- package/ts/proxies/smart-proxy/utils/index.ts +3 -5
- package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
- package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
- package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
- package/ts/routing/index.ts +2 -2
- package/ts/routing/models/http-types.ts +147 -4
- package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
- package/ts/proxies/http-proxy/connection-pool.ts +0 -228
- package/ts/proxies/http-proxy/context-creator.ts +0 -145
- package/ts/proxies/http-proxy/function-cache.ts +0 -279
- package/ts/proxies/http-proxy/handlers/index.ts +0 -5
- package/ts/proxies/http-proxy/http-proxy.ts +0 -675
- package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
- package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
- package/ts/proxies/http-proxy/index.ts +0 -13
- package/ts/proxies/http-proxy/models/http-types.ts +0 -148
- package/ts/proxies/http-proxy/models/index.ts +0 -5
- package/ts/proxies/http-proxy/models/types.ts +0 -125
- package/ts/proxies/http-proxy/request-handler.ts +0 -878
- package/ts/proxies/http-proxy/security-manager.ts +0 -433
- package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
- package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
- package/ts/proxies/smart-proxy/cert-store.ts +0 -92
- package/ts/proxies/smart-proxy/certificate-manager.ts +0 -894
- package/ts/proxies/smart-proxy/connection-manager.ts +0 -796
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -187
- package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
- package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
- package/ts/proxies/smart-proxy/port-manager.ts +0 -358
- package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1640
- package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
- package/ts/proxies/smart-proxy/security-manager.ts +0 -257
- package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
- package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
- package/ts/proxies/smart-proxy/tls-manager.ts +0 -207
- package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
|
@@ -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
|
-
//
|
|
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
|
}
|
package/ts/routing/index.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Routing functionality module
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// Export types and models
|
|
6
|
-
export * from '
|
|
5
|
+
// Export types and models
|
|
6
|
+
export * from './models/http-types.js';
|
|
7
7
|
|
|
8
8
|
// Export router functionality
|
|
9
9
|
export * from './router/index.js';
|
|
@@ -1,6 +1,149 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* in the HttpProxy module.
|
|
2
|
+
* HTTP types for routing module.
|
|
3
|
+
* These were previously in http-proxy and are now self-contained here.
|
|
5
4
|
*/
|
|
6
|
-
|
|
5
|
+
import * as plugins from '../../plugins.js';
|
|
6
|
+
import { HttpStatus as ProtocolHttpStatus, getStatusText as getProtocolStatusText } from '../../protocols/http/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HTTP-specific event types
|
|
10
|
+
*/
|
|
11
|
+
export enum HttpEvents {
|
|
12
|
+
REQUEST_RECEIVED = 'request-received',
|
|
13
|
+
REQUEST_FORWARDED = 'request-forwarded',
|
|
14
|
+
REQUEST_HANDLED = 'request-handled',
|
|
15
|
+
REQUEST_ERROR = 'request-error',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Re-export for backward compatibility with subset of commonly used codes
|
|
19
|
+
export const HttpStatus = {
|
|
20
|
+
OK: ProtocolHttpStatus.OK,
|
|
21
|
+
MOVED_PERMANENTLY: ProtocolHttpStatus.MOVED_PERMANENTLY,
|
|
22
|
+
FOUND: ProtocolHttpStatus.FOUND,
|
|
23
|
+
TEMPORARY_REDIRECT: ProtocolHttpStatus.TEMPORARY_REDIRECT,
|
|
24
|
+
PERMANENT_REDIRECT: ProtocolHttpStatus.PERMANENT_REDIRECT,
|
|
25
|
+
BAD_REQUEST: ProtocolHttpStatus.BAD_REQUEST,
|
|
26
|
+
UNAUTHORIZED: ProtocolHttpStatus.UNAUTHORIZED,
|
|
27
|
+
FORBIDDEN: ProtocolHttpStatus.FORBIDDEN,
|
|
28
|
+
NOT_FOUND: ProtocolHttpStatus.NOT_FOUND,
|
|
29
|
+
METHOD_NOT_ALLOWED: ProtocolHttpStatus.METHOD_NOT_ALLOWED,
|
|
30
|
+
REQUEST_TIMEOUT: ProtocolHttpStatus.REQUEST_TIMEOUT,
|
|
31
|
+
TOO_MANY_REQUESTS: ProtocolHttpStatus.TOO_MANY_REQUESTS,
|
|
32
|
+
INTERNAL_SERVER_ERROR: ProtocolHttpStatus.INTERNAL_SERVER_ERROR,
|
|
33
|
+
NOT_IMPLEMENTED: ProtocolHttpStatus.NOT_IMPLEMENTED,
|
|
34
|
+
BAD_GATEWAY: ProtocolHttpStatus.BAD_GATEWAY,
|
|
35
|
+
SERVICE_UNAVAILABLE: ProtocolHttpStatus.SERVICE_UNAVAILABLE,
|
|
36
|
+
GATEWAY_TIMEOUT: ProtocolHttpStatus.GATEWAY_TIMEOUT,
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Base error class for HTTP-related errors
|
|
41
|
+
*/
|
|
42
|
+
export class HttpError extends Error {
|
|
43
|
+
constructor(message: string, public readonly statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = 'HttpError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Error related to certificate operations
|
|
51
|
+
*/
|
|
52
|
+
export class CertificateError extends HttpError {
|
|
53
|
+
constructor(
|
|
54
|
+
message: string,
|
|
55
|
+
public readonly domain: string,
|
|
56
|
+
public readonly isRenewal: boolean = false
|
|
57
|
+
) {
|
|
58
|
+
super(`${message} for domain ${domain}${isRenewal ? ' (renewal)' : ''}`, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
59
|
+
this.name = 'CertificateError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Error related to server operations
|
|
65
|
+
*/
|
|
66
|
+
export class ServerError extends HttpError {
|
|
67
|
+
constructor(message: string, public readonly code?: string, statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
68
|
+
super(message, statusCode);
|
|
69
|
+
this.name = 'ServerError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Error for bad requests
|
|
75
|
+
*/
|
|
76
|
+
export class BadRequestError extends HttpError {
|
|
77
|
+
constructor(message: string) {
|
|
78
|
+
super(message, HttpStatus.BAD_REQUEST);
|
|
79
|
+
this.name = 'BadRequestError';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Error for not found resources
|
|
85
|
+
*/
|
|
86
|
+
export class NotFoundError extends HttpError {
|
|
87
|
+
constructor(message: string = 'Resource not found') {
|
|
88
|
+
super(message, HttpStatus.NOT_FOUND);
|
|
89
|
+
this.name = 'NotFoundError';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Redirect configuration for HTTP requests
|
|
95
|
+
*/
|
|
96
|
+
export interface IRedirectConfig {
|
|
97
|
+
source: string;
|
|
98
|
+
destination: string;
|
|
99
|
+
type: number;
|
|
100
|
+
preserveQuery?: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* HTTP router configuration
|
|
105
|
+
*/
|
|
106
|
+
export interface IRouterConfig {
|
|
107
|
+
routes: Array<{
|
|
108
|
+
path: string;
|
|
109
|
+
method?: string;
|
|
110
|
+
handler: (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void | Promise<void>;
|
|
111
|
+
}>;
|
|
112
|
+
notFoundHandler?: (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void;
|
|
113
|
+
errorHandler?: (error: Error, req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* HTTP request method types
|
|
118
|
+
*/
|
|
119
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Helper function to get HTTP status text
|
|
123
|
+
*/
|
|
124
|
+
export function getStatusText(status: number): string {
|
|
125
|
+
return getProtocolStatusText(status as ProtocolHttpStatus);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Legacy interfaces for backward compatibility
|
|
129
|
+
export interface IDomainOptions {
|
|
130
|
+
domainName: string;
|
|
131
|
+
sslRedirect: boolean;
|
|
132
|
+
acmeMaintenance: boolean;
|
|
133
|
+
forward?: { ip: string; port: number };
|
|
134
|
+
acmeForward?: { ip: string; port: number };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface IDomainCertificate {
|
|
138
|
+
options: IDomainOptions;
|
|
139
|
+
certObtained: boolean;
|
|
140
|
+
obtainingInProgress: boolean;
|
|
141
|
+
certificate?: string;
|
|
142
|
+
privateKey?: string;
|
|
143
|
+
expiryDate?: Date;
|
|
144
|
+
lastRenewalAttempt?: Date;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Backward compatibility exports
|
|
148
|
+
export { HttpError as Port80HandlerError };
|
|
149
|
+
export { CertificateError as CertError };
|