mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
All: Added fail-safe to prevent data from being wiped out when the sync target is empty
This commit is contained in:
parent
3e5a9cdb97
commit
348efdd7b6
@ -1480,4 +1480,20 @@ describe('Synchronizer', function() {
|
||||
expect((await decryptionWorker().decryptionDisabledItems()).length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should not wipe out user data when syncing with an empty target', asyncTest(async () => {
|
||||
await Note.save({ title: 'ma note' });
|
||||
await Note.save({ title: 'mon autre note' });
|
||||
await Note.save({ title: 'ma troisième note' });
|
||||
|
||||
Setting.setValue('sync.wipeOutFailSafe', true);
|
||||
await synchronizer().start();
|
||||
await fileApi().clearRoot(); // oops
|
||||
await synchronizer().start();
|
||||
expect((await Note.all()).length).toBe(3); // but since the fail-safe if on, the notes have not been deleted
|
||||
|
||||
Setting.setValue('sync.wipeOutFailSafe', false); // Now switch it off
|
||||
await synchronizer().start();
|
||||
expect((await Note.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -135,7 +135,9 @@ async function switchClient(id) {
|
||||
|
||||
Setting.setConstant('resourceDir', resourceDir(id));
|
||||
|
||||
return Setting.load();
|
||||
await Setting.load();
|
||||
|
||||
Setting.setValue('sync.wipeOutFailSafe', false); // To keep things simple, always disable fail-safe unless explicitely set in the test itself
|
||||
}
|
||||
|
||||
async function clearDatabase(id = null) {
|
||||
|
@ -247,6 +247,13 @@ async function basicDelta(path, getDirStatFn, options) {
|
||||
});
|
||||
newContext.statIdsCache = newContext.statsCache.filter(item => BaseItem.isSystemPath(item.path)).map(item => BaseItem.pathToId(item.path));
|
||||
newContext.statIdsCache.sort(); // Items must be sorted to use binary search below
|
||||
|
||||
// At this point statIdsCache contains the list of all the item IDs on the sync target.
|
||||
// If it's empty, it's most likely a configuration error or bug. For example, if the
|
||||
// user moves their Nextcloud directory, or if a network drive gets disconnected and
|
||||
// returns an empty dir instead of an error. In that case, we don't wipe out the user
|
||||
// data, unless they have switched off the fail-safe.
|
||||
if (options.wipeOutFailSafe && !newContext.statIdsCache.length) throw new JoplinError('Fail-safe: The delta operation was interrupted because the sync target appears to be empty. To override this behaviour disable the "Wipe out fail-safe" option in the settings.', 'failSafe');
|
||||
}
|
||||
|
||||
let output = [];
|
||||
|
@ -425,6 +425,8 @@ class Setting extends BaseModel {
|
||||
label: () => _('Ignore TLS certificate errors'),
|
||||
},
|
||||
|
||||
'sync.wipeOutFailSafe': { value: true, type: Setting.TYPE_BOOL, public: true, section: 'sync', label: () => _('Fail-safe: Do not wipe out local data when sync target is empty (often the result of a misconfiguration or bug)') },
|
||||
|
||||
'api.token': { value: null, type: Setting.TYPE_STRING, public: false },
|
||||
'api.port': { value: null, type: Setting.TYPE_INT, public: true, appTypes: ['cli'], description: () => _('Specify the port that should be used by the API server. If not set, a default will be used.') },
|
||||
|
||||
|
@ -3,6 +3,7 @@ const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const ResourceLocalState = require('lib/models/ResourceLocalState.js');
|
||||
const MasterKey = require('lib/models/MasterKey.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
@ -508,6 +509,8 @@ class Synchronizer {
|
||||
allItemIdsHandler: async () => {
|
||||
return BaseItem.syncedItemIds(syncTargetId);
|
||||
},
|
||||
|
||||
wipeOutFailSafe: Setting.value('sync.wipeOutFailSafe'),
|
||||
});
|
||||
|
||||
let remotes = listResult.items;
|
||||
@ -695,7 +698,7 @@ class Synchronizer {
|
||||
}
|
||||
} // DELTA STEP
|
||||
} catch (error) {
|
||||
if (error && ['cannotEncryptEncrypted', 'noActiveMasterKey', 'processingPathTwice'].indexOf(error.code) >= 0) {
|
||||
if (error && ['cannotEncryptEncrypted', 'noActiveMasterKey', 'processingPathTwice', 'failSafe'].indexOf(error.code) >= 0) {
|
||||
// Only log an info statement for this since this is a common condition that is reported
|
||||
// in the application, and needs to be resolved by the user.
|
||||
// Or it's a temporary issue that will be resolved on next sync.
|
||||
|
Loading…
Reference in New Issue
Block a user