feat: serve plugin http and websockets from one mount instead of ports

This commit is contained in:
LukeGus committed 2026-09-22 16:39:41 -05:00
1 parent c496a093ca
commit 8431570ff7
102 files changed
+2894 -2308

No files matched your search

@@ -42,7 +42,9 @@ describe("guacamole API origin", () => {
await getGuacdStatus("remote");
await getGuacamoleTokenFromHost(9, "remote", "vnc");
expect(authApiMock.get).toHaveBeenCalledWith("/guacamole/status");
expect(authApiMock.get).toHaveBeenCalledWith(
"/plugin-api/remote-desktop/status",
);
expect(authApiMock.post).toHaveBeenCalledOnce();
expect(remoteApiMock.get).not.toHaveBeenCalled();
expect(remoteApiMock.post).not.toHaveBeenCalled();
@@ -58,7 +60,9 @@ describe("guacamole API origin", () => {
expect(status.guacd.status).toBe("connected");
expect(token.token).toBe("remote-token");
expect(remoteApiMock.get).toHaveBeenCalledWith("/guacamole/status");
expect(remoteApiMock.get).toHaveBeenCalledWith(
"/plugin-api/remote-desktop/status",
);
expect(remoteApiMock.post).toHaveBeenCalledOnce();
expect(authApiMock.get).not.toHaveBeenCalled();
expect(authApiMock.post).not.toHaveBeenCalled();
@@ -81,7 +85,7 @@ describe("guacamole API origin", () => {
);
expect(remoteApiMock.post).toHaveBeenCalledWith(
"/guacamole/connect-host/41",
"/plugin-api/remote-desktop/connect-host/41",
{
protocol: "rdp",
promptedUsername: "admin",
@@ -101,12 +105,15 @@ describe("guacamole API origin", () => {
domain: "",
});
expect(authApiMock.post).toHaveBeenCalledWith("/guacamole/connect-host/9", {
protocol: "rdp",
promptedUsername: "local-admin",
promptedPassword: "secret",
promptedDomain: "",
});
expect(authApiMock.post).toHaveBeenCalledWith(
"/plugin-api/remote-desktop/connect-host/9",
{
protocol: "rdp",
promptedUsername: "local-admin",
promptedPassword: "secret",
promptedDomain: "",
},
);
});
// Support#1240: a host set to "This Device" is served by the embedded
@@ -118,9 +125,12 @@ describe("guacamole API origin", () => {
const token = await getGuacamoleTokenFromHost(9, "local", "rdp");
expect(token.token).toBe("local-token");
expect(authApiMock.post).toHaveBeenCalledWith("/guacamole/connect-host/9", {
protocol: "rdp",
});
expect(authApiMock.post).toHaveBeenCalledWith(
"/plugin-api/remote-desktop/connect-host/9",
{
protocol: "rdp",
},
);
expect(remoteApiMock.post).not.toHaveBeenCalled();
expect(resolveRemoteHostIdMock).not.toHaveBeenCalled();
});
@@ -131,7 +141,9 @@ describe("guacamole API origin", () => {
const status = await getGuacdStatus("local");
expect(status.guacd.status).toBe("disconnected");
expect(authApiMock.get).toHaveBeenCalledWith("/guacamole/status");
expect(authApiMock.get).toHaveBeenCalledWith(
"/plugin-api/remote-desktop/status",
);
expect(remoteApiMock.get).not.toHaveBeenCalled();
});
@@ -166,7 +178,7 @@ it("looks up the session on the backend that issued its token", async () => {
const signal = new AbortController().signal;
await getGuacamoleConnectionId("connect/id", "remote", signal);
expect(remoteApiMock.get).toHaveBeenCalledWith(
"/guacamole/connection/connect%2Fid",
"/plugin-api/remote-desktop/connection/connect%2Fid",
{ signal },
);
expect(authApiMock.get).not.toHaveBeenCalled();
@@ -6,63 +6,55 @@ const httpsLocation = {
host: "termix.example.com",
} as Location;
// The display socket moved off port 30008 to /plugin-ws/remote-desktop/display
// on the main backend, so every case below asserts the same one path. Dev is
// no longer a special case: Vite proxies the backend, so there is nothing to
// point at a separate port.
const ROUTE = "/plugin-ws/remote-desktop/display";
describe("buildGuacamoleWebSocketBaseUrl", () => {
it("uses the same origin guacamole websocket path in production web builds", () => {
it("uses the same origin in production web builds", () => {
expect(
buildGuacamoleWebSocketBaseUrl({
isDev: false,
isElectronApp: false,
isEmbeddedApp: false,
basePath: "",
location: httpsLocation,
}),
).toBe("wss://termix.example.com/guacamole/websocket/");
).toBe(`wss://termix.example.com${ROUTE}`);
});
it("preserves the runtime base path for proxied web deployments", () => {
expect(
buildGuacamoleWebSocketBaseUrl({
isDev: false,
isElectronApp: false,
isEmbeddedApp: false,
basePath: "/termix",
location: httpsLocation,
}),
).toBe("wss://termix.example.com/termix/guacamole/websocket/");
).toBe(`wss://termix.example.com/termix${ROUTE}`);
});
it("keeps direct local websocket access for development and embedded electron", () => {
it("targets the embedded backend for electron running locally", () => {
expect(
buildGuacamoleWebSocketBaseUrl({
isDev: true,
isElectronApp: false,
isEmbeddedApp: false,
basePath: "/termix",
location: httpsLocation,
}),
).toBe("ws://localhost:30008");
expect(
buildGuacamoleWebSocketBaseUrl({
isDev: false,
isElectronApp: true,
isEmbeddedApp: true,
basePath: "/termix",
location: httpsLocation,
}),
).toBe("ws://127.0.0.1:30008");
).toBe(`ws://127.0.0.1:30001${ROUTE}`);
});
it("uses the configured remote server URL for electron remote mode", () => {
expect(
buildGuacamoleWebSocketBaseUrl({
isDev: false,
isElectronApp: true,
isEmbeddedApp: false,
configuredServerUrl: "https://termix.example.com/termix/",
basePath: "",
location: httpsLocation,
}),
).toBe("wss://termix.example.com/termix/guacamole/websocket/");
).toBe(`wss://termix.example.com/termix${ROUTE}`);
});
});