Skip to content

Commit 3bf6ecc

Browse files
committed
feat: 处理内网穿透场景
1 parent a75f717 commit 3bf6ecc

2 files changed

Lines changed: 281 additions & 61 deletions

File tree

app/templates/page.tsx

Lines changed: 152 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,20 @@ interface FormData {
6767
logLevel: string;
6868
connectionPort: string;
6969
accessInfo: string;
70+
intranetTargetMaster: string; // 内网穿透的目标服务器
71+
intranetTargetPort: string; // 内网穿透的目标端口
72+
intranetExitIp: string; // 内网穿透的出口IP
73+
certPath: string; // TLS 2 证书路径
74+
keyPath: string; // TLS 2 密钥路径
7075
}
7176

7277
interface TemplateCreateRequest {
7378
log: string;
7479
listen_port: number;
7580
mode: string;
7681
tls?: number;
82+
cert_path?: string;
83+
key_path?: string;
7784
inbounds?: {
7885
target_host: string;
7986
target_port: number;
@@ -164,7 +171,12 @@ export default function TemplatesPage() {
164171
tlsLevel: '1',
165172
logLevel: 'debug', // 默认debug级别
166173
connectionPort: '',
167-
accessInfo: ''
174+
accessInfo: '',
175+
intranetTargetMaster: '',
176+
intranetTargetPort: '',
177+
intranetExitIp: '',
178+
certPath: '',
179+
keyPath: ''
168180
});
169181

170182
// 当监听类型改变时,自动设置目标IP
@@ -180,6 +192,34 @@ export default function TemplatesPage() {
180192
setFormData(prev => ({ ...prev, [field]: value }));
181193
};
182194

195+
// 清空表单数据的函数
196+
const resetFormData = () => {
197+
setFormData({
198+
userPort: '',
199+
masterServer: '',
200+
listenType: 'external', // 保持默认值
201+
targetIp: '',
202+
targetPort: '',
203+
targetMaster: '',
204+
targetMasterPort: '',
205+
tlsLevel: '1', // 保持默认值
206+
logLevel: 'debug', // 保持默认值
207+
connectionPort: '',
208+
accessInfo: '',
209+
intranetTargetMaster: '',
210+
intranetTargetPort: '',
211+
intranetExitIp: '',
212+
certPath: '',
213+
keyPath: ''
214+
});
215+
};
216+
217+
// 切换隧道模式时清空表单数据
218+
const handleModeChange = (mode: string) => {
219+
setSelectedMode(mode);
220+
resetFormData();
221+
};
222+
183223
// 从URL中提取IP/域名
184224
const extractHostFromUrl = (url: string): string => {
185225
try {
@@ -221,7 +261,7 @@ export default function TemplatesPage() {
221261
<div className="flex items-center gap-3 justify-between">
222262
<div className="flex items-center gap-3">
223263
<FontAwesomeIcon icon={faLayerGroup} className="text-2xl text-primary" />
224-
<h2 className="text-2xl font-bold">选择隧道模式</h2>
264+
<h2 className="text-2xl font-bold">选择应用模式</h2>
225265
</div>
226266
<Button
227267
variant="flat"
@@ -238,7 +278,7 @@ export default function TemplatesPage() {
238278
key={mode.id}
239279
isPressable
240280
isHoverable
241-
onPress={() => setSelectedMode(mode.id)}
281+
onPress={() => handleModeChange(mode.id)}
242282
className={`cursor-pointer transition-all duration-200 ${
243283
selectedMode === mode.id
244284
? 'ring-2 ring-primary ring-offset-2'
@@ -351,7 +391,7 @@ export default function TemplatesPage() {
351391
]
352392
},
353393
{
354-
label: '中转机器[server]',
394+
label: '入口机器[server]',
355395
type: 'relay',
356396
formFields: [
357397
{
@@ -369,7 +409,7 @@ export default function TemplatesPage() {
369409
]
370410
},
371411
{
372-
label: '目标机器[client]',
412+
label: '出口机器[client]',
373413
type: 'target',
374414
formFields: [
375415
{
@@ -401,9 +441,9 @@ export default function TemplatesPage() {
401441
type: 'user',
402442
formFields: [
403443
{
404-
label: '服务端口',
444+
label: '访问端口',
405445
key: 'userPort',
406-
placeholder: '例如: 8080',
446+
placeholder: '8080',
407447
value: formData.userPort
408448
}
409449
]
@@ -413,51 +453,46 @@ export default function TemplatesPage() {
413453
type: 'relay',
414454
formFields: [
415455
{
416-
label: '中转服务器',
456+
label: '连接服务器',
417457
key: 'masterServer',
418458
type: 'select',
419-
placeholder: '选择服务器',
459+
placeholder: '下拉选择',
420460
value: formData.masterServer,
421461
options: endpoints.map(endpoint => ({
422462
value: endpoint.name,
423463
label: `${endpoint.name} [${extractHostFromUrl(endpoint.url)}]${endpoint.status === 'FAIL' ? ' - 异常' : ''}`,
424464
disabled: endpoint.status === 'FAIL'
425465
}))
426-
},
427-
{
428-
label: 'TLS级别',
429-
key: 'tlsLevel',
430-
type: 'select',
431-
value: formData.tlsLevel,
432-
placeholder: '选择TLS级别',
433-
options: tlsLevels.map(level => ({
434-
value: level.value,
435-
label: level.label
436-
}))
437-
},
438-
{
439-
label: '日志级别',
440-
key: 'logLevel',
441-
type: 'select',
442-
value: formData.logLevel,
443-
placeholder: '选择日志级别',
444-
options: logLevels.map(level => ({
445-
value: level.value,
446-
label: level.label
447-
}))
448466
}
449467
]
450468
},
451469
{
452-
label: '外网访问[client]',
470+
label: '内网服务[client]',
453471
type: 'target',
454472
formFields: [
455473
{
456-
label: '访问信息',
457-
key: 'accessInfo',
458-
placeholder: '将自动生成',
459-
value: formData.masterServer ? `${formData.masterServer}:公网端口` : '',
460-
type: 'text'
474+
label: '连接服务器',
475+
key: 'intranetTargetMaster',
476+
type: 'select',
477+
placeholder: '下拉选择',
478+
value: formData.intranetTargetMaster,
479+
options: endpoints.map(endpoint => ({
480+
value: endpoint.name,
481+
label: `${endpoint.name} [${extractHostFromUrl(endpoint.url)}]${endpoint.status === 'FAIL' ? ' - 异常' : ''}`,
482+
disabled: endpoint.status === 'FAIL'
483+
}))
484+
},
485+
{
486+
label: '服务IP',
487+
key: 'intranetExitIp',
488+
placeholder: '服务的IP',
489+
value: formData.intranetExitIp
490+
},
491+
{
492+
label: '服务端口',
493+
key: 'intranetTargetPort',
494+
placeholder: '3306',
495+
value: formData.intranetTargetPort
461496
}
462497
]
463498
}
@@ -604,7 +639,7 @@ export default function TemplatesPage() {
604639
<div className="text-xs text-default-500 mb-2">
605640
{selectedMode === 'single' ? '访问' :
606641
selectedMode === 'double' ? '访问' :
607-
selectedMode === 'intranet' ? '穿透' : '连接'}
642+
selectedMode === 'intranet' ? '访问' : '连接'}
608643
</div>
609644
<svg width="120" height="14" viewBox="0 0 120 14" className="text-blue-600">
610645
{/* 双向箭头横线 */}
@@ -641,14 +676,29 @@ export default function TemplatesPage() {
641676
<polygon points="110,7 102,4 102,10" fill="currentColor"/>
642677
</svg>
643678

644-
{/* 双端转发的连接池配置 */}
645-
{selectedMode === 'double' && (
679+
{/* 双端转发和内网穿透的连接池配置 */}
680+
{(selectedMode === 'double' || selectedMode === 'intranet') && (
646681
<div className="bg-blue-50 border-2 border-blue-200 rounded-lg p-2 shadow-sm mt-2" style={{ width: '120px' }}>
647682
<div className="flex items-center gap-1 mb-2">
648683
<FontAwesomeIcon icon={faGear} className="text-blue-600 text-xs" />
649-
<span className="text-xs font-medium text-blue-800">连接池</span>
684+
<span className="text-xs font-medium text-blue-800">
685+
{selectedMode === 'double' ? '连接配置' : '连接配置'}
686+
</span>
650687
</div>
651688
<div className="space-y-1">
689+
<div>
690+
<label className="block text-xs text-gray-700 mb-1">
691+
{selectedMode === 'double' ? '连接端口' : '连接端口'}
692+
</label>
693+
<input
694+
type="text"
695+
value={formData.connectionPort}
696+
onChange={(e) => updateField('connectionPort', e.target.value)}
697+
placeholder="10101"
698+
className="w-full px-1 py-1 text-xs border border-gray-300 rounded"
699+
/>
700+
</div>
701+
652702
<div>
653703
<label className="block text-xs text-gray-700 mb-1">TLS</label>
654704
<select
@@ -663,15 +713,32 @@ export default function TemplatesPage() {
663713
))}
664714
</select>
665715
</div>
666-
<div>
667-
<label className="block text-xs text-gray-700 mb-1">连接端口</label>
668-
<input
669-
type="text"
670-
value={formData.connectionPort || '10101'}
671-
onChange={(e) => updateField('connectionPort', e.target.value)}
672-
className="w-full px-1 py-1 text-xs border border-gray-300 rounded"
673-
/>
674-
</div>
716+
717+
{/* TLS 2 的证书配置 */}
718+
{formData.tlsLevel === '2' && (
719+
<>
720+
<div>
721+
<label className="block text-xs text-gray-700 mb-1">证书路径</label>
722+
<input
723+
type="text"
724+
value={formData.certPath}
725+
onChange={(e) => updateField('certPath', e.target.value)}
726+
placeholder="/path/to/cert.pem"
727+
className="w-full px-1 py-1 text-xs border border-gray-300 rounded"
728+
/>
729+
</div>
730+
<div>
731+
<label className="block text-xs text-gray-700 mb-1">密钥路径</label>
732+
<input
733+
type="text"
734+
value={formData.keyPath}
735+
onChange={(e) => updateField('keyPath', e.target.value)}
736+
placeholder="/path/to/key.pem"
737+
className="w-full px-1 py-1 text-xs border border-gray-300 rounded"
738+
/>
739+
</div>
740+
</>
741+
)}
675742
</div>
676743
</div>
677744
)}
@@ -695,7 +762,7 @@ export default function TemplatesPage() {
695762
: '用户可以访问指定端口来访问目标IP的服务'
696763
)}
697764
{selectedMode === 'double' && '用户可以访问本地端口,通过双端加密隧道连接到目标服务'}
698-
{selectedMode === 'intranet' && '外部用户可以通过公网地址访问您的内网服务'}
765+
{selectedMode === 'intranet' && '用户可以通过公网端口访问内网服务,实现内网穿透'}
699766
</div>
700767
</div>
701768
</CardBody>
@@ -786,7 +853,7 @@ export default function TemplatesPage() {
786853
return null;
787854
}
788855

789-
return {
856+
const doubleRequest: TemplateCreateRequest = {
790857
log: formData.logLevel,
791858
listen_port: parseInt(formData.connectionPort),
792859
mode: 'bothway',
@@ -805,9 +872,37 @@ export default function TemplatesPage() {
805872
}
806873
};
807874

875+
// 如果是TLS 2,添加证书路径
876+
if (formData.tlsLevel === '2') {
877+
doubleRequest.cert_path = formData.certPath;
878+
doubleRequest.key_path = formData.keyPath;
879+
}
880+
881+
return doubleRequest;
882+
808883
case 'intranet':
809-
// 内网穿透暂未实现
810-
return null;
884+
if (!formData.userPort || !formData.masterServer || !formData.intranetTargetMaster || !formData.intranetTargetPort || !formData.connectionPort) {
885+
return null;
886+
}
887+
888+
return {
889+
log: formData.logLevel,
890+
listen_port: parseInt(formData.connectionPort),
891+
mode: 'intranet',
892+
tls: parseInt(formData.tlsLevel),
893+
inbounds: {
894+
target_host: '',
895+
target_port: parseInt(formData.userPort),
896+
master_id: getEndpointIdByName(formData.masterServer),
897+
type: 'server'
898+
},
899+
outbounds: {
900+
target_host: formData.intranetExitIp || '127.0.0.1',
901+
target_port: parseInt(formData.intranetTargetPort),
902+
master_id: getEndpointIdByName(formData.intranetTargetMaster),
903+
type: 'client'
904+
}
905+
};
811906

812907
default:
813908
return null;
@@ -830,8 +925,9 @@ export default function TemplatesPage() {
830925

831926
// 显示进度提示
832927
addToast({
833-
title: '正在创建隧道...',
834-
description: selectedMode === 'bothway' ? '正在创建双端隧道,请稍候' : '正在创建隧道,请稍候',
928+
timeout: 1,
929+
title: '正在创建场景中...',
930+
description: selectedMode === 'bothway' ? '正在创建场景中,请稍候' : '正在创建场景中,请稍候',
835931
color: 'primary'
836932
});
837933

0 commit comments

Comments
 (0)