From e3a978b639d441b35c4f40129fbd82cf425bddc4 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 14 Mar 2026 04:18:29 -0400 Subject: [PATCH] fix(sftp): cache SFTP channel per session to prevent channel exhaustion --- backend/src/terminal/ssh-connection.service.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/src/terminal/ssh-connection.service.ts b/backend/src/terminal/ssh-connection.service.ts index 2cb391d..63956a7 100644 --- a/backend/src/terminal/ssh-connection.service.ts +++ b/backend/src/terminal/ssh-connection.service.ts @@ -157,6 +157,7 @@ export class SshConnectionService { session.stream?.close(); session.client.end(); this.sessions.delete(sessionId); + this.sftpChannels.delete(sessionId); // Update connection log with disconnect time this.prisma.connectionLog.updateMany({ @@ -170,21 +171,32 @@ export class SshConnectionService { return this.sessions.get(sessionId); } + private sftpChannels = new Map(); + getSftpChannel(sessionId: string): Promise { const logger = this.logger; + const cached = this.sftpChannels.get(sessionId); + if (cached) { + return Promise.resolve(cached); + } return new Promise((resolve, reject) => { const session = this.sessions.get(sessionId); if (!session) { logger.error(`[SFTP] Session ${sessionId} not found in sessions map (${this.sessions.size} active sessions)`); return reject(new Error('Session not found')); } - logger.log(`[SFTP] Requesting SFTP subsystem on session ${sessionId}, client connected: ${(session.client as any)._sock?.readable ?? 'unknown'}`); + logger.log(`[SFTP] Requesting SFTP subsystem on session ${sessionId}`); session.client.sftp((err, sftp) => { if (err) { logger.error(`[SFTP] client.sftp() callback error: ${err.message}`); return reject(err); } logger.log(`[SFTP] SFTP subsystem opened successfully for session ${sessionId}`); + sftp.on('close', () => { + this.sftpChannels.delete(sessionId); + logger.log(`[SFTP] Channel closed for session ${sessionId}`); + }); + this.sftpChannels.set(sessionId, sftp); resolve(sftp); }); });