mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import log from "../services/log.js";
 | 
						|
import fileService from "./api/files.js";
 | 
						|
import scriptService from "../services/script.js";
 | 
						|
import cls from "../services/cls.js";
 | 
						|
import sql from "../services/sql.js";
 | 
						|
import becca from "../becca/becca.js";
 | 
						|
import { Request, Response, Router } from 'express';
 | 
						|
 | 
						|
function handleRequest(req: Request, res: Response) {
 | 
						|
    // express puts content after first slash into 0 index element
 | 
						|
 | 
						|
    const path = req.params.path + req.params[0];
 | 
						|
 | 
						|
    const attributeIds = sql.getColumn<string>("SELECT attributeId FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
 | 
						|
 | 
						|
    const attrs = attributeIds.map(attrId => becca.getAttribute(attrId));
 | 
						|
 | 
						|
    for (const attr of attrs) {
 | 
						|
        if (!attr?.value.trim()) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        const regex = new RegExp(`^${attr.value}$`);
 | 
						|
        let match;
 | 
						|
 | 
						|
        try {
 | 
						|
            match = path.match(regex);
 | 
						|
        }
 | 
						|
        catch (e: any) {
 | 
						|
            log.error(`Testing path for label '${attr.attributeId}', regex '${attr.value}' failed with error: ${e.message}, stack: ${e.stack}`);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!match) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (attr.name === 'customRequestHandler') {
 | 
						|
            const note = attr.getNote();
 | 
						|
 | 
						|
            log.info(`Handling custom request '${path}' with note '${note.noteId}'`);
 | 
						|
 | 
						|
            try {
 | 
						|
                scriptService.executeNote(note, {
 | 
						|
                    pathParams: match.slice(1),
 | 
						|
                    req,
 | 
						|
                    res
 | 
						|
                });
 | 
						|
            }
 | 
						|
            catch (e: any) {
 | 
						|
                log.error(`Custom handler '${note.noteId}' failed with: ${e.message}, ${e.stack}`);
 | 
						|
 | 
						|
                res.setHeader("Content-Type", "text/plain")
 | 
						|
                    .status(500)
 | 
						|
                    .send(e.message);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (attr.name === 'customResourceProvider') {
 | 
						|
            fileService.downloadNoteInt(attr.noteId, res);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            throw new Error(`Unrecognized attribute name '${attr.name}'`);
 | 
						|
        }
 | 
						|
 | 
						|
        return; // only the first handler is executed
 | 
						|
    }
 | 
						|
 | 
						|
    const message = `No handler matched for custom '${path}' request.`;
 | 
						|
 | 
						|
    log.info(message);
 | 
						|
    res.setHeader("Content-Type", "text/plain")
 | 
						|
        .status(404)
 | 
						|
        .send(message);
 | 
						|
}
 | 
						|
 | 
						|
function register(router: Router) {
 | 
						|
    // explicitly no CSRF middleware since it's meant to allow integration from external services
 | 
						|
 | 
						|
    router.all('/custom/:path*', (req: Request, res: Response, next) => {
 | 
						|
        cls.namespace.bindEmitter(req);
 | 
						|
        cls.namespace.bindEmitter(res);
 | 
						|
 | 
						|
        cls.init(() => handleRequest(req, res));
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
export default {
 | 
						|
    register
 | 
						|
};
 |