mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 01:36:24 +01:00 
			
		
		
		
	fix loading of string content saved as binary
This commit is contained in:
		| @@ -2,8 +2,7 @@ | |||||||
|  |  | ||||||
| const sql = require("../services/sql"); | const sql = require("../services/sql"); | ||||||
| const NoteSet = require("../services/search/note_set"); | const NoteSet = require("../services/search/note_set"); | ||||||
| const BAttachment = require("./entities/battachment.js"); | const NotFoundError = require("../errors/not_found_error"); | ||||||
| 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. |  * 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) { |     getRevision(revisionId) { | ||||||
|         const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [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; |         return row ? new BRevision(row) : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -186,8 +185,8 @@ class Becca { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {BBlob|null} */ |     /** @returns {BBlob|null} */ | ||||||
|     getBlob(blobId) { |     getBlob(entity) { | ||||||
|         const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [blobId]); |         const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]); | ||||||
|  |  | ||||||
|         const BBlob = require("./entities/bblob"); // avoiding circular dependency problems |         const BBlob = require("./entities/bblob"); // avoiding circular dependency problems | ||||||
|         return row ? new BBlob(row) : null; |         return row ? new BBlob(row) : null; | ||||||
| @@ -217,8 +216,6 @@ class Becca { | |||||||
|             return this.getRevision(entityId); |             return this.getRevision(entityId); | ||||||
|         } else if (entityName === 'attachments') { |         } else if (entityName === 'attachments') { | ||||||
|             return this.getAttachment(entityId); |             return this.getAttachment(entityId); | ||||||
|         } else if (entityName === 'blobs') { |  | ||||||
|             return this.getBlob(entityId); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, |         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, | ||||||
| @@ -247,7 +244,7 @@ class Becca { | |||||||
|     getRevisionsFromQuery(query, params = []) { |     getRevisionsFromQuery(query, params = []) { | ||||||
|         const rows = sql.getRows(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)); |         return rows.map(row => new BRevision(row)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,8 @@ const eventService = require("../../services/events"); | |||||||
| const dateUtils = require("../../services/date_utils"); | const dateUtils = require("../../services/date_utils"); | ||||||
| const cls = require("../../services/cls"); | const cls = require("../../services/cls"); | ||||||
| const log = require("../../services/log"); | 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; | 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}'`); |             throw new Error(`Cannot find content for ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}', blobId '${this.blobId}'`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let content = row.content; |         return blobService.processContent(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; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Mark the entity as (soft) deleted. It will be completely erased later. |      * 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] |      * @param [deleteId=null] | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ const utils = require('../../services/utils'); | |||||||
| const dateUtils = require('../../services/date_utils'); | const dateUtils = require('../../services/date_utils'); | ||||||
| const AbstractBeccaEntity = require("./abstract_becca_entity"); | const AbstractBeccaEntity = require("./abstract_becca_entity"); | ||||||
| const sql = require("../../services/sql"); | const sql = require("../../services/sql"); | ||||||
| const protectedSessionService = require("../../services/protected_session.js"); | const protectedSessionService = require("../../services/protected_session"); | ||||||
| const log = require("../../services/log.js"); | const log = require("../../services/log"); | ||||||
|  |  | ||||||
| const attachmentRoleToNoteTypeMapping = { | const attachmentRoleToNoteTypeMapping = { | ||||||
|     'image': 'image' |     'image': 'image' | ||||||
|   | |||||||
| @@ -148,8 +148,6 @@ function register(router) { | |||||||
|         // (e.g. branchIds are not seen in UI), that we export "note export" instead. |         // (e.g. branchIds are not seen in UI), that we export "note export" instead. | ||||||
|         const branch = note.getParentBranches()[0]; |         const branch = note.getParentBranches()[0]; | ||||||
|  |  | ||||||
|         console.log(note.getParentBranches()); |  | ||||||
|  |  | ||||||
|         zipExportService.exportToZip(taskContext, branch, format, res); |         zipExportService.exportToZip(taskContext, branch, format, res); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -367,7 +367,7 @@ class Froca { | |||||||
|  |  | ||||||
|             // we don't want to keep large payloads forever in memory, so we clean that up quite quickly |             // 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 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( |             this.blobPromises[key].then( | ||||||
|                 () => setTimeout(() => this.blobPromises[key] = null, 1000) |                 () => setTimeout(() => this.blobPromises[key] = null, 1000) | ||||||
|             ); |             ); | ||||||
|   | |||||||
| @@ -354,7 +354,7 @@ class NoteListRenderer { | |||||||
|             $content.append($renderedContent); |             $content.append($renderedContent); | ||||||
|             $content.addClass(`type-${type}`); |             $content.addClass(`type-${type}`); | ||||||
|         } catch (e) { |         } 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"); |             $content.append("rendering error"); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -295,13 +295,16 @@ async function openDialog($dialog, closeActDialog = true) { | |||||||
| function isHtmlEmpty(html) { | function isHtmlEmpty(html) { | ||||||
|     if (!html) { |     if (!html) { | ||||||
|         return true; |         return true; | ||||||
|  |     } else if (typeof html !== 'string') { | ||||||
|  |         logError(`Got object of type '${typeof html}' where string was expected.`); | ||||||
|  |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     html = html.toLowerCase(); |     html = html.toLowerCase(); | ||||||
|  |  | ||||||
|     return !html.includes('<img') |     return !html.includes('<img') | ||||||
|         && !html.includes('<section') |         && !html.includes('<section') | ||||||
|         // line below will actually attempt to load images so better to check for images first |         // the line below will actually attempt to load images so better to check for images first | ||||||
|         && $("<div>").html(html).text().trim().length === 0; |         && $("<div>").html(html).text().trim().length === 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -579,7 +582,6 @@ export default { | |||||||
|     sleep, |     sleep, | ||||||
|     escapeRegExp, |     escapeRegExp, | ||||||
|     formatNoteSize, |     formatNoteSize, | ||||||
|     escapeRegExp, |  | ||||||
|     areObjectsEqual, |     areObjectsEqual, | ||||||
|     copyHtmlToClipboard |     copyHtmlToClipboard | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -665,12 +665,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|         const branch = froca.getBranch(node.data.branchId); |         const branch = froca.getBranch(node.data.branchId); | ||||||
|  |  | ||||||
|         if (!note) { |         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; |             return; | ||||||
|         } |         } else if (!branch) { | ||||||
|  |             console.log(`Node update not possible because branch '${node.data.branchId}' was not found.`); | ||||||
|         if (!branch) { |  | ||||||
|             console.log(`Node update not possible because branch ${node.data.branchId} was not found.`); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
| const blobService = require("../../services/blob.js"); | const blobService = require("../../services/blob"); | ||||||
| const ValidationError = require("../../errors/validation_error"); | const ValidationError = require("../../errors/validation_error"); | ||||||
|  |  | ||||||
| function getAttachmentBlob(req) { | function getAttachmentBlob(req) { | ||||||
|   | |||||||
| @@ -10,7 +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 ValidationError = require("../../errors/validation_error.js"); | const ValidationError = require("../../errors/validation_error"); | ||||||
|  |  | ||||||
| function updateFile(req) { | function updateFile(req) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| const becca = require("../../becca/becca.js"); | const becca = require("../../becca/becca"); | ||||||
| const sql = require("../../services/sql.js"); | const sql = require("../../services/sql"); | ||||||
|  |  | ||||||
| function getRelationMap(req) { | function getRelationMap(req) { | ||||||
|     const {relationMapNoteId, noteIds} = req.body; |     const {relationMapNoteId, noteIds} = req.body; | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ const sql = require('../../services/sql'); | |||||||
| const cls = require('../../services/cls'); | const cls = require('../../services/cls'); | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
| const blobService = require("../../services/blob.js"); | const blobService = require("../../services/blob"); | ||||||
|  |  | ||||||
| function getRevisionBlob(req) { | function getRevisionBlob(req) { | ||||||
|     const preview = req.query.preview === 'true'; |     const preview = req.query.preview === 'true'; | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| const assetPath = require("../services/asset_path.js"); | const assetPath = require("../services/asset_path"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const express = require("express"); | const express = require("express"); | ||||||
| const env = require("../services/env.js"); | const env = require("../services/env"); | ||||||
|  |  | ||||||
| const persistentCacheStatic = (root, options) => { | const persistentCacheStatic = (root, options) => { | ||||||
|     if (!env.isDev()) { |     if (!env.isDev()) { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| const session = require("express-session"); | const session = require("express-session"); | ||||||
| const sessionSecret = require("../services/session_secret.js"); | const sessionSecret = require("../services/session_secret"); | ||||||
| const dataDir = require("../services/data_dir.js"); | const dataDir = require("../services/data_dir"); | ||||||
| const FileStore = require('session-file-store')(session); | const FileStore = require('session-file-store')(session); | ||||||
|  |  | ||||||
| const sessionParser = session({ | const sessionParser = session({ | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| const becca = require('../becca/becca'); | const becca = require('../becca/becca'); | ||||||
| const NotFoundError = require("../errors/not_found_error"); | const NotFoundError = require("../errors/not_found_error"); | ||||||
|  | const protectedSessionService = require("./protected_session"); | ||||||
|  |  | ||||||
| function getBlobPojo(entityName, entityId, opts = {}) { | function getBlobPojo(entityName, entityId, opts = {}) { | ||||||
|     opts.preview = !!opts.preview; |     opts.preview = !!opts.preview; | ||||||
| @@ -10,19 +11,47 @@ function getBlobPojo(entityName, entityId, opts = {}) { | |||||||
|         throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`); |         throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const blob = becca.getBlob(entity.blobId); |     const blob = becca.getBlob(entity); | ||||||
|  |  | ||||||
|     const pojo = blob.getPojo(); |     const pojo = blob.getPojo(); | ||||||
|  |  | ||||||
|     if (!entity.hasStringContent()) { |     if (!entity.hasStringContent()) { | ||||||
|         pojo.content = null; |         pojo.content = null; | ||||||
|     } else if (opts.preview && pojo.content.length > 10000) { |     } 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.`; |             pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return pojo; |     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 = { | module.exports = { | ||||||
|     getBlobPojo |     getBlobPojo, | ||||||
|  |     processContent | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| const log = require("./log"); | const log = require("./log"); | ||||||
| const revisionService = require("./revisions.js"); | const revisionService = require("./revisions"); | ||||||
| const becca = require("../becca/becca"); | const becca = require("../becca/becca"); | ||||||
| const cloningService = require("./cloning"); | const cloningService = require("./cloning"); | ||||||
| const branchService = require("./branches"); | const branchService = require("./branches"); | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ const revisionService = require('./revisions.js'); | |||||||
| const becca = require("../becca/becca"); | const becca = require("../becca/becca"); | ||||||
| const utils = require("../services/utils"); | const utils = require("../services/utils"); | ||||||
| const {sanitizeAttributeName} = require("./sanitize_attribute_name"); | const {sanitizeAttributeName} = require("./sanitize_attribute_name"); | ||||||
| const {note} = require("../../spec/search/becca_mocking.js"); |  | ||||||
| const noteTypes = require("../services/note_types").getNoteTypeNames(); | const noteTypes = require("../services/note_types").getNoteTypeNames(); | ||||||
|  |  | ||||||
| class ConsistencyChecks { | class ConsistencyChecks { | ||||||
|   | |||||||
| @@ -187,8 +187,6 @@ function importHtml(taskContext, file, parentNote) { | |||||||
| function importAttachment(taskContext, file, parentNote) { | function importAttachment(taskContext, file, parentNote) { | ||||||
|     const mime = mimeService.getMime(file.originalname) || file.mimetype; |     const mime = mimeService.getMime(file.originalname) || file.mimetype; | ||||||
|  |  | ||||||
|     console.log("mime", mime); |  | ||||||
|  |  | ||||||
|     if (mime.startsWith("image/")) { |     if (mime.startsWith("image/")) { | ||||||
|         imageService.saveImageToAttachment(parentNote.noteId, file.buffer, file.originalname, taskContext.data.shrinkImages); |         imageService.saveImageToAttachment(parentNote.noteId, file.buffer, file.originalname, taskContext.data.shrinkImages); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ const htmlSanitizer = require("./html_sanitizer"); | |||||||
| const ValidationError = require("../errors/validation_error"); | const ValidationError = require("../errors/validation_error"); | ||||||
| const noteTypesService = require("./note_types"); | const noteTypesService = require("./note_types"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const ws = require("./ws.js"); | const ws = require("./ws"); | ||||||
|  |  | ||||||
| /** @param {BNote} parentNote */ | /** @param {BNote} parentNote */ | ||||||
| function getNewNotePosition(parentNote) { | function getNewNotePosition(parentNote) { | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ const shareRoot = require("./share_root"); | |||||||
| const contentRenderer = require("./content_renderer"); | const contentRenderer = require("./content_renderer"); | ||||||
| const assetPath = require("../services/asset_path"); | const assetPath = require("../services/asset_path"); | ||||||
| const appPath = require("../services/app_path"); | const appPath = require("../services/app_path"); | ||||||
| const utils = require("../services/utils.js"); |  | ||||||
|  |  | ||||||
| function getSharedSubTreeRoot(note) { | function getSharedSubTreeRoot(note) { | ||||||
|     if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { |     if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user