import { useEffect, useState } from 'react' import { AlertCircle, Network, Play, RefreshCw, StopCircle, Trash2, } from 'lucide-react' import Modal from './Modal' import { createPortForward, listPortForwards, stopPortForward, type PortForwardTunnel, } from '../services/portForwards' import type { Connection } from '../types' interface PortForwardModalProps { open: boolean connections: Connection[] initialConnectionId?: number | null onClose: () => void } export default function PortForwardModal({ open, connections, initialConnectionId, onClose, }: PortForwardModalProps) { const [tunnels, setTunnels] = useState([]) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState(null) // Form state const [connectionId, setConnectionId] = useState(0) const [localPort, setLocalPort] = useState(8080) const [remoteHost, setRemoteHost] = useState('127.0.0.1') const [remotePort, setRemotePort] = useState(80) // Load tunnels const fetchTunnels = async (showLoading = true) => { if (showLoading) setLoading(true) setError(null) try { const data = await listPortForwards() setTunnels(data) } catch (err: any) { setError(err.message ?? '获取端口转发列表失败') } finally { if (showLoading) setLoading(false) } } useEffect(() => { if (open) { fetchTunnels(true) // Pre-select connection if provided if (initialConnectionId) { setConnectionId(initialConnectionId) } else if (connections.length > 0) { setConnectionId(connections[0].id) } } }, [open, initialConnectionId, connections]) const handleStartTunnel = async (e: React.FormEvent) => { e.preventDefault() if (!connectionId) { setError('请选择 SSH 连接') return } setError(null) setSubmitting(true) try { await createPortForward({ connectionId, localPort, remoteHost: remoteHost.trim(), remotePort, }) // Reset form options (keep connection, maybe increment local port) setLocalPort((prev) => prev + 1) await fetchTunnels(false) } catch (err: any) { setError(err.message ?? '启动端口转发失败') } finally { setSubmitting(false) } } const handleStopTunnel = async (id: string) => { setError(null) try { await stopPortForward(id) await fetchTunnels(false) } catch (err: any) { setError(err.message ?? '停止端口转发失败') } } if (!open) return null return (
{/* Left Column: Form to create a new tunnel */}

新建端口转发通道

setLocalPort(Number(e.target.value))} className="w-full rounded-xl border border-border-main bg-surface-control px-3.5 py-2 text-sm text-content-main focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition" required />
setRemoteHost(e.target.value)} className="w-full rounded-xl border border-border-main bg-surface-control px-3.5 py-2 text-sm text-content-main focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition" required />
setRemotePort(Number(e.target.value))} className="w-full rounded-xl border border-border-main bg-surface-control px-3.5 py-2 text-sm text-content-main focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition" required />
{error && (
{error}
)}
{/* Right Column: List of running tunnels */}

活动转发列表

{loading && tunnels.length === 0 ? (
加载中...
) : tunnels.length === 0 ? (
暂无活动的端口转发通道
) : (
{tunnels.map((tunnel) => (
{tunnel.connectionName} | :{tunnel.localPort} {tunnel.remoteHost}:{tunnel.remotePort}
启动时间: {new Date(tunnel.createdAt).toLocaleTimeString()} {tunnel.status === 'running' ? '运行中' : tunnel.status === 'error' ? '异常' : '已停止'}
))}
)}
) }