Merge remote-tracking branch 'origin/next58' into next58

# Conflicts:
#	src/public/app/components/note_context.js
This commit is contained in:
zadam 2022-12-10 14:37:02 +01:00
commit 1f468f81cc
61 changed files with 1895 additions and 1737 deletions

View File

@ -30,6 +30,7 @@ rm -r $BUILD_DIR/swiftshader
cp bin/tpl/anonymize-database.sql $BUILD_DIR/ cp bin/tpl/anonymize-database.sql $BUILD_DIR/
cp -r dump-db $BUILD_DIR/ cp -r dump-db $BUILD_DIR/
rm -rf $BUILD_DIR/dump-db/node_modules
cp bin/tpl/trilium-portable.sh $BUILD_DIR/ cp bin/tpl/trilium-portable.sh $BUILD_DIR/
chmod 755 $BUILD_DIR/trilium-portable.sh chmod 755 $BUILD_DIR/trilium-portable.sh

View File

@ -26,6 +26,7 @@ mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR
cp bin/tpl/anonymize-database.sql $BUILD_DIR/ cp bin/tpl/anonymize-database.sql $BUILD_DIR/
cp -r dump-db $BUILD_DIR/ cp -r dump-db $BUILD_DIR/
rm -rf $BUILD_DIR/dump-db/node_modules
echo "Zipping mac x64 electron distribution..." echo "Zipping mac x64 electron distribution..."

View File

@ -31,6 +31,7 @@ chmod 755 $PKG_DIR/trilium.sh
cp bin/tpl/anonymize-database.sql $PKG_DIR/ cp bin/tpl/anonymize-database.sql $PKG_DIR/
cp -r dump-db $PKG_DIR/ cp -r dump-db $PKG_DIR/
rm -rf $PKG_DIR/dump-db/node_modules
VERSION=`jq -r ".version" package.json` VERSION=`jq -r ".version" package.json`

View File

@ -28,6 +28,7 @@ rm -r $BUILD_DIR/swiftshader
cp bin/tpl/anonymize-database.sql $BUILD_DIR/ cp bin/tpl/anonymize-database.sql $BUILD_DIR/
cp -r dump-db $BUILD_DIR/ cp -r dump-db $BUILD_DIR/
rm -rf $BUILD_DIR/dump-db/node_modules
cp bin/tpl/trilium-{portable,no-cert-check,safe-mode}.{bat,ps1} $BUILD_DIR/ cp bin/tpl/trilium-{portable,no-cert-check,safe-mode}.{bat,ps1} $BUILD_DIR/

View File

@ -14,9 +14,6 @@ mkdir $DIR
echo "Copying Trilium to build directory $DIR" echo "Copying Trilium to build directory $DIR"
cp -r dump-db $DIR/
rm -rf $DIR/dump-db/node_modules
cp -r images $DIR/ cp -r images $DIR/
cp -r libraries $DIR/ cp -r libraries $DIR/
cp -r src $DIR/ cp -r src $DIR/

View File

@ -1,5 +1,5 @@
module.exports = () => { module.exports = () => {
const hiddenSubtreeService = require('../../src/services/hidden_subtree.js'); const hiddenSubtreeService = require('../../src/services/hidden_subtree');
const cls = require("../../src/services/cls"); const cls = require("../../src/services/cls");
const beccaLoader = require("../../src/becca/becca_loader"); const beccaLoader = require("../../src/becca/becca_loader");

View File

@ -2,7 +2,7 @@
const yargs = require('yargs/yargs') const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers') const { hideBin } = require('yargs/helpers')
const dumpService = require("./inc/dump.js"); const dumpService = require("./inc/dump");
yargs(hideBin(process.argv)) yargs(hideBin(process.argv))
.command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => { .command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => {

View File

@ -1,6 +1,6 @@
const crypto = require("crypto"); const crypto = require("crypto");
const sql = require("./sql.js"); const sql = require("./sql");
const decryptService = require("./decrypt.js"); const decryptService = require("./decrypt");
function getDataKey(password) { function getDataKey(password) {
if (!password) { if (!password) {

View File

@ -1,9 +1,9 @@
const fs = require("fs"); const fs = require("fs");
const sanitize = require("sanitize-filename"); const sanitize = require("sanitize-filename");
const sql = require("./sql.js"); const sql = require("./sql");
const decryptService = require("./decrypt.js"); const decryptService = require("./decrypt");
const dataKeyService = require("./data_key.js"); const dataKeyService = require("./data_key");
const extensionService = require("./extension.js"); const extensionService = require("./extension");
function dumpDocument(documentPath, targetPath, options) { function dumpDocument(documentPath, targetPath, options) {
const stats = { const stats = {

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2021 Aniket Suvarna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

1
package-lock.json generated
View File

@ -5,7 +5,6 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "trilium",
"version": "0.57.3", "version": "0.57.3",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",

View File

@ -4,7 +4,7 @@ const Branch = require('../../src/becca/entities/branch');
const SearchContext = require('../../src/services/search/search_context'); const SearchContext = require('../../src/services/search/search_context');
const dateUtils = require('../../src/services/date_utils'); const dateUtils = require('../../src/services/date_utils');
const becca = require('../../src/becca/becca'); const becca = require('../../src/becca/becca');
const {NoteBuilder, findNoteByTitle, note} = require('./becca_mocking.js'); const {NoteBuilder, findNoteByTitle, note} = require('./becca_mocking');
describe("Search", () => { describe("Search", () => {
let rootNote; let rootNote;

View File

@ -1,4 +1,4 @@
const {note} = require('./becca_mocking.js'); const {note} = require('./becca_mocking');
const ValueExtractor = require('../../src/services/search/value_extractor'); const ValueExtractor = require('../../src/services/search/value_extractor');
const becca = require('../../src/becca/becca'); const becca = require('../../src/becca/becca');
const SearchContext = require("../../src/services/search/search_context"); const SearchContext = require("../../src/services/search/search_context");

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
const becca = require('./becca.js'); const becca = require('./becca');
const cls = require('../services/cls'); const cls = require('../services/cls');
const protectedSessionService = require('../services/protected_session'); const protectedSessionService = require('../services/protected_session');
const log = require('../services/log'); const log = require('../services/log');

View File

@ -4,7 +4,7 @@ const Note = require('./note');
const AbstractEntity = require("./abstract_entity"); const AbstractEntity = require("./abstract_entity");
const sql = require("../../services/sql"); const sql = require("../../services/sql");
const dateUtils = require("../../services/date_utils"); const dateUtils = require("../../services/date_utils");
const utils = require("../../services/utils.js"); const utils = require("../../services/utils");
const TaskContext = require("../../services/task_context"); const TaskContext = require("../../services/task_context");
const cls = require("../../services/cls"); const cls = require("../../services/cls");
const log = require("../../services/log"); const log = require("../../services/log");

View File

@ -1329,7 +1329,7 @@ class Note extends AbstractEntity {
} }
isLaunchBarConfig() { isLaunchBarConfig() {
return this.type === 'launcher' || ['lbRoot', 'lbAvailableShortcuts', 'lbVisibleShortcuts']; return this.type === 'launcher' || ['lbRoot', 'lbAvailableLaunchers', 'lbVisibleLaunchers'].includes(this.noteId);
} }
isOptions() { isOptions() {

View File

@ -1,6 +1,6 @@
const becca = require('./becca'); const becca = require('./becca');
const log = require('../services/log'); const log = require('../services/log');
const beccaService = require('./becca_service.js'); const beccaService = require('./becca_service');
const dateUtils = require('../services/date_utils'); const dateUtils = require('../services/date_utils');
const { JSDOM } = require("jsdom"); const { JSDOM } = require("jsdom");

View File

@ -0,0 +1,7 @@
class NotFoundError {
constructor(message) {
this.message = message;
}
}
module.exports = NotFoundError;

View File

@ -0,0 +1,7 @@
class ValidationError {
constructor(message) {
this.message = message;
}
}
module.exports = ValidationError;

View File

@ -1,5 +1,5 @@
const appInfo = require('../services/app_info'); const appInfo = require('../services/app_info');
const eu = require("./etapi_utils.js"); const eu = require("./etapi_utils");
function register(router) { function register(router) {
eu.route(router, 'get', '/etapi/app-info', (req, res, next) => { eu.route(router, 'get', '/etapi/app-info', (req, res, next) => {

View File

@ -63,7 +63,10 @@ class NoteContext extends Component {
}); });
} }
if (this.hoistedNoteId === 'root' && this.notePath.startsWith("root/hidden")) { if (this.hoistedNoteId === 'root'
&& this.notePath.startsWith("root/hidden")
&& !this.note.hasLabel("keepCurrentHoisting")
) {
// hidden subtree displays only when hoisted so it doesn't make sense to keep root as hoisted note // hidden subtree displays only when hoisted so it doesn't make sense to keep root as hoisted note
let hoistedNoteId = 'hidden'; let hoistedNoteId = 'hidden';

View File

@ -828,7 +828,7 @@ class NoteShort {
} }
isLaunchBarConfig() { isLaunchBarConfig() {
return this.type === 'launcher' || ['lbRoot', 'lbAvailableShortcuts', 'lbVisibleShortcuts'].includes(this.noteId); return this.type === 'launcher' || ['lbRoot', 'lbAvailableLaunchers', 'lbVisibleLaunchers'].includes(this.noteId);
} }
isOptions() { isOptions() {

View File

@ -90,27 +90,27 @@ function getNotePathFromLink($link) {
return url ? getNotePathFromUrl(url) : null; return url ? getNotePathFromUrl(url) : null;
} }
function goToLink(e) { function goToLink(evt) {
const $link = $(e.target).closest("a,.block-link"); const $link = $(evt.target).closest("a,.block-link");
const address = $link.attr('href'); const address = $link.attr('href');
if (address?.startsWith("data:")) { if (address?.startsWith("data:")) {
return true; return true;
} }
e.preventDefault(); evt.preventDefault();
e.stopPropagation(); evt.stopPropagation();
const notePath = getNotePathFromLink($link); const notePath = getNotePathFromLink($link);
const ctrlKey = (!utils.isMac() && e.ctrlKey) || (utils.isMac() && e.metaKey); const ctrlKey = utils.isCtrlKey(evt);
if (notePath) { if (notePath) {
if ((e.which === 1 && ctrlKey) || e.which === 2) { if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
appContext.tabManager.openTabWithNoteWithHoisting(notePath); appContext.tabManager.openTabWithNoteWithHoisting(notePath);
} }
else if (e.which === 1) { else if (evt.which === 1) {
const ntxId = $(e.target).closest("[data-ntx-id]").attr("data-ntx-id"); const ntxId = $(evt.target).closest("[data-ntx-id]").attr("data-ntx-id");
const noteContext = ntxId const noteContext = ntxId
? appContext.tabManager.getNoteContextById(ntxId) ? appContext.tabManager.getNoteContextById(ntxId)
@ -124,7 +124,7 @@ function goToLink(e) {
} }
} }
else { else {
if ((e.which === 1 && ctrlKey) || e.which === 2 if ((evt.which === 1 && ctrlKey) || evt.which === 2
|| $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices
|| $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices || $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices
) { ) {

View File

@ -1,4 +1,5 @@
import utils from './utils.js'; import utils from './utils.js';
import ValidationError from "./validation_error.js";
const REQUEST_LOGGING_ENABLED = false; const REQUEST_LOGGING_ENABLED = false;
@ -102,10 +103,15 @@ async function call(method, url, data, headers = {}) {
return resp.body; return resp.body;
} }
async function reportError(method, url, status, error) { async function reportError(method, url, status, response) {
const message = "Error when calling " + method + " " + url + ": " + status + " - " + error;
const toastService = (await import("./toast.js")).default; const toastService = (await import("./toast.js")).default;
if ([400, 404].includes(status) && response && typeof response === 'object') {
toastService.showError(response.message);
throw new ValidationError(response);
}
const message = "Error when calling " + method + " " + url + ": " + status + " - " + responseText;
toastService.showError(message); toastService.showError(message);
toastService.throwError(message); toastService.throwError(message);
} }

View File

@ -60,6 +60,11 @@ function isMac() {
return navigator.platform.indexOf('Mac') > -1; return navigator.platform.indexOf('Mac') > -1;
} }
function isCtrlKey(evt) {
return (!isMac() && evt.ctrlKey)
|| (isMac() && evt.metaKey);
}
function assertArguments() { function assertArguments() {
for (const i in arguments) { for (const i in arguments) {
if (!arguments[i]) { if (!arguments[i]) {
@ -362,6 +367,7 @@ export default {
now, now,
isElectron, isElectron,
isMac, isMac,
isCtrlKey,
assertArguments, assertArguments,
escapeHtml, escapeHtml,
stopWatch, stopWatch,

View File

@ -0,0 +1,7 @@
export default class ValidationError {
constructor(resp) {
for (const key in resp) {
this[key] = resp[key];
}
}
}

View File

@ -357,7 +357,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
// disable spellcheck for attribute editor // disable spellcheck for attribute editor
this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot())); this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot()));
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js'); //await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector');
//CKEditorInspector.attach(this.textEditor); //CKEditorInspector.attach(this.textEditor);
} }

View File

@ -1,23 +1,55 @@
import AbstractLauncher from "./abstract_launcher.js"; import AbstractLauncher from "./abstract_launcher.js";
import dialogService from "../../../services/dialog.js"; import dialogService from "../../../services/dialog.js";
import appContext from "../../../components/app_context.js"; import appContext from "../../../components/app_context.js";
import utils from "../../../services/utils.js";
import linkContextMenuService from "../../../menus/link_context_menu.js";
// we're intentionally displaying the launcher title and icon instead of the target
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only downside is more work in setting up the typical case
// where you actually want to have both title and icon in sync, but for those cases there are bookmarks
export default class NoteLauncher extends AbstractLauncher { export default class NoteLauncher extends AbstractLauncher {
constructor(launcherNote) { constructor(launcherNote) {
super(launcherNote); super(launcherNote);
this.title(this.launcherNote.title) this.title(this.launcherNote.title)
.icon(this.launcherNote.getIcon()) .icon(this.launcherNote.getIcon())
.onClick(() => this.launch()); .onClick((widget, evt) => this.launch(evt))
.onAuxClick((widget, evt) => this.launch(evt))
.onContextMenu(evt => {
const targetNoteId = this.getTargetNoteId();
if (!targetNoteId) {
return;
}
linkContextMenuService.openContextMenu(targetNoteId, evt);
});
} }
launch() { launch(evt) {
// we're intentionally displaying the launcher title and icon instead of the target const targetNoteId = this.getTargetNoteId();
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok), if (!targetNoteId) {
// but on the launchpad you want them distinguishable. return;
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad }
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only (but major) downside is more work in setting up the typical case where you actually want to have both title and icon in sync. if (!evt) {
// keyboard shortcut
appContext.tabManager.getActiveContext().setNote(targetNoteId)
return;
}
const ctrlKey = utils.isCtrlKey(evt);
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId);
} else {
appContext.tabManager.getActiveContext().setNote(targetNoteId);
}
}
getTargetNoteId() {
const targetNoteId = this.launcherNote.getRelationValue('targetNote'); const targetNoteId = this.launcherNote.getRelationValue('targetNote');
if (!targetNoteId) { if (!targetNoteId) {
@ -25,7 +57,7 @@ export default class NoteLauncher extends AbstractLauncher {
return; return;
} }
appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true); return targetNoteId;
} }
getTitle() { getTitle() {

View File

@ -13,10 +13,23 @@ export default class OnClickButtonWidget extends AbstractButtonWidget {
} else { } else {
console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings); console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings);
} }
if (this.settings.onAuxClick) {
this.$widget.on("auxclick", e => {
this.$widget.tooltip("hide");
this.settings.onAuxClick(this, e);
});
}
} }
onClick(handler) { onClick(handler) {
this.settings.onClick = handler; this.settings.onClick = handler;
return this; return this;
} }
onAuxClick(handler) {
this.settings.onAuxClick = handler;
return this;
}
} }

View File

@ -452,7 +452,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
const importService = await import('../services/import.js'); const importService = await import('../services/import');
importService.uploadFiles(node.data.noteId, files, { importService.uploadFiles(node.data.noteId, files, {
safeImport: true, safeImport: true,
@ -568,7 +568,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
$span.append($refreshSearchButton); $span.append($refreshSearchButton);
} }
if (!['search', 'launcher'].includes(note.type) && !note.isOptions()) { if (!['search', 'launcher'].includes(note.type) && !note.isOptions() && !note.isLaunchBarConfig()) {
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>'); const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>');
$span.append($createChildNoteButton); $span.append($createChildNoteButton);

View File

@ -132,7 +132,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate()); this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
if (glob.isDev && ENABLE_INSPECTOR) { if (glob.isDev && ENABLE_INSPECTOR) {
await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector.js'); await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector');
CKEditorInspector.attach(this.textEditor); CKEditorInspector.attach(this.textEditor);
} }
} }

View File

@ -5,6 +5,8 @@ const log = require('../../services/log');
const attributeService = require('../../services/attributes'); const attributeService = require('../../services/attributes');
const Attribute = require('../../becca/entities/attribute'); const Attribute = require('../../becca/entities/attribute');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function getEffectiveNoteAttributes(req) { function getEffectiveNoteAttributes(req) {
const note = becca.getNote(req.params.noteId); const note = becca.getNote(req.params.noteId);
@ -21,11 +23,11 @@ function updateNoteAttribute(req) {
attribute = becca.getAttribute(body.attributeId); attribute = becca.getAttribute(body.attributeId);
if (!attribute) { if (!attribute) {
return [404, `Attribute '${body.attributeId}' does not exist.`]; throw new NotFoundError(`Attribute '${body.attributeId}' does not exist.`);
} }
if (attribute.noteId !== noteId) { if (attribute.noteId !== noteId) {
return [400, `Attribute '${body.attributeId}' is not owned by ${noteId}`]; throw new ValidationError(`Attribute '${body.attributeId}' is not owned by ${noteId}`);
} }
if (body.type !== attribute.type if (body.type !== attribute.type
@ -106,7 +108,7 @@ function deleteNoteAttribute(req) {
if (attribute) { if (attribute) {
if (attribute.noteId !== noteId) { if (attribute.noteId !== noteId) {
return [400, `Attribute ${attributeId} is not owned by ${noteId}`]; throw new ValidationError(`Attribute ${attributeId} is not owned by ${noteId}`);
} }
attribute.markAsDeleted(); attribute.markAsDeleted();

View File

@ -8,7 +8,9 @@ const noteService = require('../../services/notes');
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context'); const TaskContext = require('../../services/task_context');
const branchService = require("../../services/branches"); const branchService = require("../../services/branches");
const log = require("../../services/log.js"); const log = require("../../services/log");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
/** /**
* Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique * Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique
@ -22,7 +24,7 @@ function moveBranchToParent(req) {
const branchToMove = becca.getBranch(branchId); const branchToMove = becca.getBranch(branchId);
if (!parentBranch || !branchToMove) { if (!parentBranch || !branchToMove) {
return [400, `One or both branches ${branchId}, ${parentBranchId} have not been found`]; throw new ValidationError(`One or both branches ${branchId}, ${parentBranchId} have not been found`);
} }
return branchService.moveBranchToBranch(branchToMove, parentBranch, branchId); return branchService.moveBranchToBranch(branchToMove, parentBranch, branchId);
@ -35,11 +37,11 @@ function moveBranchBeforeNote(req) {
const beforeBranch = becca.getBranch(beforeBranchId); const beforeBranch = becca.getBranch(beforeBranchId);
if (!branchToMove) { if (!branchToMove) {
return [404, `Can't find branch ${branchId}`]; throw new NotFoundError(`Can't find branch '${branchId}'`);
} }
if (!beforeBranch) { if (!beforeBranch) {
return [404, `Can't find branch ${beforeBranchId}`]; throw new NotFoundError(`Can't find branch '${beforeBranchId}'`);
} }
const validationResult = treeService.validateParentChild(beforeBranch.parentNoteId, branchToMove.noteId, branchId); const validationResult = treeService.validateParentChild(beforeBranch.parentNoteId, branchToMove.noteId, branchId);
@ -193,7 +195,7 @@ function deleteBranch(req) {
const branch = becca.getBranch(req.params.branchId); const branch = becca.getBranch(req.params.branchId);
if (!branch) { if (!branch) {
return [404, `Branch ${req.params.branchId} not found`]; throw new NotFoundError(`Branch '${req.params.branchId}' not found`);
} }
const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes'); const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes');

View File

@ -6,6 +6,7 @@ const opmlExportService = require('../../services/export/opml');
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const TaskContext = require("../../services/task_context"); const TaskContext = require("../../services/task_context");
const log = require("../../services/log"); const log = require("../../services/log");
const NotFoundError = require("../../errors/not_found_error");
function exportBranch(req, res) { function exportBranch(req, res) {
const {branchId, type, format, version, taskId} = req.params; const {branchId, type, format, version, taskId} = req.params;
@ -34,11 +35,11 @@ function exportBranch(req, res) {
opmlExportService.exportToOpml(taskContext, branch, version, res); opmlExportService.exportToOpml(taskContext, branch, version, res);
} }
else { else {
return [404, "Unrecognized export format " + format]; throw new NotFoundError(`Unrecognized export format '${format}'`);
} }
} }
catch (e) { catch (e) {
const message = "Export failed with following error: '" + e.message + "'. More details might be in the logs."; const message = `Export failed with following error: '${e.message}'. More details might be in the logs.`;
taskContext.reportError(message); taskContext.reportError(message);
log.error(message + e.stack); log.error(message + e.stack);

View File

@ -10,6 +10,7 @@ const { Readable } = require('stream');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const ws = require('../../services/ws'); const ws = require('../../services/ws');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const NotFoundError = require("../../errors/not_found_error");
function updateFile(req) { function updateFile(req) {
const {noteId} = req.params; const {noteId} = req.params;
@ -18,7 +19,7 @@ function updateFile(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} doesn't exist.`]; throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
} }
note.saveNoteRevision(); note.saveNoteRevision();
@ -116,7 +117,7 @@ function saveToTmpDir(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404,`Note ${noteId} doesn't exist.`]; throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
} }
const tmpObj = tmp.fileSync({postfix: getFilename(note)}); const tmpObj = tmp.fileSync({postfix: getFilename(note)});

View File

@ -4,6 +4,8 @@ const imageService = require('../../services/image');
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
const fs = require('fs'); const fs = require('fs');
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function returnImage(req, res) { function returnImage(req, res) {
const image = becca.getNote(req.params.noteId); const image = becca.getNote(req.params.noteId);
@ -51,11 +53,11 @@ function uploadImage(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} doesn't exist.`]; throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
} }
if (!["image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { if (!["image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
return [400, "Unknown image type: " + file.mimetype]; throw new ValidationError(`Unknown image type: ${file.mimetype}`);
} }
const {url} = imageService.saveImage(noteId, file.buffer, file.originalname, true, true); const {url} = imageService.saveImage(noteId, file.buffer, file.originalname, true, true);
@ -73,7 +75,7 @@ function updateImage(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} doesn't exist.`]; throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
} }
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)) {

View File

@ -10,6 +10,8 @@ const becca = require('../../becca/becca');
const beccaLoader = require('../../becca/becca_loader'); const beccaLoader = require('../../becca/becca_loader');
const log = require('../../services/log'); const log = require('../../services/log');
const TaskContext = require('../../services/task_context'); const TaskContext = require('../../services/task_context');
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
async function importToBranch(req) { async function importToBranch(req) {
const {parentNoteId} = req.params; const {parentNoteId} = req.params;
@ -27,13 +29,13 @@ async function importToBranch(req) {
const file = req.file; const file = req.file;
if (!file) { if (!file) {
return [400, "No file has been uploaded"]; throw new ValidationError("No file has been uploaded");
} }
const parentNote = becca.getNote(parentNoteId); const parentNote = becca.getNote(parentNoteId);
if (!parentNote) { if (!parentNote) {
return [404, `Note ${parentNoteId} doesn't exist.`]; throw new NotFoundError(`Note '${parentNoteId}' doesn't exist.`);
} }
const extension = path.extname(file.originalname).toLowerCase(); const extension = path.extname(file.originalname).toLowerCase();

View File

@ -2,6 +2,7 @@
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const { JSDOM } = require("jsdom"); const { JSDOM } = require("jsdom");
const NotFoundError = require("../../errors/not_found_error");
function buildDescendantCountMap() { function buildDescendantCountMap() {
const noteIdToCountMap = {}; const noteIdToCountMap = {};
@ -326,7 +327,7 @@ function getBacklinkCount(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, "Not found"]; throw new NotFoundError(`Note '${noteId}' not found`);
} }
else { else {
return { return {
@ -340,7 +341,7 @@ function getBacklinks(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} was not found`]; throw new NotFoundError(`Note '${noteId}' was not found`);
} }
let backlinksWithExcerptCount = 0; let backlinksWithExcerptCount = 0;

View File

@ -6,16 +6,17 @@ const sql = require('../../services/sql');
const utils = require('../../services/utils'); const utils = require('../../services/utils');
const log = require('../../services/log'); const log = require('../../services/log');
const TaskContext = require('../../services/task_context'); const TaskContext = require('../../services/task_context');
const protectedSessionService = require('../../services/protected_session');
const fs = require('fs'); const fs = require('fs');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function getNote(req) { function getNote(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, "Note " + noteId + " has not been found."]; throw new NotFoundError(`Note '${noteId}' has not been found.`);
} }
const pojo = note.getPojo(); const pojo = note.getPojo();
@ -197,11 +198,11 @@ function changeTitle(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note '${noteId}' has not been found`]; throw new NotFoundError(`Note '${noteId}' has not been found`);
} }
if (!note.isContentAvailable()) { if (!note.isContentAvailable()) {
return [400, `Note '${noteId}' is not available for change`]; throw new ValidationError(`Note '${noteId}' is not available for change`);
} }
const noteTitleChanged = note.title !== title; const noteTitleChanged = note.title !== title;
@ -290,7 +291,7 @@ function uploadModifiedFile(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note '${noteId}' has not been found`]; throw new NotFoundError(`Note '${noteId}' has not been found`);
} }
log.info(`Updating note '${noteId}' with content from ${filePath}`); log.info(`Updating note '${noteId}' with content from ${filePath}`);
@ -300,7 +301,7 @@ function uploadModifiedFile(req) {
const fileContent = fs.readFileSync(filePath); const fileContent = fs.readFileSync(filePath);
if (!fileContent) { if (!fileContent) {
return [400, `File ${fileContent} is empty`]; throw new ValidationError(`File '${fileContent}' is empty`);
} }
note.setContent(fileContent); note.setContent(fileContent);
@ -311,11 +312,11 @@ function forceSaveNoteRevision(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} not found.`]; throw new NotFoundError(`Note '${noteId}' not found.`);
} }
if (!note.isContentAvailable()) { if (!note.isContentAvailable()) {
return [400, `Note revision of a protected note cannot be created outside of a protected session.`]; throw new ValidationError(`Note revision of a protected note cannot be created outside of a protected session.`);
} }
note.saveNoteRevision(); note.saveNoteRevision();

View File

@ -3,6 +3,7 @@
const optionService = require('../../services/options'); const optionService = require('../../services/options');
const log = require('../../services/log'); const log = require('../../services/log');
const searchService = require('../../services/search/services/search'); const searchService = require('../../services/search/services/search');
const ValidationError = require("../../errors/validation_error");
// options allowed to be updated directly in options dialog // options allowed to be updated directly in options dialog
const ALLOWED_OPTIONS = new Set([ const ALLOWED_OPTIONS = new Set([
@ -82,7 +83,7 @@ function updateOption(req) {
const {name, value} = req.params; const {name, value} = req.params;
if (!update(name, value)) { if (!update(name, value)) {
return [400, "not allowed option to change"]; throw new ValidationError("not allowed option to change");
} }
} }

View File

@ -1,6 +1,7 @@
"use strict"; "use strict";
const passwordService = require('../../services/password'); const passwordService = require('../../services/password');
const ValidationError = require("../../errors/validation_error");
function changePassword(req) { function changePassword(req) {
if (passwordService.isPasswordSet()) { if (passwordService.isPasswordSet()) {
@ -14,7 +15,7 @@ function changePassword(req) {
function resetPassword(req) { function resetPassword(req) {
// protection against accidental call (not a security measure) // protection against accidental call (not a security measure)
if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") { if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") {
return [400, "Incorrect password reset confirmation"]; throw new ValidationError("Incorrect password reset confirmation");
} }
return passwordService.resetPassword(); return passwordService.resetPassword();

View File

@ -6,12 +6,14 @@ const searchService = require('../../services/search/services/search');
const bulkActionService = require("../../services/bulk_actions"); const bulkActionService = require("../../services/bulk_actions");
const cls = require("../../services/cls"); const cls = require("../../services/cls");
const {formatAttrForSearch} = require("../../services/attribute_formatter"); const {formatAttrForSearch} = require("../../services/attribute_formatter");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function searchFromNote(req) { function searchFromNote(req) {
const note = becca.getNote(req.params.noteId); const note = becca.getNote(req.params.noteId);
if (!note) { if (!note) {
return [404, `Note ${req.params.noteId} has not been found.`]; throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
} }
if (note.isDeleted) { if (note.isDeleted) {
@ -20,7 +22,7 @@ function searchFromNote(req) {
} }
if (note.type !== 'search') { if (note.type !== 'search') {
return [400, `Note ${req.params.noteId} is not a search note.`] throw new ValidationError(`Note '${req.params.noteId}' is not a search note.`);
} }
return searchService.searchFromNote(note); return searchService.searchFromNote(note);
@ -30,16 +32,16 @@ function searchAndExecute(req) {
const note = becca.getNote(req.params.noteId); const note = becca.getNote(req.params.noteId);
if (!note) { if (!note) {
return [404, `Note ${req.params.noteId} has not been found.`]; throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
} }
if (note.isDeleted) { if (note.isDeleted) {
// this can be triggered from recent changes and it's harmless to return empty list rather than fail // this can be triggered from recent changes, and it's harmless to return empty list rather than fail
return []; return [];
} }
if (note.type !== 'search') { if (note.type !== 'search') {
return [400, `Note ${req.params.noteId} is not a search note.`] throw new ValidationError(`Note '${req.params.noteId}' is not a search note.`);
} }
const {searchResultNoteIds} = searchService.searchFromNote(note); const {searchResultNoteIds} = searchService.searchFromNote(note);

View File

@ -2,6 +2,7 @@
const similarityService = require('../../becca/similarity'); const similarityService = require('../../becca/similarity');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const NotFoundError = require("../../errors/not_found_error");
async function getSimilarNotes(req) { async function getSimilarNotes(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
@ -9,7 +10,7 @@ async function getSimilarNotes(req) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);
if (!note) { if (!note) {
return [404, `Note ${noteId} not found.`]; throw new NotFoundError(`Note '${noteId}' not found.`);
} }
return await similarityService.findSimilarNotes(noteId); return await similarityService.findSimilarNotes(noteId);

View File

@ -2,6 +2,7 @@
const sql = require('../../services/sql'); const sql = require('../../services/sql');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const NotFoundError = require("../../errors/not_found_error");
function getSchema() { function getSchema() {
const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
@ -21,7 +22,7 @@ function execute(req) {
const note = becca.getNote(req.params.noteId); const note = becca.getNote(req.params.noteId);
if (!note) { if (!note) {
return [404, `Note ${req.params.noteId} was not found.`]; throw new NotFoundError(`Note '${req.params.noteId}' was not found.`);
} }
const queries = note.getContent().split("\n---"); const queries = note.getContent().split("\n---");

View File

@ -1,5 +1,6 @@
const sql = require('../../services/sql'); const sql = require('../../services/sql');
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const NotFoundError = require("../../errors/not_found_error");
function getNoteSize(req) { function getNoteSize(req) {
const {noteId} = req.params; const {noteId} = req.params;
@ -26,7 +27,7 @@ function getSubtreeSize(req) {
const note = becca.notes[noteId]; const note = becca.notes[noteId];
if (!note) { if (!note) {
return [404, `Note ${noteId} was not found.`]; throw new NotFoundError(`Note '${noteId}' was not found.`);
} }
const subTreeNoteIds = note.getSubtreeNoteIds(); const subTreeNoteIds = note.getSubtreeNoteIds();

View File

@ -2,6 +2,7 @@
const becca = require('../../becca/becca'); const becca = require('../../becca/becca');
const log = require('../../services/log'); const log = require('../../services/log');
const NotFoundError = require("../../errors/not_found_error");
function getNotesAndBranchesAndAttributes(noteIds) { function getNotesAndBranchesAndAttributes(noteIds) {
noteIds = new Set(noteIds); noteIds = new Set(noteIds);
@ -141,7 +142,7 @@ function getTree(req) {
} }
if (!(subTreeNoteId in becca.notes)) { if (!(subTreeNoteId in becca.notes)) {
return [404, `Note ${subTreeNoteId} not found in the cache`]; throw new NotFoundError(`Note '${subTreeNoteId}' not found in the cache`);
} }
collect(becca.notes[subTreeNoteId]); collect(becca.notes[subTreeNoteId]);

View File

@ -6,6 +6,7 @@ const myScryptService = require('../services/my_scrypt');
const log = require('../services/log'); const log = require('../services/log');
const passwordService = require("../services/password"); const passwordService = require("../services/password");
const assetPath = require("../services/asset_path"); const assetPath = require("../services/asset_path");
const ValidationError = require("../errors/validation_error");
function loginPage(req, res) { function loginPage(req, res) {
res.render('login', { res.render('login', {
@ -23,7 +24,7 @@ function setPasswordPage(req, res) {
function setPassword(req, res) { function setPassword(req, res) {
if (passwordService.isPasswordSet()) { if (passwordService.isPasswordSet()) {
return [400, "Password has been already set"]; throw new ValidationError("Password has been already set");
} }
let {password1, password2} = req.body; let {password1, password2} = req.body;

View File

@ -5,6 +5,7 @@ const loginRoute = require('./login');
const indexRoute = require('./index'); const indexRoute = require('./index');
const utils = require('../services/utils'); const utils = require('../services/utils');
const multer = require('multer'); const multer = require('multer');
const ValidationError = require("../errors/validation_error");
// API routes // API routes
const treeApiRoute = require('./api/tree'); const treeApiRoute = require('./api/tree');
@ -61,6 +62,7 @@ const csurf = require('csurf');
const {createPartialContentHandler} = require("express-partial-content"); const {createPartialContentHandler} = require("express-partial-content");
const rateLimit = require("express-rate-limit"); const rateLimit = require("express-rate-limit");
const AbstractEntity = require("../becca/entities/abstract_entity"); const AbstractEntity = require("../becca/entities/abstract_entity");
const NotFoundError = require("../errors/not_found_error");
const csrfMiddleware = csurf({ const csrfMiddleware = csurf({
cookie: true, cookie: true,
@ -169,13 +171,7 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
log.request(req, res, Date.now() - start, responseLength); log.request(req, res, Date.now() - start, responseLength);
}) })
.catch(e => { .catch(e => handleException(method, path, e, res));
log.error(`${method} ${path} threw exception: ` + e.stack);
res.setHeader("Content-Type", "text/plain")
.status(500)
.send(e.message);
});
} }
else { else {
const responseLength = resultHandler(req, res, result); const responseLength = resultHandler(req, res, result);
@ -185,15 +181,33 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
} }
} }
catch (e) { catch (e) {
log.error(`${method} ${path} threw exception: ` + e.stack); handleException(method, path, e, res);
res.setHeader("Content-Type", "text/plain")
.status(500)
.send(e.message);
} }
}); });
} }
function handleException(method, path, e, res) {
log.error(`${method} ${path} threw exception: ` + e.stack);
if (e instanceof ValidationError) {
res.setHeader("Content-Type", "application/json")
.status(400)
.send({
message: e.message
});
} if (e instanceof NotFoundError) {
res.setHeader("Content-Type", "application/json")
.status(404)
.send({
message: e.message
});
} else {
res.setHeader("Content-Type", "text/plain")
.status(500)
.send(e.message);
}
}
const MAX_ALLOWED_FILE_SIZE_MB = 250; const MAX_ALLOWED_FILE_SIZE_MB = 250;
const GET = 'get', POST = 'post', PUT = 'put', PATCH = 'patch', DELETE = 'delete'; const GET = 'get', POST = 'post', PUT = 'put', PATCH = 'patch', DELETE = 'delete';

View File

@ -60,6 +60,7 @@ module.exports = [
{ type: 'label', name: 'template' }, { type: 'label', name: 'template' },
{ type: 'label', name: 'toc' }, { type: 'label', name: 'toc' },
{ type: 'label', name: 'color' }, { type: 'label', name: 'color' },
{ type: 'label', name: 'keepCurrentHoisting'},
// relation names // relation names
{ type: 'relation', name: 'internalLink' }, { type: 'relation', name: 'internalLink' },

View File

@ -1,6 +1,6 @@
const becca = require("../becca/becca"); const becca = require("../becca/becca");
const noteService = require("./notes"); const noteService = require("./notes");
const log = require("./log.js"); const log = require("./log");
const LBTPL_ROOT = "lbTplRoot"; const LBTPL_ROOT = "lbTplRoot";
const LBTPL_BASE = "lbTplBase"; const LBTPL_BASE = "lbTplBase";
@ -36,7 +36,8 @@ const HIDDEN_SUBTREE_DEFINITION = {
title: 'Note Map', title: 'Note Map',
type: 'noteMap', type: 'noteMap',
attributes: [ attributes: [
{ type: 'label', name: 'mapRootId', value: 'hoisted' } { type: 'label', name: 'mapRootNoteId', value: 'hoisted' },
{ type: 'label', name: 'keepCurrentHoisting' }
] ]
}, },
{ {
@ -56,6 +57,12 @@ const HIDDEN_SUBTREE_DEFINITION = {
title: 'Bulk action', title: 'Bulk action',
type: 'doc', type: 'doc',
}, },
{
// place for user scripts hidden stuff (scripts should not create notes directly under hidden root)
id: 'userHidden',
title: 'User Hidden',
type: 'text',
},
{ {
id: LBTPL_ROOT, id: LBTPL_ROOT,
title: 'Launch Bar Templates', title: 'Launch Bar Templates',

View File

@ -18,7 +18,8 @@ const Branch = require('../becca/entities/branch');
const Note = require('../becca/entities/note'); const Note = require('../becca/entities/note');
const Attribute = require('../becca/entities/attribute'); const Attribute = require('../becca/entities/attribute');
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const htmlSanitizer = require("./html_sanitizer.js"); const htmlSanitizer = require("./html_sanitizer");
const ValidationError = require("../errors/validation_error");
function getNewNotePosition(parentNoteId) { function getNewNotePosition(parentNoteId) {
const note = becca.notes[parentNoteId]; const note = becca.notes[parentNoteId];
@ -107,15 +108,15 @@ function getAndValidateParent(params) {
const parentNote = becca.notes[params.parentNoteId]; const parentNote = becca.notes[params.parentNoteId];
if (!parentNote) { if (!parentNote) {
throw new Error(`Parent note "${params.parentNoteId}" not found.`); throw new ValidationError(`Parent note "${params.parentNoteId}" not found.`);
} }
if (parentNote.type === 'launcher') { if (parentNote.type === 'launcher' && parentNote.noteId !== 'lbBookmarks') {
throw new Error(`Launchers should not have child notes.`); throw new ValidationError(`Creating child notes into launcher notes is not allowed.`);
} }
if (!params.ignoreForbiddenParents && (parentNote.isLaunchBarConfig() || parentNote.isOptions())) { if (!params.ignoreForbiddenParents && (['lbRoot', 'hidden'].includes(parentNote.noteId) || parentNote.isOptions())) {
throw new Error(`Creating child notes into '${parentNote.noteId}' is not allowed.`); throw new ValidationError(`Creating child notes into '${parentNote.noteId}' is not allowed.`);
} }
return parentNote; return parentNote;

View File

@ -12,7 +12,7 @@ const PropertyComparisonExp = require('../expressions/property_comparison');
const AttributeExistsExp = require('../expressions/attribute_exists'); const AttributeExistsExp = require('../expressions/attribute_exists');
const LabelComparisonExp = require('../expressions/label_comparison'); const LabelComparisonExp = require('../expressions/label_comparison');
const NoteFlatTextExp = require('../expressions/note_flat_text'); const NoteFlatTextExp = require('../expressions/note_flat_text');
const NoteContentFulltextExp = require('../expressions/note_content_fulltext.js'); const NoteContentFulltextExp = require('../expressions/note_content_fulltext');
const OrderByAndLimitExp = require('../expressions/order_by_and_limit'); const OrderByAndLimitExp = require('../expressions/order_by_and_limit');
const AncestorExp = require("../expressions/ancestor"); const AncestorExp = require("../expressions/ancestor");
const buildComparator = require('./build_comparator'); const buildComparator = require('./build_comparator');

View File

@ -10,7 +10,7 @@ const becca = require('../../../becca/becca');
const beccaService = require('../../../becca/becca_service'); const beccaService = require('../../../becca/becca_service');
const utils = require('../../utils'); const utils = require('../../utils');
const log = require('../../log'); const log = require('../../log');
const scriptService = require("../../script.js"); const scriptService = require("../../script");
function searchFromNote(note) { function searchFromNote(note) {
let searchResultNoteIds, highlightedTokens; let searchResultNoteIds, highlightedTokens;

View File

@ -4,7 +4,7 @@ const becca = require("../becca/becca");
const noteService = require("./notes"); const noteService = require("./notes");
const cls = require("./cls"); const cls = require("./cls");
const dateUtils = require("./date_utils"); const dateUtils = require("./date_utils");
const log = require("./log.js"); const log = require("./log");
const hiddenSubtreeService = require("./hidden_subtree"); const hiddenSubtreeService = require("./hidden_subtree");
function getInboxNote(date) { function getInboxNote(date) {

View File

@ -6,7 +6,7 @@ const ws = require('./ws');
const taskContexts = {}; const taskContexts = {};
class TaskContext { class TaskContext {
constructor(taskId, taskType, data) { constructor(taskId, taskType, data = null) {
this.taskId = taskId; this.taskId = taskId;
this.taskType = taskType; this.taskType = taskType;
this.data = data; this.data = data;
@ -24,7 +24,7 @@ class TaskContext {
} }
/** @returns {TaskContext} */ /** @returns {TaskContext} */
static getInstance(taskId, taskType, data) { static getInstance(taskId, taskType, data = null) {
if (!taskContexts[taskId]) { if (!taskContexts[taskId]) {
taskContexts[taskId] = new TaskContext(taskId, taskType, data); taskContexts[taskId] = new TaskContext(taskId, taskType, data);
} }

View File

@ -53,6 +53,11 @@ function init(httpServer, sessionParser) {
} }
}); });
}); });
webSocketServer.on('error', error => {
// https://github.com/zadam/trilium/issues/3374#issuecomment-1341053765
console.log(error);
});
} }
function sendMessage(client, message) { function sendMessage(client, message) {