mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	server-ts: Convert etapi/notes
This commit is contained in:
		| @@ -5,6 +5,7 @@ import becca = require('../becca/becca'); | |||||||
| import etapiTokenService = require('../services/etapi_tokens'); | import etapiTokenService = require('../services/etapi_tokens'); | ||||||
| import config = require('../services/config'); | import config = require('../services/config'); | ||||||
| import { NextFunction, Request, RequestHandler, Response, Router } from 'express'; | import { NextFunction, Request, RequestHandler, Response, Router } from 'express'; | ||||||
|  | import { AppRequest, AppRequestHandler } from '../routes/route-interface'; | ||||||
| const GENERIC_CODE = "GENERIC"; | const GENERIC_CODE = "GENERIC"; | ||||||
|  |  | ||||||
| type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; | type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; | ||||||
| @@ -44,7 +45,7 @@ function checkEtapiAuth(req: Request, res: Response, next: NextFunction) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function processRequest(req: Request, res: Response, routeHandler: RequestHandler, next: NextFunction, method: string, path: string) { | function processRequest(req: Request, res: Response, routeHandler: AppRequestHandler, next: NextFunction, method: string, path: string) { | ||||||
|     try { |     try { | ||||||
|         cls.namespace.bindEmitter(req); |         cls.namespace.bindEmitter(req); | ||||||
|         cls.namespace.bindEmitter(res); |         cls.namespace.bindEmitter(res); | ||||||
| @@ -53,7 +54,7 @@ function processRequest(req: Request, res: Response, routeHandler: RequestHandle | |||||||
|             cls.set('componentId', "etapi"); |             cls.set('componentId', "etapi"); | ||||||
|             cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']); |             cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']); | ||||||
|  |  | ||||||
|             const cb = () => routeHandler(req, res, next); |             const cb = () => routeHandler(req as AppRequest, res, next); | ||||||
|  |  | ||||||
|             return sql.transactional(cb); |             return sql.transactional(cb); | ||||||
|         }); |         }); | ||||||
| @@ -68,7 +69,7 @@ function processRequest(req: Request, res: Response, routeHandler: RequestHandle | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function route(router: Router, method: HttpMethod, path: string, routeHandler: RequestHandler) { | function route(router: Router, method: HttpMethod, path: string, routeHandler: AppRequestHandler) { | ||||||
|     router[method](path, checkEtapiAuth, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path)); |     router[method](path, checkEtapiAuth, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path)); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,20 +1,26 @@ | |||||||
| const becca = require('../becca/becca'); | import becca = require('../becca/becca'); | ||||||
| const utils = require('../services/utils'); | import utils = require('../services/utils'); | ||||||
| const eu = require('./etapi_utils'); | import eu = require('./etapi_utils'); | ||||||
| const mappers = require('./mappers'); | import mappers = require('./mappers'); | ||||||
| const noteService = require('../services/notes'); | import noteService = require('../services/notes'); | ||||||
| const TaskContext = require('../services/task_context'); | import TaskContext = require('../services/task_context'); | ||||||
| const v = require('./validators'); | import v = require('./validators'); | ||||||
| const searchService = require('../services/search/services/search'); | import searchService = require('../services/search/services/search'); | ||||||
| const SearchContext = require('../services/search/search_context'); | import SearchContext = require('../services/search/search_context'); | ||||||
| const zipExportService = require('../services/export/zip'); | import zipExportService = require('../services/export/zip'); | ||||||
| const zipImportService = require('../services/import/zip'); | import zipImportService = require('../services/import/zip'); | ||||||
|  | import { Router } from 'express'; | ||||||
|  | import { AppRequest } from '../routes/route-interface'; | ||||||
|  | import { ParsedQs } from 'qs'; | ||||||
|  | import { NoteParams } from '../services/note-interface'; | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import { SearchParams } from '../services/search/services/types'; | ||||||
| 
 | 
 | ||||||
| function register(router) { | function register(router: Router) { | ||||||
|     eu.route(router, 'get', '/etapi/notes', (req, res, next) => { |     eu.route(router, 'get', '/etapi/notes', (req, res, next) => { | ||||||
|         const { search } = req.query; |         const { search } = req.query; | ||||||
| 
 | 
 | ||||||
|         if (!search?.trim()) { |         if (typeof search !== "string" || !search?.trim()) { | ||||||
|             throw new eu.EtapiError(400, 'SEARCH_QUERY_PARAM_MANDATORY', "'search' query parameter is mandatory."); |             throw new eu.EtapiError(400, 'SEARCH_QUERY_PARAM_MANDATORY', "'search' query parameter is mandatory."); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -24,8 +30,8 @@ function register(router) { | |||||||
|         const searchResults = searchService.findResultsWithQuery(search, searchContext); |         const searchResults = searchService.findResultsWithQuery(search, searchContext); | ||||||
|         const foundNotes = searchResults.map(sr => becca.notes[sr.noteId]); |         const foundNotes = searchResults.map(sr => becca.notes[sr.noteId]); | ||||||
| 
 | 
 | ||||||
|         const resp = { |         const resp: any = { | ||||||
|             results: foundNotes.map(note => mappers.mapNoteToPojo(note)) |             results: foundNotes.map(note => mappers.mapNoteToPojo(note)), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if (searchContext.debugInfo) { |         if (searchContext.debugInfo) { | ||||||
| @@ -41,7 +47,7 @@ function register(router) { | |||||||
|         res.json(mappers.mapNoteToPojo(note)); |         res.json(mappers.mapNoteToPojo(note)); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const ALLOWED_PROPERTIES_FOR_CREATE_NOTE = { |     const ALLOWED_PROPERTIES_FOR_CREATE_NOTE: ValidatorMap = { | ||||||
|         'parentNoteId': [v.mandatory, v.notNull, v.isNoteId], |         'parentNoteId': [v.mandatory, v.notNull, v.isNoteId], | ||||||
|         'title': [v.mandatory, v.notNull, v.isString], |         'title': [v.mandatory, v.notNull, v.isString], | ||||||
|         'type': [v.mandatory, v.notNull, v.isNoteType], |         'type': [v.mandatory, v.notNull, v.isNoteType], | ||||||
| @@ -56,9 +62,9 @@ function register(router) { | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     eu.route(router, 'post', '/etapi/create-note', (req, res, next) => { |     eu.route(router, 'post', '/etapi/create-note', (req, res, next) => { | ||||||
|         const params = {}; |         const _params = {}; | ||||||
| 
 |         eu.validateAndPatch(_params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_NOTE); | ||||||
|         eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_NOTE); |         const params = _params as NoteParams; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             const resp = noteService.createNewNote(params); |             const resp = noteService.createNewNote(params); | ||||||
| @@ -68,7 +74,7 @@ function register(router) { | |||||||
|                 branch: mappers.mapBranchToPojo(resp.branch) |                 branch: mappers.mapBranchToPojo(resp.branch) | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|         catch (e) { |         catch (e: any) { | ||||||
|             return eu.sendError(res, 500, eu.GENERIC_CODE, e.message); |             return eu.sendError(res, 500, eu.GENERIC_CODE, e.message); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| @@ -143,7 +149,7 @@ function register(router) { | |||||||
|         const note = eu.getAndCheckNote(req.params.noteId); |         const note = eu.getAndCheckNote(req.params.noteId); | ||||||
|         const format = req.query.format || "html"; |         const format = req.query.format || "html"; | ||||||
| 
 | 
 | ||||||
|         if (!["html", "markdown"].includes(format)) { |         if (typeof format !== "string" || !["html", "markdown"].includes(format)) { | ||||||
|             throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`); |             throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -153,7 +159,7 @@ function register(router) { | |||||||
|         // (e.g. branchIds are not seen in UI), that we export "note export" instead.
 |         // (e.g. branchIds are not seen in UI), that we export "note export" instead.
 | ||||||
|         const branch = note.getParentBranches()[0]; |         const branch = note.getParentBranches()[0]; | ||||||
| 
 | 
 | ||||||
|         zipExportService.exportToZip(taskContext, branch, format, res); |         zipExportService.exportToZip(taskContext, branch, format as "html" | "markdown", res); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     eu.route(router, 'post', '/etapi/notes/:noteId/import', (req, res, next) => { |     eu.route(router, 'post', '/etapi/notes/:noteId/import', (req, res, next) => { | ||||||
| @@ -186,23 +192,24 @@ function register(router) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function parseSearchParams(req) { | function parseSearchParams(req: AppRequest) { | ||||||
|     const rawSearchParams = { |     const rawSearchParams: SearchParams = { | ||||||
|         fastSearch: parseBoolean(req.query, 'fastSearch'), |         fastSearch: parseBoolean(req.query, 'fastSearch'), | ||||||
|         includeArchivedNotes: parseBoolean(req.query, 'includeArchivedNotes'), |         includeArchivedNotes: parseBoolean(req.query, 'includeArchivedNotes'), | ||||||
|         ancestorNoteId: req.query['ancestorNoteId'], |         ancestorNoteId: parseString(req.query['ancestorNoteId']), | ||||||
|         ancestorDepth: req.query['ancestorDepth'], // e.g. "eq5"
 |         ancestorDepth: parseString(req.query['ancestorDepth']), // e.g. "eq5"
 | ||||||
|         orderBy: req.query['orderBy'], |         orderBy: parseString(req.query['orderBy']), | ||||||
|         orderDirection: parseOrderDirection(req.query, 'orderDirection'), |         // TODO: Check why the order direction was provided as a number, but it's a string everywhere else.
 | ||||||
|  |         orderDirection: parseOrderDirection(req.query, 'orderDirection') as unknown as string, | ||||||
|         limit: parseInteger(req.query, 'limit'), |         limit: parseInteger(req.query, 'limit'), | ||||||
|         debug: parseBoolean(req.query, 'debug') |         debug: parseBoolean(req.query, 'debug') | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const searchParams = {}; |     const searchParams: SearchParams = {}; | ||||||
| 
 | 
 | ||||||
|     for (const paramName of Object.keys(rawSearchParams)) { |     for (const paramName of Object.keys(rawSearchParams) as (keyof SearchParams)[]) { | ||||||
|         if (rawSearchParams[paramName] !== undefined) { |         if (rawSearchParams[paramName] !== undefined) { | ||||||
|             searchParams[paramName] = rawSearchParams[paramName]; |             (searchParams as any)[paramName] = rawSearchParams[paramName]; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -211,7 +218,15 @@ function parseSearchParams(req) { | |||||||
| 
 | 
 | ||||||
| const SEARCH_PARAM_ERROR = "SEARCH_PARAM_VALIDATION_ERROR"; | const SEARCH_PARAM_ERROR = "SEARCH_PARAM_VALIDATION_ERROR"; | ||||||
| 
 | 
 | ||||||
| function parseBoolean(obj, name) { | function parseString(value: string | ParsedQs | string[] | ParsedQs[] | undefined): string | undefined { | ||||||
|  |     if (typeof value === "string") { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return undefined; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function parseBoolean(obj: any, name: string) { | ||||||
|     if (!(name in obj)) { |     if (!(name in obj)) { | ||||||
|         return undefined; |         return undefined; | ||||||
|     } |     } | ||||||
| @@ -223,11 +238,7 @@ function parseBoolean(obj, name) { | |||||||
|     return obj[name] === 'true'; |     return obj[name] === 'true'; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function parseOrderDirection(obj, name) { | function parseOrderDirection(obj: any, name: string) { | ||||||
|     if (!(name in obj)) { |  | ||||||
|         return undefined; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const integer = parseInt(obj[name]); |     const integer = parseInt(obj[name]); | ||||||
| 
 | 
 | ||||||
|     if (!['asc', 'desc'].includes(obj[name])) { |     if (!['asc', 'desc'].includes(obj[name])) { | ||||||
| @@ -237,7 +248,7 @@ function parseOrderDirection(obj, name) { | |||||||
|     return integer; |     return integer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function parseInteger(obj, name) { | function parseInteger(obj: any, name: string) { | ||||||
|     if (!(name in obj)) { |     if (!(name in obj)) { | ||||||
|         return undefined; |         return undefined; | ||||||
|     } |     } | ||||||
| @@ -251,6 +262,6 @@ function parseInteger(obj, name) { | |||||||
|     return integer; |     return integer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     register |     register | ||||||
| }; | }; | ||||||
| @@ -20,6 +20,13 @@ function updateFile(req: AppRequest) { | |||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
|  |  | ||||||
|     const file = req.file; |     const file = req.file; | ||||||
|  |     if (!file) { | ||||||
|  |         return { | ||||||
|  |             uploaded: false, | ||||||
|  |             message: `Missing file.` | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     note.saveRevision(); |     note.saveRevision(); | ||||||
|  |  | ||||||
|     note.mime = file.mimetype.toLowerCase(); |     note.mime = file.mimetype.toLowerCase(); | ||||||
| @@ -39,6 +46,12 @@ function updateFile(req: AppRequest) { | |||||||
| function updateAttachment(req: AppRequest) { | function updateAttachment(req: AppRequest) { | ||||||
|     const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); |     const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); | ||||||
|     const file = req.file; |     const file = req.file; | ||||||
|  |     if (!file) { | ||||||
|  |         return { | ||||||
|  |             uploaded: false, | ||||||
|  |             message: `Missing file.` | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     attachment.getNote().saveRevision(); |     attachment.getNote().saveRevision(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -88,6 +88,13 @@ function updateImage(req: AppRequest) { | |||||||
|  |  | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
|  |  | ||||||
|  |     if (!file) { | ||||||
|  |         return { | ||||||
|  |             uploaded: false, | ||||||
|  |             message: `Missing image data.` | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { |     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { | ||||||
|         return { |         return { | ||||||
|             uploaded: false, |             uploaded: false, | ||||||
|   | |||||||
| @@ -11,6 +11,13 @@ import { AppRequest } from '../route-interface'; | |||||||
| function uploadImage(req: AppRequest) { | function uploadImage(req: AppRequest) { | ||||||
|     const file = req.file; |     const file = req.file; | ||||||
|  |  | ||||||
|  |     if (!file) { | ||||||
|  |         return { | ||||||
|  |             uploaded: false, | ||||||
|  |             message: `Missing image data.` | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { |     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { | ||||||
|         return [400, `Unknown image type: ${file.mimetype}`]; |         return [400, `Unknown image type: ${file.mimetype}`]; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ function login(req: AppRequest, res: Response) { | |||||||
|             if (rememberMe) { |             if (rememberMe) { | ||||||
|                 req.session.cookie.maxAge = 21 * 24 * 3600000;  // 3 weeks |                 req.session.cookie.maxAge = 21 * 24 * 3600000;  // 3 weeks | ||||||
|             } else { |             } else { | ||||||
|                 req.session.cookie.expires = false; |                 req.session.cookie.expires = null; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             req.session.loggedIn = true; |             req.session.loggedIn = true; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { Request } from "express"; | import { NextFunction, Request, Response } from "express"; | ||||||
| import { File } from "../services/import/common"; | import { Session, SessionData } from "express-session"; | ||||||
|  |  | ||||||
| export interface AppRequest extends Request { | export interface AppRequest extends Request { | ||||||
|     headers: { |     headers: { | ||||||
| @@ -7,14 +7,15 @@ export interface AppRequest extends Request { | |||||||
|         "trilium-cred"?: string; |         "trilium-cred"?: string; | ||||||
|         "x-local-date"?: string; |         "x-local-date"?: string; | ||||||
|         "x-labels"?: string; |         "x-labels"?: string; | ||||||
|  |         "trilium-local-now-datetime"?: string; | ||||||
|     } |     } | ||||||
|     session: { |     session: Session & Partial<SessionData> & { | ||||||
|         loggedIn: boolean;    |         loggedIn: boolean; | ||||||
|         cookie: { |  | ||||||
|             maxAge: number; |  | ||||||
|             expires: boolean |  | ||||||
|         }; |  | ||||||
|         regenerate: (callback: () => void) => void;      |  | ||||||
|     } |     } | ||||||
|     file: File; | } | ||||||
| } |  | ||||||
|  | export type AppRequestHandler = ( | ||||||
|  |     req: AppRequest, | ||||||
|  |     res: Response, | ||||||
|  |     next: NextFunction | ||||||
|  | ) => void; | ||||||
| @@ -66,7 +66,7 @@ const etapiAppInfoRoutes = require('../etapi/app_info'); | |||||||
| const etapiAttachmentRoutes = require('../etapi/attachments'); | const etapiAttachmentRoutes = require('../etapi/attachments'); | ||||||
| const etapiAttributeRoutes = require('../etapi/attributes'); | const etapiAttributeRoutes = require('../etapi/attributes'); | ||||||
| const etapiBranchRoutes = require('../etapi/branches'); | const etapiBranchRoutes = require('../etapi/branches'); | ||||||
| const etapiNoteRoutes = require('../etapi/notes.js'); | const etapiNoteRoutes = require('../etapi/notes'); | ||||||
| const etapiSpecialNoteRoutes = require('../etapi/special_notes'); | const etapiSpecialNoteRoutes = require('../etapi/special_notes'); | ||||||
| const etapiSpecRoute = require('../etapi/spec.js'); | const etapiSpecRoute = require('../etapi/spec.js'); | ||||||
| const etapiBackupRoute = require('../etapi/backup'); | const etapiBackupRoute = require('../etapi/backup'); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user