| 
									
										
										
										
											2022-05-09 16:38:23 +02:00
										 |  |  | const express = require('express'); | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  | const safeCompare = require('safe-compare'); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  | const ejs = require("ejs"); | 
					
						
							| 
									
										
										
										
											2022-05-09 16:38:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 19:34:48 +01:00
										 |  |  | const shaca = require('./shaca/shaca.js'); | 
					
						
							|  |  |  | const shacaLoader = require('./shaca/shaca_loader.js'); | 
					
						
							|  |  |  | const shareRoot = require('./share_root.js'); | 
					
						
							|  |  |  | const contentRenderer = require('./content_renderer.js'); | 
					
						
							| 
									
										
										
										
											2024-02-17 19:09:36 +02:00
										 |  |  | const assetPath = require('../services/asset_path'); | 
					
						
							| 
									
										
										
										
											2024-02-17 19:20:32 +02:00
										 |  |  | const appPath = require('../services/app_path'); | 
					
						
							| 
									
										
										
										
											2023-11-22 19:34:48 +01:00
										 |  |  | const searchService = require('../services/search/services/search.js'); | 
					
						
							|  |  |  | const SearchContext = require('../services/search/search_context.js'); | 
					
						
							| 
									
										
										
										
											2024-02-16 21:17:33 +02:00
										 |  |  | const log = require('../services/log'); | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 22:19:03 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {SNote} note | 
					
						
							|  |  |  |  * @return {{note: SNote, branch: SBranch}|{}} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-12-23 20:54:48 +01:00
										 |  |  | function getSharedSubTreeRoot(note) { | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  |     if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { | 
					
						
							| 
									
										
										
										
											2021-12-23 20:54:48 +01:00
										 |  |  |         // share root itself is not shared
 | 
					
						
							| 
									
										
										
										
											2023-07-17 22:19:03 +02:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-23 20:54:48 +01:00
										 |  |  |     // every path leads to share root, but which one to choose?
 | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  |     // for the sake of simplicity, URLs are not note paths
 | 
					
						
							| 
									
										
										
										
											2023-07-17 22:19:03 +02:00
										 |  |  |     const parentBranch = note.getParentBranches()[0]; | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 22:19:03 +02:00
										 |  |  |     if (parentBranch.parentNoteId === shareRoot.SHARE_ROOT_NOTE_ID) { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             note, | 
					
						
							|  |  |  |             branch: parentBranch | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 22:19:03 +02:00
										 |  |  |     return getSharedSubTreeRoot(parentBranch.getParentNote()); | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-17 14:44:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  | function addNoIndexHeader(note, res) { | 
					
						
							| 
									
										
										
										
											2023-06-29 00:14:12 +02:00
										 |  |  |     if (note.isLabelTruthy('shareDisallowRobotIndexing')) { | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         res.setHeader('X-Robots-Tag', 'noindex'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-01 19:56:09 +02:00
										 |  |  | function requestCredentials(res) { | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |     res.setHeader('WWW-Authenticate', 'Basic realm="User Visible Realm", charset="UTF-8"') | 
					
						
							|  |  |  |         .sendStatus(401); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  | /** @returns {SAttachment|boolean} */ | 
					
						
							|  |  |  | function checkAttachmentAccess(attachmentId, req, res) { | 
					
						
							|  |  |  |     const attachment = shaca.getAttachment(attachmentId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!attachment) { | 
					
						
							|  |  |  |         res.status(404) | 
					
						
							|  |  |  |             .json({ message: `Attachment '${attachmentId}' not found.` }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 17:01:56 +02:00
										 |  |  |     const note = checkNoteAccess(attachment.ownerId, req, res); | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  |     // truthy note means the user has access, and we can return the attachment
 | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  |     return note ? attachment : false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 22:55:50 +02:00
										 |  |  | /** @returns {SNote|boolean} */ | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  | function checkNoteAccess(noteId, req, res) { | 
					
						
							|  |  |  |     const note = shaca.getNote(noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!note) { | 
					
						
							| 
									
										
										
										
											2022-12-19 21:39:12 +01:00
										 |  |  |         res.status(404) | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  |             .json({ message: `Note '${noteId}' not found.` }); | 
					
						
							| 
									
										
										
										
											2022-12-19 21:39:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 16:11:00 +01:00
										 |  |  |     if (noteId === '_share' && !shaca.shareIndexEnabled) { | 
					
						
							| 
									
										
										
										
											2022-12-19 21:39:12 +01:00
										 |  |  |         res.status(403) | 
					
						
							|  |  |  |             .json({ message: `Accessing share index is forbidden.` }); | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const credentials = note.getCredentials(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (credentials.length === 0) { | 
					
						
							|  |  |  |         return note; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const header = req.header("Authorization"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!header?.startsWith("Basic ")) { | 
					
						
							| 
									
										
										
										
											2022-08-01 19:56:09 +02:00
										 |  |  |         requestCredentials(res); | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const base64Str = header.substring("Basic ".length); | 
					
						
							|  |  |  |     const buffer = Buffer.from(base64Str, 'base64'); | 
					
						
							|  |  |  |     const authString = buffer.toString('utf-8'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const credentialLabel of credentials) { | 
					
						
							|  |  |  |         if (safeCompare(authString, credentialLabel.value)) { | 
					
						
							|  |  |  |             return note; // success;
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-21 17:32:07 +02:00
										 |  |  | function renderImageAttachment(image, res, attachmentName) { | 
					
						
							|  |  |  |     let svgString = '<svg/>' | 
					
						
							|  |  |  |     const attachment = image.getAttachmentByTitle(attachmentName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (attachment) { | 
					
						
							|  |  |  |         svgString = attachment.getContent(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
 | 
					
						
							|  |  |  |         const contentSvg = image.getJsonContentSafely()?.svg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (contentSvg) { | 
					
						
							|  |  |  |             svgString = contentSvg; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const svg = svgString | 
					
						
							|  |  |  |     res.set('Content-Type', "image/svg+xml"); | 
					
						
							|  |  |  |     res.set("Cache-Control", "no-cache, no-store, must-revalidate"); | 
					
						
							|  |  |  |     res.send(svg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-17 14:44:59 +02:00
										 |  |  | function register(router) { | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |     function renderNote(note, req, res) { | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         if (!note) { | 
					
						
							| 
									
										
										
										
											2021-12-22 09:10:38 +01:00
										 |  |  |             res.status(404).render("share/404"); | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         if (!checkNoteAccess(note.noteId, req, res)) { | 
					
						
							| 
									
										
										
										
											2022-08-01 19:56:09 +02:00
										 |  |  |             requestCredentials(res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         addNoIndexHeader(note, res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-29 00:14:12 +02:00
										 |  |  |         if (note.isLabelTruthy('shareRaw')) { | 
					
						
							| 
									
										
										
										
											2022-07-01 00:01:29 +02:00
										 |  |  |             res.setHeader('Content-Type', note.mime) | 
					
						
							|  |  |  |                 .send(note.getContent()); | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-10-17 14:44:59 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const {header, content, isEmpty} = contentRenderer.getContent(note); | 
					
						
							|  |  |  |         const subRoot = getSharedSubTreeRoot(note); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         const opts = {note, header, content, isEmpty, subRoot, assetPath, appPath}; | 
					
						
							|  |  |  |         let useDefaultView = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Check if the user has their own template
 | 
					
						
							|  |  |  |         if (note.hasRelation('shareTemplate')) { | 
					
						
							|  |  |  |             // Get the template note and content
 | 
					
						
							|  |  |  |             const templateId = note.getRelation('shareTemplate').value; | 
					
						
							|  |  |  |             const templateNote = shaca.getNote(templateId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Make sure the note type is correct
 | 
					
						
							|  |  |  |             if (templateNote.type === 'code' && templateNote.mime === 'application/x-ejs') { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // EJS caches the result of this so we don't need to pre-cache
 | 
					
						
							|  |  |  |                 const includer = (path) => { | 
					
						
							|  |  |  |                     const childNote = templateNote.children.find(n => path === n.title); | 
					
						
							|  |  |  |                     if (!childNote) return null; | 
					
						
							|  |  |  |                     if (childNote.type !== 'code' || childNote.mime !== 'application/x-ejs') return null; | 
					
						
							|  |  |  |                     return { template: childNote.getContent() }; | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Try to render user's template, w/ fallback to default view
 | 
					
						
							|  |  |  |                 try { | 
					
						
							|  |  |  |                     const ejsResult = ejs.render(templateNote.getContent(), opts, {includer}); | 
					
						
							|  |  |  |                     res.send(ejsResult); | 
					
						
							|  |  |  |                     useDefaultView = false; // Rendering went okay, don't use default view
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 catch (e) { | 
					
						
							|  |  |  |                     log.error(`Rendering user provided share template (${templateId}) threw exception ${e.message} with stacktrace: ${e.stack}`); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         if (useDefaultView) { | 
					
						
							|  |  |  |             res.render('share/page', opts); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-17 23:13:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-30 09:05:12 +01:00
										 |  |  |     router.get('/share/', (req, res, next) => { | 
					
						
							|  |  |  |         if (req.path.substr(-1) !== '/') { | 
					
						
							|  |  |  |             res.redirect('../share/'); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-17 23:13:56 +01:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         renderNote(shaca.shareRootNote, req, res); | 
					
						
							| 
									
										
										
										
											2022-01-17 23:13:56 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     router.get('/share/:shareId', (req, res, next) => { | 
					
						
							|  |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  |         const {shareId} = req.params; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-17 23:13:56 +01:00
										 |  |  |         const note = shaca.aliasToNote[shareId] || shaca.notes[shareId]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         renderNote(note, req, res); | 
					
						
							| 
									
										
										
										
											2021-10-17 14:44:59 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  |     router.get('/share/api/notes/:noteId', (req, res, next) => { | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         let note; | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         addNoIndexHeader(note, res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  |         res.json(note.getPojo()); | 
					
						
							| 
									
										
										
										
											2021-10-19 22:48:38 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-12-06 22:53:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-22 09:36:38 +01:00
										 |  |  |     router.get('/share/api/notes/:noteId/download', (req, res, next) => { | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         let note; | 
					
						
							| 
									
										
										
										
											2021-12-06 22:53:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-12-06 22:53:17 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         addNoIndexHeader(note, res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-16 21:38:09 +02:00
										 |  |  |         const utils = require('../services/utils'); | 
					
						
							| 
									
										
										
										
											2021-12-06 22:53:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const filename = utils.formatDownloadTitle(note.title, note.type, note.mime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | 
					
						
							|  |  |  |         res.setHeader('Content-Type', note.mime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.send(note.getContent()); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-12-27 20:48:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 23:17:23 +02:00
										 |  |  |     // :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename
 | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  |     router.get('/share/api/images/:noteId/:filename', (req, res, next) => { | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         let image; | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         if (!(image = checkNoteAccess(req.params.noteId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-21 17:32:07 +02:00
										 |  |  |         if (image.type === 'image') { | 
					
						
							| 
									
										
										
										
											2022-04-11 21:38:05 +02:00
										 |  |  |             // normal image
 | 
					
						
							|  |  |  |             res.set('Content-Type', image.mime); | 
					
						
							| 
									
										
										
										
											2022-05-03 21:56:52 +02:00
										 |  |  |             addNoIndexHeader(image, res); | 
					
						
							| 
									
										
										
										
											2022-04-11 21:38:05 +02:00
										 |  |  |             res.send(image.getContent()); | 
					
						
							| 
									
										
										
										
											2023-10-21 17:32:07 +02:00
										 |  |  |         } else if (image.type === "canvas") { | 
					
						
							|  |  |  |             renderImageAttachment(image, res, 'canvas-export.svg'); | 
					
						
							|  |  |  |         } else if (image.type === 'mermaid') { | 
					
						
							|  |  |  |             renderImageAttachment(image, res, 'mermaid-export.svg'); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return res.status(400) | 
					
						
							|  |  |  |                 .json({ message: "Requested note is not a shareable image" }); | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 23:05:05 +02:00
										 |  |  |     // :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename
 | 
					
						
							|  |  |  |     router.get('/share/api/attachments/:attachmentId/image/:filename', (req, res, next) => { | 
					
						
							|  |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let attachment; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (attachment.role === "image") { | 
					
						
							|  |  |  |             res.set('Content-Type', attachment.mime); | 
					
						
							|  |  |  |             addNoIndexHeader(attachment.note, res); | 
					
						
							|  |  |  |             res.send(attachment.getContent()); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return res.status(400) | 
					
						
							|  |  |  |                 .json({ message: "Requested attachment is not a shareable image" }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 00:16:32 +02:00
										 |  |  |     router.get('/share/api/attachments/:attachmentId/download', (req, res, next) => { | 
					
						
							|  |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let attachment; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         addNoIndexHeader(attachment.note, res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-16 21:38:09 +02:00
										 |  |  |         const utils = require('../services/utils'); | 
					
						
							| 
									
										
										
										
											2023-06-06 00:16:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const filename = utils.formatDownloadTitle(attachment.title, null, attachment.mime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | 
					
						
							|  |  |  |         res.setHeader('Content-Type', attachment.mime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.send(attachment.getContent()); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-01 13:23:09 +01:00
										 |  |  |     // used for PDF viewing
 | 
					
						
							| 
									
										
										
										
											2021-12-24 21:36:31 +00:00
										 |  |  |     router.get('/share/api/notes/:noteId/view', (req, res, next) => { | 
					
						
							| 
									
										
										
										
											2022-05-01 23:16:47 +02:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         let note; | 
					
						
							| 
									
										
										
										
											2021-12-24 21:36:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:45:32 +02:00
										 |  |  |         if (!(note = checkNoteAccess(req.params.noteId, req, res))) { | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-12-24 21:36:31 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 23:17:47 +01:00
										 |  |  |         addNoIndexHeader(note, res); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-24 21:36:31 +00:00
										 |  |  |         res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | 
					
						
							|  |  |  |         res.setHeader('Content-Type', note.mime); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.send(note.getContent()); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Used for searching, require noteId so we know the subTreeRoot
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |     router.get('/share/api/notes', (req, res, next) => { | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         shacaLoader.ensureLoad(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |         const ancestorNoteId = req.query.ancestorNoteId ?? "_share"; | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         let note; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |         // This will automatically return if no ancestorNoteId is provided and there is no shareIndex
 | 
					
						
							|  |  |  |         if (!(note = checkNoteAccess(ancestorNoteId, req, res))) { | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |         const {search} = req.query; | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |         if (!search?.trim()) { | 
					
						
							|  |  |  |             return res.status(400).json({ message: "'search' parameter is mandatory." }); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |         const searchContext = new SearchContext({ancestorNoteId: ancestorNoteId}); | 
					
						
							|  |  |  |         const searchResults = searchService.findResultsWithQuery(search, searchContext); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         const filteredResults = searchResults.map(sr => { | 
					
						
							|  |  |  |             const fullNote = shaca.notes[sr.noteId]; | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |             const startIndex = sr.notePathArray.indexOf(ancestorNoteId); | 
					
						
							|  |  |  |             const localPathArray = sr.notePathArray.slice(startIndex + 1).filter(id => shaca.notes[id]); | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |             const pathTitle = localPathArray.map(id => shaca.notes[id].title).join(" / "); | 
					
						
							| 
									
										
										
										
											2023-10-08 14:54:37 -04:00
										 |  |  |             return { id: fullNote.shareId, title: fullNote.title, score: sr.score, path: pathTitle }; | 
					
						
							| 
									
										
										
										
											2023-09-27 14:34:07 -04:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res.json({ results: filteredResults }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-10-17 14:44:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = { | 
					
						
							|  |  |  |     register | 
					
						
							|  |  |  | } |