mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
feat: plugins only see their own host settings in the browser
This commit is contained in:
1 parent
50d43e29fb
commit
fb9c168b6b
17 files changed
+488
-52
No files matched your search
@@ -42,13 +42,12 @@ export function useConsoleLook(
|
||||
appTheme: string,
|
||||
): ConsoleLook {
|
||||
const [look, setLook] = useState<ConsoleLook>(FALLBACK_CONSOLE_LOOK);
|
||||
const hostKey = host
|
||||
? `${host.id}:${JSON.stringify(host.pluginSettings ?? {})}`
|
||||
: "";
|
||||
|
||||
// The terminal's settings live with the terminal, so it resolves them from
|
||||
// the host id.
|
||||
const hostId = host?.id;
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
invokeAction("terminal.resolveTheme", { host, appTheme })
|
||||
invokeAction("terminal.resolveTheme", { hostId, appTheme })
|
||||
.then((resolved) => {
|
||||
if (active) setLook((resolved as ConsoleLook) ?? FALLBACK_CONSOLE_LOOK);
|
||||
})
|
||||
@@ -58,8 +57,7 @@ export function useConsoleLook(
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hostKey, appTheme]);
|
||||
}, [hostId, appTheme]);
|
||||
|
||||
return look;
|
||||
}
|
||||
@@ -48,6 +48,10 @@ function app(): TermixApp {
|
||||
return current;
|
||||
}
|
||||
|
||||
export function listHosts(): ReturnType<TermixApp["listHosts"]> {
|
||||
return app().listHosts();
|
||||
}
|
||||
|
||||
export function fileManagerApi(): FileManagerClient {
|
||||
return app().api as unknown as FileManagerClient;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { listHosts } from "@termix/plugin-sdk/frontend";
|
||||
import { listHosts } from "./client";
|
||||
import type { SSHHost } from "../host-types";
|
||||
|
||||
export async function getSSHHosts(): Promise<SSHHost[]> {
|
||||
|
||||
@@ -11,8 +11,12 @@ import {
|
||||
DialogFooter,
|
||||
} from "@termix/plugin-sdk/ui";
|
||||
import { Select2 } from "@termix/plugin-sdk/ui";
|
||||
import { discoverProxmoxGuestsStream, importProxmoxHosts } from "./proxmox-api";
|
||||
import { listHosts, type PluginHostRecord } from "@termix/plugin-sdk/frontend";
|
||||
import {
|
||||
discoverProxmoxGuestsStream,
|
||||
importProxmoxHosts,
|
||||
listHosts,
|
||||
} from "./proxmox-api";
|
||||
import type { PluginHostRecord } from "@termix/plugin-sdk/frontend";
|
||||
import type { ProxmoxGuest } from "./types";
|
||||
import { resolveProxmoxImportAuth } from "./proxmox-import-auth";
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ function app(): TermixApp {
|
||||
return current;
|
||||
}
|
||||
|
||||
export function listHosts(): ReturnType<TermixApp["listHosts"]> {
|
||||
return app().listHosts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a guest discovery over the server-sent event stream and reports
|
||||
* progress. Returns a function that stops listening.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ComponentType, Ref } from "react";
|
||||
import { useEffect, useState, type ComponentType, type Ref } from "react";
|
||||
import {
|
||||
Info,
|
||||
MessagesSquare,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
MousePointerClick,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
invokeAction,
|
||||
useTranslation,
|
||||
type HostEditorSectionProps,
|
||||
type PluginHostRecord,
|
||||
@@ -175,21 +176,42 @@ function ToolbarCard({
|
||||
);
|
||||
}
|
||||
|
||||
/** The Wake-on-LAN address a host keeps, the default for a session's wake packet. */
|
||||
function wakeOnLanMac(host: PluginHostRecord | undefined): string | null {
|
||||
const value = host?.pluginSettings?.["wake-on-lan"]?.macAddress;
|
||||
return typeof value === "string" && value ? value : null;
|
||||
/**
|
||||
* The Wake-on-LAN address a host keeps, the default for a session's wake
|
||||
* packet. Null while that plugin is off.
|
||||
*/
|
||||
function useWakeOnLanMac(hostId: string | undefined): string | null {
|
||||
const [mac, setMac] = useState<string | null>(null);
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
if (!hostId) {
|
||||
setMac(null);
|
||||
return;
|
||||
}
|
||||
invokeAction("wakeOnLan.macAddress", hostId)
|
||||
.then((value) => {
|
||||
if (active) setMac(typeof value === "string" && value ? value : null);
|
||||
})
|
||||
.catch(() => {
|
||||
if (active) setMac(null);
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [hostId]);
|
||||
return mac;
|
||||
}
|
||||
|
||||
function RdpSection(props: HostEditorSectionProps) {
|
||||
const { form, setField, setGuacField } = remoteDesktopForm(props);
|
||||
const macAddress = useWakeOnLanMac(props.host?.id);
|
||||
return (
|
||||
<>
|
||||
<HostEditorRdpTab
|
||||
form={form}
|
||||
setField={setField}
|
||||
setGuacField={setGuacField}
|
||||
host={{ macAddress: wakeOnLanMac(props.host) }}
|
||||
host={{ macAddress }}
|
||||
credentials={props.credentials as never}
|
||||
/>
|
||||
<ToolbarCard form={form} setField={setField} />
|
||||
@@ -200,13 +222,14 @@ function RdpSection(props: HostEditorSectionProps) {
|
||||
|
||||
function VncSection(props: HostEditorSectionProps) {
|
||||
const { form, setField, setGuacField } = remoteDesktopForm(props);
|
||||
const macAddress = useWakeOnLanMac(props.host?.id);
|
||||
return (
|
||||
<>
|
||||
<HostEditorVncTab
|
||||
form={form}
|
||||
setField={setField}
|
||||
setGuacField={setGuacField}
|
||||
host={{ macAddress: wakeOnLanMac(props.host) }}
|
||||
host={{ macAddress }}
|
||||
credentials={props.credentials as never}
|
||||
/>
|
||||
<ToolbarCard form={form} setField={setField} />
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
SquareTerminal,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { listHosts } from "@termix/plugin-sdk/frontend";
|
||||
import type {
|
||||
PanelProps,
|
||||
PluginHostRecord,
|
||||
@@ -309,9 +308,11 @@ export function activate(app: TermixApp): void {
|
||||
);
|
||||
let host: PluginHostRecord | null | undefined = request.host;
|
||||
if (!host && request.hostId !== undefined) {
|
||||
host = (await listHosts().catch(() => [])).find(
|
||||
(entry) => String(entry.id) === String(request.hostId),
|
||||
);
|
||||
host =
|
||||
app.getHost(request.hostId) ??
|
||||
(await app.listHosts().catch(() => [])).find(
|
||||
(entry) => String(entry.id) === String(request.hostId),
|
||||
);
|
||||
}
|
||||
return resolveTerminalLook({ ...request, host }, settings?.user);
|
||||
}) as never);
|
||||
|
||||
@@ -5,11 +5,19 @@ import type {
|
||||
TermixApp,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
|
||||
export function macAddressOf(
|
||||
host: Pick<PluginHostRecord, "pluginSettings"> | null | undefined,
|
||||
): string | null {
|
||||
const macAddress = host?.pluginSettings?.["wake-on-lan"]?.macAddress;
|
||||
return typeof macAddress === "string" && macAddress.trim()
|
||||
? macAddress.trim()
|
||||
: null;
|
||||
}
|
||||
|
||||
export function hasMacAddress(
|
||||
host: Pick<PluginHostRecord, "pluginSettings">,
|
||||
): boolean {
|
||||
const macAddress = host.pluginSettings?.["wake-on-lan"]?.macAddress;
|
||||
return typeof macAddress === "string" && macAddress.trim().length > 0;
|
||||
return macAddressOf(host) !== null;
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown, fallback: string): string {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Zap } from "lucide-react";
|
||||
import type { TermixApp } from "@termix/plugin-sdk/frontend";
|
||||
import { hasMacAddress, wakeHost } from "./host-action.js";
|
||||
import { hasMacAddress, macAddressOf, wakeHost } from "./host-action.js";
|
||||
|
||||
export function activate(app: TermixApp): void {
|
||||
app.registerHostAction({
|
||||
@@ -12,4 +12,8 @@ export function activate(app: TermixApp): void {
|
||||
when: (host) => hasMacAddress(host),
|
||||
run: (host) => wakeHost(app.api, app.t, String(host.id)),
|
||||
});
|
||||
|
||||
// A saved host's MAC address, for plugins that send their own wake packet.
|
||||
app.registerAction("wakeOnLan.macAddress", ((hostId: string | number) =>
|
||||
macAddressOf(app.getHost(hostId))) as never);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { toast } from "sonner";
|
||||
import type { PluginApiClient } from "@termix/plugin-sdk/frontend";
|
||||
import { hasMacAddress, wakeHost } from "../../src/frontend/host-action.js";
|
||||
import {
|
||||
hasMacAddress,
|
||||
macAddressOf,
|
||||
wakeHost,
|
||||
} from "../../src/frontend/host-action.js";
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: { success: vi.fn(), error: vi.fn() },
|
||||
@@ -24,6 +28,26 @@ function stubApi(overrides: Partial<PluginApiClient> = {}): PluginApiClient {
|
||||
|
||||
const t = (key: string) => key;
|
||||
|
||||
describe("macAddressOf", () => {
|
||||
it("returns the trimmed MAC address a host keeps", () => {
|
||||
expect(
|
||||
macAddressOf({
|
||||
pluginSettings: {
|
||||
"wake-on-lan": { macAddress: " aa:bb:cc:dd:ee:ff " },
|
||||
},
|
||||
}),
|
||||
).toBe("aa:bb:cc:dd:ee:ff");
|
||||
});
|
||||
|
||||
it("is null without a host or an address", () => {
|
||||
expect(macAddressOf(undefined)).toBeNull();
|
||||
expect(macAddressOf({ pluginSettings: {} })).toBeNull();
|
||||
expect(
|
||||
macAddressOf({ pluginSettings: { "wake-on-lan": { macAddress: " " } } }),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasMacAddress", () => {
|
||||
it("is true when the host's wake-on-lan settings carry a MAC address", () => {
|
||||
expect(
|
||||
|
||||
Reference in new issue
Block a user