mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	new mechanism to wait for sync after interaction with backend in Script API using api.waitForMaxKnownSyncId()
This commit is contained in:
		| @@ -382,6 +382,11 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte | ||||
|      * @param {function} handler | ||||
|      */ | ||||
|     this.bindGlobalShortcut = utils.bindGlobalShortcut; | ||||
|  | ||||
|     /** | ||||
|      * @method | ||||
|      */ | ||||
|     this.waitUntilSynced = ws.waitForMaxKnownSyncId; | ||||
| } | ||||
|  | ||||
| export default FrontendScriptApi; | ||||
| @@ -42,12 +42,16 @@ async function remove(url, headers = {}) { | ||||
| let i = 1; | ||||
| const reqResolves = {}; | ||||
|  | ||||
| let maxKnownSyncId = 0; | ||||
|  | ||||
| async function call(method, url, data, headers = {}) { | ||||
|     let resp; | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
|         const ipc = require('electron').ipcRenderer; | ||||
|         const requestId = i++; | ||||
|  | ||||
|         return new Promise((resolve, reject) => { | ||||
|         resp = await new Promise((resolve, reject) => { | ||||
|             reqResolves[requestId] = resolve; | ||||
|  | ||||
|             if (REQUEST_LOGGING_ENABLED) { | ||||
| @@ -64,32 +68,58 @@ async function call(method, url, data, headers = {}) { | ||||
|         }); | ||||
|     } | ||||
|     else { | ||||
|         return await ajax(url, method, data, headers); | ||||
|         resp = await ajax(url, method, data, headers); | ||||
|     } | ||||
|  | ||||
|     const maxSyncIdStr = resp.headers['trilium-max-sync-id']; | ||||
|  | ||||
|     if (maxSyncIdStr && maxSyncIdStr.trim()) { | ||||
|         maxKnownSyncId = Math.max(maxKnownSyncId, parseInt(maxSyncIdStr)); | ||||
|     } | ||||
|  | ||||
|     return resp.body; | ||||
| } | ||||
|  | ||||
| async function ajax(url, method, data, headers) { | ||||
|     const options = { | ||||
|         url: baseApiUrl + url, | ||||
|         type: method, | ||||
|         headers: getHeaders(headers), | ||||
|         timeout: 60000 | ||||
|     }; | ||||
| function ajax(url, method, data, headers) { | ||||
|     return new Promise((res, rej) => { | ||||
|         const options = { | ||||
|             url: baseApiUrl + url, | ||||
|             type: method, | ||||
|             headers: getHeaders(headers), | ||||
|             timeout: 60000, | ||||
|             success: (body, textStatus, jqXhr) => { | ||||
|                 const respHeaders = {}; | ||||
|  | ||||
|     if (data) { | ||||
|         try { | ||||
|             options.data = JSON.stringify(data); | ||||
|         } | ||||
|         catch (e) { | ||||
|             console.log("Can't stringify data: ", data, " because of error: ", e) | ||||
|         } | ||||
|         options.contentType = "application/json"; | ||||
|     } | ||||
|                 jqXhr.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach(line => { | ||||
|                     const parts = line.split(': '); | ||||
|                     const header = parts.shift(); | ||||
|                     respHeaders[header] = parts.join(': '); | ||||
|                 }); | ||||
|  | ||||
|     return await $.ajax(options).catch(e => { | ||||
|         const message = "Error when calling " + method + " " + url + ": " + e.status + " - " + e.statusText; | ||||
|         toastService.showError(message); | ||||
|         toastService.throwError(message); | ||||
|                 res({ | ||||
|                     body, | ||||
|                     headers: respHeaders | ||||
|                 }); | ||||
|             }, | ||||
|             error: (jqXhr, textStatus, error) => { | ||||
|                 const message = "Error when calling " + method + " " + url + ": " + textStatus + " - " + error; | ||||
|                 toastService.showError(message); | ||||
|                 toastService.throwError(message); | ||||
|  | ||||
|                 rej(error); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if (data) { | ||||
|             try { | ||||
|                 options.data = JSON.stringify(data); | ||||
|             } catch (e) { | ||||
|                 console.log("Can't stringify data: ", data, " because of error: ", e) | ||||
|             } | ||||
|             options.contentType = "application/json"; | ||||
|         } | ||||
|  | ||||
|         $.ajax(options); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -101,7 +131,10 @@ if (utils.isElectron()) { | ||||
|             console.log(utils.now(), "Response #" + arg.requestId + ": " + arg.statusCode); | ||||
|         } | ||||
|  | ||||
|         reqResolves[arg.requestId](arg.body); | ||||
|         reqResolves[arg.requestId]({ | ||||
|             body: arg.body, | ||||
|             headers: arg.headers | ||||
|         }); | ||||
|  | ||||
|         delete reqResolves[arg.requestId]; | ||||
|     }); | ||||
| @@ -114,5 +147,6 @@ export default { | ||||
|     remove, | ||||
|     ajax, | ||||
|     // don't remove, used from CKEditor image upload! | ||||
|     getHeaders | ||||
|     getHeaders, | ||||
|     getMaxKnownSyncId: () => maxKnownSyncId | ||||
| }; | ||||
| @@ -77,7 +77,7 @@ async function stopWatch(what, func) { | ||||
| } | ||||
|  | ||||
| function formatValueWithWhitespace(val) { | ||||
|     return /[^\p{L}_-]/u.test(val) ? '"' + val + '"' : val; | ||||
|     return /[^\w_-]/.test(val) ? '"' + val + '"' : val; | ||||
| } | ||||
|  | ||||
| function formatLabel(label) { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import utils from './utils.js'; | ||||
| import toastService from "./toast.js"; | ||||
| import server from "./server.js"; | ||||
|  | ||||
| const $outstandingSyncsCount = $("#outstanding-syncs-count"); | ||||
|  | ||||
| @@ -71,8 +72,6 @@ async function handleMessage(event) { | ||||
|             // finish and set to null to signal somebody else can pick it up | ||||
|             consumeQueuePromise = null; | ||||
|         } | ||||
|  | ||||
|         checkSyncIdListeners(); | ||||
|     } | ||||
|     else if (message.type === 'sync-hash-check-failed') { | ||||
|         toastService.showError("Sync check failed!", 60000); | ||||
| @@ -98,6 +97,10 @@ function waitForSyncId(desiredSyncId) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function waitForMaxKnownSyncId() { | ||||
|     return waitForSyncId(server.getMaxKnownSyncId()); | ||||
| } | ||||
|  | ||||
| function checkSyncIdListeners() { | ||||
|     syncIdReachedListeners | ||||
|         .filter(l => l.desiredSyncId <= lastProcessedSyncId) | ||||
| @@ -129,6 +132,8 @@ async function consumeSyncData() { | ||||
|  | ||||
|         lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncData[allSyncData.length - 1].id); | ||||
|     } | ||||
|  | ||||
|     checkSyncIdListeners(); | ||||
| } | ||||
|  | ||||
| function connectWebSocket() { | ||||
| @@ -193,5 +198,6 @@ export default { | ||||
|     subscribeToMessages, | ||||
|     subscribeToAllSyncMessages, | ||||
|     subscribeToOutsideSyncMessages, | ||||
|     waitForSyncId | ||||
|     waitForSyncId, | ||||
|     waitForMaxKnownSyncId | ||||
| }; | ||||
| @@ -12,10 +12,14 @@ function init(app) { | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         const respHeaders = {}; | ||||
|  | ||||
|         const res = { | ||||
|             statusCode: 200, | ||||
|             getHeader: () => {}, | ||||
|             setHeader: () => {}, | ||||
|             getHeader: name => respHeaders[name], | ||||
|             setHeader: (name, value) => { | ||||
|                 respHeaders[name] = value.toString(); | ||||
|             }, | ||||
|             status: statusCode => { | ||||
|                 res.statusCode = statusCode; | ||||
|                 return res; | ||||
| @@ -24,6 +28,7 @@ function init(app) { | ||||
|                 event.sender.send('server-response', { | ||||
|                     requestId: arg.requestId, | ||||
|                     statusCode: res.statusCode, | ||||
|                     headers: respHeaders, | ||||
|                     body: obj | ||||
|                 }); | ||||
|             } | ||||
|   | ||||
| @@ -44,6 +44,7 @@ const auth = require('../services/auth'); | ||||
| const cls = require('../services/cls'); | ||||
| const sql = require('../services/sql'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const syncTableService = require('../services/sync_table'); | ||||
| const csurf = require('csurf'); | ||||
|  | ||||
| const csrfMiddleware = csurf({ | ||||
| @@ -52,6 +53,8 @@ const csrfMiddleware = csurf({ | ||||
| }); | ||||
|  | ||||
| function apiResultHandler(req, res, result) { | ||||
|     res.setHeader('trilium-max-sync-id', syncTableService.getMaxSyncId()); | ||||
|  | ||||
|     // if it's an array and first element is integer then we consider this to be [statusCode, response] format | ||||
|     if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) { | ||||
|         const [statusCode, response] = result; | ||||
|   | ||||
| @@ -21,6 +21,10 @@ async function addEntitySync(entityName, entityId, sourceId) { | ||||
|     setTimeout(() => require('./ws').sendPingToAllClients(), 50); | ||||
| } | ||||
|  | ||||
| function getMaxSyncId() { | ||||
|     return syncs.length === 0 ? 0 : syncs[syncs.length - 1].id; | ||||
| } | ||||
|  | ||||
| function getEntitySyncsNewerThan(syncId) { | ||||
|     return syncs.filter(s => s.id > syncId); | ||||
| } | ||||
| @@ -96,5 +100,6 @@ module.exports = { | ||||
|     addApiTokenSync: async (apiTokenId, sourceId) => await addEntitySync("api_tokens", apiTokenId, sourceId), | ||||
|     addEntitySync, | ||||
|     fillAllSyncRows, | ||||
|     getEntitySyncsNewerThan | ||||
|     getEntitySyncsNewerThan, | ||||
|     getMaxSyncId | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user