chore: cleanup files (possible RC)
This commit is contained in:
@@ -21,7 +21,6 @@ interface SSHSession {
|
||||
|
||||
const activeSessions = new Map<string, SSHSession>();
|
||||
|
||||
// WebSocket server on port 30008
|
||||
const wss = new WebSocketServer({
|
||||
port: 30008,
|
||||
verifyClient: async (info, callback) => {
|
||||
@@ -49,14 +48,8 @@ const wss = new WebSocketServer({
|
||||
return callback(false, 401, "Invalid token");
|
||||
}
|
||||
|
||||
// Store userId in the request for later use
|
||||
(info.req as any).userId = decoded.userId;
|
||||
|
||||
dockerConsoleLogger.info("WebSocket connection verified", {
|
||||
operation: "ws_verify",
|
||||
userId: decoded.userId,
|
||||
});
|
||||
|
||||
callback(true);
|
||||
} catch (error) {
|
||||
dockerConsoleLogger.error("WebSocket verification error", error, {
|
||||
@@ -67,7 +60,6 @@ const wss = new WebSocketServer({
|
||||
},
|
||||
});
|
||||
|
||||
// Helper function to detect available shell in container
|
||||
async function detectShell(
|
||||
session: SSHSession,
|
||||
containerId: string,
|
||||
@@ -102,19 +94,15 @@ async function detectShell(
|
||||
);
|
||||
});
|
||||
|
||||
// If we get here, the shell was found
|
||||
return shell;
|
||||
} catch {
|
||||
// Try next shell
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to sh if nothing else works
|
||||
return "sh";
|
||||
}
|
||||
|
||||
// Helper function to create jump host chain
|
||||
async function createJumpHostChain(
|
||||
jumpHosts: any[],
|
||||
userId: string,
|
||||
@@ -128,7 +116,6 @@ async function createJumpHostChain(
|
||||
for (let i = 0; i < jumpHosts.length; i++) {
|
||||
const jumpHostId = jumpHosts[i].hostId;
|
||||
|
||||
// Fetch jump host from database
|
||||
const jumpHostData = await SimpleDBOps.select(
|
||||
getDb()
|
||||
.select()
|
||||
@@ -154,7 +141,6 @@ async function createJumpHostChain(
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve credentials for jump host
|
||||
let resolvedCredentials: any = {
|
||||
password: jumpHost.password,
|
||||
sshKey: jumpHost.key,
|
||||
@@ -203,7 +189,6 @@ async function createJumpHostChain(
|
||||
tcpKeepAliveInitialDelay: 30000,
|
||||
};
|
||||
|
||||
// Set authentication
|
||||
if (
|
||||
resolvedCredentials.authType === "password" &&
|
||||
resolvedCredentials.password
|
||||
@@ -223,7 +208,6 @@ async function createJumpHostChain(
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a previous client, use it as the sock
|
||||
if (currentClient) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
currentClient!.forwardOut(
|
||||
@@ -252,17 +236,10 @@ async function createJumpHostChain(
|
||||
return currentClient;
|
||||
}
|
||||
|
||||
// Handle WebSocket connections
|
||||
wss.on("connection", async (ws: WebSocket, req) => {
|
||||
const userId = (req as any).userId;
|
||||
const sessionId = `docker-console-${Date.now()}-${Math.random()}`;
|
||||
|
||||
dockerConsoleLogger.info("Docker console WebSocket connected", {
|
||||
operation: "ws_connect",
|
||||
sessionId,
|
||||
userId,
|
||||
});
|
||||
|
||||
let sshSession: SSHSession | null = null;
|
||||
|
||||
ws.on("message", async (data) => {
|
||||
@@ -304,7 +281,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if Docker is enabled for this host
|
||||
if (!hostConfig.enableDocker) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
@@ -317,7 +293,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Resolve credentials
|
||||
let resolvedCredentials: any = {
|
||||
password: hostConfig.password,
|
||||
sshKey: hostConfig.key,
|
||||
@@ -355,7 +330,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Create SSH client
|
||||
const client = new SSHClient();
|
||||
|
||||
const config: any = {
|
||||
@@ -370,7 +344,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
tcpKeepAliveInitialDelay: 30000,
|
||||
};
|
||||
|
||||
// Set authentication
|
||||
if (
|
||||
resolvedCredentials.authType === "password" &&
|
||||
resolvedCredentials.password
|
||||
@@ -390,7 +363,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle jump hosts if configured
|
||||
if (hostConfig.jumpHosts && hostConfig.jumpHosts.length > 0) {
|
||||
const jumpClient = await createJumpHostChain(
|
||||
hostConfig.jumpHosts,
|
||||
@@ -413,7 +385,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to SSH
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
client.on("ready", () => resolve());
|
||||
client.on("error", reject);
|
||||
@@ -429,10 +400,8 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
|
||||
activeSessions.set(sessionId, sshSession);
|
||||
|
||||
// Validate or detect shell
|
||||
let shellToUse = shell || "bash";
|
||||
|
||||
// If a shell is explicitly provided, verify it exists in the container
|
||||
if (shell) {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -461,7 +430,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
);
|
||||
});
|
||||
} catch {
|
||||
// Requested shell not found, detect available shell
|
||||
dockerConsoleLogger.warn(
|
||||
`Requested shell ${shell} not found, detecting available shell`,
|
||||
{
|
||||
@@ -474,13 +442,11 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
shellToUse = await detectShell(sshSession, containerId);
|
||||
}
|
||||
} else {
|
||||
// No shell specified, detect available shell
|
||||
shellToUse = await detectShell(sshSession, containerId);
|
||||
}
|
||||
|
||||
sshSession.shell = shellToUse;
|
||||
|
||||
// Create docker exec PTY
|
||||
const execCommand = `docker exec -it ${containerId} /bin/${shellToUse}`;
|
||||
|
||||
client.exec(
|
||||
@@ -515,7 +481,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
|
||||
sshSession!.stream = stream;
|
||||
|
||||
// Forward stream output to WebSocket
|
||||
stream.on("data", (data: Buffer) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(
|
||||
@@ -527,15 +492,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
});
|
||||
|
||||
stream.stderr.on("data", (data: Buffer) => {
|
||||
// Log stderr but don't send to terminal to avoid duplicate error messages
|
||||
dockerConsoleLogger.debug("Docker exec stderr", {
|
||||
operation: "docker_exec_stderr",
|
||||
sessionId,
|
||||
containerId,
|
||||
data: data.toString("utf8"),
|
||||
});
|
||||
});
|
||||
stream.stderr.on("data", (data: Buffer) => {});
|
||||
|
||||
stream.on("close", () => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
@@ -547,7 +504,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (sshSession) {
|
||||
sshSession.client.end();
|
||||
activeSessions.delete(sessionId);
|
||||
@@ -564,14 +520,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
dockerConsoleLogger.info("Docker console session started", {
|
||||
operation: "console_start",
|
||||
sessionId,
|
||||
containerId,
|
||||
shell: shellToUse,
|
||||
requestedShell: shell,
|
||||
});
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -605,13 +553,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
if (sshSession && sshSession.stream) {
|
||||
const { cols, rows } = message.data;
|
||||
sshSession.stream.setWindow(rows, cols);
|
||||
|
||||
dockerConsoleLogger.debug("Console resized", {
|
||||
operation: "console_resize",
|
||||
sessionId,
|
||||
cols,
|
||||
rows,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -624,11 +565,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
sshSession.client.end();
|
||||
activeSessions.delete(sessionId);
|
||||
|
||||
dockerConsoleLogger.info("Docker console disconnected", {
|
||||
operation: "console_disconnect",
|
||||
sessionId,
|
||||
});
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "disconnected",
|
||||
@@ -640,7 +576,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}
|
||||
|
||||
case "ping": {
|
||||
// Respond with pong to acknowledge keepalive
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: "pong" }));
|
||||
}
|
||||
@@ -669,12 +604,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
});
|
||||
|
||||
ws.on("close", () => {
|
||||
dockerConsoleLogger.info("WebSocket connection closed", {
|
||||
operation: "ws_close",
|
||||
sessionId,
|
||||
});
|
||||
|
||||
// Cleanup SSH session if still active
|
||||
if (sshSession) {
|
||||
if (sshSession.stream) {
|
||||
sshSession.stream.end();
|
||||
@@ -690,7 +619,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
sessionId,
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
if (sshSession) {
|
||||
if (sshSession.stream) {
|
||||
sshSession.stream.end();
|
||||
@@ -701,37 +629,17 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
});
|
||||
});
|
||||
|
||||
dockerConsoleLogger.info(
|
||||
"Docker console WebSocket server started on port 30008",
|
||||
{
|
||||
operation: "startup",
|
||||
},
|
||||
);
|
||||
|
||||
// Graceful shutdown
|
||||
process.on("SIGTERM", () => {
|
||||
dockerConsoleLogger.info("Shutting down Docker console server...", {
|
||||
operation: "shutdown",
|
||||
});
|
||||
|
||||
// Close all active sessions
|
||||
activeSessions.forEach((session, sessionId) => {
|
||||
if (session.stream) {
|
||||
session.stream.end();
|
||||
}
|
||||
session.client.end();
|
||||
dockerConsoleLogger.info("Closed session during shutdown", {
|
||||
operation: "shutdown",
|
||||
sessionId,
|
||||
});
|
||||
});
|
||||
|
||||
activeSessions.clear();
|
||||
|
||||
wss.close(() => {
|
||||
dockerConsoleLogger.info("Docker console server closed", {
|
||||
operation: "shutdown",
|
||||
});
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,10 +11,8 @@ import { SimpleDBOps } from "../utils/simple-db-ops.js";
|
||||
import { AuthManager } from "../utils/auth-manager.js";
|
||||
import type { AuthenticatedRequest, SSHHost } from "../../types/index.js";
|
||||
|
||||
// Create dedicated logger for Docker operations
|
||||
const dockerLogger = logger;
|
||||
|
||||
// SSH Session Management
|
||||
interface SSHSession {
|
||||
client: SSHClient;
|
||||
isConnected: boolean;
|
||||
@@ -26,7 +24,6 @@ interface SSHSession {
|
||||
|
||||
const sshSessions: Record<string, SSHSession> = {};
|
||||
|
||||
// Session cleanup with 60-minute idle timeout
|
||||
const SESSION_IDLE_TIMEOUT = 60 * 60 * 1000;
|
||||
|
||||
function cleanupSession(sessionId: string) {
|
||||
@@ -47,9 +44,7 @@ function cleanupSession(sessionId: string) {
|
||||
|
||||
try {
|
||||
session.client.end();
|
||||
} catch (error) {
|
||||
dockerLogger.debug("Error ending SSH client during cleanup", { error });
|
||||
}
|
||||
} catch (error) {}
|
||||
clearTimeout(session.timeout);
|
||||
delete sshSessions[sessionId];
|
||||
dockerLogger.info("Docker SSH session cleaned up", {
|
||||
@@ -70,7 +65,6 @@ function scheduleSessionCleanup(sessionId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to resolve jump host
|
||||
async function resolveJumpHost(
|
||||
hostId: number,
|
||||
userId: string,
|
||||
@@ -131,7 +125,6 @@ async function resolveJumpHost(
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create jump host chain
|
||||
async function createJumpHostChain(
|
||||
jumpHosts: Array<{ hostId: number }>,
|
||||
userId: string,
|
||||
@@ -239,7 +232,6 @@ async function createJumpHostChain(
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to execute Docker CLI commands
|
||||
async function executeDockerCommand(
|
||||
session: SSHSession,
|
||||
command: string,
|
||||
@@ -290,7 +282,6 @@ async function executeDockerCommand(
|
||||
});
|
||||
}
|
||||
|
||||
// Express app setup
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
@@ -334,12 +325,9 @@ app.use(cookieParser());
|
||||
app.use(express.json({ limit: "100mb" }));
|
||||
app.use(express.urlencoded({ limit: "100mb", extended: true }));
|
||||
|
||||
// Initialize AuthManager and apply middleware
|
||||
const authManager = AuthManager.getInstance();
|
||||
app.use(authManager.createAuthMiddleware());
|
||||
|
||||
// Session management endpoints
|
||||
|
||||
// POST /docker/ssh/connect - Establish SSH session
|
||||
app.post("/docker/ssh/connect", async (req, res) => {
|
||||
const { sessionId, hostId } = req.body;
|
||||
@@ -373,7 +361,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Get host configuration - check both owned and shared hosts
|
||||
const hosts = await SimpleDBOps.select(
|
||||
getDb().select().from(sshData).where(eq(sshData.id, hostId)),
|
||||
"ssh_data",
|
||||
@@ -386,7 +373,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
|
||||
const host = hosts[0] as unknown as SSHHost;
|
||||
|
||||
// Verify user has access to this host (either owner or shared access)
|
||||
if (host.userId !== userId) {
|
||||
const { PermissionManager } =
|
||||
await import("../utils/permission-manager.js");
|
||||
@@ -417,7 +403,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if Docker is enabled for this host
|
||||
if (!host.enableDocker) {
|
||||
dockerLogger.warn("Docker not enabled for host", {
|
||||
operation: "docker_connect",
|
||||
@@ -431,12 +416,10 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up existing session if any
|
||||
if (sshSessions[sessionId]) {
|
||||
cleanupSession(sessionId);
|
||||
}
|
||||
|
||||
// Resolve credentials
|
||||
let resolvedCredentials: any = {
|
||||
password: host.password,
|
||||
sshKey: host.key,
|
||||
@@ -447,9 +430,7 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
if (host.credentialId) {
|
||||
const ownerId = host.userId;
|
||||
|
||||
// Check if this is a shared host access
|
||||
if (userId !== ownerId) {
|
||||
// User is accessing a shared host - use shared credential
|
||||
try {
|
||||
const { SharedCredentialManager } =
|
||||
await import("../utils/shared-credential-manager.js");
|
||||
@@ -475,7 +456,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Owner accessing their own host
|
||||
const credentials = await SimpleDBOps.select(
|
||||
getDb()
|
||||
.select()
|
||||
@@ -503,7 +483,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Create SSH client
|
||||
const client = new SSHClient();
|
||||
|
||||
const config: any = {
|
||||
@@ -518,7 +497,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
tcpKeepAliveInitialDelay: 30000,
|
||||
};
|
||||
|
||||
// Set authentication
|
||||
if (
|
||||
resolvedCredentials.authType === "password" &&
|
||||
resolvedCredentials.password
|
||||
@@ -554,13 +532,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
|
||||
scheduleSessionCleanup(sessionId);
|
||||
|
||||
dockerLogger.info("Docker SSH session established", {
|
||||
operation: "docker_connect",
|
||||
sessionId,
|
||||
hostId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({ success: true, message: "SSH connection established" });
|
||||
});
|
||||
|
||||
@@ -588,7 +559,6 @@ app.post("/docker/ssh/connect", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Handle jump hosts if configured
|
||||
if (host.jumpHosts && host.jumpHosts.length > 0) {
|
||||
const jumpClient = await createJumpHostChain(
|
||||
host.jumpHosts as Array<{ hostId: number }>,
|
||||
@@ -654,11 +624,6 @@ app.post("/docker/ssh/disconnect", async (req, res) => {
|
||||
|
||||
cleanupSession(sessionId);
|
||||
|
||||
dockerLogger.info("Docker SSH session disconnected", {
|
||||
operation: "docker_disconnect",
|
||||
sessionId,
|
||||
});
|
||||
|
||||
res.json({ success: true, message: "SSH session disconnected" });
|
||||
});
|
||||
|
||||
@@ -724,7 +689,6 @@ app.get("/docker/validate/:sessionId", async (req, res) => {
|
||||
session.activeOperations++;
|
||||
|
||||
try {
|
||||
// Check if Docker is installed
|
||||
try {
|
||||
const versionOutput = await executeDockerCommand(
|
||||
session,
|
||||
@@ -733,7 +697,6 @@ app.get("/docker/validate/:sessionId", async (req, res) => {
|
||||
const versionMatch = versionOutput.match(/Docker version ([^\s,]+)/);
|
||||
const version = versionMatch ? versionMatch[1] : "unknown";
|
||||
|
||||
// Check if Docker daemon is running
|
||||
try {
|
||||
await executeDockerCommand(session, "docker ps >/dev/null 2>&1");
|
||||
|
||||
@@ -798,7 +761,7 @@ app.get("/docker/validate/:sessionId", async (req, res) => {
|
||||
// GET /docker/containers/:sessionId - List all containers
|
||||
app.get("/docker/containers/:sessionId", async (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
const all = req.query.all !== "false"; // Default to true
|
||||
const all = req.query.all !== "false";
|
||||
const userId = (req as any).userId;
|
||||
|
||||
if (!userId) {
|
||||
@@ -942,13 +905,6 @@ app.post(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container started", {
|
||||
operation: "start_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container started successfully",
|
||||
@@ -1007,13 +963,6 @@ app.post(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container stopped", {
|
||||
operation: "stop_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container stopped successfully",
|
||||
@@ -1072,13 +1021,6 @@ app.post(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container restarted", {
|
||||
operation: "restart_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container restarted successfully",
|
||||
@@ -1137,13 +1079,6 @@ app.post(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container paused", {
|
||||
operation: "pause_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container paused successfully",
|
||||
@@ -1202,13 +1137,6 @@ app.post(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container unpaused", {
|
||||
operation: "unpause_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container unpaused successfully",
|
||||
@@ -1272,14 +1200,6 @@ app.delete(
|
||||
|
||||
session.activeOperations--;
|
||||
|
||||
dockerLogger.info("Container removed", {
|
||||
operation: "remove_container",
|
||||
sessionId,
|
||||
containerId,
|
||||
force,
|
||||
userId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Container removed successfully",
|
||||
@@ -1425,17 +1345,14 @@ app.get(
|
||||
const output = await executeDockerCommand(session, command);
|
||||
const rawStats = JSON.parse(output.trim());
|
||||
|
||||
// Parse memory usage (e.g., "1.5GiB / 8GiB" -> { used: "1.5GiB", limit: "8GiB" })
|
||||
const memoryParts = rawStats.memory.split(" / ");
|
||||
const memoryUsed = memoryParts[0]?.trim() || "0B";
|
||||
const memoryLimit = memoryParts[1]?.trim() || "0B";
|
||||
|
||||
// Parse network I/O (e.g., "1.5MB / 2.3MB" -> { input: "1.5MB", output: "2.3MB" })
|
||||
const netIOParts = rawStats.netIO.split(" / ");
|
||||
const netInput = netIOParts[0]?.trim() || "0B";
|
||||
const netOutput = netIOParts[1]?.trim() || "0B";
|
||||
|
||||
// Parse block I/O (e.g., "10MB / 5MB" -> { read: "10MB", write: "5MB" })
|
||||
const blockIOParts = rawStats.blockIO.split(" / ");
|
||||
const blockRead = blockIOParts[0]?.trim() || "0B";
|
||||
const blockWrite = blockIOParts[1]?.trim() || "0B";
|
||||
@@ -1482,13 +1399,11 @@ app.get(
|
||||
},
|
||||
);
|
||||
|
||||
// Start server
|
||||
const PORT = 30007;
|
||||
|
||||
app.listen(PORT, async () => {
|
||||
try {
|
||||
await authManager.initialize();
|
||||
dockerLogger.info(`Docker backend server started on port ${PORT}`);
|
||||
} catch (err) {
|
||||
dockerLogger.error("Failed to initialize Docker backend", err, {
|
||||
operation: "startup",
|
||||
@@ -1496,9 +1411,7 @@ app.listen(PORT, async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on("SIGINT", () => {
|
||||
dockerLogger.info("Shutting down Docker backend");
|
||||
Object.keys(sshSessions).forEach((sessionId) => {
|
||||
cleanupSession(sessionId);
|
||||
});
|
||||
@@ -1506,7 +1419,6 @@ process.on("SIGINT", () => {
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
dockerLogger.info("Shutting down Docker backend");
|
||||
Object.keys(sshSessions).forEach((sessionId) => {
|
||||
cleanupSession(sessionId);
|
||||
});
|
||||
|
||||
@@ -815,34 +815,10 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
|
||||
},
|
||||
);
|
||||
|
||||
fileLogger.info("SFTP connection request received", {
|
||||
operation: "sftp_connect_request",
|
||||
sessionId,
|
||||
hostId,
|
||||
ip,
|
||||
port,
|
||||
useSocks5,
|
||||
socks5Host,
|
||||
socks5Port,
|
||||
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))
|
||||
) {
|
||||
fileLogger.info("SOCKS5 enabled for SFTP, creating connection", {
|
||||
operation: "sftp_socks5_enabled",
|
||||
sessionId,
|
||||
socks5Host,
|
||||
socks5Port,
|
||||
hasChain: !!(socks5ProxyChain && (socks5ProxyChain as any).length > 0),
|
||||
});
|
||||
|
||||
try {
|
||||
const socks5Socket = await createSocks5Connection(ip, port, {
|
||||
useSocks5,
|
||||
@@ -854,10 +830,6 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
|
||||
});
|
||||
|
||||
if (socks5Socket) {
|
||||
fileLogger.info("SOCKS5 socket created for SFTP", {
|
||||
operation: "sftp_socks5_socket_ready",
|
||||
sessionId,
|
||||
});
|
||||
config.sock = socks5Socket;
|
||||
client.connect(config);
|
||||
return;
|
||||
@@ -883,17 +855,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
|
||||
: "Unknown error"),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fileLogger.info("SOCKS5 NOT enabled for SFTP connection", {
|
||||
operation: "sftp_no_socks5",
|
||||
sessionId,
|
||||
useSocks5,
|
||||
socks5Host,
|
||||
hasChain: !!(socks5ProxyChain && (socks5ProxyChain as any).length > 0),
|
||||
});
|
||||
}
|
||||
|
||||
if (jumpHosts && jumpHosts.length > 0 && userId) {
|
||||
} else if (jumpHosts && jumpHosts.length > 0 && userId) {
|
||||
try {
|
||||
const jumpClient = await createJumpHostChain(jumpHosts, userId);
|
||||
|
||||
@@ -976,9 +938,7 @@ app.post("/ssh/file_manager/ssh/connect-totp", async (req, res) => {
|
||||
delete pendingTOTPSessions[sessionId];
|
||||
try {
|
||||
session.client.end();
|
||||
} catch (error) {
|
||||
sshLogger.debug("Operation failed, continuing", { error });
|
||||
}
|
||||
} catch (error) {}
|
||||
fileLogger.warn("TOTP session timeout before code submission", {
|
||||
operation: "file_totp_verify",
|
||||
sessionId,
|
||||
@@ -3055,21 +3015,10 @@ app.post("/ssh/file_manager/ssh/extractArchive", async (req, res) => {
|
||||
|
||||
let errorOutput = "";
|
||||
|
||||
stream.on("data", (data: Buffer) => {
|
||||
fileLogger.debug("Extract stdout", {
|
||||
operation: "extract_archive",
|
||||
sessionId,
|
||||
output: data.toString(),
|
||||
});
|
||||
});
|
||||
stream.on("data", (data: Buffer) => {});
|
||||
|
||||
stream.stderr.on("data", (data: Buffer) => {
|
||||
errorOutput += data.toString();
|
||||
fileLogger.debug("Extract stderr", {
|
||||
operation: "extract_archive",
|
||||
sessionId,
|
||||
error: data.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
stream.on("close", (code: number) => {
|
||||
@@ -3247,21 +3196,10 @@ app.post("/ssh/file_manager/ssh/compressFiles", async (req, res) => {
|
||||
|
||||
let errorOutput = "";
|
||||
|
||||
stream.on("data", (data: Buffer) => {
|
||||
fileLogger.debug("Compress stdout", {
|
||||
operation: "compress_files",
|
||||
sessionId,
|
||||
output: data.toString(),
|
||||
});
|
||||
});
|
||||
stream.on("data", (data: Buffer) => {});
|
||||
|
||||
stream.stderr.on("data", (data: Buffer) => {
|
||||
errorOutput += data.toString();
|
||||
fileLogger.debug("Compress stderr", {
|
||||
operation: "compress_files",
|
||||
sessionId,
|
||||
error: data.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
stream.on("close", (code: number) => {
|
||||
|
||||
@@ -201,7 +201,6 @@ class SSHConnectionPool {
|
||||
private cleanupInterval: NodeJS.Timeout;
|
||||
|
||||
constructor() {
|
||||
// Reduce cleanup interval from 5 minutes to 2 minutes for faster dead connection removal
|
||||
this.cleanupInterval = setInterval(
|
||||
() => {
|
||||
this.cleanup();
|
||||
@@ -211,8 +210,6 @@ class SSHConnectionPool {
|
||||
}
|
||||
|
||||
private getHostKey(host: SSHHostWithCredentials): string {
|
||||
// Include SOCKS5 settings in the key to ensure separate connection pools
|
||||
// for direct connections vs SOCKS5 connections
|
||||
const socks5Key = host.useSocks5
|
||||
? `:socks5:${host.socks5Host}:${host.socks5Port}:${JSON.stringify(host.socks5ProxyChain || [])}`
|
||||
: "";
|
||||
@@ -221,9 +218,8 @@ class SSHConnectionPool {
|
||||
|
||||
private isConnectionHealthy(client: Client): boolean {
|
||||
try {
|
||||
// Check if the connection has been destroyed or closed
|
||||
// @ts-ignore - accessing internal property to check connection state
|
||||
if (client._sock && (client._sock.destroyed || !client._sock.writable)) {
|
||||
const sock = (client as any)._sock;
|
||||
if (sock && (sock.destroyed || !sock.writable)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -236,28 +232,13 @@ class SSHConnectionPool {
|
||||
const hostKey = this.getHostKey(host);
|
||||
let connections = this.connections.get(hostKey) || [];
|
||||
|
||||
statsLogger.info("Getting connection from pool", {
|
||||
operation: "get_connection_from_pool",
|
||||
hostKey: hostKey,
|
||||
availableConnections: connections.length,
|
||||
useSocks5: host.useSocks5,
|
||||
socks5Host: host.socks5Host,
|
||||
hasSocks5ProxyChain: !!(
|
||||
host.socks5ProxyChain && host.socks5ProxyChain.length > 0
|
||||
),
|
||||
hostId: host.id,
|
||||
});
|
||||
|
||||
// Find available connection and validate health
|
||||
const available = connections.find((conn) => !conn.inUse);
|
||||
if (available) {
|
||||
// Health check before reuse
|
||||
if (!this.isConnectionHealthy(available.client)) {
|
||||
statsLogger.warn("Removing unhealthy connection from pool", {
|
||||
operation: "remove_dead_connection",
|
||||
hostKey,
|
||||
});
|
||||
// Remove dead connection
|
||||
try {
|
||||
available.client.end();
|
||||
} catch (error) {
|
||||
@@ -265,12 +246,7 @@ class SSHConnectionPool {
|
||||
}
|
||||
connections = connections.filter((c) => c !== available);
|
||||
this.connections.set(hostKey, connections);
|
||||
// Fall through to create new connection
|
||||
} else {
|
||||
statsLogger.info("Reusing existing connection from pool", {
|
||||
operation: "reuse_connection",
|
||||
hostKey,
|
||||
});
|
||||
available.inUse = true;
|
||||
available.lastUsed = Date.now();
|
||||
return available.client;
|
||||
@@ -278,10 +254,6 @@ class SSHConnectionPool {
|
||||
}
|
||||
|
||||
if (connections.length < this.maxConnectionsPerHost) {
|
||||
statsLogger.info("Creating new connection for pool", {
|
||||
operation: "create_new_connection",
|
||||
hostKey,
|
||||
});
|
||||
const client = await this.createConnection(host);
|
||||
const pooled: PooledConnection = {
|
||||
client,
|
||||
@@ -369,24 +341,11 @@ class SSHConnectionPool {
|
||||
try {
|
||||
const config = buildSshConfig(host);
|
||||
|
||||
// Check if SOCKS5 proxy is enabled (either single proxy or chain)
|
||||
if (
|
||||
host.useSocks5 &&
|
||||
(host.socks5Host ||
|
||||
(host.socks5ProxyChain && host.socks5ProxyChain.length > 0))
|
||||
) {
|
||||
statsLogger.info("Using SOCKS5 proxy for connection", {
|
||||
operation: "socks5_enabled",
|
||||
hostIp: host.ip,
|
||||
hostPort: host.port,
|
||||
socks5Host: host.socks5Host,
|
||||
socks5Port: host.socks5Port,
|
||||
hasChain: !!(
|
||||
host.socks5ProxyChain && host.socks5ProxyChain.length > 0
|
||||
),
|
||||
chainLength: host.socks5ProxyChain?.length || 0,
|
||||
});
|
||||
|
||||
try {
|
||||
const socks5Socket = await createSocks5Connection(
|
||||
host.ip,
|
||||
@@ -402,10 +361,6 @@ class SSHConnectionPool {
|
||||
);
|
||||
|
||||
if (socks5Socket) {
|
||||
statsLogger.info("SOCKS5 socket created successfully", {
|
||||
operation: "socks5_socket_ready",
|
||||
hostIp: host.ip,
|
||||
});
|
||||
config.sock = socks5Socket;
|
||||
client.connect(config);
|
||||
return;
|
||||
@@ -492,12 +447,6 @@ class SSHConnectionPool {
|
||||
const hostKey = this.getHostKey(host);
|
||||
const connections = this.connections.get(hostKey) || [];
|
||||
|
||||
statsLogger.info("Clearing all connections for host", {
|
||||
operation: "clear_host_connections",
|
||||
hostKey,
|
||||
connectionCount: connections.length,
|
||||
});
|
||||
|
||||
for (const conn of connections) {
|
||||
try {
|
||||
conn.client.end();
|
||||
@@ -519,7 +468,6 @@ class SSHConnectionPool {
|
||||
|
||||
for (const [hostKey, connections] of this.connections.entries()) {
|
||||
const activeConnections = connections.filter((conn) => {
|
||||
// Remove if idle for too long
|
||||
if (!conn.inUse && now - conn.lastUsed > maxAge) {
|
||||
try {
|
||||
conn.client.end();
|
||||
@@ -527,7 +475,6 @@ class SSHConnectionPool {
|
||||
totalCleaned++;
|
||||
return false;
|
||||
}
|
||||
// Also remove if connection is unhealthy (even if recently used)
|
||||
if (!this.isConnectionHealthy(conn.client)) {
|
||||
statsLogger.warn("Removing unhealthy connection during cleanup", {
|
||||
operation: "cleanup_unhealthy",
|
||||
@@ -549,23 +496,9 @@ class SSHConnectionPool {
|
||||
this.connections.set(hostKey, activeConnections);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalCleaned > 0 || totalUnhealthy > 0) {
|
||||
statsLogger.info("Connection pool cleanup completed", {
|
||||
operation: "cleanup_complete",
|
||||
idleCleaned: totalCleaned,
|
||||
unhealthyCleaned: totalUnhealthy,
|
||||
remainingHosts: this.connections.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clearAllConnections(): void {
|
||||
statsLogger.info("Clearing ALL connections from pool", {
|
||||
operation: "clear_all_connections",
|
||||
totalHosts: this.connections.size,
|
||||
});
|
||||
|
||||
for (const [hostKey, connections] of this.connections.entries()) {
|
||||
for (const conn of connections) {
|
||||
try {
|
||||
@@ -601,13 +534,12 @@ class SSHConnectionPool {
|
||||
class RequestQueue {
|
||||
private queues = new Map<number, Array<() => Promise<unknown>>>();
|
||||
private processing = new Set<number>();
|
||||
private requestTimeout = 60000; // 60 second timeout for requests
|
||||
private requestTimeout = 60000;
|
||||
|
||||
async queueRequest<T>(hostId: number, request: () => Promise<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const wrappedRequest = async () => {
|
||||
try {
|
||||
// Add timeout wrapper to prevent indefinite hanging
|
||||
const result = await Promise.race<T>([
|
||||
request(),
|
||||
new Promise<never>((_, rej) =>
|
||||
@@ -646,19 +578,11 @@ class RequestQueue {
|
||||
if (request) {
|
||||
try {
|
||||
await request();
|
||||
} catch (error) {
|
||||
// Log errors but continue processing queue
|
||||
statsLogger.debug("Request queue error", {
|
||||
operation: "queue_request_error",
|
||||
hostId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
this.processing.delete(hostId);
|
||||
// Check if new items were added during processing
|
||||
const currentQueue = this.queues.get(hostId);
|
||||
if (currentQueue && currentQueue.length > 0) {
|
||||
this.processQueue(hostId);
|
||||
@@ -797,9 +721,9 @@ class AuthFailureTracker {
|
||||
|
||||
class PollingBackoff {
|
||||
private failures = new Map<number, { count: number; nextRetry: number }>();
|
||||
private baseDelay = 30000; // 30s base delay
|
||||
private maxDelay = 600000; // 10 min max delay
|
||||
private maxRetries = 5; // Max retry attempts before giving up
|
||||
private baseDelay = 30000;
|
||||
private maxDelay = 600000;
|
||||
private maxRetries = 5;
|
||||
|
||||
recordFailure(hostId: number): void {
|
||||
const existing = this.failures.get(hostId) || { count: 0, nextRetry: 0 };
|
||||
@@ -811,25 +735,16 @@ class PollingBackoff {
|
||||
count: existing.count + 1,
|
||||
nextRetry: Date.now() + delay,
|
||||
});
|
||||
|
||||
statsLogger.debug("Recorded polling backoff", {
|
||||
operation: "polling_backoff_recorded",
|
||||
hostId,
|
||||
failureCount: existing.count + 1,
|
||||
nextRetryDelay: delay,
|
||||
});
|
||||
}
|
||||
|
||||
shouldSkip(hostId: number): boolean {
|
||||
const backoff = this.failures.get(hostId);
|
||||
if (!backoff) return false;
|
||||
|
||||
// If exceeded max retries, always skip
|
||||
if (backoff.count >= this.maxRetries) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise check if we're still in backoff period
|
||||
return Date.now() < backoff.nextRetry;
|
||||
}
|
||||
|
||||
@@ -852,18 +767,13 @@ class PollingBackoff {
|
||||
|
||||
reset(hostId: number): void {
|
||||
this.failures.delete(hostId);
|
||||
statsLogger.debug("Reset polling backoff", {
|
||||
operation: "polling_backoff_reset",
|
||||
hostId,
|
||||
});
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
const maxAge = 60 * 60 * 1000; // 1 hour
|
||||
const maxAge = 60 * 60 * 1000;
|
||||
const now = Date.now();
|
||||
|
||||
for (const [hostId, backoff] of this.failures.entries()) {
|
||||
// Only cleanup if not at max retries and old enough
|
||||
if (backoff.count < this.maxRetries && now - backoff.nextRetry > maxAge) {
|
||||
this.failures.delete(hostId);
|
||||
}
|
||||
@@ -906,7 +816,6 @@ interface SSHHostWithCredentials {
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
|
||||
// SOCKS5 Proxy configuration
|
||||
useSocks5?: boolean;
|
||||
socks5Host?: string;
|
||||
socks5Port?: number;
|
||||
@@ -1051,7 +960,6 @@ class PollingManager {
|
||||
}
|
||||
|
||||
private async pollHostStatus(host: SSHHostWithCredentials): Promise<void> {
|
||||
// Refresh host data from database to get latest settings
|
||||
const refreshedHost = await fetchHostById(host.id, host.userId);
|
||||
if (!refreshedHost) {
|
||||
statsLogger.warn("Host not found during status polling", {
|
||||
@@ -1082,18 +990,11 @@ class PollingManager {
|
||||
}
|
||||
|
||||
private async pollHostMetrics(host: SSHHostWithCredentials): Promise<void> {
|
||||
// Check if we should skip due to backoff
|
||||
if (pollingBackoff.shouldSkip(host.id)) {
|
||||
const backoffInfo = pollingBackoff.getBackoffInfo(host.id);
|
||||
statsLogger.debug("Skipping metrics polling due to backoff", {
|
||||
operation: "poll_metrics_skipped",
|
||||
hostId: host.id,
|
||||
backoffInfo,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh host data from database to get latest SOCKS5 and other settings
|
||||
const refreshedHost = await fetchHostById(host.id, host.userId);
|
||||
if (!refreshedHost) {
|
||||
statsLogger.warn("Host not found during metrics polling", {
|
||||
@@ -1114,13 +1015,11 @@ class PollingManager {
|
||||
data: metrics,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
// Reset backoff on successful collection
|
||||
pollingBackoff.reset(refreshedHost.id);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
// Record failure for backoff
|
||||
pollingBackoff.recordFailure(refreshedHost.id);
|
||||
|
||||
const latestConfig = this.pollingConfigs.get(refreshedHost.id);
|
||||
@@ -1356,7 +1255,6 @@ async function resolveHostCredentials(
|
||||
createdAt: host.createdAt,
|
||||
updatedAt: host.updatedAt,
|
||||
userId: host.userId,
|
||||
// SOCKS5 proxy settings
|
||||
useSocks5: !!host.useSocks5,
|
||||
socks5Host: host.socks5Host || undefined,
|
||||
socks5Port: host.socks5Port || undefined,
|
||||
@@ -1415,21 +1313,6 @@ async function resolveHostCredentials(
|
||||
addLegacyCredentials(baseHost, host);
|
||||
}
|
||||
|
||||
statsLogger.info("Resolved host credentials with SOCKS5 settings", {
|
||||
operation: "resolve_host",
|
||||
hostId: host.id as number,
|
||||
useSocks5: baseHost.useSocks5,
|
||||
socks5Host: baseHost.socks5Host,
|
||||
socks5Port: baseHost.socks5Port,
|
||||
hasSocks5ProxyChain: !!(
|
||||
baseHost.socks5ProxyChain &&
|
||||
(baseHost.socks5ProxyChain as any[]).length > 0
|
||||
),
|
||||
proxyChainLength: baseHost.socks5ProxyChain
|
||||
? (baseHost.socks5ProxyChain as any[]).length
|
||||
: 0,
|
||||
});
|
||||
|
||||
return baseHost as unknown as SSHHostWithCredentials;
|
||||
} catch (error) {
|
||||
statsLogger.error(
|
||||
@@ -1654,12 +1537,7 @@ async function collectMetrics(host: SSHHostWithCredentials): Promise<{
|
||||
};
|
||||
try {
|
||||
login_stats = await collectLoginStats(client);
|
||||
} catch (e) {
|
||||
statsLogger.debug("Failed to collect login stats", {
|
||||
operation: "login_stats_failed",
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
const result = {
|
||||
cpu,
|
||||
@@ -1800,7 +1678,6 @@ app.post("/refresh", async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all connections to ensure fresh connections with updated settings
|
||||
connectionPool.clearAllConnections();
|
||||
|
||||
await pollingManager.refreshHostPolling(userId);
|
||||
@@ -1825,7 +1702,6 @@ app.post("/host-updated", async (req, res) => {
|
||||
try {
|
||||
const host = await fetchHostById(hostId, userId);
|
||||
if (host) {
|
||||
// Clear existing connections for this host to ensure new settings (like SOCKS5) are used
|
||||
connectionPool.clearHostConnections(host);
|
||||
|
||||
await pollingManager.startPollingForHost(host);
|
||||
|
||||
@@ -137,12 +137,10 @@ async function createJumpHostChain(
|
||||
const clients: Client[] = [];
|
||||
|
||||
try {
|
||||
// Fetch all jump host configurations in parallel
|
||||
const jumpHostConfigs = await Promise.all(
|
||||
jumpHosts.map((jh) => resolveJumpHost(jh.hostId, userId)),
|
||||
);
|
||||
|
||||
// Validate all configs resolved
|
||||
for (let i = 0; i < jumpHostConfigs.length; i++) {
|
||||
if (!jumpHostConfigs[i]) {
|
||||
sshLogger.error(`Jump host ${i + 1} not found`, undefined, {
|
||||
@@ -154,7 +152,6 @@ async function createJumpHostChain(
|
||||
}
|
||||
}
|
||||
|
||||
// Connect through jump hosts sequentially
|
||||
for (let i = 0; i < jumpHostConfigs.length; i++) {
|
||||
const jumpHostConfig = jumpHostConfigs[i];
|
||||
|
||||
@@ -1196,7 +1193,6 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if SOCKS5 proxy is enabled (either single proxy or chain)
|
||||
if (
|
||||
hostConfig.useSocks5 &&
|
||||
(hostConfig.socks5Host ||
|
||||
|
||||
@@ -594,11 +594,6 @@ async function connectSSHTunnel(
|
||||
keyType: sharedCred.keyType,
|
||||
authMethod: sharedCred.authType,
|
||||
};
|
||||
tunnelLogger.info("Resolved shared credentials for tunnel source", {
|
||||
operation: "tunnel_connect_shared_cred",
|
||||
tunnelName,
|
||||
userId: effectiveUserId,
|
||||
});
|
||||
} else {
|
||||
const errorMessage = `Cannot connect tunnel '${tunnelName}': shared credentials not available`;
|
||||
tunnelLogger.error(errorMessage);
|
||||
@@ -1126,7 +1121,6 @@ async function connectSSHTunnel(
|
||||
});
|
||||
}
|
||||
|
||||
// Check if SOCKS5 proxy is enabled (either single proxy or chain)
|
||||
if (
|
||||
tunnelConfig.useSocks5 &&
|
||||
(tunnelConfig.socks5Host ||
|
||||
@@ -1399,7 +1393,6 @@ async function killRemoteTunnelByMarker(
|
||||
callback(err);
|
||||
});
|
||||
|
||||
// Check if SOCKS5 proxy is enabled (either single proxy or chain)
|
||||
if (
|
||||
tunnelConfig.useSocks5 &&
|
||||
(tunnelConfig.socks5Host ||
|
||||
@@ -1517,12 +1510,6 @@ app.post(
|
||||
|
||||
if (accessInfo.isShared && !accessInfo.isOwner) {
|
||||
tunnelConfig.requestingUserId = userId;
|
||||
tunnelLogger.info("Shared host tunnel connect", {
|
||||
operation: "tunnel_connect_shared",
|
||||
userId,
|
||||
hostId: tunnelConfig.sourceHostId,
|
||||
tunnelName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1552,14 +1539,7 @@ app.post(
|
||||
}
|
||||
}
|
||||
|
||||
// If endpoint details are missing, resolve them from database
|
||||
if (!tunnelConfig.endpointIP || !tunnelConfig.endpointUsername) {
|
||||
tunnelLogger.info("Resolving endpoint host details from database", {
|
||||
operation: "tunnel_connect_resolve_endpoint",
|
||||
tunnelName,
|
||||
endpointHost: tunnelConfig.endpointHost,
|
||||
});
|
||||
|
||||
try {
|
||||
const systemCrypto = SystemCrypto.getInstance();
|
||||
const internalAuthToken = await systemCrypto.getInternalAuthToken();
|
||||
@@ -1587,7 +1567,6 @@ app.post(
|
||||
);
|
||||
}
|
||||
|
||||
// Populate endpoint fields
|
||||
tunnelConfig.endpointIP = endpointHost.ip;
|
||||
tunnelConfig.endpointSSHPort = endpointHost.port;
|
||||
tunnelConfig.endpointUsername = endpointHost.username;
|
||||
@@ -1598,13 +1577,6 @@ app.post(
|
||||
tunnelConfig.endpointKeyType = endpointHost.keyType;
|
||||
tunnelConfig.endpointCredentialId = endpointHost.credentialId;
|
||||
tunnelConfig.endpointUserId = endpointHost.userId;
|
||||
|
||||
tunnelLogger.info("Endpoint host details resolved", {
|
||||
operation: "tunnel_connect_endpoint_resolved",
|
||||
tunnelName,
|
||||
endpointIP: tunnelConfig.endpointIP,
|
||||
endpointUsername: tunnelConfig.endpointUsername,
|
||||
});
|
||||
} catch (resolveError) {
|
||||
tunnelLogger.error(
|
||||
"Failed to resolve endpoint host",
|
||||
|
||||
@@ -26,7 +26,6 @@ export async function collectCpuMetrics(client: Client): Promise<{
|
||||
let loadTriplet: [number, number, number] | null = null;
|
||||
|
||||
try {
|
||||
// Wrap Promise.all with timeout to prevent indefinite blocking
|
||||
const [stat1, loadAvgOut, coresOut] = await Promise.race([
|
||||
Promise.all([
|
||||
execCommand(client, "cat /proc/stat"),
|
||||
|
||||
Reference in New Issue
Block a user