mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	add api.runAsyncOnBackendWithManualTransactionHandling() variant and add toast warnings about improper usage.
This commit is contained in:
		| @@ -178,36 +178,73 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Executes given anonymous function on the backend. | ||||
|      * Internally this serializes the anonymous function into string and sends it to backend via AJAX. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string|Function} script - script to be executed on the backend | ||||
|      * @param {Array<any>} params - list of parameters to the anonymous function to be sent to backend | ||||
|      * @returns {Promise<any>} return value of the executed function on the backend | ||||
|      * @private | ||||
|      */ | ||||
|     this.runOnBackend = async (script, params = []) => { | ||||
|         if (typeof script === "function") { | ||||
|             script = script.toString(); | ||||
|     this.__runOnBackendInner = async (func, params, transactional) => { | ||||
|         if (typeof func === "function") { | ||||
|             func = func.toString(); | ||||
|         } | ||||
|  | ||||
|         const ret = await server.post('script/exec', { | ||||
|             script: script, | ||||
|             script: func, | ||||
|             params: prepareParams(params), | ||||
|             startNoteId: startNote.noteId, | ||||
|             currentNoteId: currentNote.noteId, | ||||
|             originEntityName: "notes", // currently there's no other entity on the frontend which can trigger event | ||||
|             originEntityId: originEntity ? originEntity.noteId : null | ||||
|             originEntityId: originEntity ? originEntity.noteId : null, | ||||
|             transactional | ||||
|         }, "script"); | ||||
|  | ||||
|         if (ret.success) { | ||||
|             await ws.waitForMaxKnownEntityChangeId(); | ||||
|  | ||||
|             return ret.executionResult; | ||||
|         } | ||||
|         else { | ||||
|         } else { | ||||
|             throw new Error(`server error: ${ret.error}`); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Executes given anonymous function on the backend. | ||||
|      * Internally this serializes the anonymous function into string and sends it to backend via AJAX. | ||||
|      * Please make sure that the supplied function is synchronous. Only sync functions will work correctly | ||||
|      * with transaction management. If you really know what you're doing, you can call api.runAsyncOnBackendWithManualTransactionHandling() | ||||
|      * | ||||
|      * @method | ||||
|      * @param {function|string} func - (synchronous) function to be executed on the backend | ||||
|      * @param {Array.<?>} params - list of parameters to the anonymous function to be sent to backend | ||||
|      * @returns {Promise<*>} return value of the executed function on the backend | ||||
|      */ | ||||
|     this.runOnBackend = async (func, params = []) => { | ||||
|         if (func?.constructor.name === "AsyncFunction" || func?.startsWith?.("async ")) { | ||||
|             toastService.showError("You're passing an async function to api.runOnBackend() which will likely not work as you intended. " | ||||
|                 + "Either make the function synchronous (by removing 'async' keyword), or use api.runAsyncOnBackendWithManualTransactionHandling()"); | ||||
|         } | ||||
|  | ||||
|         return await this.__runOnBackendInner(func, params, true); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Executes given anonymous function on the backend. | ||||
|      * Internally this serializes the anonymous function into string and sends it to backend via AJAX. | ||||
|      * This function is meant for advanced needs where an async function is necessary. | ||||
|      * In this case, the automatic request-scoped transaction management is not applied, | ||||
|      * and you need to manually define transaction via api.transactional(). | ||||
|      * | ||||
|      * If you have a synchronous function, please use api.runOnBackend(). | ||||
|      * | ||||
|      * @method | ||||
|      * @param {function|string} func - (synchronous) function to be executed on the backend | ||||
|      * @param {Array.<?>} params - list of parameters to the anonymous function to be sent to backend | ||||
|      * @returns {Promise<*>} return value of the executed function on the backend | ||||
|      */ | ||||
|     this.runAsyncOnBackendWithManualTransactionHandling = async (func, params = []) => { | ||||
|         if (func?.constructor.name === "Function" || func?.startsWith?.("function")) { | ||||
|             toastService.showError("You're passing a synchronous function to api.runAsyncOnBackendWithManualTransactionHandling(), " + | ||||
|                 "while you should likely use api.runOnBackend() instead."); | ||||
|         } | ||||
|  | ||||
|         return await this.__runOnBackendInner(func, params, false); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -4,12 +4,16 @@ const scriptService = require('../../services/script'); | ||||
| const attributeService = require('../../services/attributes'); | ||||
| const becca = require('../../becca/becca'); | ||||
| const syncService = require('../../services/sync'); | ||||
| const sql = require('../../services/sql'); | ||||
|  | ||||
| // The async/await here is very confusing, because the body.script may, but may not be async. If it is async, then we | ||||
| // need to await it and make the complete response including metadata available in a Promise, so that the route detects | ||||
| // this and does result.then(). | ||||
| async function exec(req) { | ||||
|     try { | ||||
|         const {body} = req; | ||||
|  | ||||
|         const result = await scriptService.executeScript( | ||||
|         const execute = body => scriptService.executeScript( | ||||
|             body.script, | ||||
|             body.params, | ||||
|             body.startNoteId, | ||||
| @@ -18,6 +22,10 @@ async function exec(req) { | ||||
|             body.originEntityId | ||||
|         ); | ||||
|  | ||||
|         const result = body.transactional | ||||
|             ? sql.transactional(() => execute(body)) | ||||
|             : await execute(body); | ||||
|  | ||||
|         return { | ||||
|             success: true, | ||||
|             executionResult: result, | ||||
|   | ||||
| @@ -302,7 +302,8 @@ function register(app) { | ||||
|  | ||||
|     apiRoute(GET, '/api/database/check-integrity', databaseRoute.checkIntegrity); | ||||
|  | ||||
|     apiRoute(PST, '/api/script/exec', scriptRoute.exec); | ||||
|     route(PST, '/api/script/exec', [auth.checkApiAuth, csrfMiddleware], scriptRoute.exec, apiResultHandler, false); | ||||
|  | ||||
|     apiRoute(PST, '/api/script/run/:noteId', scriptRoute.run); | ||||
|     apiRoute(GET, '/api/script/startup', scriptRoute.getStartupBundles); | ||||
|     apiRoute(GET, '/api/script/widgets', scriptRoute.getWidgetBundles); | ||||
| @@ -449,7 +450,7 @@ function route(method, path, middleware, routeHandler, resultHandler = null, tra | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (result && result.then) { // promise | ||||
|             if (result?.then) { // promise | ||||
|                 result | ||||
|                     .then(promiseResult => handleResponse(resultHandler, req, res, promiseResult, start)) | ||||
|                     .catch(e => handleException(e, method, path, res)); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user