diff --git a/backend/src/terminal/sftp.gateway.ts b/backend/src/terminal/sftp.gateway.ts index 52bac06..c6115ac 100644 --- a/backend/src/terminal/sftp.gateway.ts +++ b/backend/src/terminal/sftp.gateway.ts @@ -39,11 +39,30 @@ export class SftpGateway { return this.send(client, { type: 'error', message: 'sessionId required' }); } - const sftp = await this.ssh.getSftpChannel(sessionId); + this.logger.log(`[SFTP] Getting SFTP channel for session ${sessionId}...`); + let sftp: any; + try { + sftp = await this.ssh.getSftpChannel(sessionId); + this.logger.log(`[SFTP] Got SFTP channel OK — type: ${typeof sftp}, constructor: ${sftp?.constructor?.name}`); + } catch (err: any) { + this.logger.error(`[SFTP] Failed to get SFTP channel: ${err.message}`); + return this.send(client, { type: 'error', message: `SFTP channel failed: ${err.message}` }); + } + + // Listen for SFTP channel errors + sftp.on('error', (err: any) => { + this.logger.error(`[SFTP] Channel error event: ${err.message}`); + }); + sftp.on('close', () => { + this.logger.warn(`[SFTP] Channel closed`); + }); switch (msg.type) { case 'list': { - sftp.readdir(msg.path, (err: any, list: any[]) => { + this.logger.log(`[SFTP] readdir starting for path: "${msg.path}"`); + try { + sftp.readdir(msg.path, (err: any, list: any[]) => { + this.logger.log(`[SFTP] readdir callback fired, err=${err?.message || 'null'}, entries=${list?.length || 0}`); if (err) return this.send(client, { type: 'error', message: err.message }); const entries = list.map((f: any) => ({ name: f.filename, @@ -53,8 +72,13 @@ export class SftpGateway { permissions: (f.attrs.mode & 0o7777).toString(8), modified: new Date(f.attrs.mtime * 1000).toISOString(), })); + this.logger.log(`[SFTP] Sending list response with ${entries.length} entries, client.readyState=${client.readyState}`); this.send(client, { type: 'list', path: msg.path, entries }); - }); + }); + } catch (syncErr: any) { + this.logger.error(`[SFTP] readdir threw synchronously: ${syncErr.message}`); + this.send(client, { type: 'error', message: syncErr.message }); + } break; } case 'read': { diff --git a/backend/src/terminal/ssh-connection.service.ts b/backend/src/terminal/ssh-connection.service.ts index 1867772..2cb391d 100644 --- a/backend/src/terminal/ssh-connection.service.ts +++ b/backend/src/terminal/ssh-connection.service.ts @@ -171,11 +171,20 @@ export class SshConnectionService { } getSftpChannel(sessionId: string): Promise { + const logger = this.logger; return new Promise((resolve, reject) => { const session = this.sessions.get(sessionId); - if (!session) return reject(new Error('Session not found')); + 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'}`); session.client.sftp((err, sftp) => { - if (err) return reject(err); + if (err) { + logger.error(`[SFTP] client.sftp() callback error: ${err.message}`); + return reject(err); + } + logger.log(`[SFTP] SFTP subsystem opened successfully for session ${sessionId}`); resolve(sftp); }); });