joplin/CliClient/app/command-sync.js

169 lines
4.3 KiB
JavaScript
Raw Normal View History

2017-07-10 20:03:46 +00:00
import { BaseCommand } from './base-command.js';
import { app } from './app.js';
import { _ } from 'lib/locale.js';
2017-07-24 19:47:01 +00:00
import { OneDriveApiNodeUtils } from './onedrive-api-node-utils.js';
2017-07-10 20:03:46 +00:00
import { Setting } from 'lib/models/setting.js';
import { BaseItem } from 'lib/models/base-item.js';
import { Synchronizer } from 'lib/synchronizer.js';
import { reg } from 'lib/registry.js';
2017-08-04 16:50:12 +00:00
import { cliUtils } from './cli-utils.js';
import md5 from 'md5';
2017-07-17 19:37:59 +00:00
const locker = require('proper-lockfile');
const fs = require('fs-extra');
2017-07-19 19:15:55 +00:00
const osTmpdir = require('os-tmpdir');
2017-07-10 20:03:46 +00:00
class Command extends BaseCommand {
2017-07-17 18:46:09 +00:00
constructor() {
super();
this.syncTarget_ = null;
2017-07-17 19:37:59 +00:00
this.releaseLockFn_ = null;
2017-07-17 18:46:09 +00:00
}
2017-07-10 20:03:46 +00:00
usage() {
return 'sync';
}
description() {
2017-07-26 21:27:03 +00:00
return _('Synchronises with remote storage.');
2017-07-10 20:03:46 +00:00
}
options() {
return [
2017-07-28 18:13:07 +00:00
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
2017-07-18 18:49:47 +00:00
['--random-failures', 'For debugging purposes. Do not use.'],
2017-07-10 20:03:46 +00:00
];
}
2017-07-17 19:37:59 +00:00
static lockFile(filePath) {
return new Promise((resolve, reject) => {
2017-07-19 19:15:55 +00:00
locker.lock(filePath, { stale: 1000 * 60 * 5 }, (error, release) => {
2017-07-17 19:37:59 +00: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-07-10 20:03:46 +00:00
async action(args) {
2017-07-17 19:37:59 +00:00
this.releaseLockFn_ = null;
2017-07-17 18:46:09 +00:00
// Lock is unique per profile/database
const lockFilePath = osTmpdir() + '/synclock_' + md5(Setting.value('profileDir'));
2017-07-17 19:37:59 +00:00
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
2017-07-14 19:06:01 +00:00
2017-08-04 16:50:12 +00:00
try {
if (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.'));
2017-07-13 18:09:47 +00:00
2017-08-04 16:50:12 +00: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 16:30:27 +00:00
this.stdout(msg);
2017-08-04 16:50:12 +00:00
return;
}
throw error;
}
2017-07-10 20:03:46 +00:00
2017-07-17 19:37:59 +00:00
try {
this.syncTarget_ = Setting.value('sync.target');
if (args.options.target) this.syncTarget_ = args.options.target;
2017-07-24 19:47:01 +00:00
2017-07-26 21:07:27 +00:00
if (this.syncTarget_ == Setting.SYNC_TARGET_ONEDRIVE && !reg.syncHasAuth(this.syncTarget_)) {
2017-07-24 19:47:01 +00:00
const oneDriveApiUtils = new OneDriveApiNodeUtils(reg.oneDriveApi());
const auth = await oneDriveApiUtils.oauthDance(this);
Setting.setValue('sync.3.auth', auth ? JSON.stringify(auth) : null);
if (!auth) return;
}
2017-07-19 19:15:55 +00:00
let sync = await reg.synchronizer(this.syncTarget_);
2017-07-10 20:03:46 +00:00
2017-07-17 19:37:59 +00:00
let options = {
onProgress: (report) => {
let lines = Synchronizer.reportToLines(report);
2017-08-04 16:50:12 +00:00
if (lines.length) cliUtils.redraw(lines.join(' '));
2017-07-17 19:37:59 +00:00
},
onMessage: (msg) => {
2017-08-04 16:50:12 +00:00
cliUtils.redrawDone();
2017-10-07 16:30:27 +00:00
this.stdout(msg);
2017-07-17 19:37:59 +00:00
},
randomFailures: args.options['random-failures'] === true,
};
2017-10-07 16:30:27 +00:00
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTarget_), this.syncTarget_));
2017-07-17 19:37:59 +00:00
2017-07-26 21:27:03 +00:00
if (!sync) throw new Error(_('Cannot initialize synchroniser.'));
2017-07-17 19:37:59 +00:00
2017-10-07 16:30:27 +00:00
this.stdout(_('Starting synchronisation...'));
2017-07-17 19:37:59 +00:00
2017-08-19 20:56:28 +00:00
const contextKey = 'sync.' + this.syncTarget_ + '.context';
let context = Setting.value(contextKey);
2017-07-18 19:57:49 +00:00
context = context ? JSON.parse(context) : {};
options.context = context;
2017-07-24 19:47:01 +00:00
try {
let newContext = await sync.start(options);
2017-08-19 20:56:28 +00:00
Setting.setValue(contextKey, JSON.stringify(newContext));
2017-07-24 19:47:01 +00:00
} catch (error) {
if (error.code == 'alreadyStarted') {
2017-10-07 16:30:27 +00:00
this.stdout(error.message);
2017-07-24 19:47:01 +00:00
} else {
throw error;
}
}
2017-07-17 19:37:59 +00:00
await app().refreshCurrentFolder();
} catch (error) {
2017-08-04 16:50:12 +00:00
cliUtils.redrawDone();
2017-07-17 19:37:59 +00:00
this.releaseLockFn_();
this.releaseLockFn_ = null;
throw error;
}
2017-07-15 22:47:11 +00:00
2017-08-04 16:50:12 +00:00
cliUtils.redrawDone();
2017-07-17 19:37:59 +00:00
this.releaseLockFn_();
this.releaseLockFn_ = null;
2017-07-10 20:03:46 +00:00
}
async cancel() {
2017-07-17 18:46:09 +00:00
const target = this.syncTarget_ ? this.syncTarget_ : Setting.value('sync.target');
2017-08-04 16:50:12 +00:00
cliUtils.redrawDone();
2017-10-07 16:30:27 +00:00
this.stdout(_('Cancelling... Please wait.'));
2017-07-26 20:09:33 +00:00
2017-07-26 21:07:27 +00:00
if (reg.syncHasAuth(target)) {
2017-07-26 20:09:33 +00:00
let sync = await reg.synchronizer(target);
if (sync) sync.cancel();
} else {
if (this.releaseLockFn_) this.releaseLockFn_();
this.releaseLockFn_ = null;
}
2017-07-17 18:46:09 +00:00
this.syncTarget_ = null;
2017-07-10 20:03:46 +00:00
}
2017-08-20 14:29:18 +00:00
cancellable() {
return true;
}
2017-07-10 20:03:46 +00:00
}
module.exports = Command;