diff --git a/components/tunnel-config.tsx b/components/tunnel-config.tsx index 9d0de99..88c4439 100644 --- a/components/tunnel-config.tsx +++ b/components/tunnel-config.tsx @@ -18,6 +18,10 @@ interface Server { ping: number | null status: "online" | "offline" | "maintenance" pingStatus: "idle" | "testing" | "success" | "failed" | "timeout" + capabilities: { + http: boolean + tcp: boolean + } } const geoUrl = "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json" @@ -33,6 +37,20 @@ const fetchServers = async (): Promise => { await new Promise((resolve) => setTimeout(resolve, 2000)) const mockServers: Server[] = [ + { + id: "us", + name: "United States", + location: "Chicago", + subdomain: "us.tunnl.live", + coordinates: [-87.6298, 41.8781], + ping: null, + status: "online", + pingStatus: "idle", + capabilities: { + http: true, + tcp: false, + }, + }, { id: "sgp", name: "Singapore", @@ -42,16 +60,24 @@ const fetchServers = async (): Promise => { ping: null, status: "online", pingStatus: "idle", + capabilities: { + http: true, + tcp: false, + }, }, { id: "id", name: "Indonesia", - location: "Jakarta", + location: "Bogor", subdomain: "id.tunnl.live", - coordinates: [106.8456, -6.2088], + coordinates: [106.8456, -6.5950], ping: null, status: "online", pingStatus: "idle", + capabilities: { + http: true, + tcp: true, + }, }, ] @@ -194,14 +220,33 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o setServers(updatedServers) - const successfulServers = updatedServers.filter((s) => s.pingStatus === "success" && s.ping !== null) - if (successfulServers.length > 0) { - const bestServer = successfulServers.reduce((prev, current) => + const compatibleServers = updatedServers.filter( + (s) => + s.pingStatus === "success" && + s.ping !== null && + ((localConfig.type === "http" && s.capabilities.http) || + (localConfig.type === "tcp" && s.capabilities.tcp)), + ) + + if (compatibleServers.length > 0) { + const bestServer = compatibleServers.reduce((prev, current) => prev.ping! < current.ping! ? prev : current, ) onServerSelect(bestServer) - } else if (updatedServers.length > 0) { - onServerSelect(updatedServers[0]) + } else { + const successfulServers = updatedServers.filter((s) => s.pingStatus === "success" && s.ping !== null) + if (successfulServers.length > 0) { + const bestServer = successfulServers.reduce((prev, current) => + prev.ping! < current.ping! ? prev : current, + ) + onServerSelect(bestServer) + + if (localConfig.type === "tcp" && !bestServer.capabilities.tcp) { + updateConfig({ type: "http", serverPort: 443 }) + } + } else if (updatedServers.length > 0) { + onServerSelect(updatedServers[0]) + } } } catch (error) { console.error("Error testing pings:", error) @@ -212,7 +257,13 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o autoTestPings() } - }, [servers.length, isLoadingServers, hasAutoTested, onServerSelect]) + }, [servers.length, isLoadingServers, hasAutoTested, onServerSelect, localConfig.type]) + + useEffect(() => { + if (selectedServer && localConfig.type === "tcp" && !selectedServer.capabilities.tcp) { + updateConfig({ type: "http", serverPort: 443 }) + } + }, [selectedServer, localConfig.type]) const updateConfig = (updates: Partial) => { const newConfig = { ...localConfig, ...updates } @@ -351,6 +402,44 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o } } + const canSelectServer = (server: Server) => { + if (server.pingStatus === "failed" || server.pingStatus === "timeout") { + return false + } + + if (localConfig.type === "http" && !server.capabilities.http) { + return false + } + if (localConfig.type === "tcp" && !server.capabilities.tcp) { + return false + } + + return true + } + + const getServerUnavailableReason = (server: Server) => { + if (server.pingStatus === "failed" || server.pingStatus === "timeout") { + return "Server unavailable" + } + if (localConfig.type === "tcp" && !server.capabilities.tcp) { + return "TCP not supported" + } + if (localConfig.type === "http" && !server.capabilities.http) { + return "HTTP not supported" + } + return null + } + + const getCompatibleServers = () => { + return servers.filter((server) => { + if (localConfig.type === "http") return server.capabilities.http + if (localConfig.type === "tcp") return server.capabilities.tcp + return true + }) + } + + const compatibleServers = getCompatibleServers() + return (

Tunnel Configuration

@@ -386,6 +475,35 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o )}
+ {compatibleServers.length === 0 && servers.length > 0 && ( +
+
+ + + + + +

+ No servers support {localConfig.type.toUpperCase()} forwarding +

+
+

+ Please switch to HTTP/HTTPS forwarding or wait for TCP-compatible servers to come online. +

+
+ )} + {isLoadingServers ? (
@@ -483,10 +601,9 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o key={server.id} coordinates={server.coordinates} onClick={() => { - if (server.pingStatus === "failed" || server.pingStatus === "timeout") { - return + if (canSelectServer(server)) { + onServerSelect(server) } - onServerSelect(server) }} > @@ -517,75 +634,94 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o
- {servers.map((server) => ( -
{ - if (server.pingStatus === "failed" || server.pingStatus === "timeout") { - return - } - onServerSelect(server) - }} - className={`p-3 rounded-lg border transition-all duration-200 ${selectedServer?.id === server.id - ? "bg-emerald-950 border-emerald-500" - : server.pingStatus === "failed" || server.pingStatus === "timeout" - ? "bg-red-950 border-red-800 cursor-not-allowed opacity-75" - : "bg-gray-800 border-gray-700 hover:border-gray-600 cursor-pointer" + {servers.map((server) => { + const canSelect = canSelectServer(server) + const unavailableReason = getServerUnavailableReason(server) + + return ( +
{ + if (canSelect) { + onServerSelect(server) + } + }} + className={`p-3 rounded-lg border transition-all duration-200 ${ + selectedServer?.id === server.id + ? "bg-emerald-950 border-emerald-500" + : !canSelect + ? "bg-red-950 border-red-800 cursor-not-allowed opacity-75" + : "bg-gray-800 border-gray-700 hover:border-gray-600 cursor-pointer" }`} - > -
-
{server.name}
-
+
+
{server.name}
+
-
-

{server.location}

-

{server.subdomain}

-
- Ping: -
- {server.pingStatus === "testing" && ( -
- )} - {hasAutoTested ? ( - - ) : ( - {getPingDisplay(server)} - )} + />
+

{server.location}

+

{server.subdomain}

+ +
+ Supports: +
+ {server.capabilities.http && ( + HTTP + )} + {server.capabilities.tcp && ( + TCP + )} +
+
+ +
+ Ping: +
+ {server.pingStatus === "testing" && ( +
+ )} + {hasAutoTested ? ( + + ) : ( + {getPingDisplay(server)} + )} +
+
+

{getPingStatus(server)}

+ {unavailableReason && ( +

Cannot select - {unavailableReason}

+ )}
-

{getPingStatus(server)}

- {(server.pingStatus === "failed" || server.pingStatus === "timeout") && ( -

Cannot select - Server unavailable

- )} -
- ))} + ) + })}
)} @@ -596,47 +732,85 @@ export default function TunnelConfig({ config, onConfigChange, selectedServer, o
-