mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
616 lines
22 KiB
TypeScript
616 lines
22 KiB
TypeScript
import { TERMINAL_THEMES } from "@/lib/terminal-themes";
|
|
import type { Host } from "@/types/ui-types";
|
|
import type { SSHHostData } from "@/types";
|
|
import type { HostDefaults } from "@/api/settings-api";
|
|
import type {
|
|
RemoteDesktopDefaults,
|
|
TerminalDefaults,
|
|
} from "@/lib/connection-defaults";
|
|
|
|
type HostSocks5ProxyNode = NonNullable<Host["socks5ProxyChain"]>[number];
|
|
|
|
export type HostProtocols = {
|
|
enableSsh: boolean;
|
|
enableRdp: boolean;
|
|
enableVnc: boolean;
|
|
enableTelnet: boolean;
|
|
};
|
|
|
|
export type HostAuthType = Host["authType"];
|
|
export type HostCursorStyle = NonNullable<
|
|
Host["terminalConfig"]
|
|
>["cursorStyle"];
|
|
export type HostBellStyle = NonNullable<Host["terminalConfig"]>["bellStyle"];
|
|
export type HostBackspaceMode = NonNullable<
|
|
Host["terminalConfig"]
|
|
>["backspaceMode"];
|
|
export type HostFastScrollModifier = NonNullable<
|
|
Host["terminalConfig"]
|
|
>["fastScrollModifier"];
|
|
|
|
export const terminalAppearanceKeys = [
|
|
"theme",
|
|
"cursorBlink",
|
|
"cursorStyle",
|
|
"fontSize",
|
|
"fontFamily",
|
|
"scrollback",
|
|
"letterSpacing",
|
|
"lineHeight",
|
|
"bellStyle",
|
|
"minimumContrastRatio",
|
|
"backgroundImage",
|
|
"backgroundImageOpacity",
|
|
"customThemeColors",
|
|
] as const satisfies readonly (keyof TerminalDefaults)[];
|
|
|
|
export const remoteDesktopDefaultKeys = [
|
|
"colorDepth",
|
|
"resizeMethod",
|
|
"forceLossless",
|
|
"disableAudio",
|
|
"enableWallpaper",
|
|
"enableFontSmoothing",
|
|
"enableDesktopComposition",
|
|
"enablePrinting",
|
|
"enableDrive",
|
|
"disableCopy",
|
|
"disablePaste",
|
|
] as const satisfies readonly (keyof RemoteDesktopDefaults)[];
|
|
|
|
export interface UserConnectionDefaults {
|
|
terminal: TerminalDefaults;
|
|
rdp: RemoteDesktopDefaults;
|
|
}
|
|
|
|
function hasOwn(value: object | undefined, key: PropertyKey): boolean {
|
|
return !!value && Object.prototype.hasOwnProperty.call(value, key);
|
|
}
|
|
|
|
function stripKeys<T extends Record<string, unknown>>(
|
|
value: T,
|
|
keys: readonly string[],
|
|
): Partial<T> {
|
|
const result = { ...value };
|
|
for (const key of keys) delete result[key];
|
|
return result;
|
|
}
|
|
|
|
type SnippetListItem = {
|
|
id: number;
|
|
name?: string;
|
|
title?: string;
|
|
};
|
|
type SnippetResponse = SnippetListItem[] | { snippets?: SnippetListItem[] };
|
|
|
|
/**
|
|
* Whether the Connection Origin control is meaningful for a host.
|
|
*
|
|
* Every protocol Termix can dial from either backend belongs here. RDP, VNC
|
|
* and Telnet were added once they could originate from the desktop
|
|
* (Termix-SSH/Support#1240); before that the control was gated on SSH alone,
|
|
* so a host enabling only those protocols could never reach the setting.
|
|
*/
|
|
export function connectionOriginAppliesTo(protocols: HostProtocols): boolean {
|
|
return (
|
|
protocols.enableSsh ||
|
|
protocols.enableRdp ||
|
|
protocols.enableVnc ||
|
|
protocols.enableTelnet
|
|
);
|
|
}
|
|
|
|
export function mapSnippetResponse(
|
|
res: unknown,
|
|
): { id: number; name: string }[] {
|
|
const snippetRes = res as SnippetResponse;
|
|
return (
|
|
Array.isArray(snippetRes) ? snippetRes : (snippetRes.snippets ?? [])
|
|
).map((s) => ({
|
|
id: s.id,
|
|
name: s.name ?? s.title ?? `Snippet ${s.id}`,
|
|
}));
|
|
}
|
|
|
|
export function createHostEditorForm(
|
|
host: Host | null,
|
|
defaults?: HostDefaults,
|
|
connectionDefaults?: UserConnectionDefaults,
|
|
) {
|
|
const d = host ? undefined : defaults;
|
|
const terminalConfig = {
|
|
...(connectionDefaults?.terminal ?? {}),
|
|
...(host?.terminalConfig ?? {}),
|
|
};
|
|
const remoteDefaults = host?.enableRdp ? connectionDefaults?.rdp : undefined;
|
|
const guacamoleConfig = {
|
|
...(remoteDefaults ?? {}),
|
|
...(host?.guacamoleConfig ?? {}),
|
|
};
|
|
const rawTheme = terminalConfig.theme ?? d?.theme;
|
|
const normalizedTheme =
|
|
!rawTheme ||
|
|
["Termix Dark", "Termix Light", "termixDark", "termixLight"].includes(
|
|
rawTheme,
|
|
)
|
|
? "termix"
|
|
: rawTheme === "custom" || TERMINAL_THEMES[rawTheme]
|
|
? rawTheme
|
|
: "termix";
|
|
|
|
return {
|
|
name: host?.name ?? "",
|
|
ip: host?.ip ?? "",
|
|
username: host?.username ?? (host ? "" : "root"),
|
|
sshPort: host?.sshPort ?? host?.port ?? 22,
|
|
rdpPort: host?.rdpPort ?? 3389,
|
|
vncPort: host?.vncPort ?? 5900,
|
|
telnetPort: host?.telnetPort ?? 23,
|
|
authType: host?.authType ?? "password",
|
|
useWarpgate: host?.useWarpgate ?? false,
|
|
shareSshAuth: host?.shareSshAuth ?? false,
|
|
password: host?.hasPassword ? "existing_password" : (host?.password ?? ""),
|
|
key: host?.key ?? (host?.hasKey ? "existing_key" : ""),
|
|
keyPassword: host?.hasKeyPassword
|
|
? "existing_key_password"
|
|
: (host?.keyPassword ?? ""),
|
|
keyType: host?.keyType ?? "auto",
|
|
keySubTab: "paste" as "paste" | "upload",
|
|
credentialId:
|
|
host?.credentialId != null
|
|
? String(host.credentialId)
|
|
: d?.credentialId != null
|
|
? String(d.credentialId)
|
|
: "",
|
|
vaultProfileId: host?.vaultProfileId ?? "",
|
|
overrideCredentialUsername: host?.overrideCredentialUsername ?? false,
|
|
folder: host?.folder ?? "",
|
|
parentHostId: host?.parentHostId ?? "",
|
|
tags: host?.tags ?? ([] as string[]),
|
|
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,
|
|
socks5Username: host?.socks5Username ?? d?.socks5Username ?? "",
|
|
socks5Password: host?.socks5Password ?? d?.socks5Password ?? "",
|
|
socks5ProxyMode: ((host?.socks5ProxyChain ?? []).length > 0
|
|
? "chain"
|
|
: "single") as "single" | "chain",
|
|
socks5ProxyChain: (host?.socks5ProxyChain ?? []) as HostSocks5ProxyNode[],
|
|
connectionOrigin: (host?.connectionOrigin ?? null) as
|
|
"local" | "remote" | null,
|
|
enableTerminal: host?.enableTerminal ?? true,
|
|
enableSessionLogging:
|
|
host?.enableSessionLogging ?? d?.enableSessionLogging ?? true,
|
|
enableCommandHistory:
|
|
host?.enableCommandHistory ?? d?.enableCommandHistory ?? true,
|
|
enableFileManager: host?.enableFileManager ?? false,
|
|
scpLegacy: host?.scpLegacy ?? false,
|
|
enableDocker: host?.enableDocker ?? false,
|
|
enableWebUi: host?.enableWebUi ?? false,
|
|
webUiConfig: host?.webUiConfig ?? { endpoints: [] },
|
|
dockerConfig: host?.dockerConfig ?? { runtime: "docker" as const },
|
|
enableTmuxMonitor: host?.enableTmuxMonitor ?? false,
|
|
enableTerminalToolbar: host?.enableTerminalToolbar ?? true,
|
|
enableAiAssistant: host?.enableAiAssistant ?? false,
|
|
allowSessionSharing: host?.allowSessionSharing ?? true,
|
|
enableProxmox: host?.enableProxmox ?? false,
|
|
proxmoxConfig: host?.proxmoxConfig ?? {
|
|
defaultCredentialId: null as number | null,
|
|
defaultAuthType: "password" as string,
|
|
windowsPatterns: "win, windows",
|
|
dockerPatterns: "docker",
|
|
preferredPrefixes: "10., 192.168.",
|
|
autoSyncEnabled: false,
|
|
syncIntervalMinutes: 15,
|
|
markMissingGuests: true,
|
|
},
|
|
enableProxmoxStats: host?.enableProxmoxStats ?? false,
|
|
proxmoxStatsConfig: host?.proxmoxStatsConfig ?? {
|
|
pollInterval: 60,
|
|
nodeName: null as string | null,
|
|
},
|
|
enableTunnel: host?.enableTunnel ?? false,
|
|
defaultPath: host?.defaultPath ?? "/",
|
|
forceKeyboardInteractive: host?.forceKeyboardInteractive ?? false,
|
|
inheritTerminalAppearance:
|
|
!host ||
|
|
terminalAppearanceKeys.every((key) => !hasOwn(host.terminalConfig, key)),
|
|
inheritRemoteDesktopDefaults:
|
|
!host ||
|
|
remoteDesktopDefaultKeys.every(
|
|
(key) => !hasOwn(host.guacamoleConfig, key),
|
|
),
|
|
localEcho: host?.terminalConfig?.localEcho ?? "default",
|
|
fontSize: terminalConfig.fontSize ?? d?.fontSize ?? 14,
|
|
fontFamily:
|
|
terminalConfig.fontFamily ??
|
|
d?.fontFamily ??
|
|
"Caskaydia Cove Nerd Font Mono",
|
|
theme: normalizedTheme,
|
|
cursorStyle: (terminalConfig.cursorStyle ?? d?.cursorStyle ?? "bar") as
|
|
"block" | "underline" | "bar",
|
|
cursorBlink: terminalConfig.cursorBlink ?? d?.cursorBlink ?? true,
|
|
scrollback: terminalConfig.scrollback ?? 10000,
|
|
letterSpacing: terminalConfig.letterSpacing ?? 0,
|
|
lineHeight: terminalConfig.lineHeight ?? 1.0,
|
|
bellStyle: (terminalConfig.bellStyle ?? "none") as
|
|
"none" | "sound" | "visual" | "both",
|
|
rightClickSelectsWord: host?.terminalConfig?.rightClickSelectsWord ?? false,
|
|
macOptionIsMeta: host?.terminalConfig?.macOptionIsMeta ?? false,
|
|
fastScrollModifier: (host?.terminalConfig?.fastScrollModifier ?? "alt") as
|
|
"alt" | "ctrl" | "shift",
|
|
fastScrollSensitivity: host?.terminalConfig?.fastScrollSensitivity ?? 5,
|
|
minimumContrastRatio: terminalConfig.minimumContrastRatio ?? 1,
|
|
backspaceMode: (host?.terminalConfig?.backspaceMode ?? "normal") as
|
|
"normal" | "control-h",
|
|
startupSnippetId: host?.terminalConfig?.startupSnippetId ?? null,
|
|
moshCommand: host?.terminalConfig?.moshCommand ?? "",
|
|
agentForwarding: host?.terminalConfig?.agentForwarding ?? false,
|
|
autoMosh: host?.terminalConfig?.autoMosh ?? false,
|
|
autoTmux: host?.terminalConfig?.autoTmux ?? d?.autoTmux ?? false,
|
|
sudoPasswordAutoFill: host?.terminalConfig?.sudoPasswordAutoFill ?? false,
|
|
sudoPassword: host?.hasSudoPassword
|
|
? "existing_sudo_password"
|
|
: (host?.terminalConfig?.sudoPassword ?? ""),
|
|
keepaliveInterval: host?.terminalConfig?.keepaliveInterval ?? 60,
|
|
keepaliveCountMax: host?.terminalConfig?.keepaliveCountMax ?? 5,
|
|
backgroundImage: terminalConfig.backgroundImage ?? "",
|
|
backgroundImageOpacity: terminalConfig.backgroundImageOpacity ?? 0.15,
|
|
customThemeColors: terminalConfig.customThemeColors ?? null,
|
|
allowLegacyAlgorithms: host?.terminalConfig?.allowLegacyAlgorithms ?? true,
|
|
linkClickBehavior: (host?.terminalConfig?.linkClickBehavior ??
|
|
"default") as "default" | "confirm" | "direct",
|
|
agentSocketPath: host?.terminalConfig?.agentSocketPath ?? "",
|
|
agentIdentity: host?.terminalConfig?.agentIdentity ?? "",
|
|
useSSHTitle: host?.terminalConfig?.useSSHTitle ?? false,
|
|
syntaxHighlighting: host?.terminalConfig?.syntaxHighlighting ?? true,
|
|
syntaxHighlightingOptions: {
|
|
logLevels:
|
|
host?.terminalConfig?.syntaxHighlightingOptions?.logLevels ?? true,
|
|
paths: host?.terminalConfig?.syntaxHighlightingOptions?.paths ?? true,
|
|
timestamps:
|
|
host?.terminalConfig?.syntaxHighlightingOptions?.timestamps ?? true,
|
|
ipAddresses:
|
|
host?.terminalConfig?.syntaxHighlightingOptions?.ipAddresses ?? true,
|
|
urls: host?.terminalConfig?.syntaxHighlightingOptions?.urls ?? true,
|
|
numbers: host?.terminalConfig?.syntaxHighlightingOptions?.numbers ?? true,
|
|
},
|
|
environmentVariables:
|
|
host?.terminalConfig?.environmentVariables ??
|
|
([] as { key: string; value: string }[]),
|
|
serverTunnels: host?.serverTunnels ?? ([] as Host["serverTunnels"]),
|
|
jumpHosts: host?.jumpHosts ?? ([] as { hostId: string }[]),
|
|
portKnockSequence:
|
|
host?.portKnockSequence ??
|
|
([] as { port: number; protocol: "tcp" | "udp"; delay: number }[]),
|
|
quickActions:
|
|
host?.quickActions ?? ([] as { name: string; snippetId: string }[]),
|
|
rdpCredentialId: host?.rdpCredentialId ?? "",
|
|
rdpUser: host?.rdpUser ?? "",
|
|
rdpPassword: host?.hasRdpPassword
|
|
? "existing_rdp_password"
|
|
: (host?.rdpPassword ?? ""),
|
|
domain: host?.domain ?? "",
|
|
security: host?.security ?? "",
|
|
ignoreCert: host?.ignoreCert ?? false,
|
|
vncCredentialId: host?.vncCredentialId ?? "",
|
|
vncPassword: host?.hasVncPassword
|
|
? "existing_vnc_password"
|
|
: (host?.vncPassword ?? ""),
|
|
vncUser: host?.vncUser ?? "",
|
|
telnetUser: host?.telnetUser ?? "",
|
|
telnetPassword: host?.hasTelnetPassword
|
|
? "existing_telnet_password"
|
|
: (host?.telnetPassword ?? ""),
|
|
telnetCredentialId:
|
|
host?.telnetCredentialId != null ? String(host.telnetCredentialId) : "",
|
|
rdpAuthType: (host?.rdpAuthType ??
|
|
(host?.rdpCredentialId ? "credential" : "direct")) as
|
|
"direct" | "credential" | "none",
|
|
vncAuthType: (host?.vncAuthType ??
|
|
(host?.vncCredentialId ? "credential" : "direct")) as
|
|
"direct" | "credential",
|
|
telnetAuthType: (host?.telnetAuthType ??
|
|
(host?.telnetCredentialId ? "credential" : "direct")) as
|
|
"direct" | "credential",
|
|
guacamoleConfig,
|
|
statsConfig: host?.statsConfig ?? {
|
|
statusCheckEnabled: d?.statusCheckEnabled ?? true,
|
|
statusCheckInterval: 60,
|
|
useGlobalStatusInterval: true,
|
|
metricsEnabled: d?.metricsEnabled ?? true,
|
|
metricsInterval: 30,
|
|
useGlobalMetricsInterval: true,
|
|
enabledWidgets: [
|
|
"cpu",
|
|
"memory",
|
|
"disk",
|
|
"network",
|
|
"uptime",
|
|
"system",
|
|
"login_stats",
|
|
"processes",
|
|
"ports",
|
|
"firewall",
|
|
"temperature",
|
|
],
|
|
excludedMounts: [] as string[],
|
|
monitoredMounts: [] as Array<{ path: string; label?: string }>,
|
|
},
|
|
|
|
// Host-scope plugin settings, keyed by plugin id. Loaded with the host and
|
|
// saved through the plugin's own scoped route, not in the host payload.
|
|
pluginSettings: (host?.pluginSettings ?? {}) as Record<
|
|
string,
|
|
Record<string, unknown>
|
|
>,
|
|
};
|
|
}
|
|
|
|
export type HostEditorForm = ReturnType<typeof createHostEditorForm>;
|
|
|
|
export function omitOwnerSshAuthFromSharedEdit(
|
|
payload: SSHHostData,
|
|
): SSHHostData {
|
|
const {
|
|
authType: _authType,
|
|
password: _password,
|
|
key: _key,
|
|
keyPassword: _keyPassword,
|
|
keyType: _keyType,
|
|
sudoPassword: _sudoPassword,
|
|
credentialId: _credentialId,
|
|
vaultProfileId: _vaultProfileId,
|
|
overrideCredentialUsername: _overrideCredentialUsername,
|
|
shareSshAuth: _shareSshAuth,
|
|
...editableFields
|
|
} = payload;
|
|
|
|
const terminalConfig = editableFields.terminalConfig
|
|
? { ...editableFields.terminalConfig }
|
|
: undefined;
|
|
if (terminalConfig) {
|
|
delete terminalConfig.sudoPassword;
|
|
delete terminalConfig.agentSocketPath;
|
|
}
|
|
|
|
return {
|
|
...editableFields,
|
|
terminalConfig,
|
|
} as SSHHostData;
|
|
}
|
|
|
|
export function buildHostEditorPayload(
|
|
form: HostEditorForm,
|
|
protocols: HostProtocols,
|
|
): SSHHostData {
|
|
// Only carry the auth fields that belong to the selected method so switching
|
|
// method (e.g. on a cloned host) doesn't leave a stale credentialId or key
|
|
// behind that the backend would keep resolving.
|
|
const usesCredential = form.authType === "credential";
|
|
const usesKey = form.authType === "key";
|
|
const usesPassword = form.authType === "password";
|
|
const usesAgent = form.authType === "agent";
|
|
const usesVault = form.authType === "vault";
|
|
const guacamoleConfig = form.inheritRemoteDesktopDefaults
|
|
? stripKeys(form.guacamoleConfig, remoteDesktopDefaultKeys)
|
|
: form.guacamoleConfig;
|
|
const terminalConfig = {
|
|
theme: form.theme,
|
|
cursorBlink: form.cursorBlink,
|
|
cursorStyle: form.cursorStyle,
|
|
fontSize: Number(form.fontSize),
|
|
fontFamily: form.fontFamily,
|
|
scrollback: Number(form.scrollback),
|
|
letterSpacing: Number(form.letterSpacing),
|
|
lineHeight: Number(form.lineHeight),
|
|
bellStyle: form.bellStyle,
|
|
rightClickSelectsWord: form.rightClickSelectsWord,
|
|
macOptionIsMeta: form.macOptionIsMeta,
|
|
fastScrollModifier: form.fastScrollModifier,
|
|
fastScrollSensitivity: Number(form.fastScrollSensitivity),
|
|
minimumContrastRatio: Number(form.minimumContrastRatio),
|
|
backspaceMode: form.backspaceMode,
|
|
startupSnippetId: form.startupSnippetId ?? null,
|
|
moshCommand: form.moshCommand || null,
|
|
agentForwarding: form.agentForwarding,
|
|
autoMosh: form.autoMosh,
|
|
autoTmux: form.autoTmux,
|
|
sudoPasswordAutoFill: form.sudoPasswordAutoFill,
|
|
sudoPassword:
|
|
form.sudoPassword === "existing_sudo_password"
|
|
? undefined
|
|
: form.sudoPassword || null,
|
|
keepaliveInterval: Number(form.keepaliveInterval),
|
|
keepaliveCountMax: Number(form.keepaliveCountMax),
|
|
environmentVariables: form.environmentVariables,
|
|
useSSHTitle: form.useSSHTitle,
|
|
syntaxHighlighting: form.syntaxHighlighting,
|
|
syntaxHighlightingOptions: form.syntaxHighlightingOptions,
|
|
backgroundImage: form.backgroundImage || null,
|
|
backgroundImageOpacity: Number(form.backgroundImageOpacity),
|
|
customThemeColors: form.theme === "custom" ? form.customThemeColors : null,
|
|
allowLegacyAlgorithms: form.allowLegacyAlgorithms,
|
|
linkClickBehavior:
|
|
form.linkClickBehavior !== "default" ? form.linkClickBehavior : undefined,
|
|
localEcho: form.localEcho !== "default" ? form.localEcho : undefined,
|
|
agentSocketPath: usesAgent ? form.agentSocketPath || null : null,
|
|
agentIdentity: usesAgent ? form.agentIdentity || null : null,
|
|
};
|
|
const terminalOverrides = form.inheritTerminalAppearance
|
|
? stripKeys(terminalConfig, terminalAppearanceKeys)
|
|
: terminalConfig;
|
|
|
|
return {
|
|
connectionType: protocols.enableSsh
|
|
? "ssh"
|
|
: protocols.enableRdp
|
|
? "rdp"
|
|
: protocols.enableVnc
|
|
? "vnc"
|
|
: "telnet",
|
|
name: form.name,
|
|
ip: form.ip,
|
|
port: protocols.enableSsh
|
|
? Number(form.sshPort)
|
|
: protocols.enableRdp
|
|
? Number(form.rdpPort)
|
|
: protocols.enableVnc
|
|
? Number(form.vncPort)
|
|
: Number(form.telnetPort),
|
|
username: form.username,
|
|
folder: form.folder,
|
|
parentHostId: form.parentHostId ? Number(form.parentHostId) : null,
|
|
tags: form.tags,
|
|
pin: form.pin,
|
|
authType: form.authType,
|
|
useWarpgate: form.useWarpgate,
|
|
shareSshAuth: form.shareSshAuth,
|
|
password:
|
|
usesPassword || usesKey || usesCredential
|
|
? form.password === "existing_password"
|
|
? undefined
|
|
: form.password || null
|
|
: null,
|
|
key: usesKey
|
|
? form.key === "existing_key"
|
|
? undefined
|
|
: form.key || null
|
|
: null,
|
|
keyPassword: usesKey
|
|
? form.keyPassword === "existing_key_password"
|
|
? undefined
|
|
: form.keyPassword || null
|
|
: null,
|
|
keyType: usesKey && form.keyType !== "auto" ? form.keyType : null,
|
|
credentialId:
|
|
usesCredential && form.credentialId ? Number(form.credentialId) : null,
|
|
vaultProfileId:
|
|
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,
|
|
enableTunnel: form.enableTunnel,
|
|
enableFileManager: form.enableFileManager,
|
|
scpLegacy: form.scpLegacy,
|
|
enableDocker: form.enableDocker,
|
|
dockerConfig: form.enableDocker ? form.dockerConfig : null,
|
|
enableWebUi: form.enableWebUi,
|
|
webUiConfig: form.enableWebUi ? form.webUiConfig : null,
|
|
enableTmuxMonitor: form.enableTmuxMonitor,
|
|
enableTerminalToolbar: form.enableTerminalToolbar,
|
|
enableAiAssistant: form.enableAiAssistant,
|
|
allowSessionSharing: form.allowSessionSharing,
|
|
enableProxmox: form.enableProxmox,
|
|
proxmoxConfig:
|
|
form.enableProxmox || form.proxmoxConfig?.source
|
|
? form.proxmoxConfig
|
|
: null,
|
|
enableProxmoxStats: form.enableProxmoxStats,
|
|
proxmoxStatsConfig: form.enableProxmoxStats
|
|
? form.proxmoxStatsConfig
|
|
: null,
|
|
defaultPath: form.defaultPath || "/",
|
|
useSocks5: form.useSocks5,
|
|
socks5Host:
|
|
form.socks5ProxyMode === "single" ? form.socks5Host || null : null,
|
|
socks5Port:
|
|
form.socks5ProxyMode === "single" ? form.socks5Port || null : null,
|
|
socks5Username:
|
|
form.socks5ProxyMode === "single" ? form.socks5Username || null : null,
|
|
socks5Password:
|
|
form.socks5ProxyMode === "single" ? form.socks5Password || null : null,
|
|
socks5ProxyChain:
|
|
form.socks5ProxyMode === "chain" ? form.socks5ProxyChain : null,
|
|
connectionOrigin: form.connectionOrigin,
|
|
enableSsh: protocols.enableSsh,
|
|
enableRdp: protocols.enableRdp,
|
|
enableVnc: protocols.enableVnc,
|
|
enableTelnet: protocols.enableTelnet,
|
|
sshPort: Number(form.sshPort),
|
|
rdpPort: Number(form.rdpPort),
|
|
vncPort: Number(form.vncPort),
|
|
telnetPort: Number(form.telnetPort),
|
|
forceKeyboardInteractive: form.forceKeyboardInteractive,
|
|
rdpAuthType: protocols.enableRdp ? form.rdpAuthType : null,
|
|
rdpCredentialId:
|
|
protocols.enableRdp &&
|
|
form.rdpAuthType === "credential" &&
|
|
form.rdpCredentialId
|
|
? Number(form.rdpCredentialId)
|
|
: null,
|
|
rdpUser:
|
|
protocols.enableRdp && form.rdpAuthType === "direct"
|
|
? form.rdpUser || null
|
|
: null,
|
|
rdpPassword:
|
|
protocols.enableRdp &&
|
|
form.rdpAuthType === "direct" &&
|
|
form.rdpPassword !== "existing_rdp_password"
|
|
? form.rdpPassword || null
|
|
: null,
|
|
rdpDomain: form.domain || null,
|
|
rdpSecurity: form.security || null,
|
|
rdpIgnoreCert: form.ignoreCert,
|
|
vncAuthType: protocols.enableVnc ? form.vncAuthType : null,
|
|
vncCredentialId:
|
|
protocols.enableVnc &&
|
|
form.vncAuthType === "credential" &&
|
|
form.vncCredentialId
|
|
? Number(form.vncCredentialId)
|
|
: null,
|
|
vncPassword:
|
|
protocols.enableVnc &&
|
|
form.vncAuthType === "direct" &&
|
|
form.vncPassword !== "existing_vnc_password"
|
|
? form.vncPassword || null
|
|
: null,
|
|
vncUser:
|
|
protocols.enableVnc && form.vncAuthType === "direct"
|
|
? form.vncUser || null
|
|
: null,
|
|
telnetAuthType: protocols.enableTelnet ? form.telnetAuthType : null,
|
|
telnetCredentialId:
|
|
protocols.enableTelnet &&
|
|
form.telnetAuthType === "credential" &&
|
|
form.telnetCredentialId
|
|
? Number(form.telnetCredentialId)
|
|
: null,
|
|
telnetUser:
|
|
protocols.enableTelnet && form.telnetAuthType === "direct"
|
|
? form.telnetUser || null
|
|
: null,
|
|
telnetPassword:
|
|
protocols.enableTelnet &&
|
|
form.telnetAuthType === "direct" &&
|
|
form.telnetPassword !== "existing_telnet_password"
|
|
? form.telnetPassword || null
|
|
: null,
|
|
// The editor keeps ids as strings; the API and every backend lookup take
|
|
// a number, and a string id does not compare equal on Postgres/MySQL.
|
|
jumpHosts: form.jumpHosts.map((j) => ({ hostId: Number(j.hostId) })),
|
|
portKnockSequence: form.portKnockSequence,
|
|
tunnelConnections: form.serverTunnels,
|
|
quickActions: form.quickActions.map((a) => ({
|
|
name: a.name,
|
|
snippetId: Number(a.snippetId),
|
|
})),
|
|
statsConfig: form.statsConfig,
|
|
guacamoleConfig:
|
|
(protocols.enableRdp || protocols.enableVnc || protocols.enableTelnet) &&
|
|
Object.keys(guacamoleConfig).length > 0
|
|
? guacamoleConfig
|
|
: null,
|
|
terminalConfig: protocols.enableSsh ? terminalOverrides : null,
|
|
};
|
|
}
|