fix(sftp): cache SFTP channel per session to prevent channel exhaustion

This commit is contained in:
Vantz Stockwell 2026-03-14 04:18:29 -04:00
parent 3b1c1aeda1
commit e3a978b639

View File

@ -157,6 +157,7 @@ export class SshConnectionService {
session.stream?.close(); session.stream?.close();
session.client.end(); session.client.end();
this.sessions.delete(sessionId); this.sessions.delete(sessionId);
this.sftpChannels.delete(sessionId);
// Update connection log with disconnect time // Update connection log with disconnect time
this.prisma.connectionLog.updateMany({ this.prisma.connectionLog.updateMany({
@ -170,21 +171,32 @@ export class SshConnectionService {
return this.sessions.get(sessionId); return this.sessions.get(sessionId);
} }
private sftpChannels = new Map<string, any>();
getSftpChannel(sessionId: string): Promise<any> { getSftpChannel(sessionId: string): Promise<any> {
const logger = this.logger; const logger = this.logger;
const cached = this.sftpChannels.get(sessionId);
if (cached) {
return Promise.resolve(cached);
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const session = this.sessions.get(sessionId); const session = this.sessions.get(sessionId);
if (!session) { if (!session) {
logger.error(`[SFTP] Session ${sessionId} not found in sessions map (${this.sessions.size} active sessions)`); logger.error(`[SFTP] Session ${sessionId} not found in sessions map (${this.sessions.size} active sessions)`);
return reject(new Error('Session not found')); 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) => { session.client.sftp((err, sftp) => {
if (err) { if (err) {
logger.error(`[SFTP] client.sftp() callback error: ${err.message}`); logger.error(`[SFTP] client.sftp() callback error: ${err.message}`);
return reject(err); return reject(err);
} }
logger.log(`[SFTP] SFTP subsystem opened successfully for session ${sessionId}`); 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); resolve(sftp);
}); });
}); });