mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +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 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)); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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] | ||||
|      */ | ||||
|   | ||||
| @@ -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' | ||||
|   | ||||
| @@ -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); | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|             ); | ||||
|   | ||||
| @@ -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"); | ||||
|         } | ||||
|   | ||||
| @@ -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('<img') | ||||
|         && !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; | ||||
| } | ||||
|  | ||||
| @@ -579,7 +582,6 @@ export default { | ||||
|     sleep, | ||||
|     escapeRegExp, | ||||
|     formatNoteSize, | ||||
|     escapeRegExp, | ||||
|     areObjectsEqual, | ||||
|     copyHtmlToClipboard | ||||
| }; | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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'; | ||||
|   | ||||
| @@ -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()) { | ||||
|   | ||||
| @@ -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({ | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
|   | ||||
| @@ -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"); | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user