mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
fix: load and save plugin host settings in the editor and tighten settings row spacing
This commit is contained in:
1 parent
1a6f9ffad6
commit
3028ab1176
6 files changed
+66
-10
No files matched your search
@@ -20,6 +20,8 @@
|
||||
"webUiEndpointLabel": "Endpoint {{number}}",
|
||||
"webUiIntegration": "Web UI",
|
||||
"webUiEndpointsSection": "Endpoints",
|
||||
"webUiNoEndpoints": "No web endpoints yet. Use Add endpoint to create one.",
|
||||
"webUiEndpointsDisabled": "Turn on Web UI above to add endpoints.",
|
||||
"webUiRequirementsText": "A direct endpoint is opened by your browser, so its port must be reachable from here. An SSH tunnel is opened by Termix over this host's SSH connection instead, so the port only has to be reachable from the host.",
|
||||
"enableWebUi": "Enable web endpoints",
|
||||
"enableWebUiDesc": "Open this host's web interfaces from the sidebar.",
|
||||
|
||||
@@ -220,9 +220,17 @@ export function HostEditorWebUiSection({
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-4 py-3">
|
||||
{enableWebUi && (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-3 py-3">
|
||||
{!enableWebUi ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("hosts.webUiEndpointsDisabled")}
|
||||
</p>
|
||||
) : endpoints.length === 0 ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("hosts.webUiNoEndpoints")}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
{endpoints.map((endpoint, index) => {
|
||||
const rowError = webEndpointRowError(endpoint);
|
||||
return (
|
||||
@@ -497,7 +505,7 @@ export function HostEditorWebUiSection({
|
||||
);
|
||||
})}
|
||||
|
||||
{!tunnelAvailable && endpoints.length > 0 && (
|
||||
{!tunnelAvailable && (
|
||||
// Shown whenever tunnelling is unavailable, not only once a row
|
||||
// already asks for it: the Tunnel option renders disabled, and
|
||||
// a disabled control with no stated reason reads as the
|
||||
@@ -506,7 +514,7 @@ export function HostEditorWebUiSection({
|
||||
{t("hosts.webUiAccessTunnelUnavailable")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</SectionCard>
|
||||
|
||||
@@ -42,7 +42,9 @@ export function SettingRow({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-3 py-3 border-b border-border last:border-0">
|
||||
// In a gap-4 form stack the gap already spaces the row, so its own padding
|
||||
// and divider would double up.
|
||||
<div className="flex items-center justify-between gap-3 py-3 border-b border-border last:border-0 [.gap-4>&]:py-0 [.gap-4>&]:border-0">
|
||||
<div className="flex flex-col gap-0.5 min-w-0 flex-1">
|
||||
<div className="flex items-center gap-1.5 flex-wrap">
|
||||
<span className="text-sm font-medium leading-snug">{label}</span>
|
||||
|
||||
@@ -153,17 +153,23 @@ export function HostEditor({
|
||||
const savePluginHostSettings = async (
|
||||
hostId: number,
|
||||
values: Record<string, Record<string, unknown>> | undefined,
|
||||
) => {
|
||||
if (!values || !Number.isInteger(hostId)) return;
|
||||
): Promise<Record<string, Record<string, unknown>>> => {
|
||||
const saved: Record<string, Record<string, unknown>> = {};
|
||||
if (!values || !Number.isInteger(hostId)) return saved;
|
||||
|
||||
for (const [pluginId, fields] of Object.entries(values)) {
|
||||
if (!fields || Object.keys(fields).length === 0) continue;
|
||||
try {
|
||||
await updatePluginHostSettings(pluginId, hostId, fields);
|
||||
saved[pluginId] = await updatePluginHostSettings(
|
||||
pluginId,
|
||||
hostId,
|
||||
fields,
|
||||
);
|
||||
} catch {
|
||||
toast.error(t("settings.pluginSettingsSaveFailed"));
|
||||
}
|
||||
}
|
||||
return saved;
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
@@ -186,12 +192,20 @@ export function HostEditor({
|
||||
// After the host: a new one has no id to scope settings to until it
|
||||
// exists. A failure here must not claim the host itself failed to save.
|
||||
const pluginValues = withProtocolSettings(form.pluginSettings, protocols);
|
||||
await savePluginHostSettings(
|
||||
const savedPluginValues = await savePluginHostSettings(
|
||||
Number(saved.id),
|
||||
host
|
||||
? pluginValues
|
||||
: withNewHostDefaults(pluginValues ?? {}, hostSettingPlugins),
|
||||
);
|
||||
// The host was read back before these were written.
|
||||
saved = {
|
||||
...saved,
|
||||
pluginSettings: {
|
||||
...(saved.pluginSettings ?? {}),
|
||||
...savedPluginValues,
|
||||
},
|
||||
};
|
||||
|
||||
toast.success(host ? t("hosts.hostUpdated") : t("hosts.hostCreated"));
|
||||
onSave(saved);
|
||||
|
||||
@@ -79,6 +79,7 @@ export function sshHostToHost(h: SSHHostWithStatus): Host {
|
||||
h.sshPort ??
|
||||
(h.connectionType === "ssh" || !h.connectionType ? h.port : 22),
|
||||
protocolAuth: h.protocolAuth ?? {},
|
||||
pluginSettings: h.pluginSettings ?? {},
|
||||
jumpHosts: (parseJson<HostJumpHost[]>(h.jumpHosts) ?? []).map((j) => ({
|
||||
hostId: String(j.hostId ?? j.hostid ?? j),
|
||||
})),
|
||||
|
||||
@@ -32,4 +32,33 @@ describe("sshHostToHost", () => {
|
||||
expect(host.id).toBe("-12");
|
||||
expect(host.isShared).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps every plugin's host settings so the editor can load them", () => {
|
||||
const pluginSettings = {
|
||||
docker: { enableDocker: true, containerRuntime: "podman" },
|
||||
"tmux-monitor": { enableTmuxMonitor: true },
|
||||
};
|
||||
const host = sshHostToHost({
|
||||
id: 3,
|
||||
name: "box",
|
||||
ip: "10.0.0.3",
|
||||
port: 22,
|
||||
username: "root",
|
||||
pluginSettings,
|
||||
} as unknown as SSHHostWithStatus);
|
||||
|
||||
expect(host.pluginSettings).toEqual(pluginSettings);
|
||||
});
|
||||
|
||||
it("defaults plugin settings to an empty map", () => {
|
||||
const host = sshHostToHost({
|
||||
id: 4,
|
||||
name: "bare",
|
||||
ip: "10.0.0.4",
|
||||
port: 22,
|
||||
username: "root",
|
||||
} as SSHHostWithStatus);
|
||||
|
||||
expect(host.pluginSettings).toEqual({});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user