diff --git a/src/becca/becca.js b/src/becca/becca.js index ae367abdf..85ba000c1 100644 --- a/src/becca/becca.js +++ b/src/becca/becca.js @@ -2,8 +2,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"); +const NotFoundError = require("../errors/not_found_error"); /** * Becca is a backend cache of all notes, branches and attributes. There's a similar frontend cache Froca. @@ -148,7 +147,7 @@ class Becca { getRevision(revisionId) { const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]); - const BRevision = require("./entities/brevision.js"); // avoiding circular dependency problems + const BRevision = require("./entities/brevision"); // avoiding circular dependency problems return row ? new BRevision(row) : null; } @@ -186,8 +185,8 @@ class Becca { } /** @returns {BBlob|null} */ - getBlob(blobId) { - const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [blobId]); + getBlob(entity) { + const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]); const BBlob = require("./entities/bblob"); // avoiding circular dependency problems return row ? new BBlob(row) : null; @@ -217,8 +216,6 @@ class Becca { return this.getRevision(entityId); } else if (entityName === 'attachments') { return this.getAttachment(entityId); - } else if (entityName === 'blobs') { - return this.getBlob(entityId); } const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, @@ -247,7 +244,7 @@ class Becca { getRevisionsFromQuery(query, params = []) { const rows = sql.getRows(query, params); - const BRevision = require("./entities/brevision.js"); // avoiding circular dependency problems + const BRevision = require("./entities/brevision"); // avoiding circular dependency problems return rows.map(row => new BRevision(row)); } diff --git a/src/becca/entities/abstract_becca_entity.js b/src/becca/entities/abstract_becca_entity.js index d812443e1..2fd8c91f0 100644 --- a/src/becca/entities/abstract_becca_entity.js +++ b/src/becca/entities/abstract_becca_entity.js @@ -7,7 +7,8 @@ const eventService = require("../../services/events"); const dateUtils = require("../../services/date_utils"); const cls = require("../../services/cls"); const log = require("../../services/log"); -const protectedSessionService = require("../../services/protected_session.js"); +const protectedSessionService = require("../../services/protected_session"); +const blobService = require("../../services/blob"); let becca = null; @@ -246,36 +247,13 @@ class AbstractBeccaEntity { throw new Error(`Cannot find content for ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}', blobId '${this.blobId}'`); } - let content = row.content; - - if (this.isProtected) { - if (protectedSessionService.isProtectedSessionAvailable()) { - content = content === null ? null : protectedSessionService.decrypt(content); - } else { - content = ""; - } - } - - if (this.hasStringContent()) { - return content === null - ? "" - : content.toString("utf-8"); - } else { - // see https://github.com/zadam/trilium/issues/3523 - // IIRC a zero-sized buffer can be returned as null from the database - if (content === null) { - // this will force de/encryption - content = Buffer.alloc(0); - } - - return content; - } + return blobService.processContent(row.content); } /** * Mark the entity as (soft) deleted. It will be completely erased later. * - * This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. + * This is a low-level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. * * @param [deleteId=null] */ diff --git a/src/becca/entities/battachment.js b/src/becca/entities/battachment.js index b2f1f86e0..e59e13a03 100644 --- a/src/becca/entities/battachment.js +++ b/src/becca/entities/battachment.js @@ -4,8 +4,8 @@ const utils = require('../../services/utils'); const dateUtils = require('../../services/date_utils'); const AbstractBeccaEntity = require("./abstract_becca_entity"); const sql = require("../../services/sql"); -const protectedSessionService = require("../../services/protected_session.js"); -const log = require("../../services/log.js"); +const protectedSessionService = require("../../services/protected_session"); +const log = require("../../services/log"); const attachmentRoleToNoteTypeMapping = { 'image': 'image' diff --git a/src/etapi/notes.js b/src/etapi/notes.js index 2ed7964d8..1d4989114 100644 --- a/src/etapi/notes.js +++ b/src/etapi/notes.js @@ -148,8 +148,6 @@ function register(router) { // (e.g. branchIds are not seen in UI), that we export "note export" instead. const branch = note.getParentBranches()[0]; - console.log(note.getParentBranches()); - zipExportService.exportToZip(taskContext, branch, format, res); }); diff --git a/src/public/app/services/froca.js b/src/public/app/services/froca.js index f978a2fa7..d494ac795 100644 --- a/src/public/app/services/froca.js +++ b/src/public/app/services/froca.js @@ -367,7 +367,7 @@ class Froca { // we don't want to keep large payloads forever in memory, so we clean that up quite quickly // this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components) - // this is also a workaround for missing invalidation after change + // if the blob is updated within the cache lifetime, it should be invalidated by froca_updater this.blobPromises[key].then( () => setTimeout(() => this.blobPromises[key] = null, 1000) ); diff --git a/src/public/app/services/note_list_renderer.js b/src/public/app/services/note_list_renderer.js index 9c2fe09bd..2918b7e84 100644 --- a/src/public/app/services/note_list_renderer.js +++ b/src/public/app/services/note_list_renderer.js @@ -354,7 +354,7 @@ class NoteListRenderer { $content.append($renderedContent); $content.addClass(`type-${type}`); } catch (e) { - console.log(`Caught error while rendering note ${note.noteId} of type ${note.type}: ${e.message}, stack: ${e.stack}`); + console.log(`Caught error while rendering note '${note.noteId}' of type '${note.type}': ${e.message}, stack: ${e.stack}`); $content.append("rendering error"); } diff --git a/src/public/app/services/utils.js b/src/public/app/services/utils.js index ddf7e9fbb..de1b05378 100644 --- a/src/public/app/services/utils.js +++ b/src/public/app/services/utils.js @@ -295,13 +295,16 @@ async function openDialog($dialog, closeActDialog = true) { function isHtmlEmpty(html) { if (!html) { return true; + } else if (typeof html !== 'string') { + logError(`Got object of type '${typeof html}' where string was expected.`); + return false; } html = html.toLowerCase(); return !html.includes('").html(html).text().trim().length === 0; } @@ -579,7 +582,6 @@ export default { sleep, escapeRegExp, formatNoteSize, - escapeRegExp, areObjectsEqual, copyHtmlToClipboard }; diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js index bc0ae40e1..126f8b4d4 100644 --- a/src/public/app/widgets/note_tree.js +++ b/src/public/app/widgets/note_tree.js @@ -665,12 +665,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const branch = froca.getBranch(node.data.branchId); if (!note) { - console.log(`Node update not possible because note ${node.data.noteId} was not found.`); + console.log(`Node update not possible because note '${node.data.noteId}' was not found.`); return; - } - - if (!branch) { - console.log(`Node update not possible because branch ${node.data.branchId} was not found.`); + } else if (!branch) { + console.log(`Node update not possible because branch '${node.data.branchId}' was not found.`); return; } diff --git a/src/routes/api/attachments.js b/src/routes/api/attachments.js index 4714b0fe5..5af2de0b8 100644 --- a/src/routes/api/attachments.js +++ b/src/routes/api/attachments.js @@ -1,5 +1,5 @@ const becca = require("../../becca/becca"); -const blobService = require("../../services/blob.js"); +const blobService = require("../../services/blob"); const ValidationError = require("../../errors/validation_error"); function getAttachmentBlob(req) { diff --git a/src/routes/api/files.js b/src/routes/api/files.js index 4da4f53a6..8cb8c1d08 100644 --- a/src/routes/api/files.js +++ b/src/routes/api/files.js @@ -10,7 +10,7 @@ const { Readable } = require('stream'); const chokidar = require('chokidar'); const ws = require('../../services/ws'); const becca = require("../../becca/becca"); -const ValidationError = require("../../errors/validation_error.js"); +const ValidationError = require("../../errors/validation_error"); function updateFile(req) { const note = becca.getNoteOrThrow(req.params.noteId); diff --git a/src/routes/api/relation-map.js b/src/routes/api/relation-map.js index 2f4cfce26..3faaef900 100644 --- a/src/routes/api/relation-map.js +++ b/src/routes/api/relation-map.js @@ -1,5 +1,5 @@ -const becca = require("../../becca/becca.js"); -const sql = require("../../services/sql.js"); +const becca = require("../../becca/becca"); +const sql = require("../../services/sql"); function getRelationMap(req) { const {relationMapNoteId, noteIds} = req.body; diff --git a/src/routes/api/revisions.js b/src/routes/api/revisions.js index 674973f2e..35c03df40 100644 --- a/src/routes/api/revisions.js +++ b/src/routes/api/revisions.js @@ -7,7 +7,7 @@ const sql = require('../../services/sql'); const cls = require('../../services/cls'); const path = require('path'); const becca = require("../../becca/becca"); -const blobService = require("../../services/blob.js"); +const blobService = require("../../services/blob"); function getRevisionBlob(req) { const preview = req.query.preview === 'true'; diff --git a/src/routes/assets.js b/src/routes/assets.js index ce00521b0..2f323da87 100644 --- a/src/routes/assets.js +++ b/src/routes/assets.js @@ -1,7 +1,7 @@ -const assetPath = require("../services/asset_path.js"); +const assetPath = require("../services/asset_path"); const path = require("path"); const express = require("express"); -const env = require("../services/env.js"); +const env = require("../services/env"); const persistentCacheStatic = (root, options) => { if (!env.isDev()) { diff --git a/src/routes/session_parser.js b/src/routes/session_parser.js index b9619a615..06c17e40b 100644 --- a/src/routes/session_parser.js +++ b/src/routes/session_parser.js @@ -1,6 +1,6 @@ const session = require("express-session"); -const sessionSecret = require("../services/session_secret.js"); -const dataDir = require("../services/data_dir.js"); +const sessionSecret = require("../services/session_secret"); +const dataDir = require("../services/data_dir"); const FileStore = require('session-file-store')(session); const sessionParser = session({ diff --git a/src/services/blob.js b/src/services/blob.js index e85d7ae5b..bef0954ca 100644 --- a/src/services/blob.js +++ b/src/services/blob.js @@ -1,5 +1,6 @@ const becca = require('../becca/becca'); const NotFoundError = require("../errors/not_found_error"); +const protectedSessionService = require("./protected_session"); function getBlobPojo(entityName, entityId, opts = {}) { opts.preview = !!opts.preview; @@ -10,19 +11,47 @@ function getBlobPojo(entityName, entityId, opts = {}) { throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`); } - const blob = becca.getBlob(entity.blobId); + const blob = becca.getBlob(entity); const pojo = blob.getPojo(); if (!entity.hasStringContent()) { pojo.content = null; - } else if (opts.preview && pojo.content.length > 10000) { - pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; + } else { + pojo.content = processContent(pojo.content, entity.isProtected, true); + + if (opts.preview && pojo.content.length > 10000) { + pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; + } } return pojo; } +function processContent(content, isProtected, isStringContent) { + if (isProtected) { + if (protectedSessionService.isProtectedSessionAvailable()) { + content = content === null ? null : protectedSessionService.decrypt(content); + } else { + content = ""; + } + } + + if (isStringContent) { + return content === null ? "" : content.toString("utf-8"); + } else { + // see https://github.com/zadam/trilium/issues/3523 + // IIRC a zero-sized buffer can be returned as null from the database + if (content === null) { + // this will force de/encryption + content = Buffer.alloc(0); + } + + return content; + } +} + module.exports = { - getBlobPojo + getBlobPojo, + processContent }; diff --git a/src/services/bulk_actions.js b/src/services/bulk_actions.js index b1749c79b..9926aff67 100644 --- a/src/services/bulk_actions.js +++ b/src/services/bulk_actions.js @@ -1,5 +1,5 @@ const log = require("./log"); -const revisionService = require("./revisions.js"); +const revisionService = require("./revisions"); const becca = require("../becca/becca"); const cloningService = require("./cloning"); const branchService = require("./branches"); diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index 2bd90c2f0..6d2d944a6 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -13,7 +13,6 @@ const revisionService = require('./revisions.js'); const becca = require("../becca/becca"); const utils = require("../services/utils"); const {sanitizeAttributeName} = require("./sanitize_attribute_name"); -const {note} = require("../../spec/search/becca_mocking.js"); const noteTypes = require("../services/note_types").getNoteTypeNames(); class ConsistencyChecks { diff --git a/src/services/import/single.js b/src/services/import/single.js index 4e29fec3e..dd38b1ac5 100644 --- a/src/services/import/single.js +++ b/src/services/import/single.js @@ -187,8 +187,6 @@ function importHtml(taskContext, file, parentNote) { function importAttachment(taskContext, file, parentNote) { const mime = mimeService.getMime(file.originalname) || file.mimetype; - console.log("mime", mime); - if (mime.startsWith("image/")) { imageService.saveImageToAttachment(parentNote.noteId, file.buffer, file.originalname, taskContext.data.shrinkImages); diff --git a/src/services/notes.js b/src/services/notes.js index 351fb585c..7bcefc02d 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -21,7 +21,7 @@ const htmlSanitizer = require("./html_sanitizer"); const ValidationError = require("../errors/validation_error"); const noteTypesService = require("./note_types"); const fs = require("fs"); -const ws = require("./ws.js"); +const ws = require("./ws"); /** @param {BNote} parentNote */ function getNewNotePosition(parentNote) { diff --git a/src/share/routes.js b/src/share/routes.js index b33ace112..31fc29370 100644 --- a/src/share/routes.js +++ b/src/share/routes.js @@ -8,7 +8,6 @@ const shareRoot = require("./share_root"); const contentRenderer = require("./content_renderer"); const assetPath = require("../services/asset_path"); const appPath = require("../services/app_path"); -const utils = require("../services/utils.js"); function getSharedSubTreeRoot(note) { if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {