mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
fix loading of string content saved as binary
This commit is contained in:
parent
8d2958738f
commit
75c6afd1c3
@ -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 = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`;
|
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;
|
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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user