mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
All: Allow a sync client to lock a sync target, so that migration operations can be performed on it
This commit is contained in:
parent
8a097fb79c
commit
c98644b72f
File diff suppressed because it is too large
Load Diff
@ -131,7 +131,7 @@ class FileApi {
|
||||
|
||||
this.logger().debug(`list ${this.baseDir()}`);
|
||||
|
||||
const result = await tryAndRepeat(() => this.driver_.list(this.baseDir(), options), this.requestRepeatCount());
|
||||
const result = await tryAndRepeat(() => this.driver_.list(this.fullPath_(path), options), this.requestRepeatCount());
|
||||
|
||||
if (!options.includeHidden) {
|
||||
let temp = [];
|
||||
|
@ -12,6 +12,7 @@ const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { filename } = require('lib/path-utils');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget');
|
||||
const TaskQueue = require('lib/TaskQueue');
|
||||
@ -22,6 +23,7 @@ class Synchronizer {
|
||||
this.db_ = db;
|
||||
this.api_ = api;
|
||||
this.syncDirName_ = '.sync';
|
||||
this.lockDirName_ = '.lock';
|
||||
this.resourceDirName_ = BaseSyncTarget.resourceDirName();
|
||||
this.logger_ = new Logger();
|
||||
this.appType_ = appType;
|
||||
@ -29,6 +31,7 @@ class Synchronizer {
|
||||
this.autoStartDecryptionWorker_ = true;
|
||||
this.maxResourceSize_ = null;
|
||||
this.downloadQueue_ = null;
|
||||
this.clientId_ = Setting.value('clientId');
|
||||
|
||||
// Debug flags are used to test certain hard-to-test conditions
|
||||
// such as cancelling in the middle of a loop.
|
||||
@ -52,6 +55,10 @@ class Synchronizer {
|
||||
return this.api_;
|
||||
}
|
||||
|
||||
clientId() {
|
||||
return this.clientId_;
|
||||
}
|
||||
|
||||
setLogger(l) {
|
||||
this.logger_ = l;
|
||||
}
|
||||
@ -190,6 +197,66 @@ class Synchronizer {
|
||||
return state;
|
||||
}
|
||||
|
||||
async acquireLock_() {
|
||||
await this.checkLock_();
|
||||
await this.api().put(`${this.lockDirName_}/${this.clientId()}_${Date.now()}.lock`, `${Date.now()}`);
|
||||
}
|
||||
|
||||
async releaseLock_() {
|
||||
const lockFiles = await this.lockFiles_();
|
||||
for (const lockFile of lockFiles) {
|
||||
const p = this.parseLockFilePath(lockFile.path);
|
||||
if (p.clientId === this.clientId()) {
|
||||
await this.api().delete(p.fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async lockFiles_() {
|
||||
const output = await this.api().list(this.lockDirName_);
|
||||
return output.items;
|
||||
}
|
||||
|
||||
parseLockFilePath(path) {
|
||||
const splitted = filename(path).split('_');
|
||||
const fullPath = `${this.lockDirName_}/${path}`;
|
||||
if (splitted.length !== 2) throw new Error(`Sync target appears to be locked but lock filename is invalid: ${fullPath}. Please delete it on the sync target to continue.`);
|
||||
return {
|
||||
clientId: splitted[0],
|
||||
timestamp: Number(splitted[1]),
|
||||
fullPath: fullPath,
|
||||
};
|
||||
}
|
||||
|
||||
async checkLock_() {
|
||||
const lockFiles = await this.lockFiles_();
|
||||
if (lockFiles.length) {
|
||||
const lock = this.parseLockFilePath(lockFiles[0].path);
|
||||
|
||||
if (lock.clientId === this.clientId()) {
|
||||
await this.releaseLock_();
|
||||
} else {
|
||||
throw new Error(`The sync target was locked by client ${lock.clientId} on ${time.unixMsToLocalDateTime(lock.timestamp)} and cannot be accessed. If no app is currently operating on the sync target, you can delete the files in the "${this.lockDirName_}" directory on the sync target to resume.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async checkSyncTargetVersion_() {
|
||||
const supportedSyncTargetVersion = Setting.value('syncVersion');
|
||||
const syncTargetVersion = await this.api().get('.sync/version.txt');
|
||||
|
||||
if (!syncTargetVersion) {
|
||||
await this.api().put('.sync/version.txt', `${supportedSyncTargetVersion}`);
|
||||
} else {
|
||||
if (Number(syncTargetVersion) > supportedSyncTargetVersion) {
|
||||
throw new Error(sprintf('Sync version of the target (%d) does not match sync version supported by client (%d). Please upgrade your client.', Number(syncTargetVersion), supportedSyncTargetVersion));
|
||||
} else {
|
||||
await this.api().put('.sync/version.txt', `${supportedSyncTargetVersion}`);
|
||||
// TODO: do upgrade job
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronisation is done in three major steps:
|
||||
//
|
||||
// 1. UPLOAD: Send to the sync target the items that have changed since the last sync.
|
||||
@ -244,22 +311,12 @@ class Synchronizer {
|
||||
|
||||
try {
|
||||
await this.api().mkdir(this.syncDirName_);
|
||||
await this.api().mkdir(this.lockDirName_);
|
||||
this.api().setTempDirName(this.syncDirName_);
|
||||
await this.api().mkdir(this.resourceDirName_);
|
||||
|
||||
const supportedSyncTargetVersion = Setting.value('syncVersion');
|
||||
const syncTargetVersion = await this.api().get('.sync/version.txt');
|
||||
|
||||
if (!syncTargetVersion) {
|
||||
await this.api().put('.sync/version.txt', `${supportedSyncTargetVersion}`);
|
||||
} else {
|
||||
if (Number(syncTargetVersion) > supportedSyncTargetVersion) {
|
||||
throw new Error(sprintf('Sync version of the target (%d) does not match sync version supported by client (%d). Please upgrade your client.', Number(syncTargetVersion), supportedSyncTargetVersion));
|
||||
} else {
|
||||
await this.api().put('.sync/version.txt', `${supportedSyncTargetVersion}`);
|
||||
// TODO: do upgrade job
|
||||
}
|
||||
}
|
||||
await this.checkLock_();
|
||||
await this.checkSyncTargetVersion_();
|
||||
|
||||
// ========================================================================
|
||||
// 1. UPLOAD
|
||||
|
Loading…
Reference in New Issue
Block a user