refactoring + uploading attachments WIP

This commit is contained in:
zadam 2023-05-08 00:02:08 +02:00
parent b05ce12e7b
commit 626af84f42
23 changed files with 257 additions and 199 deletions

View File

@ -3,6 +3,7 @@
const sql = require("../services/sql");
const NoteSet = require("../services/search/note_set");
const BAttachment = require("./entities/battachment.js");
const NotFoundError = require("../errors/not_found_error.js");
/**
* Becca is a backend cache of all notes, branches and attributes. There's a similar frontend cache Froca.
@ -78,6 +79,16 @@ class Becca {
return this.notes[noteId];
}
/** @returns {BNote|null} */
getNoteOrThrow(noteId) {
const note = this.notes[noteId];
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
return note;
}
/** @returns {BNote[]} */
getNotes(noteIds, ignoreMissing = false) {
const filteredNotes = [];
@ -104,11 +115,30 @@ class Becca {
return this.branches[branchId];
}
/** @returns {BBranch|null} */
getBranchOrThrow(branchId) {
const branch = this.getBranch(branchId);
if (!branch) {
throw new NotFoundError(`Branch '${branchId}' was not found in becca.`);
}
return branch;
}
/** @returns {BAttribute|null} */
getAttribute(attributeId) {
return this.attributes[attributeId];
}
/** @returns {BAttribute} */
getAttributeOrThrow(attributeId) {
const attribute = this.getAttribute(attributeId);
if (!attribute) {
throw new NotFoundError(`Attribute '${attributeId}' does not exist.`);
}
return attribute;
}
/** @returns {BBranch|null} */
getBranchFromChildAndParent(childNoteId, parentNoteId) {
return this.childParentToBranch[`${childNoteId}-${parentNoteId}`];
@ -130,6 +160,15 @@ class Becca {
return row ? new BAttachment(row) : null;
}
/** @returns {BAttachment} */
getAttachmentOrThrow(attachmentId) {
const attachment = this.getAttachment(attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' has not been found.`);
}
return attachment;
}
/** @returns {BAttachment[]} */
getAttachments(attachmentIds) {
const BAttachment = require("./entities/battachment"); // avoiding circular dependency problems

View File

@ -27,7 +27,7 @@ function load() {
const start = Date.now();
becca.reset();
// using raw query and passing arrays to avoid allocating new objects
// using a raw query and passing arrays to avoid allocating new objects,
// this is worth it for becca load since it happens every run and blocks the app until finished
for (const row of sql.getRawRows(`SELECT noteId, title, type, mime, isProtected, blobId, dateCreated, dateModified, utcDateCreated, utcDateModified FROM notes WHERE isDeleted = 0`)) {

View File

@ -11,7 +11,6 @@ const BAttachment = require("./battachment");
const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs");
const utc = require('dayjs/plugin/utc');
const NotFoundError = require("../../errors/not_found_error.js");
const eventService = require("../../services/events.js");
dayjs.extend(utc);
@ -1622,11 +1621,7 @@ class BNote extends AbstractBeccaEntity {
let attachment;
if (attachmentId) {
attachment = this.becca.getAttachment(attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' has not been found.`);
}
attachment = this.becca.getAttachmentOrThrow(attachmentId);
} else {
attachment = new BAttachment({
parentId: this.noteId,

View File

@ -4,7 +4,11 @@ import ws from "./ws.js";
import utils from "./utils.js";
import appContext from "../components/app_context.js";
export async function uploadFiles(parentNoteId, files, options) {
export async function uploadFiles(entityType, parentNoteId, files, options) {
if (!['notes', 'attachments'].includes(entityType)) {
throw new Error(`Unrecognized import entity type '${entityType}'.`);
}
if (files.length === 0) {
return;
}
@ -25,7 +29,7 @@ export async function uploadFiles(parentNoteId, files, options) {
}
await $.ajax({
url: `${window.glob.baseApiUrl}notes/${parentNoteId}/import`,
url: `${window.glob.baseApiUrl}notes/${parentNoteId}/${entityType}-import`,
headers: await server.getHeaders(),
data: formData,
dataType: 'json',

View File

@ -154,6 +154,6 @@ export default class ImportDialog extends BasicWidget {
this.$widget.modal('hide');
await importService.uploadFiles(parentNoteId, files, options);
await importService.uploadFiles('notes', parentNoteId, files, options);
}
}

View File

@ -0,0 +1,107 @@
import utils from '../../services/utils.js';
import treeService from "../../services/tree.js";
import importService from "../../services/import.js";
import options from "../../services/options.js";
import BasicWidget from "../basic_widget.js";
const TPL = `
<div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Import into note</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form class="upload-attachment-form">
<div class="modal-body">
<div class="form-group">
<label for="upload-attachment-file-upload-input"><strong>Choose files</strong></label>
<input type="file" class="upload-attachment-file-upload-input form-control-file" multiple />
<p>Content of the file will be imported as child note(s) into <strong class="upload-attachment-note-title"></strong>.
</div>
<div class="form-group">
<strong>Options:</strong>
<div class="checkbox">
<label data-toggle="tooltip" title="<p>If you check this option, Trilium will attempt to shrink the uploaded images by scaling and optimization which may affect the perceived image quality. If unchecked, images will be uploaded without changes.</p>">
<input class="shrink-images-checkbox" value="1" type="checkbox" checked> <span>Shrink images</span>
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button class="upload-attachment-button btn btn-primary">Upload</button>
</div>
</form>
</div>
</div>
</div>`;
export default class UploadAttachmentsDialog extends BasicWidget {
constructor() {
super();
this.parentNoteId = null;
}
doRender() {
this.$widget = $(TPL);
this.$form = this.$widget.find(".upload-attachment-form");
this.$noteTitle = this.$widget.find(".upload-attachment-note-title");
this.$fileUploadInput = this.$widget.find(".upload-attachment-file-upload-input");
this.$uploadButton = this.$widget.find(".upload-attachment-button");
this.$shrinkImagesCheckbox = this.$widget.find(".shrink-images-checkbox");
this.$form.on('submit', () => {
// disabling so that import is not triggered again.
this.$uploadButton.attr("disabled", "disabled");
this.uploadAttachments(this.parentNoteId);
return false;
});
this.$fileUploadInput.on('change', () => {
if (this.$fileUploadInput.val()) {
this.$uploadButton.removeAttr("disabled");
}
else {
this.$uploadButton.attr("disabled", "disabled");
}
});
this.$widget.find('[data-toggle="tooltip"]').tooltip({
html: true
});
}
async showUploadAttachmentsDialogEvent({noteId}) {
this.parentNoteId = noteId;
this.$fileUploadInput.val('').trigger('change'); // to trigger upload button disabling listener below
this.$shrinkImagesCheckbox.prop("checked", options.is('compressImages'));
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
utils.openDialog(this.$widget);
}
async uploadAttachments(parentNoteId) {
const files = Array.from(this.$fileUploadInput[0].files); // shallow copy since we're resetting the upload button below
const boolToString = $el => $el.is(":checked") ? "true" : "false";
const options = {
shrinkImages: boolToString(this.$shrinkImagesCheckbox),
};
this.$widget.modal('hide');
await importService.uploadFiles('attachments', parentNoteId, files, options);
}
}

View File

@ -116,7 +116,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
const importService = await import('../services/import.js');
importService.uploadFiles(activeNote.noteId, files, {
importService.uploadFiles('notes', activeNote.noteId, files, {
safeImport: true,
shrinkImages: true,
textImportedAsText: true,

View File

@ -442,7 +442,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const importService = await import('../services/import.js');
importService.uploadFiles(node.data.noteId, files, {
importService.uploadFiles('notes', node.data.noteId, files, {
safeImport: true,
shrinkImages: true,
textImportedAsText: true,

View File

@ -37,7 +37,10 @@ export default class AttachmentListTypeWidget extends TypeWidget {
async doRefresh(note) {
this.$linksWrapper.append(
"Owning note: ",
await linkService.createNoteLink(this.noteId)
await linkService.createNoteLink(this.noteId),
$('<button class="btn btn-sm">')
.text("Upload attachments")
.on('click', () => this.triggerCommand("showUploadAttachmentsDialog", {noteId: this.noteId}))
);
this.$list.empty();

View File

@ -1,5 +1,4 @@
const becca = require("../../becca/becca");
const NotFoundError = require("../../errors/not_found_error");
const utils = require("../../services/utils");
const blobService = require("../../services/blob.js");
@ -11,13 +10,7 @@ function getAttachmentBlob(req) {
function getAttachments(req) {
const includeContent = req.query.includeContent === 'true';
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
return note.getAttachments()
.map(attachment => processAttachment(attachment, includeContent));
@ -27,11 +20,7 @@ function getAttachment(req) {
const includeContent = req.query.includeContent === 'true';
const {attachmentId} = req.params;
const attachment = becca.getAttachment(attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' doesn't exist.`);
}
const attachment = becca.getAttachmentOrThrow(attachmentId);
return processAttachment(attachment, includeContent);
}
@ -62,12 +51,7 @@ function saveAttachment(req) {
const {noteId} = req.params;
const {attachmentId, role, mime, title, content} = req.body;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(noteId);
note.saveAttachment({attachmentId, role, mime, title, content});
}
@ -84,12 +68,7 @@ function deleteAttachment(req) {
function convertAttachmentToNote(req) {
const {attachmentId} = req.params;
const attachment = becca.getAttachment(attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' doesn't exist.`);
}
const attachment = becca.getAttachmentOrThrow(attachmentId);
return attachment.convertToNote();
}

View File

@ -6,7 +6,6 @@ const attributeService = require('../../services/attributes');
const BAttribute = require('../../becca/entities/battribute');
const becca = require("../../becca/becca");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function getEffectiveNoteAttributes(req) {
const note = becca.getNote(req.params.noteId);
@ -20,11 +19,7 @@ function updateNoteAttribute(req) {
let attribute;
if (body.attributeId) {
attribute = becca.getAttribute(body.attributeId);
if (!attribute) {
throw new NotFoundError(`Attribute '${body.attributeId}' does not exist.`);
}
attribute = becca.getAttributeOrThrow(body.attributeId);
if (attribute.noteId !== noteId) {
throw new ValidationError(`Attribute '${body.attributeId}' is not owned by ${noteId}`);

View File

@ -10,7 +10,6 @@ const TaskContext = require('../../services/task_context');
const branchService = require("../../services/branches");
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. The relationship between note and parent note is unique
@ -33,16 +32,8 @@ function moveBranchToParent(req) {
function moveBranchBeforeNote(req) {
const {branchId, beforeBranchId} = req.params;
const branchToMove = becca.getBranch(branchId);
const beforeBranch = becca.getBranch(beforeBranchId);
if (!branchToMove) {
throw new NotFoundError(`Can't find branch '${branchId}'`);
}
if (!beforeBranch) {
throw new NotFoundError(`Can't find branch '${beforeBranchId}'`);
}
const branchToMove = becca.getBranchOrThrow(branchId);
const beforeBranch = becca.getBranchOrThrow(beforeBranchId);
const validationResult = treeService.validateParentChild(beforeBranch.parentNoteId, branchToMove.noteId, branchId);
@ -192,11 +183,7 @@ function setExpandedForSubtree(req) {
function deleteBranch(req) {
const last = req.query.last === 'true';
const eraseNotes = req.query.eraseNotes === 'true';
const branch = becca.getBranch(req.params.branchId);
if (!branch) {
throw new NotFoundError(`Branch '${req.params.branchId}' not found`);
}
const branch = becca.getBranchOrThrow(req.params.branchId);
const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes');

View File

@ -10,14 +10,10 @@ const { Readable } = require('stream');
const chokidar = require('chokidar');
const ws = require('../../services/ws');
const becca = require("../../becca/becca");
const NotFoundError = require("../../errors/not_found_error");
const ValidationError = require("../../errors/validation_error.js");
function updateFile(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
const file = req.file;
note.saveNoteRevision();
@ -37,11 +33,7 @@ function updateFile(req) {
}
function updateAttachment(req) {
const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`);
}
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const file = req.file;
attachment.getNote().saveNoteRevision();
@ -107,20 +99,14 @@ const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachment
function fileContentProvider(req) {
// Read the file name from route params.
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
return streamContent(note.getContent(), note.getFileName(), note.mime);
}
function attachmentContentProvider(req) {
// Read the file name from route params.
const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`);
}
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
return streamContent(attachment.getContent(), attachment.getFileName(), attachment.mime);
}
@ -152,11 +138,7 @@ function streamContent(content, fileName, mimeType) {
}
function saveNoteToTmpDir(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
const fileName = note.getFileName();
const content = note.getContent();
@ -164,11 +146,7 @@ function saveNoteToTmpDir(req) {
}
function saveAttachmentToTmpDir(req) {
const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`);
}
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const fileName = attachment.getFileName();
const content = attachment.getContent();
@ -204,11 +182,7 @@ function uploadModifiedFileToNote(req) {
const noteId = req.params.noteId;
const {filePath} = req.body;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' has not been found`);
}
const note = becca.getNoteOrThrow(noteId);
log.info(`Updating note '${noteId}' with content from '${filePath}'`);
@ -227,11 +201,7 @@ function uploadModifiedFileToAttachment(req) {
const {attachmentId} = req.params;
const {filePath} = req.body;
const attachment = becca.getAttachment(attachmentId);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' has not been found`);
}
const attachment = becca.getAttachmentOrThrow(attachmentId);
log.info(`Updating attachment '${attachmentId}' with content from '${filePath}'`);

View File

@ -5,7 +5,6 @@ const becca = require('../../becca/becca');
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
const fs = require('fs');
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function returnImage(req, res) {
const image = becca.getNote(req.params.noteId);
@ -64,11 +63,7 @@ function uploadImage(req) {
const {noteId} = req.query;
const {file} = req;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(noteId);
if (!["image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
throw new ValidationError(`Unknown image type '${file.mimetype}'`);
@ -86,11 +81,7 @@ function updateImage(req) {
const {noteId} = req.params;
const {file} = req;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
}
const note = becca.getNoteOrThrow(noteId);
if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
return {

View File

@ -11,9 +11,8 @@ const beccaLoader = require('../../becca/becca_loader');
const log = require('../../services/log');
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 importNotesToBranch(req) {
const {parentNoteId} = req.params;
const {taskId, last} = req.body;
@ -32,11 +31,7 @@ async function importToBranch(req) {
throw new ValidationError("No file has been uploaded");
}
const parentNote = becca.getNote(parentNoteId);
if (!parentNote) {
throw new NotFoundError(`Note '${parentNoteId}' doesn't exist.`);
}
const parentNote = becca.getNoteOrThrow(parentNoteId);
const extension = path.extname(file.originalname).toLowerCase();
@ -79,14 +74,68 @@ async function importToBranch(req) {
}), 1000);
}
// import has deactivated note events so becca is not updated
// instead we force it to reload (can be async)
// import has deactivated note events so becca is not updated, instead we force it to reload
beccaLoader.load();
return note.getPojo();
}
async function importAttachmentsToNote(req) {
const {parentNoteId} = req.params;
const {taskId, last} = req.body;
const options = {
shrinkImages: req.body.shrinkImages !== 'false',
};
const file = req.file;
if (!file) {
throw new ValidationError("No file has been uploaded");
}
const parentNote = becca.getNoteOrThrow(parentNoteId);
// running all the event handlers on imported notes (and attributes) is slow
// and may produce unintended consequences
cls.disableEntityEvents();
// eliminate flickering during import
cls.ignoreEntityChangeIds();
let note; // typically root of the import - client can show it after finishing the import
const taskContext = TaskContext.getInstance(taskId, 'import', options);
try {
// FIXME
note = await singleImportService.importSingleFile(taskContext, file, parentNote);
}
catch (e) {
const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`;
taskContext.reportError(message);
log.error(message + e.stack);
return [500, message];
}
if (last === "true") {
// small timeout to avoid race condition (the message is received before the transaction is committed)
setTimeout(() => taskContext.taskSucceeded({
parentNoteId: parentNoteId,
importedNoteId: note.noteId
}), 1000);
}
// import has deactivated note events so becca is not updated, instead we force it to reload
beccaLoader.load();
return note.getPojo();
}
module.exports = {
importToBranch
importNotesToBranch,
importAttachmentsToNote
};

View File

@ -2,7 +2,7 @@
const becca = require("../../becca/becca");
const { JSDOM } = require("jsdom");
const NotFoundError = require("../../errors/not_found_error");
function buildDescendantCountMap(noteIdsToCount) {
if (!Array.isArray(noteIdsToCount)) {
throw new Error('noteIdsToCount: type error');
@ -345,25 +345,16 @@ function getFilteredBacklinks(note) {
function getBacklinkCount(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
const note = becca.getNoteOrThrow(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' not found`);
}
else {
return {
count: getFilteredBacklinks(note).length
};
}
return {
count: getFilteredBacklinks(note).length
};
}
function getBacklinks(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' was not found`);
}
const note = becca.getNoteOrThrow(noteId);
let backlinksWithExcerptCount = 0;

View File

@ -8,16 +8,10 @@ const log = require('../../services/log');
const TaskContext = require('../../services/task_context');
const becca = require("../../becca/becca");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
const blobService = require("../../services/blob");
function getNote(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
}
return note;
return becca.getNoteOrThrow(req.params.noteId);
}
function getNoteBlob(req) {
@ -27,11 +21,7 @@ function getNoteBlob(req) {
}
function getNoteMetadata(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
const contentMetadata = note.getContentMetadata();
return {
@ -132,11 +122,7 @@ function changeTitle(req) {
const noteId = req.params.noteId;
const title = req.body.title;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' has not been found`);
}
const note = becca.getNoteOrThrow(noteId);
if (!note.isContentAvailable()) {
throw new ValidationError(`Note '${noteId}' is not available for change`);
@ -232,11 +218,7 @@ function getDeleteNotesPreview(req) {
function forceSaveNoteRevision(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' not found.`);
}
const note = becca.getNoteOrThrow(noteId);
if (!note.isContentAvailable()) {
throw new ValidationError(`Note revision of a protected note cannot be created outside of a protected session.`);
@ -247,11 +229,7 @@ function forceSaveNoteRevision(req) {
function convertNoteToAttachment(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Note '${noteId}' not found.`);
}
const note = becca.getNoteOrThrow(noteId);
return {
attachment: note.convertToParentAttachment({ force: true })

View File

@ -7,14 +7,9 @@ const bulkActionService = require("../../services/bulk_actions");
const cls = require("../../services/cls");
const {formatAttrForSearch} = require("../../services/attribute_formatter");
const ValidationError = require("../../errors/validation_error");
const NotFoundError = require("../../errors/not_found_error");
function searchFromNote(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
if (note.isDeleted) {
// this can be triggered from recent changes, and it's harmless to return empty list rather than fail
@ -29,11 +24,7 @@ function searchFromNote(req) {
}
function searchAndExecute(req) {
const note = becca.getNote(req.params.noteId);
if (!note) {
throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`);
}
const note = becca.getNoteOrThrow(req.params.noteId);
if (note.isDeleted) {
// this can be triggered from recent changes, and it's harmless to return empty list rather than fail

View File

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

View File

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

View File

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

View File

@ -180,7 +180,8 @@ function register(app) {
route(GET, '/api/branches/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
route(PST, '/api/notes/:parentNoteId/import', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], importRoute.importToBranch, apiResultHandler);
route(PST, '/api/notes/:parentNoteId/notes-import', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], importRoute.importNotesToBranch, apiResultHandler);
route(PST, '/api/notes/:parentNoteId/attachments-import', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], importRoute.importAttachmentsToNote, apiResultHandler);
apiRoute(GET, '/api/notes/:noteId/attributes', attributesRoute.getEffectiveNoteAttributes);
apiRoute(PST, '/api/notes/:noteId/attributes', attributesRoute.addNoteAttribute);

View File

@ -12,8 +12,6 @@ const sanitizeFilename = require('sanitize-filename');
const isSvg = require('is-svg');
const isAnimated = require('is-animated');
const htmlSanitizer = require("./html_sanitizer");
const {attach} = require("jsdom/lib/jsdom/living/helpers/svg/basic-types.js");
const NotFoundError = require("../errors/not_found_error.js");
async function processImage(uploadBuffer, originalName, shrinkImageSwitch) {
const compressImages = optionService.getOptionBool("compressImages");
@ -142,11 +140,7 @@ function saveImageToAttachment(noteId, uploadBuffer, originalName, shrinkImageSw
}
const fileName = sanitizeFilename(originalName);
const note = becca.getNote(noteId);
if (!note) {
throw new NotFoundError(`Could not find note '${noteId}'`);
}
const note = becca.getNoteOrThrow(noteId);
const attachment = note.saveAttachment({
role: 'image',