feat: convert wake-on-lan into a plugin (B20)

This commit is contained in:
LukeGus committed 2026-09-24 15:49:55 -05:00
1 parent d4ec444558
commit 2ac7779eda
50 files changed
+17844 -199

No files matched your search

-9
View File
@@ -126,15 +126,6 @@ export async function updateSSHHost(
}
}
export async function wakeOnLan(hostId: number): Promise<{ success: boolean }> {
try {
const response = await sshHostApi.post(`/db/host/${hostId}/wake`);
return response.data;
} catch (error) {
throw handleApiError(error, "wake on LAN");
}
}
export async function bulkImportSSHHosts(
hosts: SSHHostData[],
overwrite = false,
+4 -6
View File
@@ -570,9 +570,6 @@
"secureShell": "Secure Shell",
"addressIp": "Address / IP",
"friendlyName": "Friendly Name",
"macAddress": "MAC Address",
"wolBroadcastAddress": "WoL Broadcast Address",
"wolBroadcastAddressDesc": "Optional directed broadcast for Docker/routed networks (e.g. 192.168.1.255). Leave empty to use 255.255.255.255.",
"folderAndAdvanced": "Folder & Advanced",
"showAdvancedSettings": "Show advanced settings",
"hideAdvancedSettings": "Hide advanced settings",
@@ -971,9 +968,6 @@
"remoteDesktopUrlCopied": "Remote Desktop URL copied",
"expandActions": "Expand actions",
"collapseActions": "Collapse actions",
"wakeOnLanAction": "Wake on LAN",
"wakeOnLanSuccess": "Magic packet sent to {{name}}",
"wakeOnLanError": "Failed to send magic packet",
"cloneHostAction": "Clone Host",
"copyAddress": "Copy Address",
"copyLink": "Copy Link",
@@ -2796,6 +2790,10 @@
"title": "Accept connections",
"consequence": "It opens a port on the Termix server."
},
"network:broadcast": {
"title": "Send network broadcasts",
"consequence": "It can send packets to devices on the local network, such as a Wake-on-LAN signal."
},
"users:read": {
"title": "See usernames and roles",
"consequence": "Never password hashes, never two factor state."
-1
View File
@@ -1340,7 +1340,6 @@ export {
getSSHHosts,
createSSHHost,
updateSSHHost,
wakeOnLan,
bulkImportSSHHosts,
importSSHConfigHosts,
discoverProxmoxGuests,
-4
View File
@@ -143,8 +143,6 @@ export function createHostEditorForm(
tagInput: "",
notes: host?.notes ?? "",
pin: host?.pin ?? false,
macAddress: host?.macAddress ?? "",
wolBroadcastAddress: host?.wolBroadcastAddress ?? "",
useSocks5: host?.useSocks5 ?? d?.useSocks5 ?? false,
socks5Host: host?.socks5Host ?? d?.socks5Host ?? "",
socks5Port: host?.socks5Port ?? d?.socks5Port ?? 1080,
@@ -418,8 +416,6 @@ export function buildHostEditorPayload(
usesVault && form.vaultProfileId ? Number(form.vaultProfileId) : null,
overrideCredentialUsername: form.overrideCredentialUsername,
notes: form.notes,
macAddress: form.macAddress || null,
wolBroadcastAddress: form.wolBroadcastAddress || null,
enableTerminal: form.enableTerminal,
enableSessionLogging: form.enableSessionLogging,
enableCommandHistory: form.enableCommandHistory,
-39
View File
@@ -190,45 +190,6 @@ export function HostEditorGeneralTab({
onChange={(e) => setField("name", e.target.value)}
/>
</div>
{protocols.enableSsh && !simpleMode && (
<div className="flex flex-col gap-1.5">
<div className="flex items-center justify-between">
<label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
{t("hosts.macAddress")}
</label>
<a
href="https://docs.termix.site/features/networking/wake-on-lan"
target="_blank"
rel="noreferrer"
className="text-[10px] text-accent-brand hover:underline"
>
{t("hosts.docsLink")}
</a>
</div>
<Input
placeholder="AA:BB:CC:DD:EE:FF"
value={form.macAddress}
onChange={(e) => setField("macAddress", e.target.value)}
/>
</div>
)}
{protocols.enableSsh && !simpleMode && form.macAddress && (
<div className="flex flex-col gap-1.5">
<label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
{t("hosts.wolBroadcastAddress")}
</label>
<Input
placeholder="192.168.1.255"
value={form.wolBroadcastAddress}
onChange={(e) =>
setField("wolBroadcastAddress", e.target.value)
}
/>
<p className="text-[10px] text-muted-foreground/60">
{t("hosts.wolBroadcastAddressDesc")}
</p>
</div>
)}
</div>
</div>
</SectionCard>
-2
View File
@@ -85,8 +85,6 @@ export function sshHostToHost(h: SSHHostWithStatus): Host {
notes: h.notes,
pin: h.pin ?? false,
sortOrder: h.sortOrder ?? null,
macAddress: h.macAddress,
wolBroadcastAddress: h.wolBroadcastAddress,
connectionOrigin: h.connectionOrigin ?? null,
enableSsh: h.enableSsh != null ? h.enableSsh : isSshHost,
enableTerminal:
+1 -27
View File
@@ -22,7 +22,6 @@ import {
Terminal,
Trash2,
Users,
Zap,
} from "lucide-react";
import {
DropdownMenu,
@@ -35,7 +34,7 @@ import {
DropdownMenuTrigger,
} from "@/components/dropdown-menu";
import { toast } from "sonner";
import { getHostPassword, wakeOnLan } from "@/main-axios";
import { getHostPassword } from "@/main-axios";
import type { Host, TabType } from "@/types/ui-types";
import type {
HostDensity,
@@ -366,16 +365,6 @@ export function HostItem({
}
}
async function handleWakeOnLan(e: MouseEvent) {
e.stopPropagation();
try {
await wakeOnLan(Number(host.id));
toast.success(t("hosts.wakeOnLanSuccess", { name: host.name }));
} catch {
toast.error(t("hosts.wakeOnLanError"));
}
}
if (query && !hostMatchesQuery(host, query)) return null;
const depthStyle =
@@ -506,15 +495,6 @@ export function HostItem({
);
return separator ? [separator, button] : button;
})}
{host.macAddress && (
<button
title={t("hosts.wakeOnLanAction")}
onClick={handleWakeOnLan}
className={trayButtonClass}
>
<Zap className="size-3.5" />
</button>
)}
</>
);
@@ -704,12 +684,6 @@ export function HostItem({
</DropdownMenuItem>
);
})}
{host.macAddress && (
<DropdownMenuItem onClick={handleWakeOnLan}>
<Zap className="size-3.5 mr-2" />
{t("hosts.wakeOnLanAction")}
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={(e) => {
-1
View File
@@ -596,7 +596,6 @@ export function SidebarTree({
tags: host.tags ?? [],
pin: host.pin ?? false,
notes: host.notes,
macAddress: host.macAddress,
// Key material is never sent to the frontend, so a cloned key-auth
// host would have authType "key" with no key — unusable. Reset to
// password so the clone is in a connectable (editable) state.
@@ -3,19 +3,6 @@ import type { SSHHostWithStatus } from "@/main-axios";
import { sshHostToHost } from "@/sidebar/HostManagerData";
describe("sshHostToHost", () => {
it("preserves the Wake-on-LAN broadcast address for editing", () => {
const host = sshHostToHost({
id: 1,
name: "server",
ip: "192.168.0.10",
port: 22,
username: "root",
wolBroadcastAddress: "192.168.0.255",
} as SSHHostWithStatus);
expect(host.wolBroadcastAddress).toBe("192.168.0.255");
});
it.each(["local", "remote"] as const)(
"preserves the %s connection origin for editing",
(connectionOrigin) => {
@@ -36,7 +36,6 @@ vi.mock("@/hooks/use-status-color-scheme", () => ({
vi.mock("@/main-axios", () => ({
getHostPassword: vi.fn(),
wakeOnLan: vi.fn(),
}));
import { HostItem } from "../../../../sidebar/tree/HostItem/HostItem";
@@ -64,7 +63,6 @@ const baseHost: Host = {
enableRdp: true,
enableVnc: true,
enableTelnet: true,
macAddress: "aa:bb:cc:dd:ee:ff",
quickActions: [],
} as unknown as Host;
@@ -140,7 +138,6 @@ describe("HostItem density parity", () => {
0,
);
expect(screen.getAllByText("hosts.shareHost").length).toBeGreaterThan(0);
expect(screen.getByText("hosts.wakeOnLanAction")).toBeTruthy();
});
it.each(["comfortable", "compact"] as const)(
@@ -177,14 +174,6 @@ describe("HostItem density parity", () => {
},
);
it.each(["comfortable", "compact"] as const)(
"exposes the wake-on-LAN button when a MAC address is set, in %s density",
(density) => {
renderHostItem(density);
expect(screen.getByTitle("hosts.wakeOnLanAction")).toBeTruthy();
},
);
it.each(["comfortable", "compact"] as const)(
"exposes the copy-password action in %s density",
(density) => {