mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Sync fixes
This commit is contained in:
parent
df3e5ac40c
commit
5ca8647d35
@ -7,6 +7,7 @@ import { vorpalUtils } from './vorpal-utils.js';
|
||||
import { Synchronizer } from 'lib/synchronizer.js';
|
||||
const locker = require('proper-lockfile');
|
||||
const fs = require('fs-extra');
|
||||
const osTmpdir = require('os-tmpdir');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@ -34,7 +35,7 @@ class Command extends BaseCommand {
|
||||
|
||||
static lockFile(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
locker.lock(filePath, (error, release) => {
|
||||
locker.lock(filePath, { stale: 1000 * 60 * 5 }, (error, release) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
@ -61,7 +62,7 @@ class Command extends BaseCommand {
|
||||
async action(args) {
|
||||
this.releaseLockFn_ = null;
|
||||
|
||||
const lockFilePath = Setting.value('tempDir') + '/synclock';
|
||||
const lockFilePath = osTmpdir() + '/synclock';
|
||||
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
|
||||
|
||||
if (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.'));
|
||||
@ -71,7 +72,7 @@ class Command extends BaseCommand {
|
||||
try {
|
||||
this.syncTarget_ = Setting.value('sync.target');
|
||||
if (args.options.target) this.syncTarget_ = args.options.target;
|
||||
|
||||
|
||||
let syncInitOptions = {};
|
||||
if (args.options['filesystem-path']) syncInitOptions['sync.filesystem.path'] = args.options['filesystem-path'];
|
||||
|
||||
@ -100,6 +101,7 @@ class Command extends BaseCommand {
|
||||
options.context = context;
|
||||
let newContext = await sync.start(options);
|
||||
Setting.setValue('sync.context', JSON.stringify(newContext));
|
||||
|
||||
vorpalUtils.redrawDone();
|
||||
|
||||
await app().refreshCurrentFolder();
|
||||
|
@ -97,8 +97,8 @@ async function saveNoteResources(note) {
|
||||
let existingResource = await Resource.load(toSave.id);
|
||||
if (existingResource) continue;
|
||||
|
||||
await Resource.save(toSave, { isNew: true });
|
||||
await filePutContents(Resource.fullPath(toSave), resource.data)
|
||||
await Resource.save(toSave, { isNew: true });
|
||||
resourcesCreated++;
|
||||
}
|
||||
return resourcesCreated;
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-07-18 23:34+0100\n"
|
||||
"POT-Creation-Date: 2017-07-19 20:00+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -287,40 +287,40 @@ msgstr ""
|
||||
msgid "Displays summary about the notes and notebooks."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:24
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:26
|
||||
msgid "Synchronizes with remote storage."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:29
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:31
|
||||
msgid "Sync to provided target (defaults to sync.target config value)"
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:30
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:32
|
||||
msgid "For \"filesystem\" target only: Path to sync to."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:67
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:69
|
||||
msgid "Synchronisation is already in progress."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:92
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#, javascript-format
|
||||
msgid "Synchronization target: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:94
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:98
|
||||
msgid "Cannot initialize synchronizer."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:100
|
||||
msgid "Starting synchronization..."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:107
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:112
|
||||
msgid "Done."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:122
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:127
|
||||
#: /mnt/d/Web/www/joplin/ReactNativeClient/lib/synchronizer.js:60
|
||||
msgid "Cancelling..."
|
||||
msgstr ""
|
||||
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-07-18 23:18+0100\n"
|
||||
"POT-Creation-Date: 2017-07-19 19:53+0100\n"
|
||||
"PO-Revision-Date: 2017-07-18 13:27+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@ -310,42 +310,42 @@ msgstr "Assigner la valeur [value] à la propriété <name> de la <note> donnée
|
||||
msgid "Displays summary about the notes and notebooks."
|
||||
msgstr "Afficher un résumé des notes et carnets."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:24
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:26
|
||||
msgid "Synchronizes with remote storage."
|
||||
msgstr "Synchroniser les notes et carnets."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:29
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:31
|
||||
msgid "Sync to provided target (defaults to sync.target config value)"
|
||||
msgstr ""
|
||||
"Synchroniser avec la cible donnée (par défaut, la valeur de configuration "
|
||||
"`sync.target`)."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:30
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:32
|
||||
msgid "For \"filesystem\" target only: Path to sync to."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:67
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:69
|
||||
msgid "Synchronisation is already in progress."
|
||||
msgstr "Synchronisation est déjà en cours."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:92
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#, javascript-format
|
||||
msgid "Synchronization target: %s"
|
||||
msgstr "Cible de la synchronisation : %s"
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:94
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:98
|
||||
msgid "Cannot initialize synchronizer."
|
||||
msgstr "Impossible d'initialiser le synchroniseur."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:100
|
||||
msgid "Starting synchronization..."
|
||||
msgstr "Commencement de la synchronisation..."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:107
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:112
|
||||
msgid "Done."
|
||||
msgstr "Terminé."
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:122
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:127
|
||||
#: /mnt/d/Web/www/joplin/ReactNativeClient/lib/synchronizer.js:60
|
||||
msgid "Cancelling..."
|
||||
msgstr "Annulation..."
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-07-18 23:34+0100\n"
|
||||
"POT-Creation-Date: 2017-07-19 20:00+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -287,40 +287,40 @@ msgstr ""
|
||||
msgid "Displays summary about the notes and notebooks."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:24
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:26
|
||||
msgid "Synchronizes with remote storage."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:29
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:31
|
||||
msgid "Sync to provided target (defaults to sync.target config value)"
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:30
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:32
|
||||
msgid "For \"filesystem\" target only: Path to sync to."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:67
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:69
|
||||
msgid "Synchronisation is already in progress."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:92
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#, javascript-format
|
||||
msgid "Synchronization target: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:94
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:98
|
||||
msgid "Cannot initialize synchronizer."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:96
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:100
|
||||
msgid "Starting synchronization..."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:107
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:112
|
||||
msgid "Done."
|
||||
msgstr ""
|
||||
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:122
|
||||
#: /mnt/d/Web/www/joplin/CliClient/app/command-sync.js:127
|
||||
#: /mnt/d/Web/www/joplin/ReactNativeClient/lib/synchronizer.js:60
|
||||
msgid "Cancelling..."
|
||||
msgstr ""
|
||||
|
@ -7,7 +7,7 @@
|
||||
"url": "https://github.com/laurent22/joplin"
|
||||
},
|
||||
"url": "git://github.com/laurent22/joplin.git",
|
||||
"version": "0.8.58",
|
||||
"version": "0.8.62",
|
||||
"bin": {
|
||||
"joplin": "./main_launcher.js"
|
||||
},
|
||||
@ -24,6 +24,7 @@
|
||||
"moment": "^2.18.1",
|
||||
"moment-timezone": "^0.5.13",
|
||||
"node-fetch": "^1.7.1",
|
||||
"os-tmpdir": "^1.0.2",
|
||||
"promise": "^7.1.1",
|
||||
"proper-lockfile": "^2.0.1",
|
||||
"query-string": "4.3.4",
|
||||
|
@ -6,6 +6,7 @@ import { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi,
|
||||
import { Folder } from 'lib/models/folder.js';
|
||||
import { Note } from 'lib/models/note.js';
|
||||
import { Tag } from 'lib/models/tag.js';
|
||||
import { Database } from 'lib/database.js';
|
||||
import { Setting } from 'lib/models/setting.js';
|
||||
import { BaseItem } from 'lib/models/base-item.js';
|
||||
import { BaseModel } from 'lib/base-model.js';
|
||||
@ -16,6 +17,8 @@ process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 9000; // The first test is slow because the database needs to be built
|
||||
|
||||
const syncTargetId = Database.enumId('syncTarget', 'memory');
|
||||
|
||||
async function allItems() {
|
||||
let folders = await Folder.all();
|
||||
let notes = await Note.all();
|
||||
@ -237,7 +240,7 @@ describe('Synchronizer', function() {
|
||||
expect(files.length).toBe(1);
|
||||
expect(files[0].path).toBe(Folder.systemPath(folder1));
|
||||
|
||||
let deletedItems = await BaseItem.deletedItems();
|
||||
let deletedItems = await BaseItem.deletedItems(syncTargetId);
|
||||
expect(deletedItems.length).toBe(0);
|
||||
|
||||
done();
|
||||
@ -259,7 +262,7 @@ describe('Synchronizer', function() {
|
||||
await synchronizer().start();
|
||||
let items = await allItems();
|
||||
expect(items.length).toBe(1);
|
||||
let deletedItems = await BaseItem.deletedItems();
|
||||
let deletedItems = await BaseItem.deletedItems(syncTargetId);
|
||||
expect(deletedItems.length).toBe(0);
|
||||
|
||||
done();
|
||||
@ -565,7 +568,7 @@ describe('Synchronizer', function() {
|
||||
await synchronizer().start();
|
||||
await Note.save({ id: n1.id, is_conflict: 1 });
|
||||
await Note.delete(n1.id);
|
||||
const deletedItems = await BaseItem.deletedItems();
|
||||
const deletedItems = await BaseItem.deletedItems(syncTargetId);
|
||||
|
||||
expect(deletedItems.length).toBe(0);
|
||||
|
||||
|
@ -72,6 +72,9 @@ function clearDatabase(id = null) {
|
||||
'DELETE FROM resources',
|
||||
'DELETE FROM tags',
|
||||
'DELETE FROM note_tags',
|
||||
|
||||
'DELETE FROM deleted_items',
|
||||
'DELETE FROM sync_items',
|
||||
];
|
||||
|
||||
return databases_[id].transactionExecBatch(queries);
|
||||
|
@ -666,7 +666,7 @@ babel-register@^6.24.1:
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.2"
|
||||
|
||||
babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0:
|
||||
babel-runtime@^6.18.0, babel-runtime@^6.22.0:
|
||||
version "6.23.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
|
||||
dependencies:
|
||||
@ -1641,7 +1641,7 @@ os-homedir@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
|
||||
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
|
||||
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
|
||||
|
@ -90,8 +90,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 21
|
||||
versionName "0.9.8"
|
||||
versionCode 23
|
||||
versionName "0.9.10"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
|
@ -52,10 +52,13 @@ class BaseModel {
|
||||
static fieldNames(withPrefix = false) {
|
||||
let output = this.db().tableFieldNames(this.tableName());
|
||||
if (!withPrefix) return output;
|
||||
|
||||
let p = withPrefix === true ? this.tableName() : withPrefix;
|
||||
let temp = [];
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
temp.push(this.tableName() + '.' + output[i]);
|
||||
temp.push(p + '.' + output[i]);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ class SideMenuContentComponent extends Component {
|
||||
sync.cancel();
|
||||
} else {
|
||||
if (reg.oneDriveApi().auth()) {
|
||||
sync.start();
|
||||
reg.scheduleSync(1);
|
||||
} else {
|
||||
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||
|
||||
|
@ -166,6 +166,11 @@ class Database {
|
||||
throw new Error('Unknown enum type or id: ' + type + ', ' + id);
|
||||
}
|
||||
|
||||
static enumIds(type) {
|
||||
if (type == 'syncTarget') return [1,2,3];
|
||||
throw new Error('Unknown enum type: ' + type);
|
||||
}
|
||||
|
||||
static formatValue(type, value) {
|
||||
if (value === null || value === undefined) return null;
|
||||
if (type == this.TYPE_INT) return Number(value);
|
||||
@ -182,7 +187,8 @@ class Database {
|
||||
var line = lines[i];
|
||||
if (line == '') continue;
|
||||
if (line.substr(0, 2) == "--") continue;
|
||||
statement += line;
|
||||
statement += line.trim();
|
||||
if (line[line.length - 1] == ',') statement += ' ';
|
||||
if (line[line.length - 1] == ';') {
|
||||
output.push(statement);
|
||||
statement = '';
|
||||
|
@ -42,13 +42,6 @@ CREATE INDEX notes_is_conflict ON notes (is_conflict);
|
||||
CREATE INDEX notes_is_todo ON notes (is_todo);
|
||||
CREATE INDEX notes_order ON notes (\`order\`);
|
||||
|
||||
CREATE TABLE deleted_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
item_type INT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
deleted_time INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE tags (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL DEFAULT "",
|
||||
@ -97,10 +90,6 @@ CREATE TABLE table_fields (
|
||||
field_default TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE version (
|
||||
version INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE sync_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
sync_target INT NOT NULL,
|
||||
@ -114,6 +103,17 @@ CREATE INDEX sync_items_sync_target ON sync_items (sync_target);
|
||||
CREATE INDEX sync_items_item_type ON sync_items (item_type);
|
||||
CREATE INDEX sync_items_item_id ON sync_items (item_id);
|
||||
|
||||
CREATE TABLE deleted_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
item_type INT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
deleted_time INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE version (
|
||||
version INT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO version (version) VALUES (1);
|
||||
`;
|
||||
|
||||
@ -187,28 +187,59 @@ class JoplinDatabase extends Database {
|
||||
});
|
||||
}
|
||||
|
||||
async upgradeDatabase(fromVersion) {
|
||||
// INSTRUCTIONS TO UPGRADE THE DATABASE:
|
||||
//
|
||||
// 1. Add the new version number to the existingDatabaseVersions array
|
||||
// 2. Add the upgrade logic to the "switch (targetVersion)" statement below
|
||||
|
||||
const existingDatabaseVersions = [1, 2];
|
||||
|
||||
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
||||
if (currentVersionIndex == existingDatabaseVersions.length - 1) return false;
|
||||
|
||||
while (currentVersionIndex < existingDatabaseVersions.length - 1) {
|
||||
const targetVersion = existingDatabaseVersions[currentVersionIndex + 1];
|
||||
this.logger().info("Converting database to version " + targetVersion);
|
||||
|
||||
let queries = [];
|
||||
|
||||
if (targetVersion == 2) {
|
||||
const newTableSql = `
|
||||
CREATE TABLE deleted_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
item_type INT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
deleted_time INT NOT NULL,
|
||||
sync_target INT NOT NULL
|
||||
);
|
||||
`;
|
||||
|
||||
queries.push({ sql: 'DROP TABLE deleted_items' });
|
||||
queries.push({ sql: this.sqlStringToLines(newTableSql)[0] });
|
||||
queries.push({ sql: "CREATE INDEX deleted_items_sync_target ON deleted_items (sync_target)" });
|
||||
}
|
||||
|
||||
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
||||
await this.transactionExecBatch(queries);
|
||||
|
||||
currentVersionIndex++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.logger().info('Checking for database schema update...');
|
||||
|
||||
for (let initLoopCount = 1; initLoopCount <= 2; initLoopCount++) {
|
||||
try {
|
||||
// await this.exec('DROP TABLE folders');
|
||||
// await this.exec('DROP TABLE notes');
|
||||
// await this.exec('DROP TABLE deleted_items');
|
||||
// await this.exec('DROP TABLE tags');
|
||||
// await this.exec('DROP TABLE note_tags');
|
||||
// await this.exec('DROP TABLE resources');
|
||||
// await this.exec('DROP TABLE settings');
|
||||
// await this.exec('DROP TABLE table_fields');
|
||||
// await this.exec('DROP TABLE version');
|
||||
// await this.exec('DROP TABLE sync_items');
|
||||
|
||||
let row = await this.selectOne('SELECT * FROM version LIMIT 1');
|
||||
this.logger().info('Current database version', row);
|
||||
let currentVersion = row.version;
|
||||
this.logger().info('Current database version', currentVersion);
|
||||
|
||||
// TODO: version update logic
|
||||
// TODO: only do this if db has been updated:
|
||||
// return this.refreshTableFields();
|
||||
const upgraded = await this.upgradeDatabase(currentVersion);
|
||||
if (upgraded) await this.refreshTableFields();
|
||||
} catch (error) {
|
||||
if (error && error.code != 0 && error.code != 'SQLITE_ERROR') throw this.sqliteErrorToJsError(error);
|
||||
|
||||
|
@ -131,31 +131,36 @@ class BaseItem extends BaseModel {
|
||||
await super.batchDelete(ids, options);
|
||||
|
||||
if (trackDeleted) {
|
||||
const syncTargetIds = Database.enumIds('syncTarget');
|
||||
let queries = [];
|
||||
let now = time.unixMs();
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
if (conflictNoteIds.indexOf(ids[i]) >= 0) continue;
|
||||
|
||||
queries.push({
|
||||
sql: 'INSERT INTO deleted_items (item_type, item_id, deleted_time) VALUES (?, ?, ?)',
|
||||
params: [this.modelType(), ids[i], now],
|
||||
});
|
||||
// For each deleted item, for each sync target, we need to add an entry in deleted_items.
|
||||
// That way, each target can later delete the remote item.
|
||||
for (let j = 0; j < syncTargetIds.length; j++) {
|
||||
queries.push({
|
||||
sql: 'INSERT INTO deleted_items (item_type, item_id, deleted_time, sync_target) VALUES (?, ?, ?, ?)',
|
||||
params: [this.modelType(), ids[i], now, syncTargetIds[j]],
|
||||
});
|
||||
}
|
||||
}
|
||||
await this.db().transactionExecBatch(queries);
|
||||
}
|
||||
}
|
||||
|
||||
static deletedItems() {
|
||||
return this.db().selectAll('SELECT * FROM deleted_items');
|
||||
static deletedItems(syncTarget) {
|
||||
return this.db().selectAll('SELECT * FROM deleted_items WHERE sync_target = ?', [syncTarget]);
|
||||
}
|
||||
|
||||
static async deletedItemCount() {
|
||||
let r = await this.db().selectOne('SELECT count(*) as total FROM deleted_items');
|
||||
static async deletedItemCount(syncTarget) {
|
||||
let r = await this.db().selectOne('SELECT count(*) as total FROM deleted_items WHERE sync_target = ?', [syncTarget]);
|
||||
return r['total'];
|
||||
}
|
||||
|
||||
static remoteDeletedItem(itemId) {
|
||||
return this.db().exec('DELETE FROM deleted_items WHERE item_id = ?', [itemId]);
|
||||
static remoteDeletedItem(syncTarget, itemId) {
|
||||
return this.db().exec('DELETE FROM deleted_items WHERE item_id = ? AND sync_target = ?', [itemId, syncTarget]);
|
||||
}
|
||||
|
||||
static serialize_format(propName, propValue) {
|
||||
@ -275,33 +280,131 @@ class BaseItem extends BaseModel {
|
||||
for (let i = 0; i < classNames.length; i++) {
|
||||
const className = classNames[i];
|
||||
const ItemClass = this.getClass(className);
|
||||
const fieldNames = ItemClass.fieldNames(true);
|
||||
fieldNames.push('sync_time');
|
||||
let fieldNames = ItemClass.fieldNames('items');
|
||||
|
||||
// // NEVER SYNCED:
|
||||
// 'SELECT * FROM [ITEMS] WHERE id NOT INT (SELECT item_id FROM sync_items WHERE sync_target = ?)'
|
||||
|
||||
// // CHANGED:
|
||||
// 'SELECT * FROM [ITEMS] items JOIN sync_items s ON s.item_id = items.id WHERE sync_target = ? AND'
|
||||
|
||||
let extraWhere = className == 'Note' ? 'AND is_conflict = 0' : '';
|
||||
|
||||
// First get all the items that have never been synced under this sync target
|
||||
|
||||
let sql = sprintf(`
|
||||
SELECT %s FROM %s
|
||||
LEFT JOIN sync_items t ON t.item_id = %s.id
|
||||
WHERE
|
||||
(t.id IS NULL OR t.sync_time < %s.updated_time)
|
||||
%s
|
||||
SELECT %s
|
||||
FROM %s items
|
||||
WHERE id NOT IN (
|
||||
SELECT item_id FROM sync_items WHERE sync_target = %d
|
||||
)
|
||||
%s
|
||||
LIMIT %d
|
||||
`,
|
||||
this.db().escapeFields(fieldNames),
|
||||
this.db().escapeField(ItemClass.tableName()),
|
||||
this.db().escapeField(ItemClass.tableName()),
|
||||
this.db().escapeField(ItemClass.tableName()),
|
||||
Number(syncTarget),
|
||||
extraWhere,
|
||||
limit);
|
||||
|
||||
const items = await ItemClass.modelSelectAll(sql);
|
||||
let neverSyncedItem = await ItemClass.modelSelectAll(sql);
|
||||
//for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0;
|
||||
|
||||
// console.info(sql);
|
||||
// console.info('NEVER', neverSyncedItem);
|
||||
|
||||
// Secondly get the items that have been synced under this sync target but that have been changed since then
|
||||
|
||||
const newLimit = limit - neverSyncedItem.length;
|
||||
|
||||
let changedItems = [];
|
||||
|
||||
if (newLimit > 0) {
|
||||
fieldNames.push('sync_time');
|
||||
|
||||
let sql = sprintf(`
|
||||
SELECT %s FROM %s items
|
||||
JOIN sync_items s ON s.item_id = items.id
|
||||
WHERE sync_target = %d
|
||||
AND s.sync_time < items.updated_time
|
||||
%s
|
||||
LIMIT %d
|
||||
`,
|
||||
this.db().escapeFields(fieldNames),
|
||||
this.db().escapeField(ItemClass.tableName()),
|
||||
Number(syncTarget),
|
||||
extraWhere,
|
||||
newLimit);
|
||||
|
||||
changedItems = await ItemClass.modelSelectAll(sql);
|
||||
}
|
||||
|
||||
// console.info('CHANGED', changedItems);
|
||||
|
||||
const items = neverSyncedItem.concat(changedItems);
|
||||
|
||||
if (i >= classNames.length - 1) {
|
||||
return { hasMore: items.length >= limit, items: items };
|
||||
} else {
|
||||
if (items.length) return { hasMore: true, items: items };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//let extraWhere = className == 'Note' ? 'AND is_conflict = 0' : '';
|
||||
|
||||
// First get all the items that have never been synced under this sync target
|
||||
|
||||
// let sql = sprintf(`
|
||||
// SELECT %s FROM %s items
|
||||
// LEFT JOIN sync_items t ON t.item_id = items.id
|
||||
// WHERE (t.id IS NULL OR t.sync_target != %d) %s
|
||||
// LIMIT %d
|
||||
// `,
|
||||
// this.db().escapeFields(fieldNames),
|
||||
// this.db().escapeField(ItemClass.tableName()),
|
||||
// Number(syncTarget),
|
||||
// extraWhere,
|
||||
// limit);
|
||||
|
||||
// let neverSyncedItem = await ItemClass.modelSelectAll(sql);
|
||||
// for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0;
|
||||
|
||||
// console.info(sql);
|
||||
// console.info('NEVER', neverSyncedItem);
|
||||
|
||||
// // Secondly get the items that have been synced under this sync target but that have been changed since then
|
||||
|
||||
// const newLimit = limit - neverSyncedItem.length;
|
||||
|
||||
// let changedItems = [];
|
||||
|
||||
// if (newLimit > 0) {
|
||||
// let sql = sprintf(`
|
||||
// SELECT %s FROM %s items
|
||||
// LEFT JOIN sync_items t ON t.item_id = items.id
|
||||
// WHERE (t.sync_time < items.updated_time AND t.sync_target = %d) %s
|
||||
// LIMIT %d
|
||||
// `,
|
||||
// this.db().escapeFields(fieldNames),
|
||||
// this.db().escapeField(ItemClass.tableName()),
|
||||
// Number(syncTarget),
|
||||
// extraWhere,
|
||||
// newLimit);
|
||||
|
||||
// changedItems = await ItemClass.modelSelectAll(sql);
|
||||
// }
|
||||
|
||||
// console.info('CHANGED', changedItems);
|
||||
|
||||
// const items = neverSyncedItem.concat(changedItems);
|
||||
|
||||
// if (i >= classNames.length - 1) {
|
||||
// return { hasMore: items.length >= limit, items: items };
|
||||
// } else {
|
||||
// if (items.length) return { hasMore: true, items: items };
|
||||
// }
|
||||
}
|
||||
|
||||
throw new Error('Unreachable');
|
||||
|
@ -74,7 +74,9 @@ reg.synchronizer = async () => {
|
||||
return reg.synchronizer_;
|
||||
}
|
||||
|
||||
reg.scheduleSync = async () => {
|
||||
reg.scheduleSync = async (delay = null) => {
|
||||
if (delay === null) delay = 1000 * 10;
|
||||
|
||||
if (reg.scheduleSyncId_) {
|
||||
clearTimeout(reg.scheduleSyncId_);
|
||||
reg.scheduleSyncId_ = null;
|
||||
@ -92,8 +94,12 @@ reg.scheduleSync = async () => {
|
||||
}
|
||||
|
||||
const sync = await reg.synchronizer();
|
||||
sync.start();
|
||||
}, 1000 * 10);
|
||||
|
||||
let context = Setting.value('sync.context');
|
||||
context = context ? JSON.parse(context) : {};
|
||||
let newContext = await sync.start({ context: context });
|
||||
Setting.setValue('sync.context', JSON.stringify(newContext));
|
||||
}, delay);
|
||||
}
|
||||
|
||||
reg.setDb = (v) => {
|
||||
|
@ -34,7 +34,7 @@ class ReportService {
|
||||
};
|
||||
|
||||
output.toDelete = {
|
||||
total: await BaseItem.deletedItemCount(),
|
||||
total: await BaseItem.deletedItemCount(syncTarget),
|
||||
};
|
||||
|
||||
output.conflicted = {
|
||||
|
@ -202,7 +202,7 @@ class Synchronizer {
|
||||
let content = await ItemClass.serialize(local);
|
||||
let action = null;
|
||||
let updateSyncTimeOnly = true;
|
||||
let reason = '';
|
||||
let reason = '';
|
||||
|
||||
if (!remote) {
|
||||
if (!local.sync_time) {
|
||||
@ -230,7 +230,14 @@ class Synchronizer {
|
||||
|
||||
if (local.type_ == BaseModel.TYPE_RESOURCE && (action == 'createRemote' || (action == 'itemConflict' && remote))) {
|
||||
let remoteContentPath = this.resourceDirName_ + '/' + local.id;
|
||||
let resourceContent = await Resource.content(local);
|
||||
let resourceContent = '';
|
||||
try {
|
||||
resourceContent = await Resource.content(local);
|
||||
} catch (error) {
|
||||
error.message = 'Cannot read resource content: ' + local.id + ': ' + error.message;
|
||||
this.logger().error(error);
|
||||
this.progressReport_.errors.push(error);
|
||||
}
|
||||
await this.api().put(remoteContentPath, resourceContent);
|
||||
}
|
||||
|
||||
@ -302,7 +309,7 @@ class Synchronizer {
|
||||
// Delete the remote items that have been deleted locally.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
let deletedItems = await BaseItem.deletedItems();
|
||||
let deletedItems = await BaseItem.deletedItems(syncTargetId);
|
||||
for (let i = 0; i < deletedItems.length; i++) {
|
||||
if (this.cancelling()) break;
|
||||
|
||||
@ -311,7 +318,7 @@ class Synchronizer {
|
||||
this.logSyncOperation('deleteRemote', null, { id: item.item_id }, 'local has been deleted');
|
||||
await this.api().delete(path);
|
||||
if (this.randomFailure(options, 3)) return;
|
||||
await BaseItem.remoteDeletedItem(item.item_id);
|
||||
await BaseItem.remoteDeletedItem(syncTargetId, item.item_id);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user