v1.10.0 #471

Merged
LukeGus merged 106 commits from dev-1.10.0 into main 2026-01-01 04:20:12 +00:00
2 changed files with 103 additions and 52 deletions
Showing only changes of commit bfda81f9c2 - Show all commits

View File

@@ -13,6 +13,7 @@ import {
recentActivity,
hostAccess,
userRoles,
sessionRecordings,
} from "../db/schema.js";
import {
eq,
@@ -1069,60 +1070,40 @@ router.delete(
const numericHostId = Number(hostId);
// Delete all related data in correct order (child tables first)
await db
.delete(fileManagerRecent)
.where(
and(
eq(fileManagerRecent.hostId, numericHostId),
eq(fileManagerRecent.userId, userId),
),
);
.where(eq(fileManagerRecent.hostId, numericHostId));
await db
.delete(fileManagerPinned)
.where(
and(
eq(fileManagerPinned.hostId, numericHostId),
eq(fileManagerPinned.userId, userId),
),
);
.where(eq(fileManagerPinned.hostId, numericHostId));
await db
.delete(fileManagerShortcuts)
.where(
and(
eq(fileManagerShortcuts.hostId, numericHostId),
eq(fileManagerShortcuts.userId, userId),
),
);
.where(eq(fileManagerShortcuts.hostId, numericHostId));
await db
.delete(commandHistory)
.where(
and(
eq(commandHistory.hostId, numericHostId),
eq(commandHistory.userId, userId),
),
);
.where(eq(commandHistory.hostId, numericHostId));
await db
.delete(sshCredentialUsage)
.where(
and(
eq(sshCredentialUsage.hostId, numericHostId),
eq(sshCredentialUsage.userId, userId),
),
);
.where(eq(sshCredentialUsage.hostId, numericHostId));
await db
.delete(recentActivity)
.where(
and(
eq(recentActivity.hostId, numericHostId),
eq(recentActivity.userId, userId),
),
);
.where(eq(recentActivity.hostId, numericHostId));
// Delete RBAC host access entries
await db.delete(hostAccess).where(eq(hostAccess.hostId, numericHostId));
// Delete session recordings
await db
.delete(sessionRecordings)
.where(eq(sessionRecordings.hostId, numericHostId));
// Finally delete the host itself
await db
.delete(sshData)
.where(and(eq(sshData.id, numericHostId), eq(sshData.userId, userId)));
@@ -1887,10 +1868,49 @@ router.delete(
});
}
const hostIds = hostsToDelete.map((host) => host.id);
// Delete all related data for all hosts in the folder (child tables first)
if (hostIds.length > 0) {
await db
.delete(fileManagerRecent)
.where(inArray(fileManagerRecent.hostId, hostIds));
await db
.delete(fileManagerPinned)
.where(inArray(fileManagerPinned.hostId, hostIds));
await db
.delete(fileManagerShortcuts)
.where(inArray(fileManagerShortcuts.hostId, hostIds));
await db
.delete(commandHistory)
.where(inArray(commandHistory.hostId, hostIds));
await db
.delete(sshCredentialUsage)
.where(inArray(sshCredentialUsage.hostId, hostIds));
await db
.delete(recentActivity)
.where(inArray(recentActivity.hostId, hostIds));
// Delete RBAC host access entries
await db.delete(hostAccess).where(inArray(hostAccess.hostId, hostIds));
// Delete session recordings
await db
.delete(sessionRecordings)
.where(inArray(sessionRecordings.hostId, hostIds));
}
// Now delete the hosts themselves
await db
.delete(sshData)
.where(and(eq(sshData.userId, userId), eq(sshData.folder, folderName)));
// Finally delete the folder metadata
await db
.delete(sshFolders)
.where(

View File

@@ -824,12 +824,17 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
useSocks5,
socks5Host,
socks5Port,
hasSocks5ProxyChain: !!(socks5ProxyChain && (socks5ProxyChain as any).length > 0),
hasSocks5ProxyChain: !!(
socks5ProxyChain && (socks5ProxyChain as any).length > 0
),
proxyChainLength: socks5ProxyChain ? (socks5ProxyChain as any).length : 0,
});
// Check if SOCKS5 proxy is enabled (either single proxy or chain)
if (useSocks5 && (socks5Host || (socks5ProxyChain && (socks5ProxyChain as any).length > 0))) {
if (
useSocks5 &&
(socks5Host || (socks5ProxyChain && (socks5ProxyChain as any).length > 0))
) {
fileLogger.info("SOCKS5 enabled for SFTP, creating connection", {
operation: "sftp_socks5_enabled",
sessionId,
@@ -839,18 +844,14 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
});
try {
const socks5Socket = await createSocks5Connection(
ip,
port,
{
useSocks5,
socks5Host,
socks5Port,
socks5Username,
socks5Password,
socks5ProxyChain: socks5ProxyChain as any,
},
);
const socks5Socket = await createSocks5Connection(ip, port, {
useSocks5,
socks5Host,
socks5Port,
socks5Username,
socks5Password,
socks5ProxyChain: socks5ProxyChain as any,
});
if (socks5Socket) {
fileLogger.info("SOCKS5 socket created for SFTP", {
@@ -1545,7 +1546,22 @@ app.post("/ssh/file_manager/ssh/writeFile", async (req, res) => {
const tryFallbackMethod = () => {
try {
const base64Content = Buffer.from(content, "utf8").toString("base64");
let contentBuffer: Buffer;
if (typeof content === "string") {
try {
contentBuffer = Buffer.from(content, "base64");
if (contentBuffer.toString("base64") !== content) {
contentBuffer = Buffer.from(content, "utf8");
}
} catch {
contentBuffer = Buffer.from(content, "utf8");
}
} else if (Buffer.isBuffer(content)) {
contentBuffer = content;
} else {
contentBuffer = Buffer.from(content);
}
const base64Content = contentBuffer.toString("base64");
const escapedPath = filePath.replace(/'/g, "'\"'\"'");
const writeCommand = `echo '${base64Content}' | base64 -d > '${escapedPath}' && echo "SUCCESS"`;
@@ -1746,7 +1762,22 @@ app.post("/ssh/file_manager/ssh/uploadFile", async (req, res) => {
const tryFallbackMethod = () => {
try {
const base64Content = Buffer.from(content, "utf8").toString("base64");
let contentBuffer: Buffer;
if (typeof content === "string") {
try {
contentBuffer = Buffer.from(content, "base64");
if (contentBuffer.toString("base64") !== content) {
contentBuffer = Buffer.from(content, "utf8");
}
} catch {
contentBuffer = Buffer.from(content, "utf8");
}
} else if (Buffer.isBuffer(content)) {
contentBuffer = content;
} else {
contentBuffer = Buffer.from(content);
}
const base64Content = contentBuffer.toString("base64");
const chunkSize = 1000000;
const chunks = [];