This commit is contained in:
LukeGus
2026-05-28 22:29:20 -05:00
parent 33dcde0827
commit 5777351145
238 changed files with 62303 additions and 98955 deletions
+75 -176
View File
@@ -865,11 +865,16 @@ export let dockerApi: AxiosInstance;
// Pre-initialize with default values to avoid undefined errors during early mounting
initializeApiInstances();
let _resolveAppReady!: () => void;
export const appReadyPromise: Promise<void> = new Promise((resolve) => {
_resolveAppReady = resolve;
});
function initializeApp() {
if (isElectron()) {
Promise.all([getServerConfig(), getEmbeddedServerStatus()])
.then(([config, status]) => {
if (status?.embedded && status?.running) {
if (status?.embedded && status?.running && !config?.serverUrl) {
embeddedMode = true;
}
if (config?.serverUrl) {
@@ -895,9 +900,13 @@ function initializeApp() {
error,
);
initializeApiInstances();
})
.finally(() => {
_resolveAppReady();
});
} else {
initializeApiInstances();
_resolveAppReady();
}
}
@@ -1107,102 +1116,18 @@ export async function getSSHHosts(): Promise<SSHHostWithStatus[]> {
export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
try {
const submitData = {
connectionType: hostData.connectionType || "ssh",
name: hostData.name || "",
ip: hostData.ip,
port: parseInt(hostData.port.toString()) || 22,
username: hostData.username,
folder: hostData.folder || "",
tags: hostData.tags || [],
pin: Boolean(hostData.pin),
authType: hostData.authType,
password:
hostData.connectionType !== "ssh"
? hostData.password || null
: hostData.authType === "password"
? hostData.password
: null,
key: hostData.authType === "key" ? hostData.key : null,
keyPassword: hostData.authType === "key" ? hostData.keyPassword : null,
keyType: hostData.authType === "key" ? hostData.keyType : null,
credentialId:
hostData.authType === "credential" ? hostData.credentialId : null,
overrideCredentialUsername: Boolean(hostData.overrideCredentialUsername),
enableTerminal: Boolean(hostData.enableTerminal),
enableTunnel: Boolean(hostData.enableTunnel),
enableFileManager: Boolean(hostData.enableFileManager),
enableDocker: Boolean(hostData.enableDocker),
showTerminalInSidebar: Boolean(hostData.showTerminalInSidebar),
showFileManagerInSidebar: Boolean(hostData.showFileManagerInSidebar),
showTunnelInSidebar: Boolean(hostData.showTunnelInSidebar),
showDockerInSidebar: Boolean(hostData.showDockerInSidebar),
showServerStatsInSidebar: Boolean(hostData.showServerStatsInSidebar),
defaultPath: hostData.defaultPath || "/",
tunnelConnections: hostData.tunnelConnections || [],
jumpHosts: hostData.jumpHosts || [],
quickActions: hostData.quickActions || [],
sudoPassword: hostData.sudoPassword || null,
statsConfig: hostData.statsConfig || null,
dockerConfig: hostData.dockerConfig || null,
terminalConfig: hostData.terminalConfig || null,
forceKeyboardInteractive: Boolean(hostData.forceKeyboardInteractive),
domain: hostData.domain || null,
security: hostData.security || null,
ignoreCert: Boolean(hostData.ignoreCert),
guacamoleConfig: hostData.guacamoleConfig || null,
notes: hostData.notes || "",
useSocks5: Boolean(hostData.useSocks5),
socks5Host: hostData.socks5Host || null,
socks5Port: hostData.socks5Port || null,
socks5Username: hostData.socks5Username || null,
socks5Password: hostData.socks5Password || null,
socks5ProxyChain: hostData.socks5ProxyChain || null,
macAddress: hostData.macAddress || null,
portKnockSequence: hostData.portKnockSequence || null,
enableSsh: hostData.enableSsh !== false,
enableRdp: Boolean(hostData.enableRdp),
enableVnc: Boolean(hostData.enableVnc),
enableTelnet: Boolean(hostData.enableTelnet),
sshPort: hostData.sshPort || hostData.port || 22,
rdpPort: hostData.rdpPort || 3389,
vncPort: hostData.vncPort || 5900,
telnetPort: hostData.telnetPort || 23,
rdpUser: hostData.rdpUser || null,
rdpPassword: hostData.rdpPassword || null,
rdpDomain: hostData.rdpDomain || null,
rdpSecurity: hostData.rdpSecurity || null,
rdpIgnoreCert: Boolean(hostData.rdpIgnoreCert),
vncPassword: hostData.vncPassword || null,
vncUser: hostData.vncUser || null,
telnetUser: hostData.telnetUser || null,
telnetPassword: hostData.telnetPassword || null,
};
if (!submitData.enableTunnel) {
submitData.tunnelConnections = [];
}
if (!submitData.enableFileManager) {
submitData.defaultPath = "";
}
if (hostData.authType === "key" && hostData.key instanceof File) {
const formData = new FormData();
formData.append("key", hostData.key);
const dataWithoutFile = { ...submitData };
delete dataWithoutFile.key;
const dataWithoutFile = { ...hostData, key: undefined };
formData.append("data", JSON.stringify(dataWithoutFile));
const response = await sshHostApi.post("/db/host", formData, {
headers: { "Content-Type": "multipart/form-data" },
});
return response.data;
} else {
const response = await sshHostApi.post("/db/host", submitData);
return response.data;
}
const response = await sshHostApi.post("/db/host", hostData);
return response.data;
} catch (error) {
throw handleApiError(error, "create SSH host");
}
@@ -1213,101 +1138,18 @@ export async function updateSSHHost(
hostData: SSHHostData,
): Promise<SSHHost> {
try {
const submitData = {
connectionType: hostData.connectionType || "ssh",
name: hostData.name || "",
ip: hostData.ip,
port: parseInt(hostData.port.toString()) || 22,
username: hostData.username,
folder: hostData.folder || "",
tags: hostData.tags || [],
pin: Boolean(hostData.pin),
authType: hostData.authType,
password:
hostData.connectionType !== "ssh"
? hostData.password || null
: hostData.authType === "password"
? hostData.password
: null,
key: hostData.authType === "key" ? hostData.key : null,
keyPassword: hostData.authType === "key" ? hostData.keyPassword : null,
keyType: hostData.authType === "key" ? hostData.keyType : null,
credentialId:
hostData.authType === "credential" ? hostData.credentialId : null,
overrideCredentialUsername: Boolean(hostData.overrideCredentialUsername),
enableTerminal: Boolean(hostData.enableTerminal),
enableTunnel: Boolean(hostData.enableTunnel),
enableFileManager: Boolean(hostData.enableFileManager),
enableDocker: Boolean(hostData.enableDocker),
showTerminalInSidebar: Boolean(hostData.showTerminalInSidebar),
showFileManagerInSidebar: Boolean(hostData.showFileManagerInSidebar),
showTunnelInSidebar: Boolean(hostData.showTunnelInSidebar),
showDockerInSidebar: Boolean(hostData.showDockerInSidebar),
showServerStatsInSidebar: Boolean(hostData.showServerStatsInSidebar),
defaultPath: hostData.defaultPath || "/",
tunnelConnections: hostData.tunnelConnections || [],
jumpHosts: hostData.jumpHosts || [],
quickActions: hostData.quickActions || [],
sudoPassword: hostData.sudoPassword || null,
statsConfig: hostData.statsConfig || null,
dockerConfig: hostData.dockerConfig || null,
terminalConfig: hostData.terminalConfig || null,
forceKeyboardInteractive: Boolean(hostData.forceKeyboardInteractive),
domain: hostData.domain || null,
security: hostData.security || null,
ignoreCert: Boolean(hostData.ignoreCert),
guacamoleConfig: hostData.guacamoleConfig || null,
notes: hostData.notes || "",
useSocks5: Boolean(hostData.useSocks5),
socks5Host: hostData.socks5Host || null,
socks5Port: hostData.socks5Port || null,
socks5Username: hostData.socks5Username || null,
socks5Password: hostData.socks5Password || null,
socks5ProxyChain: hostData.socks5ProxyChain || null,
macAddress: hostData.macAddress || null,
portKnockSequence: hostData.portKnockSequence || null,
enableSsh: hostData.enableSsh !== false,
enableRdp: Boolean(hostData.enableRdp),
enableVnc: Boolean(hostData.enableVnc),
enableTelnet: Boolean(hostData.enableTelnet),
sshPort: hostData.sshPort || hostData.port || 22,
rdpPort: hostData.rdpPort || 3389,
vncPort: hostData.vncPort || 5900,
telnetPort: hostData.telnetPort || 23,
rdpUser: hostData.rdpUser || null,
rdpPassword: hostData.rdpPassword || null,
rdpDomain: hostData.rdpDomain || null,
rdpSecurity: hostData.rdpSecurity || null,
rdpIgnoreCert: Boolean(hostData.rdpIgnoreCert),
vncPassword: hostData.vncPassword || null,
vncUser: hostData.vncUser || null,
telnetUser: hostData.telnetUser || null,
telnetPassword: hostData.telnetPassword || null,
};
if (!submitData.enableTunnel) {
submitData.tunnelConnections = [];
}
if (!submitData.enableFileManager) {
submitData.defaultPath = "";
}
if (hostData.authType === "key" && hostData.key instanceof File) {
const formData = new FormData();
formData.append("key", hostData.key);
const dataWithoutFile = { ...submitData };
delete dataWithoutFile.key;
const dataWithoutFile = { ...hostData, key: undefined };
formData.append("data", JSON.stringify(dataWithoutFile));
const response = await sshHostApi.put(`/db/host/${hostId}`, formData, {
headers: { "Content-Type": "multipart/form-data" },
});
return response.data;
} else {
const response = await sshHostApi.put(`/db/host/${hostId}`, submitData);
return response.data;
}
const response = await sshHostApi.put(`/db/host/${hostId}`, hostData);
return response.data;
} catch (error) {
throw handleApiError(error, "update SSH host");
}
@@ -2082,6 +1924,25 @@ export async function downloadSSHFile(
}
}
export async function downloadSSHFileStream(
sessionId: string,
filePath: string,
): Promise<void> {
const response = await fileManagerApi.post(
"/ssh/downloadFileStream",
{ sessionId, path: filePath },
{ responseType: "blob" },
);
const blob = response.data as Blob;
const fileName = filePath.split("/").pop() || "download";
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
a.click();
URL.revokeObjectURL(url);
}
export async function createSSHFile(
sessionId: string,
path: string,
@@ -2910,6 +2771,10 @@ export async function loginUser(
}
}
if (response.data.token) {
localStorage.setItem("jwt", response.data.token);
}
if (response.data.success && !response.data.requires_totp) {
markUserAuthenticated();
}
@@ -3121,10 +2986,11 @@ export async function changePassword(oldPassword: string, newPassword: string) {
export async function getOIDCAuthorizeUrl(
rememberMe = false,
desktopCallbackPort?: number,
): Promise<OIDCAuthorize> {
try {
const response = await authApi.get("/users/oidc/authorize", {
params: { rememberMe },
params: { rememberMe, desktopCallbackPort },
});
return response.data;
} catch (error) {
@@ -3306,6 +3172,28 @@ export async function updateRegistrationAllowed(
}
}
export async function getOidcAutoProvision(): Promise<{ enabled: boolean }> {
try {
const response = await authApi.get("/users/oidc-auto-provision");
return response.data;
} catch (error) {
handleApiError(error, "check OIDC auto-provision status");
}
}
export async function updateOidcAutoProvision(
enabled: boolean,
): Promise<Record<string, unknown>> {
try {
const response = await authApi.patch("/users/oidc-auto-provision", {
enabled,
});
return response.data;
} catch (error) {
handleApiError(error, "update OIDC auto-provision");
}
}
export async function updatePasswordLoginAllowed(
allowed: boolean,
): Promise<{ allowed: boolean }> {
@@ -4472,15 +4360,26 @@ export async function getGuacamoleToken(
export async function getGuacamoleTokenFromHost(
hostId: number,
protocol?: "rdp" | "vnc" | "telnet",
): Promise<GuacamoleTokenResponse> {
try {
const response = await authApi.post(`/guacamole/connect-host/${hostId}`);
const response = await authApi.post(
`/guacamole/connect-host/${hostId}`,
protocol ? { protocol } : {},
);
return response.data;
} catch (error) {
throw handleApiError(error, "get guacamole token from host");
}
}
export async function getGuacdStatus(): Promise<{
guacd: { status: string };
}> {
const response = await authApi.get("/guacamole/status");
return response.data;
}
// ============================================================================
// RBAC MANAGEMENT
// ============================================================================