mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	
		
			
	
	
		
			132 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			132 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | const cls = require("../services/cls.js"); | ||
|  | const sql = require("../services/sql.js"); | ||
|  | const log = require("../services/log.js"); | ||
|  | const becca = require("../becca/becca.js"); | ||
|  | const GENERIC_CODE = "GENERIC"; | ||
|  | 
 | ||
|  | class EtapiError extends Error { | ||
|  |     constructor(statusCode, code, message) { | ||
|  |         super(); | ||
|  |          | ||
|  |         this.statusCode = statusCode; | ||
|  |         this.code = code; | ||
|  |         this.message = message; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function sendError(res, statusCode, code, message) { | ||
|  |     return res | ||
|  |         .set('Content-Type', 'application/json') | ||
|  |         .status(statusCode) | ||
|  |         .send(JSON.stringify({ | ||
|  |             "status": statusCode, | ||
|  |             "code": code, | ||
|  |             "message": message | ||
|  |         })); | ||
|  | } | ||
|  | 
 | ||
|  | function checkEtapiAuth(req, res, next) { | ||
|  |     if (false) { | ||
|  |         sendError(res, 401, "NOT_AUTHENTICATED", "Not authenticated"); | ||
|  |     } | ||
|  |     else { | ||
|  |         next(); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function route(router, method, path, routeHandler) { | ||
|  |     router[method](path, checkEtapiAuth, (req, res, next) => { | ||
|  |         try { | ||
|  |             cls.namespace.bindEmitter(req); | ||
|  |             cls.namespace.bindEmitter(res); | ||
|  | 
 | ||
|  |             cls.init(() => { | ||
|  |                 cls.set('sourceId', "etapi"); | ||
|  |                 cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']); | ||
|  | 
 | ||
|  |                 const cb = () => routeHandler(req, res, next); | ||
|  | 
 | ||
|  |                 return sql.transactional(cb); | ||
|  |             }); | ||
|  |         } | ||
|  |         catch (e) { | ||
|  |             log.error(`${method} ${path} threw exception ${e.message} with stacktrace: ${e.stack}`); | ||
|  |              | ||
|  |             if (e instanceof EtapiError) { | ||
|  |                 sendError(res, e.statusCode, e.code, e.message); | ||
|  |             } | ||
|  |             else { | ||
|  |                 sendError(res, 500, GENERIC_CODE, e.message); | ||
|  |             } | ||
|  |         } | ||
|  |     }); | ||
|  | } | ||
|  | 
 | ||
|  | function getAndCheckNote(noteId) { | ||
|  |     const note = becca.getNote(noteId); | ||
|  |      | ||
|  |     if (note) { | ||
|  |         return note; | ||
|  |     } | ||
|  |     else { | ||
|  |         throw new EtapiError(404, "NOTE_NOT_FOUND", `Note '${noteId}' not found`); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function getAndCheckBranch(branchId) { | ||
|  |     const branch = becca.getBranch(branchId); | ||
|  | 
 | ||
|  |     if (branch) { | ||
|  |         return branch; | ||
|  |     } | ||
|  |     else { | ||
|  |         throw new EtapiError(404, "BRANCH_NOT_FOUND", `Branch '${branchId}' not found`); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function getAndCheckAttribute(attributeId) { | ||
|  |     const attribute = becca.getAttribute(attributeId); | ||
|  | 
 | ||
|  |     if (attribute) { | ||
|  |         return attribute; | ||
|  |     } | ||
|  |     else { | ||
|  |         throw new EtapiError(404, "ATTRIBUTE_NOT_FOUND", `Attribute '${attributeId}' not found`); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function validateAndPatch(entity, props, allowedProperties) { | ||
|  |     for (const key of Object.keys(props)) { | ||
|  |         if (!(key in allowedProperties)) { | ||
|  |             throw new EtapiError(400, "PROPERTY_NOT_ALLOWED_FOR_PATCH", `Property '${key}' is not allowed for PATCH.`); | ||
|  |         } | ||
|  |         else { | ||
|  |             const validator = allowedProperties[key]; | ||
|  |             const validationResult = validator(props[key]); | ||
|  |              | ||
|  |             if (validationResult) { | ||
|  |                 throw new EtapiError(400, "PROPERTY_VALIDATION_ERROR", `Validation failed on property '${key}': ${validationResult}`); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     // validation passed, let's patch
 | ||
|  |     for (const propName of Object.keys(props)) { | ||
|  |         entity[propName] = props[propName]; | ||
|  |     } | ||
|  |      | ||
|  |     entity.save(); | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |     EtapiError, | ||
|  |     sendError, | ||
|  |     checkEtapiAuth, | ||
|  |     route, | ||
|  |     GENERIC_CODE, | ||
|  |     validateAndPatch, | ||
|  |     getAndCheckNote, | ||
|  |     getAndCheckBranch, | ||
|  |     getAndCheckAttribute, | ||
|  |     getNotAllowedPatchPropertyError: (propertyName, allowedProperties) => new EtapiError(400, "PROPERTY_NOT_ALLOWED_FOR_PATCH", `Property '${propertyName}' is not allowed to be patched, allowed properties are ${allowedProperties}.`), | ||
|  | } |