feat: finish the remote-desktop plugin (B14)

This commit is contained in:
LukeGus committed 2026-09-24 01:02:45 -05:00
1 parent 0af2c94983
commit 14eb558b85
215 files changed
+30029 -3921

No files matched your search

-9
View File
@@ -1,4 +1,3 @@
import type { GuacamoleConfig } from "@/types/guacamole-config";
import type { TerminalConfig } from "@/types";
export type TerminalDefaults = Partial<
@@ -20,8 +19,6 @@ export type TerminalDefaults = Partial<
>
>;
export type RemoteDesktopDefaults = Partial<GuacamoleConfig>;
function parseObject<T extends object>(value?: string | null): Partial<T> {
if (!value) return {};
try {
@@ -38,12 +35,6 @@ export function parseTerminalDefaults(value?: string | null): TerminalDefaults {
return parseObject<TerminalDefaults>(value);
}
export function parseRemoteDesktopDefaults(
value?: string | null,
): RemoteDesktopDefaults {
return parseObject<RemoteDesktopDefaults>(value);
}
// Booleans have three states in the defaults UI: unset means the host decides.
export type TriState = "inherit" | "on" | "off";
+8 -11
View File
@@ -4,12 +4,9 @@ import { websocketAuthProtocols } from "@/lib/ws-auth";
export type ConnectionOrigin = "local" | "remote";
interface OriginResolvableHost {
connectionType?: string | null;
connectionOrigin?: ConnectionOrigin | null;
}
const GUACAMOLE_CONNECTION_TYPES = new Set(["rdp", "vnc", "telnet"]);
/**
* Resolves which backend a given host's interactive connection (SSH, Docker
* console, RDP/VNC/Telnet) should dial: the desktop app's embedded local
@@ -21,16 +18,16 @@ const GUACAMOLE_CONNECTION_TYPES = new Set(["rdp", "vnc", "telnet"]);
* Everything else follows the host's own override if set, falling back to
* the desktop-wide default.
*
* RDP/VNC/Telnet are the exception to that fallback: left on Default they
* resolve to "remote" rather than following the desktop-wide setting. They
* need a guacd, which the desktop does not ship, so originating them here
* only works once the user has pointed Termix at one of their own (the
* global guacd URL setting, or a host's guacd Proxy override). Making that
* opt-in per host keeps an upgrade from moving working connections onto a
* guacd that isn't there -- see Termix-SSH/Support#1240.
* `defaultRemote` makes a host left on Default resolve to "remote" instead of
* following the desktop-wide setting. Remote desktop passes it: it needs a
* guacd, which the desktop does not ship, so originating it here only works
* once the user has pointed Termix at one of their own. Making that opt-in
* per host keeps an upgrade from moving working connections onto a guacd
* that isn't there -- see Termix-SSH/Support#1240.
*/
export async function resolveConnectionOrigin(
host: OriginResolvableHost,
options: { defaultRemote?: boolean } = {},
): Promise<ConnectionOrigin> {
if (!isElectron()) {
return "local";
@@ -38,7 +35,7 @@ export async function resolveConnectionOrigin(
if (host.connectionOrigin === "local" || host.connectionOrigin === "remote") {
return host.connectionOrigin;
}
if (GUACAMOLE_CONNECTION_TYPES.has(host.connectionType ?? "")) {
if (options.defaultRemote) {
return "remote";
}
+22 -1
View File
@@ -17,7 +17,7 @@
*/
import type { AxiosInstance } from "axios";
import { authApi } from "@/main-axios";
import { authApi, createRemoteOriginApiInstance } from "@/main-axios";
import { getBasePath } from "@/lib/base-path";
import { isElectron } from "@/lib/electron";
import { websocketAuthProtocols } from "@/lib/ws-auth";
@@ -75,6 +75,27 @@ export function createPluginApi(pluginId: string): AxiosInstance {
}) as AxiosInstance;
}
const remotePluginApis = new Map<string, AxiosInstance>();
/**
* The same client pointed at the connected remote server, for a desktop app
* host whose connection origin is "remote". Outside Electron, and for
* "local", it is the ordinary client.
*/
export function pluginApiFor(
pluginId: string,
origin: ConnectionOrigin | undefined,
local: AxiosInstance,
): AxiosInstance {
if (origin !== "remote" || !isElectron()) return local;
let client = remotePluginApis.get(pluginId);
if (!client) {
client = createRemoteOriginApiInstance(pluginApiPath(pluginId));
remotePluginApis.set(pluginId, client);
}
return client;
}
/**
* pluginWsUrl for a path a backend handed out ("/plugin-ws/<id>/<path>?q"),
* so a caller can dial a plugin's socket without naming the plugin.