@@ -21,7 +21,8 @@ export class SSHService {
2121 } , 100 ) ;
2222 }
2323
24- static checkExecError ( err ) {
24+ static checkExecError ( err , accept_empty_result = false ) {
25+ if ( accept_empty_result ) return err . rc != 0 ;
2526 return ! err || err . rc != 0 ;
2627 }
2728
@@ -62,8 +63,12 @@ export class SSHService {
6263 let lastIndex = this . connectionPool . length - 1 ;
6364 const threshholdIndex = lastIndex - 2 ;
6465
65- if ( this . connectionInfo && ! this . addingConnection && ( this . connectionPool . length < 6 || this . connectionPool [ threshholdIndex ] ?. _chanMgr ?. _count > 0 ) ) {
66- await this . connect ( this . connectionInfo )
66+ if (
67+ this . connectionInfo &&
68+ ! this . addingConnection &&
69+ ( this . connectionPool . length < 6 || this . connectionPool [ threshholdIndex ] ?. _chanMgr ?. _count > 0 )
70+ ) {
71+ await this . connect ( this . connectionInfo ) ;
6772 }
6873 if ( this . connectionPool . length > 5 && this . connectionPool [ threshholdIndex ] ?. _chanMgr ?. _count === 0 ) {
6974 this . removeConnectionCount ++ ;
@@ -302,12 +307,12 @@ export class SSHService {
302307 const keyPair = generateKeyPairSync ( opts . keyType , opts ) ;
303308 let exitingKeys = await this . readSSHKeyFile ( ) ;
304309 if ( keyPair . public ) {
305- let allKeys = [ ...exitingKeys , keyPair . public ]
306- await this . writeSSHKeyFile ( allKeys )
307- const savePath = path . join ( opts . pickPath , opts . keyType . toLowerCase ( ) )
308- await fs . promises . writeFile ( savePath , keyPair . private )
309- await fs . promises . writeFile ( savePath + ".pub" , keyPair . public )
310- return allKeys
310+ let allKeys = [ ...exitingKeys , keyPair . public ] ;
311+ await this . writeSSHKeyFile ( allKeys ) ;
312+ const savePath = path . join ( opts . pickPath , opts . keyType . toLowerCase ( ) ) ;
313+ await fs . promises . writeFile ( savePath , keyPair . private ) ;
314+ await fs . promises . writeFile ( savePath + ".pub" , keyPair . public ) ;
315+ return allKeys ;
311316 }
312317 return exitingKeys ;
313318 } catch ( err ) {
@@ -316,7 +321,7 @@ export class SSHService {
316321 }
317322
318323 async readSSHKeyFile ( sshDirPath = `~/.ssh` ) {
319- let authorizedKeys = [ ]
324+ let authorizedKeys = [ ] ;
320325 try {
321326 if ( sshDirPath . endsWith ( "/" ) ) sshDirPath = sshDirPath . slice ( 0 , - 1 , "" ) ; //if path ends with '/' remove it
322327 let result = await this . exec ( `cat ${ sshDirPath } /authorized_keys` ) ;
@@ -335,8 +340,10 @@ export class SSHService {
335340 async writeSSHKeyFile ( keys = [ ] , sshDirPath = `~/.ssh` ) {
336341 try {
337342 if ( sshDirPath . endsWith ( "/" ) ) sshDirPath = sshDirPath . slice ( 0 , - 1 , "" ) ; //if path ends with '/' remove it
338- let newKeys = keys . join ( "\n" )
339- let result = await this . exec ( `echo -e ${ StringUtils . escapeStringForShell ( newKeys ) } > ${ sshDirPath } /authorized_keys` ) ;
343+ let newKeys = keys . join ( "\n" ) ;
344+ let result = await this . exec (
345+ `echo -e ${ StringUtils . escapeStringForShell ( newKeys ) } > ${ sshDirPath } /authorized_keys`
346+ ) ;
340347 if ( SSHService . checkExecError ( result ) ) {
341348 throw new Error ( "Failed writing authorized keys:\n" + SSHService . extractExecError ( result ) ) ;
342349 }
@@ -351,7 +358,7 @@ export class SSHService {
351358 * Checks if mode is a directory with fs constants https://nodejs.org/api/fs.html#fsconstants.
352359 * `S_IFMT` is a bit mask used to extract the file type code.
353360 * `S_IFDIR` is a file type constant for a directory.
354- * @param {Integer } mode
361+ * @param {Integer } mode
355362 * @returns `True` if mode is a directory, `False` otherwise
356363 */
357364 isDir ( mode ) {
@@ -361,11 +368,11 @@ export class SSHService {
361368 /**
362369 * Get an SFTP session object from the connection pool.
363370 * Optionally takes a ssh session object as an argument, otherwise it will get a new connection from the pool.
364- * @param {Client } [conn]
371+ * @param {Client } [conn]
365372 * @returns sftp session object
366373 */
367374 async getSFTPSession ( conn = null ) {
368- conn = this . getConnectionFromPool ( )
375+ conn = this . getConnectionFromPool ( ) ;
369376 return new Promise ( ( resolve , reject ) => {
370377 conn . sftp ( ( err , sftp ) => {
371378 if ( err ) {
@@ -379,8 +386,8 @@ export class SSHService {
379386
380387 /**
381388 * Reads a directory's contents from remotePath using SFTP
382- * @param {String } remotePath
383- * @param {SFTP Session } [sftp]
389+ * @param {String } remotePath
390+ * @param {SFTP Session } [sftp]
384391 * @returns Array of objects containing filename and mode of the contents of a given directory on the remote server.
385392 */
386393 async readDirectorySFTP ( remotePath , sftp = null ) {
@@ -401,23 +408,23 @@ export class SSHService {
401408 /**
402409 * Returns an array of objects containing filename and mode of the contents of a given directory on the remote server.
403410 * Workaround for readdir not running with sudo permissions.
404- * @param {String } remotePath
405- * @returns
411+ * @param {String } remotePath
412+ * @returns
406413 */
407414 async readDirectorySSH ( remotePath ) {
408415 try {
409416 const result = await this . exec ( `find ${ remotePath } -maxdepth 1 -exec stat --format '%n\n%f\n' {} +` ) ;
410417 if ( SSHService . checkExecError ( result ) ) {
411418 throw new Error ( "Failed reading directory: " + SSHService . extractExecError ( result ) ) ;
412419 }
413- let files = result . stdout . split ( "\n\n" ) . filter ( ( e ) => e )
420+ let files = result . stdout . split ( "\n\n" ) . filter ( ( e ) => e ) ;
414421 files . shift ( ) ; //remove the first element which is the directory itself
415422 return files . map ( ( file ) => {
416423 let [ filename , mode ] = file . split ( "\n" ) ;
417424 filename = path . posix . basename ( path . posix . normalize ( filename ) ) ; // normalize path
418425 mode = parseInt ( mode , 16 ) ; // convert mode from hex to integer
419426 return { filename, mode } ;
420- } )
427+ } ) ;
421428 } catch ( error ) {
422429 log . error ( "Failed reading directory via SSH: " , error ) ;
423430 return [ ] ;
@@ -426,14 +433,14 @@ export class SSHService {
426433
427434 /**
428435 * Reads a directory's contents from localPath
429- * @param {String } localPath
436+ * @param {String } localPath
430437 * @returns Array of Dirent objects or an empty array on error
431438 */
432439 async readDirectoryLocal ( localPath ) {
433440 try {
434- log . info ( "localPath" , localPath )
441+ log . info ( "localPath" , localPath ) ;
435442 const filenames = await fs . promises . readdir ( localPath , { withFileTypes : true } ) ;
436- log . info ( "filenames" , filenames )
443+ log . info ( "filenames" , filenames ) ;
437444 return filenames ;
438445 } catch ( error ) {
439446 console . error ( "Failed reading local directory: " , error ) ;
@@ -445,30 +452,30 @@ export class SSHService {
445452 * Downloads a file from remotePath to localPath.
446453 * Uses "cat" to get file contents and pipes that stream to a local write stream.
447454 * This is a workaround for the lack of sudo permissions with sftp createReadStream.
448- * @param {String } remotePath
449- * @param {String } localPath
450- * @param {Client } [conn]
455+ * @param {String } remotePath
456+ * @param {String } localPath
457+ * @param {Client } [conn]
451458 * @returns `void`
452459 */
453460 async downloadFileSSH ( remotePath , localPath , conn = this . getConnectionFromPool ( ) ) {
454461 return new Promise ( ( resolve , reject ) => {
455462 const writeStream = fs . createWriteStream ( localPath ) ;
456- writeStream . on ( ' error' , reject ) ;
457- writeStream . on ( ' close' , resolve ) ;
463+ writeStream . on ( " error" , reject ) ;
464+ writeStream . on ( " close" , resolve ) ;
458465
459466 conn . exec ( `sudo cat ${ remotePath } ` , function ( err , stream ) {
460467 if ( err ) throw err ;
461- stream . on ( ' error' , reject ) ;
468+ stream . on ( " error" , reject ) ;
462469 stream . pipe ( writeStream ) ;
463470 } ) ;
464471 } ) ;
465472 }
466473
467474 /**
468475 * Downloads a Directory and all its contents recursively from the remotePath to the localPath
469- * @param {String } remotePath
470- * @param {String } localPath
471- * @param {Client } [conn]
476+ * @param {String } remotePath
477+ * @param {String } localPath
478+ * @param {Client } [conn]
472479 * @returns `true` if download was successful, `false` otherwise
473480 */
474481 async downloadDirectorySSH ( remotePath , localPath , conn = null ) {
@@ -481,7 +488,7 @@ export class SSHService {
481488 fs . mkdirSync ( localPath , { recursive : true } ) ;
482489 }
483490
484- const dirContents = await this . readDirectorySSH ( remotePath )
491+ const dirContents = await this . readDirectorySSH ( remotePath ) ;
485492 for ( let item of dirContents ) {
486493 const remoteFilePath = path . posix . join ( remotePath , item . filename ) ;
487494 const localFilePath = path . join ( localPath , item . filename ) ;
@@ -492,37 +499,37 @@ export class SSHService {
492499 await this . downloadFileSSH ( remoteFilePath , localFilePath ) ;
493500 }
494501 }
495- return true
502+ return true ;
496503 } catch ( error ) {
497504 log . error ( "Failed to download directory via SSH: " , error ) ;
498- return false
505+ return false ;
499506 }
500507 }
501508 /**
502509 * Uploads a file from localPath to remotePath
503- * @param {String } localPath
504- * @param {String } remotePath
505- * @param {Client } [conn]
510+ * @param {String } localPath
511+ * @param {String } remotePath
512+ * @param {Client } [conn]
506513 * @returns `void`
507514 */
508515 async uploadFileSSH ( localPath , remotePath , conn = this . getConnectionFromPool ( ) ) {
509516 return new Promise ( ( resolve , reject ) => {
510517 const readStream = fs . createReadStream ( localPath ) ;
511- readStream . on ( ' error' , reject ) ;
512- readStream . on ( ' close' , resolve ) ;
518+ readStream . on ( " error" , reject ) ;
519+ readStream . on ( " close" , resolve ) ;
513520
514521 conn . exec ( `sudo cat > ${ remotePath } ` , function ( err , stream ) {
515522 if ( err ) throw err ;
516- stream . on ( ' error' , reject ) ;
517- stream . on ( ' close' , resolve ) ;
523+ stream . on ( " error" , reject ) ;
524+ stream . on ( " close" , resolve ) ;
518525 readStream . pipe ( stream . stdin ) ;
519526 } ) ;
520527 } ) ;
521528 }
522529 /**
523530 * Ensures that the remotePath exists and is owned by the current user
524- * @param {String } remotePath
525- * @param {Client } [conn]
531+ * @param {String } remotePath
532+ * @param {Client } [conn]
526533 */
527534 async ensureRemotePathExists ( remotePath , conn = this . getConnectionFromPool ( ) ) {
528535 return new Promise ( ( resolve , reject ) => {
@@ -535,9 +542,9 @@ export class SSHService {
535542
536543 /**
537544 * Uploads a directory and all its contents recursively from the localPath to the remotePath
538- * @param {String } localPath
539- * @param {String } remotePath
540- * @param {Client } [conn]
545+ * @param {String } localPath
546+ * @param {String } remotePath
547+ * @param {Client } [conn]
541548 * @returns `true` if upload was successful, `false` otherwise
542549 */
543550 async uploadDirectorySSH ( localPath , remotePath , conn = null ) {
@@ -546,9 +553,9 @@ export class SSHService {
546553 conn = await this . getConnectionFromPool ( ) ;
547554 }
548555
549- await this . ensureRemotePathExists ( remotePath )
556+ await this . ensureRemotePathExists ( remotePath ) ;
550557
551- const dirContents = await this . readDirectoryLocal ( localPath )
558+ const dirContents = await this . readDirectoryLocal ( localPath ) ;
552559 for ( let item of dirContents ) {
553560 const remoteFilePath = path . posix . join ( remotePath , item . name ) ;
554561 const localFilePath = path . join ( localPath , item . name ) ;
@@ -558,10 +565,10 @@ export class SSHService {
558565 await this . uploadFileSSH ( localFilePath , remoteFilePath ) ;
559566 }
560567 }
561- return true
568+ return true ;
562569 } catch ( error ) {
563570 log . error ( "Failed to upload directory via SSH: " , error ) ;
564- return false
571+ return false ;
565572 }
566573 }
567574}
0 commit comments