mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	server-ts: Port share/routes
This commit is contained in:
		
							
								
								
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -92,6 +92,7 @@ | |||||||
|         "@types/better-sqlite3": "^7.6.9", |         "@types/better-sqlite3": "^7.6.9", | ||||||
|         "@types/cls-hooked": "^4.3.8", |         "@types/cls-hooked": "^4.3.8", | ||||||
|         "@types/csurf": "^1.11.5", |         "@types/csurf": "^1.11.5", | ||||||
|  |         "@types/ejs": "^3.1.5", | ||||||
|         "@types/escape-html": "^1.0.4", |         "@types/escape-html": "^1.0.4", | ||||||
|         "@types/express": "^4.17.21", |         "@types/express": "^4.17.21", | ||||||
|         "@types/express-session": "^1.18.0", |         "@types/express-session": "^1.18.0", | ||||||
| @@ -101,6 +102,7 @@ | |||||||
|         "@types/mime-types": "^2.1.4", |         "@types/mime-types": "^2.1.4", | ||||||
|         "@types/multer": "^1.4.11", |         "@types/multer": "^1.4.11", | ||||||
|         "@types/node": "^20.11.19", |         "@types/node": "^20.11.19", | ||||||
|  |         "@types/safe-compare": "^1.1.2", | ||||||
|         "@types/sanitize-html": "^2.11.0", |         "@types/sanitize-html": "^2.11.0", | ||||||
|         "@types/sax": "^1.2.7", |         "@types/sax": "^1.2.7", | ||||||
|         "@types/stream-throttle": "^0.1.4", |         "@types/stream-throttle": "^0.1.4", | ||||||
| @@ -1271,6 +1273,12 @@ | |||||||
|         "@types/ms": "*" |         "@types/ms": "*" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/ejs": { | ||||||
|  |       "version": "3.1.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", | ||||||
|  |       "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "node_modules/@types/escape-html": { |     "node_modules/@types/escape-html": { | ||||||
|       "version": "1.0.4", |       "version": "1.0.4", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz", | ||||||
| @@ -1537,6 +1545,12 @@ | |||||||
|         "@types/node": "*" |         "@types/node": "*" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/safe-compare": { | ||||||
|  |       "version": "1.1.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/safe-compare/-/safe-compare-1.1.2.tgz", | ||||||
|  |       "integrity": "sha512-kK/IM1+pvwCMom+Kezt/UlP8LMEwm8rP6UgGbRc6zUnhU/csoBQ5rWgmD2CJuHxiMiX+H1VqPGpo0kDluJGXYA==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "node_modules/@types/sanitize-html": { |     "node_modules/@types/sanitize-html": { | ||||||
|       "version": "2.11.0", |       "version": "2.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", |       "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", | ||||||
| @@ -14276,6 +14290,12 @@ | |||||||
|         "@types/ms": "*" |         "@types/ms": "*" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "@types/ejs": { | ||||||
|  |       "version": "3.1.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", | ||||||
|  |       "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "@types/escape-html": { |     "@types/escape-html": { | ||||||
|       "version": "1.0.4", |       "version": "1.0.4", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz", | ||||||
| @@ -14535,6 +14555,12 @@ | |||||||
|         "@types/node": "*" |         "@types/node": "*" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "@types/safe-compare": { | ||||||
|  |       "version": "1.1.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/safe-compare/-/safe-compare-1.1.2.tgz", | ||||||
|  |       "integrity": "sha512-kK/IM1+pvwCMom+Kezt/UlP8LMEwm8rP6UgGbRc6zUnhU/csoBQ5rWgmD2CJuHxiMiX+H1VqPGpo0kDluJGXYA==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "@types/sanitize-html": { |     "@types/sanitize-html": { | ||||||
|       "version": "2.11.0", |       "version": "2.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", |       "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", | ||||||
|   | |||||||
| @@ -113,6 +113,7 @@ | |||||||
|     "@types/better-sqlite3": "^7.6.9", |     "@types/better-sqlite3": "^7.6.9", | ||||||
|     "@types/cls-hooked": "^4.3.8", |     "@types/cls-hooked": "^4.3.8", | ||||||
|     "@types/csurf": "^1.11.5", |     "@types/csurf": "^1.11.5", | ||||||
|  |     "@types/ejs": "^3.1.5", | ||||||
|     "@types/escape-html": "^1.0.4", |     "@types/escape-html": "^1.0.4", | ||||||
|     "@types/express": "^4.17.21", |     "@types/express": "^4.17.21", | ||||||
|     "@types/express-session": "^1.18.0", |     "@types/express-session": "^1.18.0", | ||||||
| @@ -122,6 +123,7 @@ | |||||||
|     "@types/mime-types": "^2.1.4", |     "@types/mime-types": "^2.1.4", | ||||||
|     "@types/multer": "^1.4.11", |     "@types/multer": "^1.4.11", | ||||||
|     "@types/node": "^20.11.19", |     "@types/node": "^20.11.19", | ||||||
|  |     "@types/safe-compare": "^1.1.2", | ||||||
|     "@types/sanitize-html": "^2.11.0", |     "@types/sanitize-html": "^2.11.0", | ||||||
|     "@types/sax": "^1.2.7", |     "@types/sax": "^1.2.7", | ||||||
|     "@types/stream-throttle": "^0.1.4", |     "@types/stream-throttle": "^0.1.4", | ||||||
|   | |||||||
| @@ -209,7 +209,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|             .map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId)) as BBranch[]; |             .map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId)) as BBranch[]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* |     /** | ||||||
|      * Note content has quite special handling - it's not a separate entity, but a lazily loaded |      * Note content has quite special handling - it's not a separate entity, but a lazily loaded | ||||||
|      * part of Note entity with its own sync. Reasons behind this hybrid design has been: |      * part of Note entity with its own sync. Reasons behind this hybrid design has been: | ||||||
|      * |      * | ||||||
| @@ -222,7 +222,8 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @throws Error in case of invalid JSON */ |      * @throws Error in case of invalid JSON | ||||||
|  |      */ | ||||||
|     getJsonContent(): any | null { |     getJsonContent(): any | null { | ||||||
|         const content = this.getContent(); |         const content = this.getContent(); | ||||||
|  |  | ||||||
| @@ -233,7 +234,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|         return JSON.parse(content); |         return JSON.parse(content); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {*|null} valid object or null if the content cannot be parsed as JSON */ |     /** @returns valid object or null if the content cannot be parsed as JSON */ | ||||||
|     getJsonContentSafely() { |     getJsonContentSafely() { | ||||||
|         try { |         try { | ||||||
|             return this.getJsonContent(); |             return this.getJsonContent(); | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ const fontsRoute = require('./api/fonts'); | |||||||
| const etapiTokensApiRoutes = require('./api/etapi_tokens'); | const etapiTokensApiRoutes = require('./api/etapi_tokens'); | ||||||
| const relationMapApiRoute = require('./api/relation-map'); | const relationMapApiRoute = require('./api/relation-map'); | ||||||
| const otherRoute = require('./api/other'); | const otherRoute = require('./api/other'); | ||||||
| const shareRoutes = require('../share/routes.js'); | const shareRoutes = require('../share/routes'); | ||||||
|  |  | ||||||
| const etapiAuthRoutes = require('../etapi/auth'); | const etapiAuthRoutes = require('../etapi/auth'); | ||||||
| const etapiAppInfoRoutes = require('../etapi/app_info'); | const etapiAppInfoRoutes = require('../etapi/app_info'); | ||||||
|   | |||||||
| @@ -1,23 +1,24 @@ | |||||||
| const express = require('express'); | import express = require('express'); | ||||||
| const path = require('path'); | import path = require('path'); | ||||||
| const safeCompare = require('safe-compare'); | import safeCompare = require('safe-compare'); | ||||||
| const ejs = require("ejs"); | import ejs = require("ejs"); | ||||||
| 
 | 
 | ||||||
| const shaca = require('./shaca/shaca'); | import shaca = require('./shaca/shaca'); | ||||||
| const shacaLoader = require('./shaca/shaca_loader'); | import shacaLoader = require('./shaca/shaca_loader'); | ||||||
| const shareRoot = require('./share_root'); | import shareRoot = require('./share_root'); | ||||||
| const contentRenderer = require('./content_renderer'); | import contentRenderer = require('./content_renderer'); | ||||||
| const assetPath = require('../services/asset_path'); | import assetPath = require('../services/asset_path'); | ||||||
| const appPath = require('../services/app_path'); | import appPath = require('../services/app_path'); | ||||||
| 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 log = require('../services/log'); | import log = require('../services/log'); | ||||||
|  | import SNote = require('./shaca/entities/snote'); | ||||||
|  | import SBranch = require('./shaca/entities/sbranch'); | ||||||
|  | import SAttachment = require('./shaca/entities/sattachment'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import BRevision = require('../becca/entities/brevision'); | ||||||
| 
 | 
 | ||||||
| /** | function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } { | ||||||
|  * @param {SNote} note |  | ||||||
|  * @return {{note: SNote, branch: SBranch}|{}} |  | ||||||
|  */ |  | ||||||
| function getSharedSubTreeRoot(note) { |  | ||||||
|     if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { |     if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { | ||||||
|         // share root itself is not shared
 |         // share root itself is not shared
 | ||||||
|         return {}; |         return {}; | ||||||
| @@ -37,19 +38,18 @@ function getSharedSubTreeRoot(note) { | |||||||
|     return getSharedSubTreeRoot(parentBranch.getParentNote()); |     return getSharedSubTreeRoot(parentBranch.getParentNote()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function addNoIndexHeader(note, res) { | function addNoIndexHeader(note: SNote, res: express.Response) { | ||||||
|     if (note.isLabelTruthy('shareDisallowRobotIndexing')) { |     if (note.isLabelTruthy('shareDisallowRobotIndexing')) { | ||||||
|         res.setHeader('X-Robots-Tag', 'noindex'); |         res.setHeader('X-Robots-Tag', 'noindex'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function requestCredentials(res) { | function requestCredentials(res: express.Response) { | ||||||
|     res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm", charset="UTF-8"') |     res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm", charset="UTF-8"') | ||||||
|         .sendStatus(401); |         .sendStatus(401); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** @returns {SAttachment|boolean} */ | function checkAttachmentAccess(attachmentId: string, req: express.Request, res: express.Response) { | ||||||
| function checkAttachmentAccess(attachmentId, req, res) { |  | ||||||
|     const attachment = shaca.getAttachment(attachmentId); |     const attachment = shaca.getAttachment(attachmentId); | ||||||
| 
 | 
 | ||||||
|     if (!attachment) { |     if (!attachment) { | ||||||
| @@ -65,8 +65,7 @@ function checkAttachmentAccess(attachmentId, req, res) { | |||||||
|     return note ? attachment : false; |     return note ? attachment : false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** @returns {SNote|boolean} */ | function checkNoteAccess(noteId: string, req: express.Request, res: express.Response) { | ||||||
| function checkNoteAccess(noteId, req, res) { |  | ||||||
|     const note = shaca.getNote(noteId); |     const note = shaca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
| @@ -109,12 +108,16 @@ function checkNoteAccess(noteId, req, res) { | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function renderImageAttachment(image, res, attachmentName) { | function renderImageAttachment(image: SNote, res: express.Response, attachmentName: string) { | ||||||
|     let svgString = '<svg/>' |     let svgString = '<svg/>' | ||||||
|     const attachment = image.getAttachmentByTitle(attachmentName); |     const attachment = image.getAttachmentByTitle(attachmentName); | ||||||
| 
 |     if (!attachment) { | ||||||
|     if (attachment) { |         res.status(404).render("share/404"); | ||||||
|         svgString = attachment.getContent(); |         return; | ||||||
|  |     } | ||||||
|  |     const content = attachment.getContent(); | ||||||
|  |     if (typeof content === "string") { | ||||||
|  |         svgString = content; | ||||||
|     } else { |     } else { | ||||||
|         // backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
 |         // backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
 | ||||||
|         const contentSvg = image.getJsonContentSafely()?.svg; |         const contentSvg = image.getJsonContentSafely()?.svg; | ||||||
| @@ -130,8 +133,8 @@ function renderImageAttachment(image, res, attachmentName) { | |||||||
|     res.send(svg); |     res.send(svg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function register(router) { | function register(router: express.Router) { | ||||||
|     function renderNote(note, req, res) { |     function renderNote(note: SNote, req: express.Request, res: express.Response) { | ||||||
|         if (!note) { |         if (!note) { | ||||||
|             res.status(404).render("share/404"); |             res.status(404).render("share/404"); | ||||||
|             return; |             return; | ||||||
| @@ -160,27 +163,34 @@ function register(router) { | |||||||
|         // Check if the user has their own template
 |         // Check if the user has their own template
 | ||||||
|         if (note.hasRelation('shareTemplate')) { |         if (note.hasRelation('shareTemplate')) { | ||||||
|             // Get the template note and content
 |             // Get the template note and content
 | ||||||
|             const templateId = note.getRelation('shareTemplate').value; |             const templateId = note.getRelation('shareTemplate')?.value; | ||||||
|             const templateNote = shaca.getNote(templateId); |             const templateNote = templateId && shaca.getNote(templateId); | ||||||
| 
 | 
 | ||||||
|             // Make sure the note type is correct
 |             // Make sure the note type is correct
 | ||||||
|             if (templateNote.type === 'code' && templateNote.mime === 'application/x-ejs') { |             if (templateNote && templateNote.type === 'code' && templateNote.mime === 'application/x-ejs') { | ||||||
| 
 | 
 | ||||||
|                 // EJS caches the result of this so we don't need to pre-cache
 |                 // EJS caches the result of this so we don't need to pre-cache
 | ||||||
|                 const includer = (path) => { |                 const includer = (path: string) => { | ||||||
|                     const childNote = templateNote.children.find(n => path === n.title); |                     const childNote = templateNote.children.find(n => path === n.title); | ||||||
|                     if (!childNote) return null; |                     if (!childNote) throw new Error("Unable to find child note."); | ||||||
|                     if (childNote.type !== 'code' || childNote.mime !== 'application/x-ejs') return null; |                     if (childNote.type !== 'code' || childNote.mime !== 'application/x-ejs') throw new Error("Incorrect child note type."); | ||||||
|                     return { template: childNote.getContent() }; | 
 | ||||||
|  |                     const template = childNote.getContent(); | ||||||
|  |                     if (typeof template !== "string") throw new Error("Invalid template content type."); | ||||||
|  | 
 | ||||||
|  |                     return { template }; | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 // Try to render user's template, w/ fallback to default view
 |                 // Try to render user's template, w/ fallback to default view
 | ||||||
|                 try { |                 try { | ||||||
|                     const ejsResult = ejs.render(templateNote.getContent(), opts, { includer }); |                     const content = templateNote.getContent(); | ||||||
|  |                     if (typeof content === "string") { | ||||||
|  |                         const ejsResult = ejs.render(content, opts, { includer }); | ||||||
|                         res.send(ejsResult); |                         res.send(ejsResult); | ||||||
|                         useDefaultView = false; // Rendering went okay, don't use default view
 |                         useDefaultView = false; // Rendering went okay, don't use default view
 | ||||||
|                     } |                     } | ||||||
|                 catch (e) { |                 } | ||||||
|  |                 catch (e: any) { | ||||||
|                     log.error(`Rendering user provided share template (${templateId}) threw exception ${e.message} with stacktrace: ${e.stack}`); |                     log.error(`Rendering user provided share template (${templateId}) threw exception ${e.message} with stacktrace: ${e.stack}`); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -199,6 +209,11 @@ function register(router) { | |||||||
| 
 | 
 | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|  |         if (!shaca.shareRootNote) { | ||||||
|  |             return res.status(404) | ||||||
|  |                 .json({ message: "Share root note not found" }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         renderNote(shaca.shareRootNote, req, res); |         renderNote(shaca.shareRootNote, req, res); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @@ -214,7 +229,7 @@ function register(router) { | |||||||
| 
 | 
 | ||||||
|     router.get('/share/api/notes/:noteId', (req, res, next) => { |     router.get('/share/api/notes/:noteId', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
|         let note; |         let note: SNote | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -228,7 +243,7 @@ function register(router) { | |||||||
|     router.get('/share/api/notes/:noteId/download', (req, res, next) => { |     router.get('/share/api/notes/:noteId/download', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|         let note; |         let note: SNote | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -252,7 +267,7 @@ function register(router) { | |||||||
|     router.get('/share/api/images/:noteId/:filename', (req, res, next) => { |     router.get('/share/api/images/:noteId/:filename', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|         let image; |         let image: SNote | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(image = checkNoteAccess(req.params.noteId, req, res))) { |         if (!(image = checkNoteAccess(req.params.noteId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -277,7 +292,7 @@ function register(router) { | |||||||
|     router.get('/share/api/attachments/:attachmentId/image/:filename', (req, res, next) => { |     router.get('/share/api/attachments/:attachmentId/image/:filename', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|         let attachment; |         let attachment: SAttachment | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { |         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -296,7 +311,7 @@ function register(router) { | |||||||
|     router.get('/share/api/attachments/:attachmentId/download', (req, res, next) => { |     router.get('/share/api/attachments/:attachmentId/download', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|         let attachment; |         let attachment: SAttachment | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { |         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -320,7 +335,7 @@ function register(router) { | |||||||
|     router.get('/share/api/notes/:noteId/view', (req, res, next) => { |     router.get('/share/api/notes/:noteId/view', (req, res, next) => { | ||||||
|         shacaLoader.ensureLoad(); |         shacaLoader.ensureLoad(); | ||||||
| 
 | 
 | ||||||
|         let note; |         let note: SNote | boolean; | ||||||
| 
 | 
 | ||||||
|         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -341,6 +356,10 @@ function register(router) { | |||||||
|         const ancestorNoteId = req.query.ancestorNoteId ?? "_share"; |         const ancestorNoteId = req.query.ancestorNoteId ?? "_share"; | ||||||
|         let note; |         let note; | ||||||
| 
 | 
 | ||||||
|  |         if (typeof ancestorNoteId !== "string") { | ||||||
|  |             return res.status(400).json({ message: "'ancestorNoteId' parameter is mandatory." }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // This will automatically return if no ancestorNoteId is provided and there is no shareIndex
 |         // This will automatically return if no ancestorNoteId is provided and there is no shareIndex
 | ||||||
|         if (!(note = checkNoteAccess(ancestorNoteId, req, res))) { |         if (!(note = checkNoteAccess(ancestorNoteId, req, res))) { | ||||||
|             return; |             return; | ||||||
| @@ -348,7 +367,7 @@ function register(router) { | |||||||
| 
 | 
 | ||||||
|         const { search } = req.query; |         const { search } = req.query; | ||||||
| 
 | 
 | ||||||
|         if (!search?.trim()) { |         if (typeof search !== "string" || !search?.trim()) { | ||||||
|             return res.status(400).json({ message: "'search' parameter is mandatory." }); |             return res.status(400).json({ message: "'search' parameter is mandatory." }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -366,6 +385,6 @@ function register(router) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     register |     register | ||||||
| } | } | ||||||
| @@ -8,10 +8,10 @@ import { Blob } from '../../../services/blob-interface'; | |||||||
|  |  | ||||||
| class SAttachment extends AbstractShacaEntity { | class SAttachment extends AbstractShacaEntity { | ||||||
|     private attachmentId: string; |     private attachmentId: string; | ||||||
|     private ownerId: string; |     ownerId: string; | ||||||
|     title: string; |     title: string; | ||||||
|     role: string; |     role: string; | ||||||
|     private mime: string; |     mime: string; | ||||||
|     private blobId: string; |     private blobId: string; | ||||||
|     /** used for caching of images */ |     /** used for caching of images */ | ||||||
|     private utcDateModified: string; |     private utcDateModified: string; | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ class SBranch extends AbstractShacaEntity { | |||||||
|  |  | ||||||
|     private branchId: string; |     private branchId: string; | ||||||
|     private noteId: string; |     private noteId: string; | ||||||
|     private parentNoteId: string; |     parentNoteId: string; | ||||||
|     private prefix: string; |     private prefix: string; | ||||||
|     private isExpanded: boolean; |     private isExpanded: boolean; | ||||||
|     isHidden: boolean; |     isHidden: boolean; | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ const isCredentials = (attr: SAttribute) => attr.type === 'label' && attr.name = | |||||||
|  |  | ||||||
| class SNote extends AbstractShacaEntity { | class SNote extends AbstractShacaEntity { | ||||||
|     noteId: string; |     noteId: string; | ||||||
|     private title: string; |     title: string; | ||||||
|     type: string; |     type: string; | ||||||
|     mime: string; |     mime: string; | ||||||
|     private blobId: string; |     private blobId: string; | ||||||
| @@ -223,6 +223,29 @@ class SNote extends AbstractShacaEntity { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @throws Error in case of invalid JSON | ||||||
|  |      */ | ||||||
|  |     getJsonContent(): any | null { | ||||||
|  |         const content = this.getContent(); | ||||||
|  |  | ||||||
|  |         if (typeof content !== "string" || !content || !content.trim()) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return JSON.parse(content); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns valid object or null if the content cannot be parsed as JSON */ | ||||||
|  |     getJsonContentSafely() { | ||||||
|  |         try { | ||||||
|  |             return this.getJsonContent(); | ||||||
|  |         } | ||||||
|  |         catch (e) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     hasAttribute(type: string, name: string) { |     hasAttribute(type: string, name: string) { | ||||||
|         return !!this.getAttributes().find(attr => attr.type === type && attr.name === name); |         return !!this.getAttributes().find(attr => attr.type === type && attr.name === name); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -10,10 +10,10 @@ export default class Shaca { | |||||||
|     childParentToBranch!: Record<string, SBranch>; |     childParentToBranch!: Record<string, SBranch>; | ||||||
|     private attributes!: Record<string, SAttribute>; |     private attributes!: Record<string, SAttribute>; | ||||||
|     attachments!: Record<string, SAttachment>; |     attachments!: Record<string, SAttachment>; | ||||||
|     private aliasToNote!: Record<string, SNote>; |     aliasToNote!: Record<string, SNote>; | ||||||
|     private shareRootNote!: SNote | null; |     shareRootNote!: SNote | null; | ||||||
|     /** true if the index of all shared subtrees is enabled */ |     /** true if the index of all shared subtrees is enabled */ | ||||||
|     private shareIndexEnabled!: boolean; |     shareIndexEnabled!: boolean; | ||||||
|     loaded!: boolean; |     loaded!: boolean; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user