* Guacd, Docker-Compose, RDP (#475)

* fix select edit host but not update view (#438)

* fix: Checksum issue with chocolatey

* fix: Remove homebrew old stuff

* Add Korean translation (#439)

Co-authored-by: 송준우 <2484@coreit.co.kr>

* feat: Automate flatpak

* fix: Add imagemagik to electron builder to resolve build error

* fix: Build error with runtime repo flag

* fix: Flatpak runtime error and install freedesktop ver warning

* fix: Flatpak runtime error and install freedesktop ver warning

* feat: Re-add homebrew cask and move scripts to backend

* fix: No sandbox flag issue

* fix: Change name for electron macos cask output

* fix: Sandbox error with Linux

* fix: Remove comming soon for app stores in readme

* Adding Comment at the end of the public_key on the host on deploy (#440)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* -Add New Interface for Credential DB
-Add Credential Name as a comment into the server authorized_key file

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Sudo auto fill password (#441)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Feature Sudo password auto-fill;

* Fix locale json shema;

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Added Italian Language; (#445)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Added Italian Language;

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Auto collapse snippet folders (#448)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* feat: Add collapsable snippets (customizable in user profile)

* Translations (#447)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Added Italian Language;

* Fix translations;

Removed duplicate keys, synchronised other languages using English as the source, translated added keys, fixed inaccurate translations.

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Remove PTY-level keepalive (#449)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Remove PTY-level keepalive to prevent unwanted terminal output; use SSH-level keepalive instead

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* feat: add Guacamole support for RDP, VNC, and Telnet connections

- Implemented WebSocket support for Guacamole in Nginx configuration.
- Added REST API endpoints for generating connection tokens and checking guacd status.
- Created Guacamole server using guacamole-lite for handling connections.
- Developed frontend components for testing RDP/VNC connections and displaying the remote session.
- Updated package dependencies to include guacamole-common-js and guacamole-lite.
- Enhanced logging for Guacamole operations.

* feat: enhance Guacamole support with RDP and VNC connection settings and UI updates

* feat: Seperate server stats and tunnel management (improved both UI's) then started initial docker implementation

* fix: finalize adding docker to db

* fix: merge syntax errors

* feat: implement mouse coordinate adjustment based on scale factor in GuacamoleDisplay

* feat: add TypeScript definitions for guacamole-common-js module

* feat: enhance Mouse.State constructor to accept optional parameters and object destructuring

* feat: Add support for RDP and VNC connections in SSH host management

- Introduced connectionType field to differentiate between SSH, RDP, VNC, and Telnet in host data structures.
- Updated backend routes to handle RDP/VNC specific fields: domain, security, and ignoreCert.
- Enhanced the HostManagerEditor to include RDP/VNC specific settings and authentication options.
- Implemented token retrieval for RDP/VNC connections using Guacamole API.
- Updated UI components to reflect connection type changes and provide appropriate connection buttons.
- Removed the GuacamoleTestDialog component as its functionality is integrated into the HostManagerEditor.
- Adjusted the TopNavbar and Host components to accommodate new connection types and their respective actions.

* feat: Enhance Guacamole integration with extended configuration options

- Added detailed Guacamole configuration interface for RDP/VNC/Telnet connections, including display, audio, performance, and session settings.
- Implemented logging for token requests and received options for better debugging.
- Updated HostManagerEditor to support new Guacamole configuration fields with validation and default values.
- Integrated Guacamole configuration parsing in HostManagerViewer and Host components.
- Enhanced API requests to include extended Guacamole configuration parameters in the token request.
- Refactored code to convert camelCase configuration keys to kebab-case for compatibility with Guacamole API.

* feat: merge guacd into 2.0.0 and improve UI for host manager and made general bug fixes

---------

Co-authored-by: Tran Trung Kien <kientt13.7@gmail.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>
Co-authored-by: junu <bigdwarf_@naver.com>
Co-authored-by: 송준우 <2484@coreit.co.kr>
Co-authored-by: SlimGary <trash.slim@gmail.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: Nunzio Marfè <nunzio.marfe@protonmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: rename api routes and files

* feat: improve guacd ui/backend

* feat: improve guacd ui/backend

* fix: state persistance issues causing refresh

* feat: improge guacd connections, fixed telnet not opening, and improved general guacd integration

* feat: continue improving integration also with bug fixes

* Merge 2.0.0 with 2.0.0 that includes bug fixes (#620)

* Guacd, Docker-Compose, RDP (#475)

* fix select edit host but not update view (#438)

* fix: Checksum issue with chocolatey

* fix: Remove homebrew old stuff

* Add Korean translation (#439)

Co-authored-by: 송준우 <2484@coreit.co.kr>

* feat: Automate flatpak

* fix: Add imagemagik to electron builder to resolve build error

* fix: Build error with runtime repo flag

* fix: Flatpak runtime error and install freedesktop ver warning

* fix: Flatpak runtime error and install freedesktop ver warning

* feat: Re-add homebrew cask and move scripts to backend

* fix: No sandbox flag issue

* fix: Change name for electron macos cask output

* fix: Sandbox error with Linux

* fix: Remove comming soon for app stores in readme

* Adding Comment at the end of the public_key on the host on deploy (#440)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* -Add New Interface for Credential DB
-Add Credential Name as a comment into the server authorized_key file

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Sudo auto fill password (#441)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Feature Sudo password auto-fill;

* Fix locale json shema;

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Added Italian Language; (#445)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Added Italian Language;

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Auto collapse snippet folders (#448)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* feat: Add collapsable snippets (customizable in user profile)

* Translations (#447)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Added Italian Language;

* Fix translations;

Removed duplicate keys, synchronised other languages using English as the source, translated added keys, fixed inaccurate translations.

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* Remove PTY-level keepalive (#449)

* Add termix.rb Cask file

* Update Termix to version 1.9.0 with new checksum

* Update README to remove 'coming soon' notes

* Remove PTY-level keepalive to prevent unwanted terminal output; use SSH-level keepalive instead

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* feat: add Guacamole support for RDP, VNC, and Telnet connections

- Implemented WebSocket support for Guacamole in Nginx configuration.
- Added REST API endpoints for generating connection tokens and checking guacd status.
- Created Guacamole server using guacamole-lite for handling connections.
- Developed frontend components for testing RDP/VNC connections and displaying the remote session.
- Updated package dependencies to include guacamole-common-js and guacamole-lite.
- Enhanced logging for Guacamole operations.

* feat: enhance Guacamole support with RDP and VNC connection settings and UI updates

* feat: Seperate server stats and tunnel management (improved both UI's) then started initial docker implementation

* fix: finalize adding docker to db

* fix: merge syntax errors

* feat: implement mouse coordinate adjustment based on scale factor in GuacamoleDisplay

* feat: add TypeScript definitions for guacamole-common-js module

* feat: enhance Mouse.State constructor to accept optional parameters and object destructuring

* feat: Add support for RDP and VNC connections in SSH host management

- Introduced connectionType field to differentiate between SSH, RDP, VNC, and Telnet in host data structures.
- Updated backend routes to handle RDP/VNC specific fields: domain, security, and ignoreCert.
- Enhanced the HostManagerEditor to include RDP/VNC specific settings and authentication options.
- Implemented token retrieval for RDP/VNC connections using Guacamole API.
- Updated UI components to reflect connection type changes and provide appropriate connection buttons.
- Removed the GuacamoleTestDialog component as its functionality is integrated into the HostManagerEditor.
- Adjusted the TopNavbar and Host components to accommodate new connection types and their respective actions.

* feat: Enhance Guacamole integration with extended configuration options

- Added detailed Guacamole configuration interface for RDP/VNC/Telnet connections, including display, audio, performance, and session settings.
- Implemented logging for token requests and received options for better debugging.
- Updated HostManagerEditor to support new Guacamole configuration fields with validation and default values.
- Integrated Guacamole configuration parsing in HostManagerViewer and Host components.
- Enhanced API requests to include extended Guacamole configuration parameters in the token request.
- Refactored code to convert camelCase configuration keys to kebab-case for compatibility with Guacamole API.

* feat: merge guacd into 2.0.0 and improve UI for host manager and made general bug fixes

---------

Co-authored-by: Tran Trung Kien <kientt13.7@gmail.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>
Co-authored-by: junu <bigdwarf_@naver.com>
Co-authored-by: 송준우 <2484@coreit.co.kr>
Co-authored-by: SlimGary <trash.slim@gmail.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: Nunzio Marfè <nunzio.marfe@protonmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: rename api routes and files

* feat: improve guacd ui/backend

* feat: improve guacd ui/backend

* fix: state persistance issues causing refresh

* feat: improge guacd connections, fixed telnet not opening, and improved general guacd integration

* feat: continue improving integration also with bug fixes

---------

Co-authored-by: Wesley Reid <starhound@lostsouls.org>
Co-authored-by: Tran Trung Kien <kientt13.7@gmail.com>
Co-authored-by: junu <bigdwarf_@naver.com>
Co-authored-by: 송준우 <2484@coreit.co.kr>
Co-authored-by: SlimGary <trash.slim@gmail.com>
Co-authored-by: Nunzio Marfè <nunzio.marfe@protonmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: allow customizing guacd backened url

* fix: ssh route mistmatching and guacamole url not changing

* chore: increment ver

* feat: change default to work with default compose, added splits creen support, updated readmes

* fix: linux app not starting due to better sqlite isuses, improved copy/paste system so no context menu, added oidc remember me toggle, improved OS detection for sessions, flatpak invalid key, and sharing hosts with other users errors

* fix: global settings not setting

* chore: update compose

* feat: improve the global status input

* chore: cleanup files

* chore: update export/improt with new host fields

* fix: file manager and docker not loading properly

---------

Co-authored-by: Wesley Reid <starhound@lostsouls.org>
Co-authored-by: Tran Trung Kien <kientt13.7@gmail.com>
Co-authored-by: junu <bigdwarf_@naver.com>
Co-authored-by: 송준우 <2484@coreit.co.kr>
Co-authored-by: SlimGary <trash.slim@gmail.com>
Co-authored-by: Nunzio Marfè <nunzio.marfe@protonmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Luke Gustafson
2026-03-14 20:05:05 -05:00
committed by GitHub
parent a255a08903
commit e9e30cd318
85 changed files with 7053 additions and 2286 deletions
@@ -53,6 +53,7 @@ import {
revokeHostAccess,
getSSHHostById,
notifyHostCreatedOrUpdated,
getGuacamoleSettings,
type Role,
type AccessRecord,
} from "@/ui/main-axios.ts";
@@ -130,7 +131,9 @@ import { HostDockerTab } from "./tabs/HostDockerTab";
import { HostTunnelTab } from "./tabs/HostTunnelTab";
import { HostFileManagerTab } from "./tabs/HostFileManagerTab";
import { HostStatisticsTab } from "./tabs/HostStatisticsTab";
import { HostStatusTab } from "./tabs/HostStatusTab";
import { HostSharingTab } from "./tabs/HostSharingTab";
import { HostRemoteDesktopTab } from "./tabs/HostRemoteDesktopTab";
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
interface User {
@@ -176,11 +179,18 @@ export function HostManagerEditor({
const [isSubmitting, setIsSubmitting] = useState(false);
const [activeTab, setActiveTab] = useState("general");
const [formError, setFormError] = useState<string | null>(null);
const [guacEnabled, setGuacEnabled] = useState(true);
useEffect(() => {
setFormError(null);
}, [activeTab]);
useEffect(() => {
getGuacamoleSettings()
.then((data) => setGuacEnabled(data.enabled))
.catch(() => {});
}, []);
const [statusIntervalUnit, setStatusIntervalUnit] = useState<
"seconds" | "minutes"
>("seconds");
@@ -265,10 +275,11 @@ export function HostManagerEditor({
const formSchema = z
.object({
connectionType: z.enum(["ssh", "rdp", "vnc", "telnet"]).default("ssh"),
name: z.string().optional(),
ip: z.string().min(1),
ip: z.string().min(1, t("hosts.ipRequired", "IP address is required")),
port: z.coerce.number().min(1).max(65535),
username: z.string().min(1),
username: z.string().optional(),
folder: z.string().optional(),
tags: z.array(z.string().min(1)).default([]),
pin: z.boolean().default(false),
@@ -350,6 +361,7 @@ export function HostManagerEditor({
metricsEnabled: z.boolean().default(true),
metricsInterval: z.number().min(5).max(3600).default(30),
useGlobalMetricsInterval: z.boolean().default(true),
disableTcpPing: z.boolean().default(false),
})
.default({
enabledWidgets: [
@@ -369,6 +381,7 @@ export function HostManagerEditor({
metricsEnabled: true,
metricsInterval: 30,
useGlobalMetricsInterval: true,
disableTcpPing: false,
}),
terminalConfig: z
.object({
@@ -436,6 +449,10 @@ export function HostManagerEditor({
)
.optional(),
enableDocker: z.boolean().default(false),
domain: z.string().optional(),
security: z.string().optional(),
ignoreCert: z.boolean().default(true),
guacamoleConfig: z.record(z.string(), z.unknown()).optional(),
showTerminalInSidebar: z.boolean().default(true),
showFileManagerInSidebar: z.boolean().default(false),
showTunnelInSidebar: z.boolean().default(false),
@@ -443,6 +460,20 @@ export function HostManagerEditor({
showServerStatsInSidebar: z.boolean().default(false),
})
.superRefine((data, ctx) => {
if (data.connectionType !== "ssh") {
return;
}
if (!data.username || data.username.trim() === "") {
if (data.authType !== "credential") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t("hosts.usernameRequired", "Username is required"),
path: ["username"],
});
}
}
if (data.authType === "none") {
return;
}
@@ -458,25 +489,27 @@ export function HostManagerEditor({
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t("hosts.passwordRequired"),
message: t("hosts.passwordRequired", "Password is required"),
path: ["password"],
});
}
} else if (data.authType === "key") {
if (
!data.key ||
(typeof data.key === "string" && data.key.trim() === "")
(typeof data.key === "string" &&
data.key.trim() === "" &&
data.key !== "existing_key")
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t("hosts.sshKeyRequired"),
message: t("hosts.sshKeyRequired", "SSH key is required"),
path: ["key"],
});
}
if (!data.keyType) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t("hosts.keyTypeRequired"),
message: t("hosts.keyTypeRequired", "Key type is required"),
path: ["keyType"],
});
}
@@ -484,7 +517,7 @@ export function HostManagerEditor({
if (!data.credentialId) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t("hosts.credentialRequired"),
message: t("hosts.credentialRequired", "Credential is required"),
path: ["credentialId"],
});
}
@@ -510,6 +543,7 @@ export function HostManagerEditor({
resolver: zodResolver(formSchema) as any,
mode: "all",
defaultValues: {
connectionType: "ssh" as const,
name: "",
ip: "",
port: 22,
@@ -547,32 +581,80 @@ export function HostManagerEditor({
socks5Password: "",
socks5ProxyChain: [],
enableDocker: false,
domain: "",
security: "any",
ignoreCert: true,
guacamoleConfig: {},
},
});
const watchedFields = form.watch();
const formState = form.formState;
const watchedConnectionType = form.watch("connectionType") || "ssh";
const prevConnectionTypeRef = useRef<string>("ssh");
useEffect(() => {
const prev = prevConnectionTypeRef.current;
const current = watchedConnectionType;
if (prev === current) return;
prevConnectionTypeRef.current = current;
const portDefaults: Record<string, number> = {
ssh: 22,
rdp: 3389,
vnc: 5900,
telnet: 23,
};
const currentPort = form.getValues("port");
const oldDefault = portDefaults[prev] || 22;
if (currentPort === oldDefault) {
form.setValue("port", portDefaults[current] || 22);
}
if (current !== "ssh") {
const currentStatsConfig = form.getValues("statsConfig");
form.setValue("statsConfig", {
...currentStatsConfig,
metricsEnabled: false,
disableTcpPing: false,
});
}
if (activeTab !== "general" && current !== "ssh") {
setActiveTab("general");
}
}, [watchedConnectionType]);
const isFormValid = React.useMemo(() => {
const values = form.getValues();
const errors = formState.errors;
if (!values.ip || !values.username || values.username.trim() === "")
if (!watchedFields.ip) return false;
if (watchedFields.connectionType !== "ssh") {
const port = Number(watchedFields.port);
return !errors.ip && port >= 1 && port <= 65535;
}
if (!watchedFields.username || watchedFields.username.trim() === "")
return false;
if (authTab === "password") {
return !!(values.password && values.password.trim() !== "");
if (!watchedFields.password || watchedFields.password.trim() === "")
return false;
} else if (authTab === "key") {
return !!(values.key && values.keyType);
if (!watchedFields.key || !watchedFields.keyType) return false;
} else if (authTab === "credential") {
return !!values.credentialId;
if (!watchedFields.credentialId) return false;
} else if (authTab === "none") {
return true;
// No auth required
} else if (authTab === "opkssh") {
return true;
// No auth required
} else {
return false;
}
return false;
}, [watchedFields, authTab]);
return Object.keys(errors).length === 0;
}, [watchedFields, authTab, formState.errors]);
useEffect(() => {
const updateAuthFields = async () => {
@@ -605,7 +687,10 @@ export function HostManagerEditor({
}
}
} else if (authTab === "none") {
form.setValue("password", "", { shouldValidate: true });
const connectionType = form.getValues("connectionType");
if (connectionType === "ssh") {
form.setValue("password", "", { shouldValidate: true });
}
form.setValue("key", null, { shouldValidate: true });
form.setValue("keyPassword", "", { shouldValidate: true });
form.setValue("keyType", "auto", { shouldValidate: true });
@@ -628,14 +713,16 @@ export function HostManagerEditor({
useEffect(() => {
if (editingHost) {
const cleanedHost = { ...editingHost };
if (cleanedHost.credentialId && cleanedHost.key) {
cleanedHost.key = undefined;
cleanedHost.keyPassword = undefined;
cleanedHost.keyType = undefined;
} else if (cleanedHost.credentialId && cleanedHost.password) {
cleanedHost.password = undefined;
} else if (cleanedHost.key && cleanedHost.password) {
cleanedHost.password = undefined;
if ((cleanedHost as any).connectionType === "ssh") {
if (cleanedHost.credentialId && cleanedHost.key) {
cleanedHost.key = undefined;
cleanedHost.keyPassword = undefined;
cleanedHost.keyType = undefined;
} else if (cleanedHost.credentialId && cleanedHost.password) {
cleanedHost.password = undefined;
} else if (cleanedHost.key && cleanedHost.password) {
cleanedHost.password = undefined;
}
}
const defaultAuthType = (cleanedHost.authType ||
@@ -669,6 +756,7 @@ export function HostManagerEditor({
parsedStatsConfig = { ...DEFAULT_STATS_CONFIG, ...parsedStatsConfig };
const formData: Partial<FormData> = {
connectionType: (cleanedHost as any).connectionType || "ssh",
name: cleanedHost.name || "",
ip: cleanedHost.ip || "",
port: cleanedHost.port || 22,
@@ -734,6 +822,21 @@ export function HostManagerEditor({
? cleanedHost.socks5ProxyChain
: [],
enableDocker: Boolean(cleanedHost.enableDocker),
domain: (cleanedHost as any).domain || "",
security: (cleanedHost as any).security || "any",
ignoreCert: (cleanedHost as any).ignoreCert ?? true,
guacamoleConfig: (() => {
const cfg = (cleanedHost as any).guacamoleConfig;
if (!cfg) return {};
if (typeof cfg === "string") {
try {
return JSON.parse(cfg);
} catch {
return {};
}
}
return cfg;
})(),
showTerminalInSidebar: cleanedHost.showTerminalInSidebar ?? true,
showFileManagerInSidebar: cleanedHost.showFileManagerInSidebar ?? false,
showTunnelInSidebar: cleanedHost.showTunnelInSidebar ?? false,
@@ -750,7 +853,11 @@ export function HostManagerEditor({
setProxyMode("single");
}
if (defaultAuthType === "password") {
if (cleanedHost.connectionType !== "ssh") {
if (cleanedHost.password) {
formData.password = cleanedHost.password;
}
} else if (defaultAuthType === "password") {
formData.password = cleanedHost.password || "";
} else if (defaultAuthType === "key") {
formData.key = editingHost.id ? "existing_key" : editingHost.key;
@@ -774,6 +881,7 @@ export function HostManagerEditor({
} else {
setAuthTab("password");
const defaultFormData: Partial<FormData> = {
connectionType: "ssh" as const,
name: "",
ip: "",
port: 22,
@@ -799,6 +907,10 @@ export function HostManagerEditor({
terminalConfig: DEFAULT_TERMINAL_CONFIG,
forceKeyboardInteractive: false,
enableDocker: false,
domain: "",
security: "any",
ignoreCert: true,
guacamoleConfig: {},
showTerminalInSidebar: true,
showFileManagerInSidebar: false,
showTunnelInSidebar: false,
@@ -834,23 +946,46 @@ export function HostManagerEditor({
...data,
};
if (
data.terminalConfig?.sudoPasswordAutoFill &&
data.terminalConfig?.sudoPassword
) {
submitData.sudoPassword = data.terminalConfig.sudoPassword;
}
(submitData as any).connectionType = data.connectionType;
(submitData as any).domain = data.domain;
(submitData as any).security = data.security;
(submitData as any).ignoreCert = data.ignoreCert;
(submitData as any).guacamoleConfig = data.guacamoleConfig;
if (data.authType !== "credential") {
submitData.credentialId = undefined;
}
if (data.authType !== "password") {
submitData.password = undefined;
}
if (data.authType !== "key") {
if (data.connectionType !== "ssh") {
submitData.authType = "none";
submitData.key = undefined;
submitData.keyPassword = undefined;
submitData.keyType = undefined;
submitData.credentialId = undefined;
submitData.tunnelConnections = [];
submitData.jumpHosts = [];
(submitData as any).useSocks5 = false;
(submitData as any).socks5ProxyChain = [];
submitData.forceKeyboardInteractive = false;
submitData.enableTunnel = false;
submitData.enableFileManager = false;
submitData.enableDocker = false;
submitData.enableTerminal = true;
} else {
if (
data.terminalConfig?.sudoPasswordAutoFill &&
data.terminalConfig?.sudoPassword
) {
submitData.sudoPassword = data.terminalConfig.sudoPassword;
}
if (data.authType !== "credential") {
submitData.credentialId = undefined;
}
if (data.authType !== "password") {
submitData.password = undefined;
}
if (data.authType !== "key") {
submitData.key = undefined;
submitData.keyPassword = undefined;
submitData.keyType = undefined;
}
}
if (data.authType === "key") {
@@ -955,6 +1090,11 @@ export function HostManagerEditor({
enableTerminal: "terminal",
terminalConfig: "terminal",
enableDocker: "docker",
domain: "general",
security: "general",
ignoreCert: "general",
guacamoleConfig: "remote_desktop",
connectionType: "general",
enableTunnel: "tunnel",
tunnelConnections: "tunnel",
enableFileManager: "file_manager",
@@ -1194,6 +1334,50 @@ export function HostManagerEditor({
: t("hosts.addHost")}
</h3>
</div>
{guacEnabled && (
<FormField
control={form.control}
name="connectionType"
render={({ field }) => (
<FormItem className="mb-4">
<FormControl>
<Tabs
value={field.value || "ssh"}
onValueChange={field.onChange}
className="w-full"
>
<TabsList className="bg-button border border-edge-medium">
<TabsTrigger
value="ssh"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.ssh")}
</TabsTrigger>
<TabsTrigger
value="rdp"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.rdp")}
</TabsTrigger>
<TabsTrigger
value="vnc"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.vnc")}
</TabsTrigger>
<TabsTrigger
value="telnet"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.telnet")}
</TabsTrigger>
</TabsList>
</Tabs>
</FormControl>
</FormItem>
)}
/>
)}
<Tabs
value={activeTab}
onValueChange={setActiveTab}
@@ -1206,45 +1390,63 @@ export function HostManagerEditor({
>
{t("hosts.general")}
</TabsTrigger>
<TabsTrigger
value="terminal"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.terminal")}
</TabsTrigger>
<TabsTrigger
value="docker"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
Docker
</TabsTrigger>
<TabsTrigger
value="tunnel"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.tunnel")}
</TabsTrigger>
<TabsTrigger
value="file_manager"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.fileManager")}
</TabsTrigger>
{watchedConnectionType === "ssh" && (
<>
<TabsTrigger
value="terminal"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.terminal")}
</TabsTrigger>
<TabsTrigger
value="docker"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
Docker
</TabsTrigger>
<TabsTrigger
value="tunnel"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.tunnel")}
</TabsTrigger>
<TabsTrigger
value="file_manager"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.fileManager")}
</TabsTrigger>
</>
)}
<TabsTrigger
value="statistics"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.statistics")}
{watchedConnectionType === "ssh"
? t("hosts.statistics")
: t("hosts.status")}
</TabsTrigger>
{!editingHost?.isShared && (
<TabsTrigger value="sharing">
{t("rbac.sharing")}
{watchedConnectionType !== "ssh" && (
<TabsTrigger
value="remote_desktop"
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
>
{t("hosts.remoteDesktop")}
</TabsTrigger>
)}
{watchedConnectionType === "ssh" &&
!editingHost?.isShared && (
<TabsTrigger value="sharing">
{t("rbac.sharing")}
</TabsTrigger>
)}
</TabsList>
<TabsContent value="general" className="pt-2">
<HostGeneralTab
form={form}
connectionType={
watchedConnectionType as "ssh" | "rdp" | "vnc" | "telnet"
}
authTab={authTab}
setAuthTab={setAuthTab}
keyInputMethod={keyInputMethod}
@@ -1273,44 +1475,70 @@ export function HostManagerEditor({
t={t}
/>
</TabsContent>
<TabsContent value="terminal" className="space-y-1">
<HostTerminalTab form={form} snippets={snippets} t={t} />
</TabsContent>
<TabsContent value="docker" className="space-y-4">
<HostDockerTab form={form} t={t} />
</TabsContent>
<TabsContent value="tunnel">
<HostTunnelTab
form={form}
sshConfigDropdownOpen={sshConfigDropdownOpen}
setSshConfigDropdownOpen={setSshConfigDropdownOpen}
sshConfigInputRefs={sshConfigInputRefs}
sshConfigDropdownRefs={sshConfigDropdownRefs}
getFilteredSshConfigs={getFilteredSshConfigs}
handleSshConfigClick={handleSshConfigClick}
t={t}
/>
</TabsContent>
<TabsContent value="file_manager">
<HostFileManagerTab form={form} t={t} />
</TabsContent>
{watchedConnectionType === "ssh" && (
<>
<TabsContent value="terminal" className="space-y-1">
<HostTerminalTab form={form} snippets={snippets} t={t} />
</TabsContent>
<TabsContent value="docker" className="space-y-4">
<HostDockerTab form={form} t={t} />
</TabsContent>
<TabsContent value="tunnel">
<HostTunnelTab
form={form}
sshConfigDropdownOpen={sshConfigDropdownOpen}
setSshConfigDropdownOpen={setSshConfigDropdownOpen}
sshConfigInputRefs={sshConfigInputRefs}
sshConfigDropdownRefs={sshConfigDropdownRefs}
getFilteredSshConfigs={getFilteredSshConfigs}
handleSshConfigClick={handleSshConfigClick}
t={t}
/>
</TabsContent>
<TabsContent value="file_manager">
<HostFileManagerTab form={form} t={t} />
</TabsContent>
</>
)}
<TabsContent value="statistics" className="space-y-6">
<HostStatisticsTab
form={form}
statusIntervalUnit={statusIntervalUnit}
setStatusIntervalUnit={setStatusIntervalUnit}
metricsIntervalUnit={metricsIntervalUnit}
setMetricsIntervalUnit={setMetricsIntervalUnit}
snippets={snippets}
t={t}
/>
</TabsContent>
<TabsContent value="sharing" className="space-y-6">
<HostSharingTab
hostId={editingHost?.id}
isNewHost={!editingHost}
/>
{watchedConnectionType === "ssh" ? (
<HostStatisticsTab
form={form}
statusIntervalUnit={statusIntervalUnit}
setStatusIntervalUnit={setStatusIntervalUnit}
metricsIntervalUnit={metricsIntervalUnit}
setMetricsIntervalUnit={setMetricsIntervalUnit}
snippets={snippets}
t={t}
/>
) : (
<HostStatusTab
form={form}
statusIntervalUnit={statusIntervalUnit}
setStatusIntervalUnit={setStatusIntervalUnit}
t={t}
/>
)}
</TabsContent>
{watchedConnectionType !== "ssh" && (
<TabsContent value="remote_desktop" className="space-y-4">
<HostRemoteDesktopTab
form={form}
connectionType={
watchedConnectionType as "rdp" | "vnc" | "telnet"
}
t={t}
/>
</TabsContent>
)}
{watchedConnectionType === "ssh" && (
<TabsContent value="sharing" className="space-y-6">
<HostSharingTab
hostId={editingHost?.id}
isNewHost={!editingHost}
/>
</TabsContent>
)}
</Tabs>
</div>
</ScrollArea>