mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
#98, sync setup now doesn't copy the whole DB file, but sets up minimal database and starts off sync
This commit is contained in:
parent
a06618d851
commit
1fe7c62f5a
2
db/migrations/0102__fix_sync_entityIds.sql
Normal file
2
db/migrations/0102__fix_sync_entityIds.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DELETE FROM sync WHERE entityName = 'note_tree';
|
||||||
|
DELETE FROM sync WHERE entityName = 'attributes';
|
@ -61,7 +61,6 @@
|
|||||||
"simple-node-logger": "^0.93.37",
|
"simple-node-logger": "^0.93.37",
|
||||||
"sqlite": "^2.9.2",
|
"sqlite": "^2.9.2",
|
||||||
"tar-stream": "^1.6.1",
|
"tar-stream": "^1.6.1",
|
||||||
"tmp-promise": "^1.0.5",
|
|
||||||
"unescape": "^1.0.1",
|
"unescape": "^1.0.1",
|
||||||
"ws": "^5.2.1",
|
"ws": "^5.2.1",
|
||||||
"xml2js": "^0.4.19"
|
"xml2js": "^0.4.19"
|
||||||
|
@ -34,7 +34,7 @@ function SetupModel() {
|
|||||||
this.setupSyncFromDesktop(false);
|
this.setupSyncFromDesktop(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.finish = () => {
|
this.finish = async () => {
|
||||||
if (this.setupNewDocument()) {
|
if (this.setupNewDocument()) {
|
||||||
const username = this.username();
|
const username = this.username();
|
||||||
const password1 = this.password1();
|
const password1 = this.password1();
|
||||||
@ -84,20 +84,33 @@ function SetupModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not using server.js because it loads too many dependencies
|
// not using server.js because it loads too many dependencies
|
||||||
$.post('/api/setup/sync-from-server', {
|
const resp = await $.post('/api/setup/sync-from-server', {
|
||||||
serverAddress: serverAddress,
|
serverAddress: serverAddress,
|
||||||
username: username,
|
username: username,
|
||||||
password: password
|
password: password
|
||||||
}).then(() => {
|
|
||||||
window.location.replace("/");
|
|
||||||
}).catch((err) => {
|
|
||||||
alert("Error, see dev console for details.");
|
|
||||||
console.error(err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (resp.result === 'success') {
|
||||||
|
this.step('sync-in-progress');
|
||||||
|
|
||||||
|
checkOutstandingSyncs();
|
||||||
|
|
||||||
|
setInterval(checkOutstandingSyncs, 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showAlert('Sync setup failed: ', resp.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkOutstandingSyncs() {
|
||||||
|
const stats = await $.get('/api/sync/stats');
|
||||||
|
const totalOutstandingSyncs = stats.outstandingPushes + stats.outstandingPulls;
|
||||||
|
|
||||||
|
$("#outstanding-syncs").html(totalOutstandingSyncs);
|
||||||
|
}
|
||||||
|
|
||||||
function showAlert(message) {
|
function showAlert(message) {
|
||||||
$("#alert").html(message);
|
$("#alert").html(message);
|
||||||
$("#alert").show();
|
$("#alert").show();
|
||||||
|
@ -2,14 +2,10 @@
|
|||||||
|
|
||||||
const sqlInit = require('../../services/sql_init');
|
const sqlInit = require('../../services/sql_init');
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const cls = require('../../services/cls');
|
const rp = require('request-promise');
|
||||||
const tmp = require('tmp-promise');
|
const Option = require('../../entities/option');
|
||||||
const http = require('http');
|
const syncService = require('../../services/sync');
|
||||||
const fs = require('fs');
|
|
||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const DOCUMENT_PATH = require('../../services/data_dir').DOCUMENT_PATH;
|
|
||||||
const sourceIdService = require('../../services/source_id');
|
|
||||||
const url = require('url');
|
|
||||||
|
|
||||||
async function setupNewDocument(req) {
|
async function setupNewDocument(req) {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
@ -20,52 +16,44 @@ async function setupNewDocument(req) {
|
|||||||
async function setupSyncFromServer(req) {
|
async function setupSyncFromServer(req) {
|
||||||
const { serverAddress, username, password } = req.body;
|
const { serverAddress, username, password } = req.body;
|
||||||
|
|
||||||
const tempFile = await tmp.file();
|
try {
|
||||||
|
log.info("Getting document options from sync server.");
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
// response is expected to contain documentId and documentSecret options
|
||||||
const file = fs.createWriteStream(tempFile.path);
|
const options = await rp.get({
|
||||||
const parsedAddress = url.parse(serverAddress);
|
uri: serverAddress + '/api/sync/document',
|
||||||
|
auth: {
|
||||||
|
'user': username,
|
||||||
|
'pass': password
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
});
|
||||||
|
|
||||||
const options = {
|
log.info("Creating database for sync");
|
||||||
method: 'GET',
|
|
||||||
protocol: parsedAddress.protocol,
|
await sql.transactional(async () => {
|
||||||
host: parsedAddress.hostname,
|
await sqlInit.createDatabaseForSync(serverAddress);
|
||||||
port: parsedAddress.port,
|
|
||||||
path: '/api/sync/document',
|
for (const opt of options) {
|
||||||
auth: username + ':' + password
|
await new Option(opt).save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("Triggering sync.");
|
||||||
|
|
||||||
|
// it's ok to not wait for it here
|
||||||
|
syncService.sync();
|
||||||
|
|
||||||
|
return { result: 'success' };
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
log.error("Sync failed: " + e.message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: 'failure',
|
||||||
|
error: e.message
|
||||||
};
|
};
|
||||||
|
}
|
||||||
log.info("Getting document from: " + serverAddress);
|
|
||||||
|
|
||||||
http.request(options, function(response) {
|
|
||||||
response.pipe(file);
|
|
||||||
|
|
||||||
file.on('finish', function() {
|
|
||||||
log.info("Document download finished, closing & renaming.");
|
|
||||||
|
|
||||||
file.close(() => { // close() is async, call after close completes.
|
|
||||||
fs.rename(tempFile.path, DOCUMENT_PATH, async () => {
|
|
||||||
cls.reset();
|
|
||||||
|
|
||||||
await sqlInit.initDbConnection();
|
|
||||||
|
|
||||||
// we need to generate new source ID for this instance, otherwise it will
|
|
||||||
// match the original server one
|
|
||||||
await sql.transactional(async () => {
|
|
||||||
await sourceIdService.generateSourceId();
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).on('error', function(err) { // Handle errors
|
|
||||||
fs.unlink(tempFile.path); // Delete the file async. (But we don't check the result)
|
|
||||||
|
|
||||||
reject(err.message);
|
|
||||||
log.error(err.message);
|
|
||||||
}).end();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -7,7 +7,7 @@ const sql = require('../../services/sql');
|
|||||||
const optionService = require('../../services/options');
|
const optionService = require('../../services/options');
|
||||||
const contentHashService = require('../../services/content_hash');
|
const contentHashService = require('../../services/content_hash');
|
||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const DOCUMENT_PATH = require('../../services/data_dir').DOCUMENT_PATH;
|
const repository = require('../../services/repository');
|
||||||
|
|
||||||
async function testSync() {
|
async function testSync() {
|
||||||
try {
|
try {
|
||||||
@ -23,6 +23,10 @@ async function testSync() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getStats() {
|
||||||
|
return syncService.stats;
|
||||||
|
}
|
||||||
|
|
||||||
async function checkSync() {
|
async function checkSync() {
|
||||||
return {
|
return {
|
||||||
hashes: await contentHashService.getHashes(),
|
hashes: await contentHashService.getHashes(),
|
||||||
@ -75,7 +79,10 @@ async function getChanged(req) {
|
|||||||
|
|
||||||
const syncs = await sql.getRows("SELECT * FROM sync WHERE id > ? LIMIT 1000", [lastSyncId]);
|
const syncs = await sql.getRows("SELECT * FROM sync WHERE id > ? LIMIT 1000", [lastSyncId]);
|
||||||
|
|
||||||
return await syncService.getSyncRecords(syncs);
|
return {
|
||||||
|
syncs: await syncService.getSyncRecords(syncs),
|
||||||
|
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(req) {
|
async function update(req) {
|
||||||
@ -87,10 +94,13 @@ async function update(req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDocument(req, resp) {
|
async function getDocument() {
|
||||||
log.info("Serving document.");
|
log.info("Serving document options.");
|
||||||
|
|
||||||
resp.sendFile(DOCUMENT_PATH);
|
return [
|
||||||
|
await repository.getOption('documentId'),
|
||||||
|
await repository.getOption('documentSecret')
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -102,5 +112,6 @@ module.exports = {
|
|||||||
forceNoteSync,
|
forceNoteSync,
|
||||||
getChanged,
|
getChanged,
|
||||||
update,
|
update,
|
||||||
getDocument
|
getDocument,
|
||||||
|
getStats
|
||||||
};
|
};
|
@ -156,7 +156,8 @@ function register(app) {
|
|||||||
apiRoute(POST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync);
|
apiRoute(POST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync);
|
||||||
apiRoute(GET, '/api/sync/changed', syncApiRoute.getChanged);
|
apiRoute(GET, '/api/sync/changed', syncApiRoute.getChanged);
|
||||||
apiRoute(PUT, '/api/sync/update', syncApiRoute.update);
|
apiRoute(PUT, '/api/sync/update', syncApiRoute.update);
|
||||||
route(GET, '/api/sync/document', [auth.checkBasicAuth], syncApiRoute.getDocument);
|
route(GET, '/api/sync/document', [auth.checkBasicAuth], syncApiRoute.getDocument, apiResultHandler);
|
||||||
|
route(GET, '/api/sync/stats', [], syncApiRoute.getStats, apiResultHandler);
|
||||||
|
|
||||||
apiRoute(GET, '/api/event-log', eventLogRoute.getEventLog);
|
apiRoute(GET, '/api/event-log', eventLogRoute.getEventLog);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 101;
|
const APP_DB_VERSION = 102;
|
||||||
const SYNC_VERSION = 1;
|
const SYNC_VERSION = 1;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -5,6 +5,7 @@ const repository = require('./repository');
|
|||||||
const protectedSessionService = require('./protected_session');
|
const protectedSessionService = require('./protected_session');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
let loaded = false;
|
||||||
let noteTitles;
|
let noteTitles;
|
||||||
let protectedNoteTitles;
|
let protectedNoteTitles;
|
||||||
let noteIds;
|
let noteIds;
|
||||||
@ -34,6 +35,8 @@ async function load() {
|
|||||||
for (const noteId of hiddenLabels) {
|
for (const noteId of hiddenLabels) {
|
||||||
archived[noteId] = true;
|
archived[noteId] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findNotes(query) {
|
function findNotes(query) {
|
||||||
@ -226,6 +229,10 @@ function getNotePath(noteId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
||||||
|
if (!loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (entityName === 'notes') {
|
if (entityName === 'notes') {
|
||||||
const note = await repository.getNote(entityId);
|
const note = await repository.getNote(entityId);
|
||||||
|
|
||||||
@ -277,6 +284,10 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId
|
|||||||
});
|
});
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => {
|
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => {
|
||||||
|
if (!loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`);
|
protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`);
|
||||||
|
|
||||||
for (const noteId in protectedNoteTitles) {
|
for (const noteId in protectedNoteTitles) {
|
||||||
|
@ -5,21 +5,14 @@ const appInfo = require('./app_info');
|
|||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
|
|
||||||
async function initOptions(startNotePath, username, password) {
|
async function initDocumentOptions() {
|
||||||
await optionService.createOption('documentId', utils.randomSecureToken(16), false);
|
await optionService.createOption('documentId', utils.randomSecureToken(16), false);
|
||||||
await optionService.createOption('documentSecret', utils.randomSecureToken(16), false);
|
await optionService.createOption('documentSecret', utils.randomSecureToken(16), false);
|
||||||
|
}
|
||||||
|
|
||||||
await optionService.createOption('startNotePath', startNotePath, false);
|
async function initSyncedOptions(username, password) {
|
||||||
await optionService.createOption('protectedSessionTimeout', 600, true);
|
await optionService.createOption('protectedSessionTimeout', 600);
|
||||||
await optionService.createOption('noteRevisionSnapshotTimeInterval', 600, true);
|
await optionService.createOption('noteRevisionSnapshotTimeInterval', 600);
|
||||||
await optionService.createOption('lastBackupDate', dateUtils.nowDate(), false);
|
|
||||||
await optionService.createOption('dbVersion', appInfo.dbVersion, false);
|
|
||||||
|
|
||||||
await optionService.createOption('lastSyncedPull', appInfo.dbVersion, false);
|
|
||||||
await optionService.createOption('lastSyncedPush', 0, false);
|
|
||||||
|
|
||||||
await optionService.createOption('zoomFactor', 1.0, false);
|
|
||||||
await optionService.createOption('theme', 'white', false);
|
|
||||||
|
|
||||||
await optionService.createOption('username', username);
|
await optionService.createOption('username', username);
|
||||||
|
|
||||||
@ -34,12 +27,26 @@ async function initOptions(startNotePath, username, password) {
|
|||||||
await optionService.createOption('encryptedDataKeyIv', '');
|
await optionService.createOption('encryptedDataKeyIv', '');
|
||||||
|
|
||||||
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
|
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
|
||||||
|
}
|
||||||
|
|
||||||
await optionService.createOption('syncServerHost', '', false);
|
async function initNotSyncedOptions(startNotePath = '', syncServerHost = '') {
|
||||||
|
await optionService.createOption('startNotePath', startNotePath, false);
|
||||||
|
await optionService.createOption('lastBackupDate', dateUtils.nowDate(), false);
|
||||||
|
await optionService.createOption('dbVersion', appInfo.dbVersion, false);
|
||||||
|
|
||||||
|
await optionService.createOption('lastSyncedPull', appInfo.dbVersion, false);
|
||||||
|
await optionService.createOption('lastSyncedPush', 0, false);
|
||||||
|
|
||||||
|
await optionService.createOption('zoomFactor', 1.0, false);
|
||||||
|
await optionService.createOption('theme', 'white', false);
|
||||||
|
|
||||||
|
await optionService.createOption('syncServerHost', syncServerHost, false);
|
||||||
await optionService.createOption('syncServerTimeout', 5000, false);
|
await optionService.createOption('syncServerTimeout', 5000, false);
|
||||||
await optionService.createOption('syncProxy', '', false);
|
await optionService.createOption('syncProxy', '', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
initOptions
|
initDocumentOptions,
|
||||||
|
initSyncedOptions,
|
||||||
|
initNotSyncedOptions
|
||||||
};
|
};
|
@ -12,9 +12,13 @@ async function createConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let dbReadyResolve = null;
|
let dbReadyResolve = null;
|
||||||
const dbReady = new Promise((resolve, reject) => {
|
const dbReady = new Promise(async (resolve, reject) => {
|
||||||
dbReadyResolve = resolve;
|
dbReadyResolve = resolve;
|
||||||
|
|
||||||
|
// no need to create new connection now since DB stays the same all the time
|
||||||
|
const db = await createConnection();
|
||||||
|
sql.setDbConnection(db);
|
||||||
|
|
||||||
initDbConnection();
|
initDbConnection();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -26,9 +30,6 @@ async function isDbInitialized() {
|
|||||||
|
|
||||||
async function initDbConnection() {
|
async function initDbConnection() {
|
||||||
await cls.init(async () => {
|
await cls.init(async () => {
|
||||||
const db = await createConnection();
|
|
||||||
sql.setDbConnection(db);
|
|
||||||
|
|
||||||
await sql.execute("PRAGMA foreign_keys = ON");
|
await sql.execute("PRAGMA foreign_keys = ON");
|
||||||
|
|
||||||
if (!await isDbInitialized()) {
|
if (!await isDbInitialized()) {
|
||||||
@ -45,12 +46,12 @@ async function initDbConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("DB ready.");
|
log.info("DB ready.");
|
||||||
dbReadyResolve(db);
|
dbReadyResolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInitialDatabase(username, password) {
|
async function createInitialDatabase(username, password) {
|
||||||
log.info("Connected to db, but schema doesn't exist. Initializing schema ...");
|
log.info("Creating initial database ...");
|
||||||
|
|
||||||
const schema = fs.readFileSync(resourceDir.DB_INIT_DIR + '/schema.sql', 'UTF-8');
|
const schema = fs.readFileSync(resourceDir.DB_INIT_DIR + '/schema.sql', 'UTF-8');
|
||||||
const notesSql = fs.readFileSync(resourceDir.DB_INIT_DIR + '/main_notes.sql', 'UTF-8');
|
const notesSql = fs.readFileSync(resourceDir.DB_INIT_DIR + '/main_notes.sql', 'UTF-8');
|
||||||
@ -67,15 +68,34 @@ async function createInitialDatabase(username, password) {
|
|||||||
|
|
||||||
const startNoteId = await sql.getValue("SELECT noteId FROM branches WHERE parentNoteId = 'root' AND isDeleted = 0 ORDER BY notePosition");
|
const startNoteId = await sql.getValue("SELECT noteId FROM branches WHERE parentNoteId = 'root' AND isDeleted = 0 ORDER BY notePosition");
|
||||||
|
|
||||||
await require('./options_init').initOptions(startNoteId, username, password);
|
const optionsInitService = require('./options_init');
|
||||||
|
|
||||||
|
await optionsInitService.initDocumentOptions();
|
||||||
|
await optionsInitService.initSyncedOptions(username, password);
|
||||||
|
await optionsInitService.initNotSyncedOptions(startNoteId);
|
||||||
|
|
||||||
await require('./sync_table').fillAllSyncRows();
|
await require('./sync_table').fillAllSyncRows();
|
||||||
});
|
});
|
||||||
|
|
||||||
log.info("Schema and initial content generated. Waiting for user to enter username/password to finish setup.");
|
log.info("Schema and initial content generated.");
|
||||||
|
|
||||||
await initDbConnection();
|
await initDbConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createDatabaseForSync(syncServerHost) {
|
||||||
|
log.info("Creating database for sync with server ...");
|
||||||
|
|
||||||
|
const schema = fs.readFileSync(resourceDir.DB_INIT_DIR + '/schema.sql', 'UTF-8');
|
||||||
|
|
||||||
|
await sql.transactional(async () => {
|
||||||
|
await sql.executeScript(schema);
|
||||||
|
|
||||||
|
await require('./options_init').initNotSyncedOptions('', syncServerHost);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("Schema and not synced options generated.");
|
||||||
|
}
|
||||||
|
|
||||||
async function isDbUpToDate() {
|
async function isDbUpToDate() {
|
||||||
const dbVersion = parseInt(await sql.getValue("SELECT value FROM options WHERE name = 'dbVersion'"));
|
const dbVersion = parseInt(await sql.getValue("SELECT value FROM options WHERE name = 'dbVersion'"));
|
||||||
|
|
||||||
@ -93,5 +113,6 @@ module.exports = {
|
|||||||
isDbInitialized,
|
isDbInitialized,
|
||||||
initDbConnection,
|
initDbConnection,
|
||||||
isDbUpToDate,
|
isDbUpToDate,
|
||||||
createInitialDatabase
|
createInitialDatabase,
|
||||||
|
createDatabaseForSync
|
||||||
};
|
};
|
@ -18,6 +18,11 @@ const cls = require('./cls');
|
|||||||
|
|
||||||
let proxyToggle = true;
|
let proxyToggle = true;
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
outstandingPushes: 0,
|
||||||
|
outstandingPulls: 0
|
||||||
|
};
|
||||||
|
|
||||||
async function sync() {
|
async function sync() {
|
||||||
try {
|
try {
|
||||||
await syncMutexService.doExclusively(async () => {
|
await syncMutexService.doExclusively(async () => {
|
||||||
@ -82,21 +87,33 @@ async function login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function pullSync(syncContext) {
|
async function pullSync(syncContext) {
|
||||||
const changesUri = '/api/sync/changed?lastSyncId=' + await getLastSyncedPull();
|
while (true) {
|
||||||
|
const lastSyncedPull = await getLastSyncedPull();
|
||||||
|
const changesUri = '/api/sync/changed?lastSyncId=' + lastSyncedPull;
|
||||||
|
|
||||||
const rows = await syncRequest(syncContext, 'GET', changesUri);
|
const resp = await syncRequest(syncContext, 'GET', changesUri);
|
||||||
|
stats.outstandingPulls = resp.maxSyncId - lastSyncedPull;
|
||||||
|
|
||||||
log.info("Pulled " + rows.length + " changes from " + changesUri);
|
const rows = resp.syncs;
|
||||||
|
|
||||||
for (const {sync, entity} of rows) {
|
if (rows.length === 0) {
|
||||||
if (sourceIdService.isLocalSourceId(sync.sourceId)) {
|
break;
|
||||||
log.info(`Skipping pull #${sync.id} ${sync.entityName} ${sync.entityId} because ${sync.sourceId} is a local source id.`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await syncUpdateService.updateEntity(sync, entity, syncContext.sourceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await setLastSyncedPull(sync.id);
|
log.info("Pulled " + rows.length + " changes from " + changesUri);
|
||||||
|
|
||||||
|
for (const {sync, entity} of rows) {
|
||||||
|
if (sourceIdService.isLocalSourceId(sync.sourceId)) {
|
||||||
|
log.info(`Skipping pull #${sync.id} ${sync.entityName} ${sync.entityId} because ${sync.sourceId} is a local source id.`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await syncUpdateService.updateEntity(sync, entity, syncContext.sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.outstandingPulls = resp.maxSyncId - sync.id;
|
||||||
|
|
||||||
|
await setLastSyncedPull(sync.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Finished pull");
|
log.info("Finished pull");
|
||||||
@ -127,6 +144,8 @@ async function pushSync(syncContext) {
|
|||||||
if (filteredSyncs.length === 0) {
|
if (filteredSyncs.length === 0) {
|
||||||
log.info("Nothing to push");
|
log.info("Nothing to push");
|
||||||
|
|
||||||
|
stats.outstandingPushes = 0;
|
||||||
|
|
||||||
await setLastSyncedPush(lastSyncedPush);
|
await setLastSyncedPush(lastSyncedPush);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -144,6 +163,8 @@ async function pushSync(syncContext) {
|
|||||||
lastSyncedPush = syncRecords[syncRecords.length - 1].sync.id;
|
lastSyncedPush = syncRecords[syncRecords.length - 1].sync.id;
|
||||||
|
|
||||||
await setLastSyncedPush(lastSyncedPush);
|
await setLastSyncedPush(lastSyncedPush);
|
||||||
|
|
||||||
|
stats.outstandingPushes = await sql.getValue(`SELECT MAX(id) FROM sync`) - lastSyncedPush;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,5 +311,6 @@ sqlInit.dbReady.then(async () => {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
sync,
|
sync,
|
||||||
login,
|
login,
|
||||||
getSyncRecords
|
getSyncRecords,
|
||||||
|
stats
|
||||||
};
|
};
|
@ -86,6 +86,16 @@
|
|||||||
|
|
||||||
<button type="button" data-bind="click: finish" class="btn btn-primary">Finish setup</button>
|
<button type="button" data-bind="click: finish" class="btn btn-primary">Finish setup</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-bind="visible: step() == 'sync-in-progress'">
|
||||||
|
<h2>Sync in progress</h2>
|
||||||
|
|
||||||
|
<div class="alert alert-success">Sync has been correctly set up. It will take some time for the initial sync to finish. Once it's done, you'll be redirected to the login page.</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Outstanding sync items: <strong id="outstanding-syncs">N/A</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user