Skip to content

Commit 1ddd25b

Browse files
committed
fix(provider): support port ranges for tuic and hysteria2
- Add extractFirstPort helper to parse port ranges - Enable port fallback to first port from port range if not specified - Extract common TUIC fields to reduce code duplication - Add validation to ensure port is available for both protocols - Make TUIC version optional with default value of 5 - Update tests to reflect new behavior Fixes port configuration when nodes only provide port ranges.
1 parent c09d1af commit 1ddd25b

File tree

3 files changed

+46
-42
lines changed

3 files changed

+46
-42
lines changed

src/provider/ClashProvider.ts

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -533,51 +533,44 @@ export const parseClashConfig = (
533533
case 'tuic': {
534534
let input: TuicNodeConfigInput
535535

536-
if (item.version >= 5) {
536+
const port = item.port ?? extractFirstPort(item.ports)
537+
538+
if (!port) {
539+
throw new SurgioError('Tuic 节点配置校验失败,未指定端口或端口范围')
540+
}
541+
542+
const tuicCommonFields = {
543+
type: NodeTypeEnum.Tuic as const,
544+
nodeName: item.name,
545+
hostname: item.server,
546+
port,
547+
...('skip-cert-verify' in item
548+
? { skipCertVerify: item['skip-cert-verify'] === true }
549+
: null),
550+
tls13: tls13 ?? false,
551+
...('sni' in item ? { sni: item.sni } : null),
552+
...('alpn' in item ? { alpn: item.alpn } : null),
553+
...('ports' in item
554+
? {
555+
portHopping: item.ports,
556+
}
557+
: null),
558+
...('hop-interval' in item
559+
? { portHoppingInterval: item['hop-interval'] }
560+
: null),
561+
}
562+
563+
if (item.uuid) {
537564
input = {
538-
type: NodeTypeEnum.Tuic,
539-
version: item.version,
540-
nodeName: item.name,
541-
hostname: item.server,
542-
port: item.port,
565+
...tuicCommonFields,
543566
password: item.password,
544567
uuid: item.uuid,
545-
...('skip-cert-verify' in item
546-
? { skipCertVerify: item['skip-cert-verify'] === true }
547-
: null),
548-
tls13: tls13 ?? false,
549-
...('sni' in item ? { sni: item.sni } : null),
550-
...('alpn' in item ? { alpn: item.alpn } : null),
551-
...('ports' in item
552-
? {
553-
portHopping: item.ports,
554-
}
555-
: null),
556-
...('hop-interval' in item
557-
? { portHoppingInterval: item['hop-interval'] }
558-
: null),
568+
version: item.version ?? 5,
559569
}
560570
} else {
561571
input = {
562-
type: NodeTypeEnum.Tuic,
563-
nodeName: item.name,
564-
hostname: item.server,
565-
port: item.port,
572+
...tuicCommonFields,
566573
token: item.token,
567-
...('skip-cert-verify' in item
568-
? { skipCertVerify: item['skip-cert-verify'] === true }
569-
: null),
570-
tls13: tls13 ?? false,
571-
...('sni' in item ? { sni: item.sni } : null),
572-
...('alpn' in item ? { alpn: item.alpn } : null),
573-
...('ports' in item
574-
? {
575-
portHopping: item.ports,
576-
}
577-
: null),
578-
...('hop-interval' in item
579-
? { portHoppingInterval: item['hop-interval'] }
580-
: null),
581574
}
582575
}
583576

@@ -601,11 +594,19 @@ export const parseClashConfig = (
601594
)
602595
}
603596

597+
const port = item.port ?? extractFirstPort(item.ports)
598+
599+
if (!port) {
600+
throw new SurgioError(
601+
'Hysteria2 节点配置校验失败,未指定端口或端口范围',
602+
)
603+
}
604+
604605
const input: Hysteria2NodeConfigInput = {
605606
type: NodeTypeEnum.Hysteria2,
606607
nodeName: item.name,
607608
hostname: item.server,
608-
port: item.port,
609+
port,
609610
password: item.auth || item.password,
610611
...(item.down
611612
? { downloadBandwidth: parseBitrate(item.down) }
@@ -729,6 +730,10 @@ function resolveUdpRelay(val?: boolean, defaultVal = false): boolean {
729730
return defaultVal
730731
}
731732

733+
function extractFirstPort(ports: string): number {
734+
return Number(ports.split(/[;,-]/)[0])
735+
}
736+
732737
function resolveVmessHttpHeaders(
733738
headers: Record<string, string[]>,
734739
): Record<string, string> {

src/provider/__tests__/ClashProvider.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,6 @@ test('parseClashConfig tuic configurations', (t) => {
10701070
name: 'tuic-v5',
10711071
server: 'example.com',
10721072
port: 443,
1073-
version: 5,
10741073
uuid: 'uuid',
10751074
password: 'password',
10761075
'skip-cert-verify': true,
@@ -1095,12 +1094,12 @@ test('parseClashConfig tuic configurations', (t) => {
10951094
[
10961095
{
10971096
type: NodeTypeEnum.Tuic,
1098-
version: 5,
10991097
nodeName: 'tuic-v5',
11001098
hostname: 'example.com',
11011099
port: 443,
11021100
password: 'password',
11031101
uuid: 'uuid',
1102+
version: 5,
11041103
skipCertVerify: true,
11051104
tls13: false,
11061105
sni: 'sni.example.com',

src/utils/__tests__/surge.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,10 @@ test('getSurgeNodes - Tuic', (t) => {
542542
port: 443,
543543
password: 'password',
544544
uuid: 'uuid',
545+
version: 5,
545546
alpn: ['h3'],
546547
sni: 'sni.example.com',
547548
skipCertVerify: true,
548-
version: 5,
549549
ecn: true,
550550
},
551551
]),

0 commit comments

Comments
 (0)