fix: rdp/vnc dupliocated icon, mobile hover fix for menu, and guacd hosts not working when ssh is enabeld

This commit is contained in:
LukeGus
2026-05-27 19:11:05 -05:00
parent 4816844bf0
commit e421a2ccc5
7 changed files with 79 additions and 16 deletions
+26 -1
View File
@@ -144,6 +144,17 @@ router.post("/token", async (req, res) => {
* schema:
* type: integer
* description: Host ID to connect to
* requestBody:
* required: false
* content:
* application/json:
* schema:
* type: object
* properties:
* protocol:
* type: string
* enum: [rdp, vnc, telnet]
* description: Override the host's default connection type
* responses:
* 200:
* description: Connection token generated successfully
@@ -205,13 +216,27 @@ router.post(
}
}
const connectionType = (host.connectionType as string) || "ssh";
const requestedProtocol = req.body?.protocol as string | undefined;
const connectionType =
requestedProtocol || (host.connectionType as string);
if (!["rdp", "vnc", "telnet"].includes(connectionType)) {
return res.status(400).json({
error: `Connection type '${connectionType}' is not supported for remote desktop. Only RDP, VNC, and Telnet are supported.`,
});
}
const protocolEnabledMap: Record<string, boolean> = {
rdp: !!host.enableRdp,
vnc: !!host.enableVnc,
telnet: !!host.enableTelnet,
};
if (!protocolEnabledMap[connectionType]) {
return res.status(400).json({
error: `${connectionType.toUpperCase()} is not enabled for this host.`,
});
}
let guacConfig: Record<string, unknown> = {};
if (host.guacamoleConfig) {
try {
+25 -6
View File
@@ -15,9 +15,14 @@ import type { SSHHost } from "@/types";
interface GuacamoleAppProps {
hostId?: string;
tabId?: string;
protocol?: "rdp" | "vnc" | "telnet";
}
const GuacamoleApp: React.FC<GuacamoleAppProps> = ({ hostId, tabId }) => {
const GuacamoleApp: React.FC<GuacamoleAppProps> = ({
hostId,
tabId,
protocol,
}) => {
const { t } = useTranslation();
return (
@@ -76,6 +81,7 @@ const GuacamoleApp: React.FC<GuacamoleAppProps> = ({ hostId, tabId }) => {
hostId={parseInt(hostId, 10)}
hostConfig={hostConfig}
tabId={tabId}
protocol={protocol}
/>
);
}}
@@ -87,12 +93,14 @@ interface GuacamoleAppInnerProps {
hostId: number;
hostConfig: Pick<SSHHost, "connectionType">;
tabId?: string;
protocol?: "rdp" | "vnc" | "telnet";
}
const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
hostId,
hostConfig,
tabId,
protocol,
}) => {
const { t } = useTranslation();
const [token, setToken] = useState<string | null>(null);
@@ -110,7 +118,7 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
setError(t("guacamole.guacdUnavailable"));
return;
}
return getGuacamoleTokenFromHost(hostId);
return getGuacamoleTokenFromHost(hostId, protocol);
})
.then((result) => {
if (result) setToken(result.token);
@@ -172,14 +180,21 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
<SimpleLoader
visible={true}
message={t("guacamole.connecting", {
type: (hostConfig.connectionType || "remote").toUpperCase(),
type: (
protocol ||
hostConfig.connectionType ||
"remote"
).toUpperCase(),
})}
/>
</div>
);
}
const protocol = hostConfig.connectionType as "rdp" | "vnc" | "telnet";
const resolvedProtocol = (protocol ?? hostConfig.connectionType) as
| "rdp"
| "vnc"
| "telnet";
return (
<div className="relative w-full h-full">
@@ -213,13 +228,17 @@ const GuacamoleAppInner: React.FC<GuacamoleAppInnerProps> = ({
<GuacamoleDisplay
key={token}
ref={displayRef}
connectionConfig={{ token, protocol, type: protocol }}
connectionConfig={{
token,
protocol: resolvedProtocol,
type: resolvedProtocol,
}}
isVisible={true}
onError={(err) => setConnectionError(err)}
/>
<GuacamoleToolbar
displayRef={displayRef}
protocol={protocol}
protocol={resolvedProtocol}
onReconnect={handleReconnect}
/>
</div>
+5 -1
View File
@@ -4360,9 +4360,13 @@ export async function getGuacamoleToken(
export async function getGuacamoleTokenFromHost(
hostId: number,
protocol?: "rdp" | "vnc" | "telnet",
): Promise<GuacamoleTokenResponse> {
try {
const response = await authApi.post(`/guacamole/connect-host/${hostId}`);
const response = await authApi.post(
`/guacamole/connect-host/${hostId}`,
protocol ? { protocol } : {},
);
return response.data;
} catch (error) {
throw handleApiError(error, "get guacamole token from host");
+3 -2
View File
@@ -27,6 +27,7 @@ import {
KeyRound,
LayoutDashboard,
Monitor,
MousePointerClick,
Clock,
Folder,
Pencil,
@@ -48,7 +49,7 @@ const ACTIVITY_ICONS: Record<string, React.ReactNode> = {
tunnel: <Network className="size-3.5" />,
docker: <Box className="size-3.5" />,
telnet: <MessagesSquare className="size-3.5" />,
vnc: <Monitor className="size-3.5" />,
vnc: <MousePointerClick className="size-3.5" />,
rdp: <Monitor className="size-3.5" />,
};
@@ -424,7 +425,7 @@ export function CommandPalette({
}}
className="flex items-center gap-1 px-2 h-6 rounded text-xs font-medium text-muted-foreground/70 hover:text-foreground hover:bg-muted-foreground/10 transition-colors border border-border/40"
>
<Monitor className="size-3" />
<MousePointerClick className="size-3" />
VNC
</button>
)}
+7 -1
View File
@@ -230,7 +230,13 @@ export function renderTabContent(
return (
<EmptyState icon={Monitor} messageKey="guacamole.noHostSelected" />
);
return <GuacamoleApp hostId={host.id} tabId={tab.id} />;
return (
<GuacamoleApp
hostId={host.id}
tabId={tab.id}
protocol={tab.type as "rdp" | "vnc" | "telnet"}
/>
);
case "network_graph":
return <NetworkGraphCard embedded={false} />;
+3 -2
View File
@@ -31,6 +31,7 @@ import {
Lock,
Monitor,
Network,
MousePointerClick,
Palette,
Pencil,
Plus,
@@ -220,7 +221,7 @@ function makeHostTabs(t: (key: string) => string): HostTab[] {
{
id: "vnc",
label: t("hosts.tabVnc"),
icon: <Monitor className="size-3" />,
icon: <MousePointerClick className="size-3" />,
},
{
id: "telnet",
@@ -687,7 +688,7 @@ function HostEditor({
proto: "enableVnc" as const,
label: t("hosts.tabVnc"),
desc: t("hosts.virtualNetwork"),
icon: <Monitor className="size-4" />,
icon: <MousePointerClick className="size-4" />,
portField: "vncPort" as const,
},
{
+10 -3
View File
@@ -16,6 +16,7 @@ import {
MessagesSquare,
Monitor,
MoreHorizontal,
MousePointerClick,
Network,
Pencil,
Pin,
@@ -215,11 +216,17 @@ export function HostItem({
? "bg-muted/20"
: ""
} ${isMenuOpen ? "bg-muted/40" : ""}`}
onClick={() => {
onClick={(e) => {
if (selectionMode) {
onToggleSelect?.();
return;
}
// On touch devices open the action tray instead of immediately launching a tab
if (window.matchMedia("(hover: none)").matches) {
e.stopPropagation();
onMenuOpenChange?.(!isMenuOpen);
return;
}
if (host.enableSsh) onOpenTab("terminal");
else if (host.enableRdp) onOpenTab("rdp");
else if (host.enableVnc) onOpenTab("vnc");
@@ -360,7 +367,7 @@ export function HostItem({
}}
className="flex items-center justify-center size-7 rounded text-muted-foreground/50 hover:text-foreground hover:bg-muted-foreground/10 transition-colors"
>
<Monitor className="size-3.5" />
<MousePointerClick className="size-3.5" />
</button>
)}
{host.enableTelnet && (
@@ -526,7 +533,7 @@ export function HostItem({
toast.success(t("hosts.vncUrlCopied"));
}}
>
<Monitor className="size-3.5 mr-2" />
<MousePointerClick className="size-3.5 mr-2" />
{t("hosts.copyVncUrlAction")}
</DropdownMenuItem>
)}