Files
Termix/src/ui/Mobile/Navigation/Hosts/Host.tsx
2025-09-09 21:41:42 -05:00

107 lines
3.4 KiB
TypeScript

import React, {useEffect, useState} from "react";
import {Status, StatusIndicator} from "@/components/ui/shadcn-io/status";
import {Button} from "@/components/ui/button.tsx";
import {ButtonGroup} from "@/components/ui/button-group.tsx";
import {Server, Terminal} from "lucide-react";
import {getServerStatusById} from "@/ui/main-axios.ts";
import {useTabs} from "@/ui/Mobile/Navigation/Tabs/TabContext.tsx";
interface SSHHost {
id: number;
name: string;
ip: string;
port: number;
username: string;
folder: string;
tags: string[];
pin: boolean;
authType: string;
password?: string;
key?: string;
keyPassword?: string;
keyType?: string;
enableTerminal: boolean;
enableTunnel: boolean;
enableFileManager: boolean;
defaultPath: string;
tunnelConnections: any[];
createdAt: string;
updatedAt: string;
}
interface HostProps {
host: SSHHost;
onHostConnect: () => void;
}
export function Host({host, onHostConnect}: HostProps): React.ReactElement {
const {addTab} = useTabs();
const [serverStatus, setServerStatus] = useState<'online' | 'offline' | 'degraded'>('degraded');
const tags = Array.isArray(host.tags) ? host.tags : [];
const hasTags = tags.length > 0;
const title = host.name?.trim() ? host.name : `${host.username}@${host.ip}:${host.port}`;
useEffect(() => {
let intervalId: number | undefined;
let cancelled = false;
const fetchStatus = async () => {
try {
const res = await getServerStatusById(host.id);
if (!cancelled) {
setServerStatus(res?.status === 'online' ? 'online' : 'offline');
}
} catch {
if (!cancelled) setServerStatus('offline');
}
};
fetchStatus();
intervalId = window.setInterval(fetchStatus, 10000);
return () => {
cancelled = true;
if (intervalId) window.clearInterval(intervalId);
};
}, [host.id]);
const handleTerminalClick = () => {
addTab({type: 'terminal', title, hostConfig: host});
onHostConnect();
};
return (
<div>
<div className="flex items-center gap-2">
<Status status={serverStatus} className="!bg-transparent !p-0.75 flex-shrink-0">
<StatusIndicator/>
</Status>
<p className="font-semibold flex-1 min-w-0 break-words text-sm">
{host.name || host.ip}
</p>
<ButtonGroup className="flex-shrink-0">
{host.enableTerminal && (
<Button
variant="outline"
className="!px-2 border-1 w-[60px] border-dark-border"
onClick={handleTerminalClick}
>
<Terminal/>
</Button>
)}
</ButtonGroup>
</div>
{hasTags && (
<div className="flex flex-wrap items-center gap-2 mt-1">
{tags.map((tag: string) => (
<div key={tag} className="bg-dark-bg border-1 border-dark-border pl-2 pr-2 rounded-[10px]">
<p className="text-sm">{tag}</p>
</div>
))}
</div>
)}
</div>
)
}