import React from "react"; import { Button } from "@/components/ui/button.tsx"; import { Card } from "@/components/ui/card.tsx"; import { Separator } from "@/components/ui/separator.tsx"; import { useTranslation } from "react-i18next"; import { Loader2, Pin, Network, Tag, Play, Square, AlertCircle, Clock, Wifi, WifiOff, X, } from "lucide-react"; import { Badge } from "@/components/ui/badge.tsx"; import type { TunnelStatus, SSHTunnelObjectProps, } from "../../../types/index.js"; export function TunnelObject({ host, tunnelStatuses, tunnelActions, onTunnelAction, compact = false, bare = false, }: SSHTunnelObjectProps): React.ReactElement { const { t } = useTranslation(); const getTunnelStatus = (tunnelIndex: number): TunnelStatus | undefined => { const tunnel = host.tunnelConnections[tunnelIndex]; const tunnelName = `${host.name || `${host.username}@${host.ip}`}_${tunnel.sourcePort}_${tunnel.endpointPort}`; return tunnelStatuses[tunnelName]; }; const getTunnelStatusDisplay = (status: TunnelStatus | undefined) => { if (!status) return { icon: , text: t("tunnels.unknown"), color: "text-muted-foreground", bgColor: "bg-muted/50", borderColor: "border-border", }; const statusValue = status.status || "DISCONNECTED"; switch (statusValue.toUpperCase()) { case "CONNECTED": return { icon: , text: t("tunnels.connected"), color: "text-green-600 dark:text-green-400", bgColor: "bg-green-500/10 dark:bg-green-400/10", borderColor: "border-green-500/20 dark:border-green-400/20", }; case "CONNECTING": return { icon: , text: t("tunnels.connecting"), color: "text-blue-600 dark:text-blue-400", bgColor: "bg-blue-500/10 dark:bg-blue-400/10", borderColor: "border-blue-500/20 dark:border-blue-400/20", }; case "DISCONNECTING": return { icon: , text: t("tunnels.disconnecting"), color: "text-orange-600 dark:text-orange-400", bgColor: "bg-orange-500/10 dark:bg-orange-400/10", borderColor: "border-orange-500/20 dark:border-orange-400/20", }; case "DISCONNECTED": return { icon: , text: t("tunnels.disconnected"), color: "text-muted-foreground", bgColor: "bg-muted/30", borderColor: "border-border", }; case "WAITING": return { icon: , color: "text-blue-600 dark:text-blue-400", bgColor: "bg-blue-500/10 dark:bg-blue-400/10", borderColor: "border-blue-500/20 dark:border-blue-400/20", }; case "ERROR": case "FAILED": return { icon: , text: status.reason || t("tunnels.error"), color: "text-red-600 dark:text-red-400", bgColor: "bg-red-500/10 dark:bg-red-400/10", borderColor: "border-red-500/20 dark:border-red-400/20", }; default: return { icon: , text: statusValue, color: "text-muted-foreground", bgColor: "bg-muted/30", borderColor: "border-border", }; } }; if (bare) { return (
{host.tunnelConnections && host.tunnelConnections.length > 0 ? (
{host.tunnelConnections.map((tunnel, tunnelIndex) => { const status = getTunnelStatus(tunnelIndex); const statusDisplay = getTunnelStatusDisplay(status); const tunnelName = `${host.name || `${host.username}@${host.ip}`}_${tunnel.sourcePort}_${tunnel.endpointPort}`; const isActionLoading = tunnelActions[tunnelName]; const statusValue = status?.status?.toUpperCase() || "DISCONNECTED"; const isConnected = statusValue === "CONNECTED"; const isConnecting = statusValue === "CONNECTING"; const isDisconnecting = statusValue === "DISCONNECTING"; const isRetrying = statusValue === "RETRYING"; const isWaiting = statusValue === "WAITING"; return (
{statusDisplay.icon}
{t("tunnels.port")} {tunnel.sourcePort} →{" "} {tunnel.endpointHost}:{tunnel.endpointPort}
{statusDisplay.text}
{!isActionLoading ? (
{isConnected ? ( <> ) : isRetrying || isWaiting ? ( ) : ( )}
) : ( )}
{(statusValue === "ERROR" || statusValue === "FAILED") && status?.reason && (
{t("tunnels.error")}:
{status.reason} {status.reason && status.reason.includes("Max retries exhausted") && ( <>
{t("tunnels.checkDockerLogs")}{" "} {t("tunnels.discord")} {" "} or create a{" "} {t("tunnels.githubIssue")} {" "} {t("tunnels.forHelp")}.
)}
)} {(statusValue === "RETRYING" || statusValue === "WAITING") && status?.retryCount && status?.maxRetries && (
{statusValue === "WAITING" ? t("tunnels.waitingForRetry") : t("tunnels.retryingConnection")}
{t("tunnels.attempt", { current: status.retryCount, max: status.maxRetries, })} {status.nextRetryIn && ( {" "} •{" "} {t("tunnels.nextRetryIn", { seconds: status.nextRetryIn, })} )}
)}
); })}
) : (

{t("tunnels.noTunnelConnections")}

)}
); } return (
{!compact && (
{host.pin && ( )}

{host.name || `${host.username}@${host.ip}`}

{host.ip}:{host.port} • {host.username}

)} {!compact && host.tags && host.tags.length > 0 && (
{host.tags.slice(0, 3).map((tag, index) => ( {tag} ))} {host.tags.length > 3 && ( +{host.tags.length - 3} )}
)} {!compact && }
{!compact && (

{t("tunnels.tunnelConnections")} ({host.tunnelConnections.length})

)} {host.tunnelConnections && host.tunnelConnections.length > 0 ? (
{host.tunnelConnections.map((tunnel, tunnelIndex) => { const status = getTunnelStatus(tunnelIndex); const statusDisplay = getTunnelStatusDisplay(status); const tunnelName = `${host.name || `${host.username}@${host.ip}`}_${tunnel.sourcePort}_${tunnel.endpointPort}`; const isActionLoading = tunnelActions[tunnelName]; const statusValue = status?.status?.toUpperCase() || "DISCONNECTED"; const isConnected = statusValue === "CONNECTED"; const isConnecting = statusValue === "CONNECTING"; const isDisconnecting = statusValue === "DISCONNECTING"; const isRetrying = statusValue === "RETRYING"; const isWaiting = statusValue === "WAITING"; return (
{statusDisplay.icon}
{t("tunnels.port")} {tunnel.sourcePort} →{" "} {tunnel.endpointHost}:{tunnel.endpointPort}
{statusDisplay.text}
{!isActionLoading && (
{isConnected ? ( <> ) : isRetrying || isWaiting ? ( ) : ( )}
)} {isActionLoading && ( )}
{(statusValue === "ERROR" || statusValue === "FAILED") && status?.reason && (
{t("tunnels.error")}:
{status.reason} {status.reason && status.reason.includes("Max retries exhausted") && ( <>
{t("tunnels.checkDockerLogs")}{" "} {t("tunnels.discord")} {" "} or create a{" "} {t("tunnels.githubIssue")} {" "} {t("tunnels.forHelp")}.
)}
)} {(statusValue === "RETRYING" || statusValue === "WAITING") && status?.retryCount && status?.maxRetries && (
{statusValue === "WAITING" ? t("tunnels.waitingForRetry") : t("tunnels.retryingConnection")}
{t("tunnels.attempt", { current: status.retryCount, max: status.maxRetries, })} {status.nextRetryIn && ( {" "} •{" "} {t("tunnels.nextRetryIn", { seconds: status.nextRetryIn, })} )}
)}
); })}
) : (

{t("tunnels.noTunnelConnections")}

)}
); }