mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-16 05:13:36 +00:00
v2.3.0
This commit is contained in:
+75
-176
@@ -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
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user