You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +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:
		
										
											
												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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user