mirror of https://github.com/laurent22/joplin.git
221 lines
5.5 KiB
JavaScript
221 lines
5.5 KiB
JavaScript
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/BaseItem.js");
|
|
const { Synchronizer } = require("lib/synchronizer.js");
|
|
const { reg } = require("lib/registry.js");
|
|
const { cliUtils } = require("./cli-utils.js");
|
|
const md5 = require("md5");
|
|
const locker = require("proper-lockfile");
|
|
const fs = require("fs-extra");
|
|
const SyncTargetRegistry = require("lib/SyncTargetRegistry");
|
|
|
|
class Command extends BaseCommand {
|
|
constructor() {
|
|
super();
|
|
this.syncTargetId_ = null;
|
|
this.releaseLockFn_ = null;
|
|
this.oneDriveApiUtils_ = null;
|
|
}
|
|
|
|
usage() {
|
|
return "sync";
|
|
}
|
|
|
|
description() {
|
|
return _("Synchronises with remote storage.");
|
|
}
|
|
|
|
options() {
|
|
return [["--target <target>", _("Sync to provided target (defaults to sync.target config value)")]];
|
|
}
|
|
|
|
static lockFile(filePath) {
|
|
return new Promise((resolve, reject) => {
|
|
locker.lock(filePath, { stale: 1000 * 60 * 5 }, (error, release) => {
|
|
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);
|
|
});
|
|
});
|
|
}
|
|
|
|
async doAuth() {
|
|
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
|
const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_);
|
|
|
|
if (this.syncTargetId_ === 3 || this.syncTargetId_ === 4) {
|
|
// OneDrive
|
|
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
|
|
const auth = await this.oneDriveApiUtils_.oauthDance({
|
|
log: (...s) => {
|
|
return this.stdout(...s);
|
|
},
|
|
});
|
|
this.oneDriveApiUtils_ = null;
|
|
|
|
Setting.setValue("sync." + this.syncTargetId_ + ".auth", auth ? JSON.stringify(auth) : null);
|
|
if (!auth) {
|
|
this.stdout(_("Authentication was not completed (did not receive an authentication token)."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
this.stdout(_("Not authentified with %s. Please provide any missing credentials.", syncTarget.label()));
|
|
return false;
|
|
}
|
|
|
|
cancelAuth() {
|
|
if (this.oneDriveApiUtils_) {
|
|
this.oneDriveApiUtils_.cancelOAuthDance();
|
|
return;
|
|
}
|
|
}
|
|
|
|
doingAuth() {
|
|
return !!this.oneDriveApiUtils_;
|
|
}
|
|
|
|
async action(args) {
|
|
this.releaseLockFn_ = null;
|
|
|
|
// Lock is unique per profile/database
|
|
const lockFilePath = require("os").tmpdir() + "/synclock_" + md5(escape(Setting.value("profileDir"))); // https://github.com/pvorb/node-md5/issues/41
|
|
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, "synclock");
|
|
|
|
try {
|
|
if (await Command.isLocked(lockFilePath)) throw new Error(_("Synchronisation is already in progress."));
|
|
|
|
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);
|
|
this.stdout(msg);
|
|
return;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const cleanUp = () => {
|
|
cliUtils.redrawDone();
|
|
if (this.releaseLockFn_) {
|
|
this.releaseLockFn_();
|
|
this.releaseLockFn_ = null;
|
|
}
|
|
};
|
|
|
|
try {
|
|
this.syncTargetId_ = Setting.value("sync.target");
|
|
if (args.options.target) this.syncTargetId_ = args.options.target;
|
|
|
|
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
|
|
|
if (!syncTarget.isAuthenticated()) {
|
|
app()
|
|
.gui()
|
|
.showConsole();
|
|
app()
|
|
.gui()
|
|
.maximizeConsole();
|
|
|
|
const authDone = await this.doAuth();
|
|
if (!authDone) return cleanUp();
|
|
}
|
|
|
|
const sync = await syncTarget.synchronizer();
|
|
|
|
let options = {
|
|
onProgress: report => {
|
|
let lines = Synchronizer.reportToLines(report);
|
|
if (lines.length) cliUtils.redraw(lines.join(" "));
|
|
},
|
|
onMessage: msg => {
|
|
cliUtils.redrawDone();
|
|
this.stdout(msg);
|
|
},
|
|
};
|
|
|
|
this.stdout(_("Synchronisation target: %s (%s)", Setting.enumOptionLabel("sync.target", this.syncTargetId_), this.syncTargetId_));
|
|
|
|
if (!sync) throw new Error(_("Cannot initialize synchroniser."));
|
|
|
|
this.stdout(_("Starting synchronisation..."));
|
|
|
|
const contextKey = "sync." + this.syncTargetId_ + ".context";
|
|
let context = Setting.value(contextKey);
|
|
|
|
context = context ? JSON.parse(context) : {};
|
|
options.context = context;
|
|
|
|
try {
|
|
let newContext = await sync.start(options);
|
|
Setting.setValue(contextKey, JSON.stringify(newContext));
|
|
} catch (error) {
|
|
if (error.code == "alreadyStarted") {
|
|
this.stdout(error.message);
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
await app().refreshCurrentFolder();
|
|
} catch (error) {
|
|
cleanUp();
|
|
throw error;
|
|
}
|
|
|
|
cleanUp();
|
|
}
|
|
|
|
async cancel() {
|
|
if (this.doingAuth()) {
|
|
this.cancelAuth();
|
|
return;
|
|
}
|
|
|
|
const syncTargetId = this.syncTargetId_ ? this.syncTargetId_ : Setting.value("sync.target");
|
|
|
|
cliUtils.redrawDone();
|
|
|
|
this.stdout(_("Cancelling... Please wait."));
|
|
|
|
const syncTarget = reg.syncTarget(syncTargetId);
|
|
|
|
if (syncTarget.isAuthenticated()) {
|
|
const sync = await syncTarget.synchronizer();
|
|
if (sync) await sync.cancel();
|
|
} else {
|
|
if (this.releaseLockFn_) this.releaseLockFn_();
|
|
this.releaseLockFn_ = null;
|
|
}
|
|
|
|
this.syncTargetId_ = null;
|
|
}
|
|
|
|
cancellable() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
module.exports = Command;
|