diff --git a/public/javascripts/dialogs/settings.js b/public/javascripts/dialogs/settings.js index 3d9187f8d..2eb385695 100644 --- a/public/javascripts/dialogs/settings.js +++ b/public/javascripts/dialogs/settings.js @@ -154,6 +154,7 @@ settings.addModule((async function () { settings.addModule((async function () { const forceFullSyncButton = $("#force-full-sync-button"); + const fillSyncRowsButton = $("#fill-sync-rows-button"); forceFullSyncButton.click(async () => { await server.post('sync/force-full-sync'); @@ -161,6 +162,12 @@ settings.addModule((async function () { showMessage("Full sync triggered"); }); + fillSyncRowsButton.click(async () => { + await server.post('sync/fill-sync-rows'); + + showMessage("Sync rows filled successfully"); + }); + return {}; })()); diff --git a/routes/api/sync.js b/routes/api/sync.js index b90b8ebd4..c5bbeab58 100644 --- a/routes/api/sync.js +++ b/routes/api/sync.js @@ -8,6 +8,8 @@ const syncUpdate = require('../../services/sync_update'); const sql = require('../../services/sql'); const options = require('../../services/options'); const content_hash = require('../../services/content_hash'); +const utils = require('../../services/utils'); +const log = require('../../services/log'); router.get('/check', auth.checkApiAuth, async (req, res, next) => { res.send({ @@ -20,6 +22,44 @@ router.post('/now', auth.checkApiAuth, async (req, res, next) => { res.send(await sync.sync()); }); +async function fillSyncRows(entityName, entityKey) { + // cleanup sync rows for missing entities + await sql.execute(` + DELETE + FROM sync + WHERE sync.entity_name = '${entityName}' + AND sync.entity_id NOT IN (SELECT ${entityKey} FROM ${entityName})`); + + const entityIds = await sql.getFlattenedResults(`SELECT ${entityKey} FROM ${entityName}`); + + for (const entityId of entityIds) { + const existingRows = await sql.getSingleValue("SELECT COUNT(id) FROM sync WHERE entity_name = ? AND entity_id = ?", [entityName, entityId]); + + // we don't want to replace existing entities (which would effectively cause full resync) + if (existingRows === 0) { + log.info(`Creating missing sync record for ${entityName} ${entityId}`); + + await sql.insert("sync", { + entity_name: entityName, + entity_id: entityId, + source_id: "SYNC_FILL", + sync_date: utils.nowDate() + }); + } + } +} + +router.post('/fill-sync-rows', auth.checkApiAuth, async (req, res, next) => { + await sql.doInTransaction(async () => { + await fillSyncRows("notes", "note_id"); + await fillSyncRows("notes_tree", "note_tree_id"); + await fillSyncRows("notes_history", "note_history_id"); + await fillSyncRows("recent_notes", "note_tree_id"); + }); + + res.send({}); +}); + router.post('/force-full-sync', auth.checkApiAuth, async (req, res, next) => { await sql.doInTransaction(async () => { await options.setOption('last_synced_pull', 0); diff --git a/services/consistency_checks.js b/services/consistency_checks.js index aacd5f19d..5a74a727f 100644 --- a/services/consistency_checks.js +++ b/services/consistency_checks.js @@ -16,7 +16,7 @@ async function runCheck(query, errorText, errorList) { } async function runSyncRowChecks(table, key, errorList) { - await runCheck(`SELECT ${key} FROM ${table} LEFT JOIN sync ON sync.entity_name = '${table}' AND entity_id = ${key} WHERE entity_id != 'root' AND sync.id IS NULL`, + await runCheck(`SELECT ${key} FROM ${table} LEFT JOIN sync ON sync.entity_name = '${table}' AND entity_id = ${key} WHERE sync.id IS NULL`, `Missing sync records for ${key} in table ${table}`, errorList); await runCheck(`SELECT entity_id FROM sync LEFT JOIN ${table} ON entity_id = ${key} WHERE sync.entity_name = '${table}' AND ${key} IS NULL`, diff --git a/views/index.ejs b/views/index.ejs index e249db3f0..c8e0d879f 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -219,6 +219,11 @@