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