2017-11-03 02:09:34 +02:00
const { BaseCommand } = require ( './base-command.js' ) ;
const { app } = require ( './app.js' ) ;
const { _ } = require ( 'lib/locale.js' ) ;
const { OneDriveApiNodeUtils } = require ( './onedrive-api-node-utils.js' ) ;
const { Setting } = require ( 'lib/models/setting.js' ) ;
const { BaseItem } = require ( 'lib/models/base-item.js' ) ;
const { Synchronizer } = require ( 'lib/synchronizer.js' ) ;
const { reg } = require ( 'lib/registry.js' ) ;
const { cliUtils } = require ( './cli-utils.js' ) ;
const md5 = require ( 'md5' ) ;
2017-07-17 21:37:59 +02:00
const locker = require ( 'proper-lockfile' ) ;
const fs = require ( 'fs-extra' ) ;
2017-07-19 21:15:55 +02:00
const osTmpdir = require ( 'os-tmpdir' ) ;
2017-07-10 22:03:46 +02:00
class Command extends BaseCommand {
2017-07-17 20:46:09 +02:00
constructor ( ) {
super ( ) ;
2017-11-24 01:10:55 +02:00
this . syncTargetId _ = null ;
2017-07-17 21:37:59 +02:00
this . releaseLockFn _ = null ;
2017-10-24 20:03:12 +02:00
this . oneDriveApiUtils _ = null ;
2017-07-17 20:46:09 +02:00
}
2017-07-10 22:03:46 +02:00
usage ( ) {
return 'sync' ;
}
description ( ) {
2017-07-26 23:27:03 +02:00
return _ ( 'Synchronises with remote storage.' ) ;
2017-07-10 22:03:46 +02:00
}
options ( ) {
return [
2017-07-28 20:13:07 +02:00
[ '--target <target>' , _ ( 'Sync to provided target (defaults to sync.target config value)' ) ] ,
2017-07-10 22:03:46 +02:00
] ;
}
2017-07-17 21:37:59 +02:00
static lockFile ( filePath ) {
return new Promise ( ( resolve , reject ) => {
2017-07-19 21:15:55 +02:00
locker . lock ( filePath , { stale : 1000 * 60 * 5 } , ( error , release ) => {
2017-07-17 21:37:59 +02:00
if ( error ) {
reject ( error ) ;
return ;
}
resolve ( release ) ;
} ) ;
} ) ;
}
static isLocked ( filePath ) {
return new Promise ( ( resolve , reject ) => {
locker . check ( filePath , ( error , isLocked ) => {
if ( error ) {
reject ( error ) ;
return ;
}
resolve ( isLocked ) ;
} ) ;
} ) ;
}
2017-11-24 01:10:55 +02:00
async doAuth ( syncTargetId ) {
const syncTarget = reg . syncTarget ( this . syncTargetId _ ) ;
2017-11-24 20:06:30 +02:00
this . oneDriveApiUtils _ = new OneDriveApiNodeUtils ( syncTarget . api ( ) ) ;
2017-11-24 01:10:55 +02:00
const auth = await this . oneDriveApiUtils _ . oauthDance ( {
log : ( ... s ) => { return this . stdout ( ... s ) ; }
} ) ;
this . oneDriveApiUtils _ = null ;
return auth ;
}
cancelAuth ( ) {
if ( this . oneDriveApiUtils _ ) {
this . oneDriveApiUtils _ . cancelOAuthDance ( ) ;
return ;
}
}
doingAuth ( ) {
return ! ! this . oneDriveApiUtils _ ;
}
2017-07-10 22:03:46 +02:00
async action ( args ) {
2017-07-17 21:37:59 +02:00
this . releaseLockFn _ = null ;
2017-07-17 20:46:09 +02:00
2017-07-24 20:01:40 +02:00
// Lock is unique per profile/database
const lockFilePath = osTmpdir ( ) + '/synclock_' + md5 ( Setting . value ( 'profileDir' ) ) ;
2017-07-17 21:37:59 +02:00
if ( ! await fs . pathExists ( lockFilePath ) ) await fs . writeFile ( lockFilePath , 'synclock' ) ;
2017-07-14 21:06:01 +02:00
2017-08-04 18:50:12 +02:00
try {
if ( await Command . isLocked ( lockFilePath ) ) throw new Error ( _ ( 'Synchronisation is already in progress.' ) ) ;
2017-07-13 20:09:47 +02:00
2017-08-04 18:50:12 +02:00
this . releaseLockFn _ = await Command . lockFile ( lockFilePath ) ;
} catch ( error ) {
if ( error . code == 'ELOCKED' ) {
const msg = _ ( 'Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at "%s" and resume the operation.' , error . file ) ;
2017-10-07 18:30:27 +02:00
this . stdout ( msg ) ;
2017-08-04 18:50:12 +02:00
return ;
}
throw error ;
}
2017-07-10 22:03:46 +02:00
2017-10-24 20:03:12 +02:00
const cleanUp = ( ) => {
cliUtils . redrawDone ( ) ;
if ( this . releaseLockFn _ ) {
this . releaseLockFn _ ( ) ;
this . releaseLockFn _ = null ;
}
} ;
2017-07-17 21:37:59 +02:00
try {
2017-11-24 01:10:55 +02:00
this . syncTargetId _ = Setting . value ( 'sync.target' ) ;
if ( args . options . target ) this . syncTargetId _ = args . options . target ;
const syncTarget = reg . syncTarget ( this . syncTargetId _ ) ;
2017-07-24 21:47:01 +02:00
2017-11-24 20:59:16 +02:00
if ( ! syncTarget . isAuthenticated ( ) ) {
2017-10-19 00:13:53 +02:00
app ( ) . gui ( ) . showConsole ( ) ;
app ( ) . gui ( ) . maximizeConsole ( ) ;
2017-11-24 01:10:55 +02:00
const auth = await this . doAuth ( this . syncTargetId _ ) ;
Setting . setValue ( 'sync.' + this . syncTargetId _ + '.auth' , auth ? JSON . stringify ( auth ) : null ) ;
2017-10-24 20:03:12 +02:00
if ( ! auth ) {
this . stdout ( _ ( 'Authentication was not completed (did not receive an authentication token).' ) ) ;
return cleanUp ( ) ;
}
2017-07-24 21:47:01 +02:00
}
2017-07-19 21:15:55 +02:00
2017-11-24 01:10:55 +02:00
const sync = await syncTarget . synchronizer ( ) ;
2017-07-10 22:03:46 +02:00
2017-07-17 21:37:59 +02:00
let options = {
onProgress : ( report ) => {
2017-07-18 00:43:29 +02:00
let lines = Synchronizer . reportToLines ( report ) ;
2017-08-04 18:50:12 +02:00
if ( lines . length ) cliUtils . redraw ( lines . join ( ' ' ) ) ;
2017-07-17 21:37:59 +02:00
} ,
onMessage : ( msg ) => {
2017-08-04 18:50:12 +02:00
cliUtils . redrawDone ( ) ;
2017-10-07 18:30:27 +02:00
this . stdout ( msg ) ;
2017-07-17 21:37:59 +02:00
} ,
} ;
2017-11-24 01:10:55 +02:00
this . stdout ( _ ( 'Synchronisation target: %s (%s)' , Setting . enumOptionLabel ( 'sync.target' , this . syncTargetId _ ) , this . syncTargetId _ ) ) ;
2017-07-17 21:37:59 +02:00
2017-07-26 23:27:03 +02:00
if ( ! sync ) throw new Error ( _ ( 'Cannot initialize synchroniser.' ) ) ;
2017-07-17 21:37:59 +02:00
2017-10-07 18:30:27 +02:00
this . stdout ( _ ( 'Starting synchronisation...' ) ) ;
2017-07-17 21:37:59 +02:00
2017-11-24 01:10:55 +02:00
const contextKey = 'sync.' + this . syncTargetId _ + '.context' ;
2017-08-19 22:56:28 +02:00
let context = Setting . value ( contextKey ) ;
2017-07-18 21:57:49 +02:00
context = context ? JSON . parse ( context ) : { } ;
options . context = context ;
2017-07-24 21:47:01 +02:00
try {
let newContext = await sync . start ( options ) ;
2017-08-19 22:56:28 +02:00
Setting . setValue ( contextKey , JSON . stringify ( newContext ) ) ;
2017-07-24 21:47:01 +02:00
} catch ( error ) {
if ( error . code == 'alreadyStarted' ) {
2017-10-07 18:30:27 +02:00
this . stdout ( error . message ) ;
2017-07-24 21:47:01 +02:00
} else {
throw error ;
}
}
2017-07-17 21:37:59 +02:00
await app ( ) . refreshCurrentFolder ( ) ;
} catch ( error ) {
2017-10-24 20:03:12 +02:00
cleanUp ( ) ;
2017-07-17 21:37:59 +02:00
throw error ;
}
2017-07-16 00:47:11 +02:00
2017-10-24 20:03:12 +02:00
cleanUp ( ) ;
2017-07-10 22:03:46 +02:00
}
async cancel ( ) {
2017-11-24 01:10:55 +02:00
if ( this . doingAuth ( ) ) {
this . cancelAuth ( ) ;
2017-10-24 20:03:12 +02:00
return ;
}
2017-11-24 01:10:55 +02:00
const syncTargetId = this . syncTargetId _ ? this . syncTargetId _ : Setting . value ( 'sync.target' ) ;
2017-07-17 20:46:09 +02:00
2017-08-04 18:50:12 +02:00
cliUtils . redrawDone ( ) ;
2017-10-07 18:30:27 +02:00
this . stdout ( _ ( 'Cancelling... Please wait.' ) ) ;
2017-07-26 22:09:33 +02:00
2017-11-24 01:10:55 +02:00
const syncTarget = reg . syncTarget ( syncTargetId ) ;
if ( syncTarget . isAuthenticated ( ) ) {
const sync = await syncTarget . synchronizer ( ) ;
2017-10-14 20:03:23 +02:00
if ( sync ) await sync . cancel ( ) ;
2017-07-26 22:09:33 +02:00
} else {
if ( this . releaseLockFn _ ) this . releaseLockFn _ ( ) ;
this . releaseLockFn _ = null ;
}
2017-07-17 20:46:09 +02:00
2017-11-24 01:10:55 +02:00
this . syncTargetId _ = null ;
2017-07-10 22:03:46 +02:00
}
2017-08-20 16:29:18 +02:00
cancellable ( ) {
return true ;
}
2017-07-10 22:03:46 +02:00
}
module . exports = Command ;