@@ -21,7 +21,8 @@ export class SSHService {
21
21
} , 100 ) ;
22
22
}
23
23
24
- static checkExecError ( err ) {
24
+ static checkExecError ( err , accept_empty_result = false ) {
25
+ if ( accept_empty_result ) return err . rc != 0 ;
25
26
return ! err || err . rc != 0 ;
26
27
}
27
28
@@ -62,8 +63,12 @@ export class SSHService {
62
63
let lastIndex = this . connectionPool . length - 1 ;
63
64
const threshholdIndex = lastIndex - 2 ;
64
65
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 ) ;
67
72
}
68
73
if ( this . connectionPool . length > 5 && this . connectionPool [ threshholdIndex ] ?. _chanMgr ?. _count === 0 ) {
69
74
this . removeConnectionCount ++ ;
@@ -302,12 +307,12 @@ export class SSHService {
302
307
const keyPair = generateKeyPairSync ( opts . keyType , opts ) ;
303
308
let exitingKeys = await this . readSSHKeyFile ( ) ;
304
309
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 ;
311
316
}
312
317
return exitingKeys ;
313
318
} catch ( err ) {
@@ -316,7 +321,7 @@ export class SSHService {
316
321
}
317
322
318
323
async readSSHKeyFile ( sshDirPath = `~/.ssh` ) {
319
- let authorizedKeys = [ ]
324
+ let authorizedKeys = [ ] ;
320
325
try {
321
326
if ( sshDirPath . endsWith ( "/" ) ) sshDirPath = sshDirPath . slice ( 0 , - 1 , "" ) ; //if path ends with '/' remove it
322
327
let result = await this . exec ( `cat ${ sshDirPath } /authorized_keys` ) ;
@@ -335,8 +340,10 @@ export class SSHService {
335
340
async writeSSHKeyFile ( keys = [ ] , sshDirPath = `~/.ssh` ) {
336
341
try {
337
342
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
+ ) ;
340
347
if ( SSHService . checkExecError ( result ) ) {
341
348
throw new Error ( "Failed writing authorized keys:\n" + SSHService . extractExecError ( result ) ) ;
342
349
}
@@ -351,7 +358,7 @@ export class SSHService {
351
358
* Checks if mode is a directory with fs constants https://nodejs.org/api/fs.html#fsconstants.
352
359
* `S_IFMT` is a bit mask used to extract the file type code.
353
360
* `S_IFDIR` is a file type constant for a directory.
354
- * @param {Integer } mode
361
+ * @param {Integer } mode
355
362
* @returns `True` if mode is a directory, `False` otherwise
356
363
*/
357
364
isDir ( mode ) {
@@ -361,11 +368,11 @@ export class SSHService {
361
368
/**
362
369
* Get an SFTP session object from the connection pool.
363
370
* 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]
365
372
* @returns sftp session object
366
373
*/
367
374
async getSFTPSession ( conn = null ) {
368
- conn = this . getConnectionFromPool ( )
375
+ conn = this . getConnectionFromPool ( ) ;
369
376
return new Promise ( ( resolve , reject ) => {
370
377
conn . sftp ( ( err , sftp ) => {
371
378
if ( err ) {
@@ -379,8 +386,8 @@ export class SSHService {
379
386
380
387
/**
381
388
* 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]
384
391
* @returns Array of objects containing filename and mode of the contents of a given directory on the remote server.
385
392
*/
386
393
async readDirectorySFTP ( remotePath , sftp = null ) {
@@ -401,23 +408,23 @@ export class SSHService {
401
408
/**
402
409
* Returns an array of objects containing filename and mode of the contents of a given directory on the remote server.
403
410
* Workaround for readdir not running with sudo permissions.
404
- * @param {String } remotePath
405
- * @returns
411
+ * @param {String } remotePath
412
+ * @returns
406
413
*/
407
414
async readDirectorySSH ( remotePath ) {
408
415
try {
409
416
const result = await this . exec ( `find ${ remotePath } -maxdepth 1 -exec stat --format '%n\n%f\n' {} +` ) ;
410
417
if ( SSHService . checkExecError ( result ) ) {
411
418
throw new Error ( "Failed reading directory: " + SSHService . extractExecError ( result ) ) ;
412
419
}
413
- let files = result . stdout . split ( "\n\n" ) . filter ( ( e ) => e )
420
+ let files = result . stdout . split ( "\n\n" ) . filter ( ( e ) => e ) ;
414
421
files . shift ( ) ; //remove the first element which is the directory itself
415
422
return files . map ( ( file ) => {
416
423
let [ filename , mode ] = file . split ( "\n" ) ;
417
424
filename = path . posix . basename ( path . posix . normalize ( filename ) ) ; // normalize path
418
425
mode = parseInt ( mode , 16 ) ; // convert mode from hex to integer
419
426
return { filename, mode } ;
420
- } )
427
+ } ) ;
421
428
} catch ( error ) {
422
429
log . error ( "Failed reading directory via SSH: " , error ) ;
423
430
return [ ] ;
@@ -426,14 +433,14 @@ export class SSHService {
426
433
427
434
/**
428
435
* Reads a directory's contents from localPath
429
- * @param {String } localPath
436
+ * @param {String } localPath
430
437
* @returns Array of Dirent objects or an empty array on error
431
438
*/
432
439
async readDirectoryLocal ( localPath ) {
433
440
try {
434
- log . info ( "localPath" , localPath )
441
+ log . info ( "localPath" , localPath ) ;
435
442
const filenames = await fs . promises . readdir ( localPath , { withFileTypes : true } ) ;
436
- log . info ( "filenames" , filenames )
443
+ log . info ( "filenames" , filenames ) ;
437
444
return filenames ;
438
445
} catch ( error ) {
439
446
console . error ( "Failed reading local directory: " , error ) ;
@@ -445,30 +452,30 @@ export class SSHService {
445
452
* Downloads a file from remotePath to localPath.
446
453
* Uses "cat" to get file contents and pipes that stream to a local write stream.
447
454
* 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]
451
458
* @returns `void`
452
459
*/
453
460
async downloadFileSSH ( remotePath , localPath , conn = this . getConnectionFromPool ( ) ) {
454
461
return new Promise ( ( resolve , reject ) => {
455
462
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 ) ;
458
465
459
466
conn . exec ( `sudo cat ${ remotePath } ` , function ( err , stream ) {
460
467
if ( err ) throw err ;
461
- stream . on ( ' error' , reject ) ;
468
+ stream . on ( " error" , reject ) ;
462
469
stream . pipe ( writeStream ) ;
463
470
} ) ;
464
471
} ) ;
465
472
}
466
473
467
474
/**
468
475
* 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]
472
479
* @returns `true` if download was successful, `false` otherwise
473
480
*/
474
481
async downloadDirectorySSH ( remotePath , localPath , conn = null ) {
@@ -481,7 +488,7 @@ export class SSHService {
481
488
fs . mkdirSync ( localPath , { recursive : true } ) ;
482
489
}
483
490
484
- const dirContents = await this . readDirectorySSH ( remotePath )
491
+ const dirContents = await this . readDirectorySSH ( remotePath ) ;
485
492
for ( let item of dirContents ) {
486
493
const remoteFilePath = path . posix . join ( remotePath , item . filename ) ;
487
494
const localFilePath = path . join ( localPath , item . filename ) ;
@@ -492,37 +499,37 @@ export class SSHService {
492
499
await this . downloadFileSSH ( remoteFilePath , localFilePath ) ;
493
500
}
494
501
}
495
- return true
502
+ return true ;
496
503
} catch ( error ) {
497
504
log . error ( "Failed to download directory via SSH: " , error ) ;
498
- return false
505
+ return false ;
499
506
}
500
507
}
501
508
/**
502
509
* 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]
506
513
* @returns `void`
507
514
*/
508
515
async uploadFileSSH ( localPath , remotePath , conn = this . getConnectionFromPool ( ) ) {
509
516
return new Promise ( ( resolve , reject ) => {
510
517
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 ) ;
513
520
514
521
conn . exec ( `sudo cat > ${ remotePath } ` , function ( err , stream ) {
515
522
if ( err ) throw err ;
516
- stream . on ( ' error' , reject ) ;
517
- stream . on ( ' close' , resolve ) ;
523
+ stream . on ( " error" , reject ) ;
524
+ stream . on ( " close" , resolve ) ;
518
525
readStream . pipe ( stream . stdin ) ;
519
526
} ) ;
520
527
} ) ;
521
528
}
522
529
/**
523
530
* 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]
526
533
*/
527
534
async ensureRemotePathExists ( remotePath , conn = this . getConnectionFromPool ( ) ) {
528
535
return new Promise ( ( resolve , reject ) => {
@@ -535,9 +542,9 @@ export class SSHService {
535
542
536
543
/**
537
544
* 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]
541
548
* @returns `true` if upload was successful, `false` otherwise
542
549
*/
543
550
async uploadDirectorySSH ( localPath , remotePath , conn = null ) {
@@ -546,9 +553,9 @@ export class SSHService {
546
553
conn = await this . getConnectionFromPool ( ) ;
547
554
}
548
555
549
- await this . ensureRemotePathExists ( remotePath )
556
+ await this . ensureRemotePathExists ( remotePath ) ;
550
557
551
- const dirContents = await this . readDirectoryLocal ( localPath )
558
+ const dirContents = await this . readDirectoryLocal ( localPath ) ;
552
559
for ( let item of dirContents ) {
553
560
const remoteFilePath = path . posix . join ( remotePath , item . name ) ;
554
561
const localFilePath = path . join ( localPath , item . name ) ;
@@ -558,10 +565,10 @@ export class SSHService {
558
565
await this . uploadFileSSH ( localFilePath , remoteFilePath ) ;
559
566
}
560
567
}
561
- return true
568
+ return true ;
562
569
} catch ( error ) {
563
570
log . error ( "Failed to upload directory via SSH: " , error ) ;
564
- return false
571
+ return false ;
565
572
}
566
573
}
567
574
}
0 commit comments