mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
server-ts: Port share/routes
This commit is contained in:
parent
88aba1c844
commit
c08393f04b
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() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user