diff --git a/db/schema.sql b/db/schema.sql index 98c1c31f4..c015f826b 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -90,16 +90,6 @@ CREATE TABLE IF NOT EXISTS "links" ( `utcDateModified` TEXT NOT NULL, PRIMARY KEY(`linkId`) ); -CREATE TABLE IF NOT EXISTS "note_contents" ( - `noteContentId` TEXT NOT NULL, - `noteId` TEXT NOT NULL, - `isProtected` INT NOT NULL DEFAULT 0, - `content` TEXT NULL DEFAULT NULL, - `hash` TEXT DEFAULT "" NOT NULL, - `utcDateCreated` TEXT NOT NULL, - `utcDateModified` TEXT NOT NULL, - PRIMARY KEY(`noteContentId`) -); CREATE TABLE IF NOT EXISTS "notes" ( `noteId` TEXT NOT NULL, `title` TEXT NOT NULL DEFAULT "note", @@ -150,4 +140,10 @@ CREATE INDEX IDX_attributes_noteId_index on attributes (noteId); CREATE INDEX IDX_attributes_value_index on attributes (value); -CREATE UNIQUE INDEX `IDX_note_contents_noteId` ON `note_contents` (`noteId`); +CREATE TABLE IF NOT EXISTS "note_contents" ( + `noteId` TEXT NOT NULL, + `content` TEXT NULL DEFAULT NULL, + `hash` TEXT DEFAULT "" NOT NULL, + `utcDateModified` TEXT NOT NULL, + PRIMARY KEY(`noteId`) +); diff --git a/docs/backend_api/ApiToken.html b/docs/backend_api/ApiToken.html index d188973b1..1458d93ee 100644 --- a/docs/backend_api/ApiToken.html +++ b/docs/backend_api/ApiToken.html @@ -282,7 +282,7 @@
diff --git a/docs/backend_api/Attribute.html b/docs/backend_api/Attribute.html index 63f30a392..dc991dd30 100644 --- a/docs/backend_api/Attribute.html +++ b/docs/backend_api/Attribute.html @@ -724,7 +724,7 @@
diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html index fd3b7582f..fb02df5ea 100644 --- a/docs/backend_api/BackendScriptApi.html +++ b/docs/backend_api/BackendScriptApi.html @@ -4799,7 +4799,7 @@ transactional by default.
diff --git a/docs/backend_api/Branch.html b/docs/backend_api/Branch.html index f77a63a33..b53324408 100644 --- a/docs/backend_api/Branch.html +++ b/docs/backend_api/Branch.html @@ -505,7 +505,7 @@ Each note can have multiple (at least one) branches, meaning it can be placed in
diff --git a/docs/backend_api/Entity.html b/docs/backend_api/Entity.html index 859b391e6..4259b6dfd 100644 --- a/docs/backend_api/Entity.html +++ b/docs/backend_api/Entity.html @@ -210,7 +210,7 @@
diff --git a/docs/backend_api/Link.html b/docs/backend_api/Link.html index 4dc31a2a3..d0933aff5 100644 --- a/docs/backend_api/Link.html +++ b/docs/backend_api/Link.html @@ -352,7 +352,7 @@ this is different concept than attribute/relation.
diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html index 7289ae598..ceaa2bbd1 100644 --- a/docs/backend_api/Note.html +++ b/docs/backend_api/Note.html @@ -396,7 +396,7 @@
Source:
@@ -581,7 +581,7 @@
Source:
@@ -746,7 +746,7 @@
Source:
@@ -922,7 +922,7 @@
Source:
@@ -1026,7 +1026,7 @@
Source:
@@ -1126,7 +1126,7 @@
Source:
@@ -1230,7 +1230,7 @@
Source:
@@ -1334,7 +1334,7 @@
Source:
@@ -1434,7 +1434,7 @@
Source:
@@ -1665,7 +1665,7 @@
Source:
@@ -1861,7 +1861,7 @@
Source:
@@ -2057,7 +2057,7 @@
Source:
@@ -2157,7 +2157,7 @@
Source:
@@ -2306,7 +2306,7 @@
Source:
@@ -2471,7 +2471,7 @@
Source:
@@ -2636,7 +2636,7 @@
Source:
@@ -2789,7 +2789,7 @@
Source:
@@ -2897,7 +2897,7 @@
Source:
@@ -3001,7 +3001,7 @@
Source:
@@ -3053,106 +3053,6 @@ -

(async) getNoteContent() → {Promise.<NoteContent>}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -Promise.<NoteContent> - - -
-
- - - - - - - - - - - - -

(async) getOwnedAttributes() → {Promise.<Array.<Attribute>>}

@@ -3201,7 +3101,7 @@
Source:
@@ -3305,7 +3205,7 @@
Source:
@@ -3458,7 +3358,7 @@
Source:
@@ -3623,7 +3523,7 @@
Source:
@@ -3788,7 +3688,7 @@
Source:
@@ -3941,7 +3841,7 @@
Source:
@@ -4097,7 +3997,7 @@
Source:
@@ -4205,7 +4105,7 @@
Source:
@@ -4305,7 +4205,7 @@
Source:
@@ -4413,7 +4313,7 @@
Source:
@@ -4513,7 +4413,7 @@
Source:
@@ -4689,7 +4589,7 @@
Source:
@@ -4793,7 +4693,7 @@
Source:
@@ -4946,7 +4846,7 @@
Source:
@@ -5099,7 +4999,7 @@
Source:
@@ -5208,7 +5108,7 @@ Cache is note instance scoped.
Source:
@@ -5290,7 +5190,7 @@ Cache is note instance scoped.
Source:
@@ -5394,7 +5294,7 @@ Cache is note instance scoped.
Source:
@@ -5498,7 +5398,7 @@ Cache is note instance scoped.
Source:
@@ -5602,7 +5502,7 @@ Cache is note instance scoped.
Source:
@@ -5706,7 +5606,7 @@ Cache is note instance scoped.
Source:
@@ -5810,7 +5710,7 @@ Cache is note instance scoped.
Source:
@@ -6037,7 +5937,7 @@ Cache is note instance scoped.
Source:
@@ -6233,7 +6133,7 @@ Cache is note instance scoped.
Source:
@@ -6429,7 +6329,7 @@ Cache is note instance scoped.
Source:
@@ -6656,7 +6556,7 @@ Cache is note instance scoped.
Source:
@@ -6756,7 +6656,7 @@ Cache is note instance scoped.
Source:
@@ -6856,7 +6756,7 @@ Cache is note instance scoped.
Source:
@@ -7052,7 +6952,7 @@ Cache is note instance scoped.
Source:
@@ -7248,7 +7148,7 @@ Cache is note instance scoped.
Source:
@@ -7506,7 +7406,7 @@ Cache is note instance scoped.
Source:
@@ -7733,7 +7633,7 @@ Cache is note instance scoped.
Source:
@@ -7960,7 +7860,7 @@ Cache is note instance scoped.
Source:
@@ -8022,7 +7922,7 @@ Cache is note instance scoped.
diff --git a/docs/backend_api/NoteContent.html b/docs/backend_api/NoteContent.html deleted file mode 100644 index 092f9575a..000000000 --- a/docs/backend_api/NoteContent.html +++ /dev/null @@ -1,494 +0,0 @@ - - - - - JSDoc: Class: NoteContent - - - - - - - - - - -
- -

Class: NoteContent

- - - - - - -
- -
- -

NoteContent(row)

- -
This represents a Note which is a central object in the Trilium Notes project.
- - -
- -
-
- - - - -

Constructor

- - - -

new NoteContent(row)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
row - - object containing database row from "note_contents" table
- - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
noteContentId - - -string - - - - primary key
noteId - - -string - - - - reference to owning note
isProtected - - -boolean - - - - true if note content is protected
content - - -blob - - - - note content - e.g. HTML text for text notes, file payload for files
utcDateCreated - - -string - - - -
utcDateModified - - -string - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - -

Extends

- - - - - - - - - - - - - - - - - - - - - - -

Methods

- - - - - - - -

(async) getNote() → {Promise.<Note>}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -Promise.<Note> - - -
-
- - - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - \ No newline at end of file diff --git a/docs/backend_api/NoteRevision.html b/docs/backend_api/NoteRevision.html index 0777854ad..e42a6ad16 100644 --- a/docs/backend_api/NoteRevision.html +++ b/docs/backend_api/NoteRevision.html @@ -443,7 +443,7 @@
diff --git a/docs/backend_api/Option.html b/docs/backend_api/Option.html index dd1f67aa6..fae9bbe8c 100644 --- a/docs/backend_api/Option.html +++ b/docs/backend_api/Option.html @@ -305,7 +305,7 @@
diff --git a/docs/backend_api/RecentNote.html b/docs/backend_api/RecentNote.html index c79ee40ef..7e4fe023e 100644 --- a/docs/backend_api/RecentNote.html +++ b/docs/backend_api/RecentNote.html @@ -282,7 +282,7 @@
diff --git a/docs/backend_api/entities_api_token.js.html b/docs/backend_api/entities_api_token.js.html index a86dcfd95..a47c05e77 100644 --- a/docs/backend_api/entities_api_token.js.html +++ b/docs/backend_api/entities_api_token.js.html @@ -69,7 +69,7 @@ module.exports = ApiToken;
diff --git a/docs/backend_api/entities_attribute.js.html b/docs/backend_api/entities_attribute.js.html index c330d76cf..07dbf4e5d 100644 --- a/docs/backend_api/entities_attribute.js.html +++ b/docs/backend_api/entities_attribute.js.html @@ -150,7 +150,7 @@ module.exports = Attribute;
diff --git a/docs/backend_api/entities_branch.js.html b/docs/backend_api/entities_branch.js.html index 983dfe370..dedd0198f 100644 --- a/docs/backend_api/entities_branch.js.html +++ b/docs/backend_api/entities_branch.js.html @@ -104,7 +104,7 @@ module.exports = Branch;
diff --git a/docs/backend_api/entities_entity.js.html b/docs/backend_api/entities_entity.js.html index 23564c26d..a680f2931 100644 --- a/docs/backend_api/entities_entity.js.html +++ b/docs/backend_api/entities_entity.js.html @@ -54,7 +54,13 @@ class Entity { this.hash = this.generateHash(); - this.isChanged = origHash !== this.hash; + if (this.forcedChange) { + this.isChanged = true; + delete this.forcedChange; + } + else { + this.isChanged = origHash !== this.hash; + } } generateIdIfNecessary() { @@ -90,7 +96,7 @@ module.exports = Entity;
diff --git a/docs/backend_api/entities_link.js.html b/docs/backend_api/entities_link.js.html index e9a2aa3c6..b4f0f1376 100644 --- a/docs/backend_api/entities_link.js.html +++ b/docs/backend_api/entities_link.js.html @@ -86,7 +86,7 @@ module.exports = Link;
diff --git a/docs/backend_api/entities_note.js.html b/docs/backend_api/entities_note.js.html index bc1207d7d..62eaad682 100644 --- a/docs/backend_api/entities_note.js.html +++ b/docs/backend_api/entities_note.js.html @@ -30,12 +30,13 @@ const Entity = require('./entity'); const Attribute = require('./attribute'); -const NoteContent = require('./note_content'); const protectedSessionService = require('../services/protected_session'); const repository = require('../services/repository'); const sql = require('../services/sql'); +const utils = require('../services/utils'); const dateUtils = require('../services/date_utils'); const noteFulltextService = require('../services/note_fulltext'); +const syncTableService = require('../services/sync_table'); const LABEL = 'label'; const LABEL_DEFINITION = 'label-definition'; @@ -84,37 +85,33 @@ class Note extends Entity { protectedSessionService.decryptNote(this); } else { - // saving ciphertexts in case we do want to update protected note outside of protected session - // (which is allowed) - this.titleCipherText = this.title; this.title = "[protected]"; } } } - /** @returns {Promise<NoteContent>} */ - async getNoteContent() { - if (!this.noteContent) { - this.noteContent = await repository.getEntity(`SELECT * FROM note_contents WHERE noteId = ?`, [this.noteId]); + /** @returns {Promise<*>} */ + async getContent() { + if (this.content === undefined) { + this.content = await sql.getValue(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]); - if (!this.noteContent) { - throw new Error("Note content not found for noteId=" + this.noteId); + if (this.isProtected) { + if (this.isContentAvailable) { + protectedSessionService.decryptNoteContent(this); + } + else { + this.content = ""; + } } if (this.isStringNote()) { - this.noteContent.content = this.noteContent.content === null - ? "" : this.noteContent.content.toString("UTF-8"); + this.content = this.content === null + ? "" + : this.content.toString("UTF-8"); } } - return this.noteContent; - } - - /** @returns {Promise<*>} */ - async getContent() { - const noteContent = await this.getNoteContent(); - - return noteContent.content; + return this.content; } /** @returns {Promise<*>} */ @@ -126,14 +123,31 @@ class Note extends Entity { /** @returns {Promise} */ async setContent(content) { - if (!this.noteContent) { - // make sure it is loaded - await this.getNoteContent(); + this.content = content; + + const pojo = { + noteId: this.noteId, + content: content, + utcDateModified: dateUtils.utcNowDateTime(), + hash: utils.hash(this.noteId + "|" + content) + }; + + if (this.isProtected) { + if (this.isContentAvailable) { + protectedSessionService.encryptNoteContent(pojo); + } + else { + throw new Error(`Cannot update content of noteId=${this.noteId} since we're out of protected session.`); + } } - this.noteContent.content = content; + await sql.upsert("note_contents", "noteId", pojo); - await this.noteContent.save(); + await syncTableService.addNoteContentSync(this.noteId); + + this.forcedChange = true; + + await this.save(); } /** @returns {Promise} */ @@ -715,14 +729,13 @@ class Note extends Entity { } else { // updating protected note outside of protected session means we will keep original ciphertexts - pojo.title = pojo.titleCipherText; + delete pojo.title; } } delete pojo.isContentAvailable; delete pojo.__attributeCache; - delete pojo.titleCipherText; - delete pojo.noteContent; + delete pojo.content; } async afterSaving() { @@ -740,7 +753,7 @@ module.exports = Note;
diff --git a/docs/backend_api/entities_note_content.js.html b/docs/backend_api/entities_note_content.js.html deleted file mode 100644 index 1a170261f..000000000 --- a/docs/backend_api/entities_note_content.js.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - JSDoc: Source: entities/note_content.js - - - - - - - - - - -
- -

Source: entities/note_content.js

- - - - - - -
-
-
"use strict";
-
-const Entity = require('./entity');
-const protectedSessionService = require('../services/protected_session');
-const repository = require('../services/repository');
-const dateUtils = require('../services/date_utils');
-const noteFulltextService = require('../services/note_fulltext');
-
-/**
- * This represents a Note which is a central object in the Trilium Notes project.
- *
- * @property {string} noteContentId - primary key
- * @property {string} noteId - reference to owning note
- * @property {boolean} isProtected - true if note content is protected
- * @property {blob} content - note content - e.g. HTML text for text notes, file payload for files
- * @property {string} utcDateCreated
- * @property {string} utcDateModified
- *
- * @extends Entity
- */
-class NoteContent extends Entity {
-    static get entityName() {
-        return "note_contents";
-    }
-
-    static get primaryKeyName() {
-        return "noteContentId";
-    }
-
-    static get hashedProperties() {
-        return ["noteContentId", "noteId", "isProtected", "content"];
-    }
-
-    /**
-     * @param row - object containing database row from "note_contents" table
-     */
-    constructor(row) {
-        super(row);
-
-        this.isProtected = !!this.isProtected;
-        /* true if content (meaning any kind of potentially encrypted content) is either not encrypted
-         * or encrypted, but with available protected session (so effectively decrypted) */
-        this.isContentAvailable = true;
-
-        // check if there's noteContentId, otherwise this is a new entity which wasn't encrypted yet
-        if (this.isProtected && this.noteContentId) {
-            this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
-
-            if (this.isContentAvailable) {
-                protectedSessionService.decryptNoteContent(this);
-            }
-            else {
-                // saving ciphertexts in case we do want to update protected note outside of protected session
-                // (which is allowed)
-                this.contentCipherText = this.content;
-                this.content = "";
-            }
-        }
-    }
-
-    /**
-     * @returns {Promise<Note>}
-     */
-    async getNote() {
-        return await repository.getNote(this.noteId);
-    }
-
-    beforeSaving() {
-        if (!this.utcDateCreated) {
-            this.utcDateCreated = dateUtils.utcNowDateTime();
-        }
-
-        super.beforeSaving();
-
-        if (this.isChanged) {
-            this.utcDateModified = dateUtils.utcNowDateTime();
-        }
-    }
-
-    // cannot be static!
-    updatePojo(pojo) {
-        if (pojo.isProtected) {
-            if (this.isContentAvailable) {
-                protectedSessionService.encryptNoteContent(pojo);
-            }
-            else {
-                // updating protected note outside of protected session means we will keep original ciphertext
-                pojo.content = pojo.contentCipherText;
-            }
-        }
-
-        delete pojo.isContentAvailable;
-        delete pojo.contentCipherText;
-    }
-
-    async afterSaving() {
-        noteFulltextService.triggerNoteFulltextUpdate(this.noteId);
-    }
-}
-
-module.exports = NoteContent;
-
-
- - - - -
- - - -
- - - - - - - diff --git a/docs/backend_api/entities_note_revision.js.html b/docs/backend_api/entities_note_revision.js.html index 4403ed921..a8f1993fe 100644 --- a/docs/backend_api/entities_note_revision.js.html +++ b/docs/backend_api/entities_note_revision.js.html @@ -87,7 +87,7 @@ module.exports = NoteRevision;
diff --git a/docs/backend_api/entities_option.js.html b/docs/backend_api/entities_option.js.html index 3437b7dbd..bbc0fb54a 100644 --- a/docs/backend_api/entities_option.js.html +++ b/docs/backend_api/entities_option.js.html @@ -54,6 +54,10 @@ class Option extends Entity { } beforeSaving() { + if (!this.utcDateCreated) { + this.utcDateCreated = dateUtils.utcNowDateTime(); + } + super.beforeSaving(); if (this.isChanged) { @@ -72,7 +76,7 @@ module.exports = Option;
diff --git a/docs/backend_api/entities_recent_note.js.html b/docs/backend_api/entities_recent_note.js.html index f71776f20..b8e6177ad 100644 --- a/docs/backend_api/entities_recent_note.js.html +++ b/docs/backend_api/entities_recent_note.js.html @@ -69,7 +69,7 @@ module.exports = RecentNote;
diff --git a/docs/backend_api/global.html b/docs/backend_api/global.html index ad0a5b1e3..40d6e0b4e 100644 --- a/docs/backend_api/global.html +++ b/docs/backend_api/global.html @@ -588,7 +588,7 @@
diff --git a/docs/backend_api/index.html b/docs/backend_api/index.html index 49f2ec3e4..263759f06 100644 --- a/docs/backend_api/index.html +++ b/docs/backend_api/index.html @@ -50,7 +50,7 @@
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html index 528781c7e..5303b0d7b 100644 --- a/docs/backend_api/services_backend_script_api.js.html +++ b/docs/backend_api/services_backend_script_api.js.html @@ -339,7 +339,7 @@ module.exports = BackendScriptApi;
diff --git a/docs/frontend_api/NoteFull.html b/docs/frontend_api/NoteFull.html index e745fc962..655bc3f93 100644 --- a/docs/frontend_api/NoteFull.html +++ b/docs/frontend_api/NoteFull.html @@ -141,7 +141,7 @@ -

noteContent

+

content

diff --git a/docs/frontend_api/entities_note_full.js.html b/docs/frontend_api/entities_note_full.js.html index 0019ba967..d052c4b01 100644 --- a/docs/frontend_api/entities_note_full.js.html +++ b/docs/frontend_api/entities_note_full.js.html @@ -36,7 +36,7 @@ class NoteFull extends NoteShort { super(treeCache, row); /** @param {string} */ - this.noteContent = row.noteContent; + this.content = row.content; /** @param {string} */ this.utcDateCreated = row.utcDateCreated; diff --git a/package-lock.json b/package-lock.json index d9b7947ad..3e28c4546 100644 --- a/package-lock.json +++ b/package-lock.json @@ -623,7 +623,7 @@ "plist": "3.0.1", "read-config-file": "3.2.2", "sanitize-filename": "1.6.1", - "semver": "5.6.0", + "semver": "5.7.0", "temp-file": "3.3.2" }, "dependencies": { @@ -650,7 +650,7 @@ "requires": { "hosted-git-info": "2.7.1", "resolve": "1.10.0", - "semver": "5.6.0", + "semver": "5.7.0", "validate-npm-package-license": "3.0.4" } }, @@ -662,6 +662,12 @@ "requires": { "path-parse": "1.0.6" } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true } } }, @@ -1747,7 +1753,14 @@ "requires": { "async-hook-jl": "1.7.6", "emitter-listener": "1.1.2", - "semver": "5.6.0" + "semver": "5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "co": { @@ -2832,9 +2845,17 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "execa": { @@ -3158,7 +3179,7 @@ "nugget": "2.0.1", "path-exists": "3.0.0", "rc": "1.2.8", - "semver": "5.6.0", + "semver": "5.7.0", "sumchecker": "2.0.2" }, "dependencies": { @@ -3187,6 +3208,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true } } }, @@ -3209,8 +3236,16 @@ "glob-promise": "3.4.0", "lodash": "4.17.11", "parse-author": "2.0.0", - "semver": "5.6.0", + "semver": "5.7.0", "tmp-promise": "1.0.5" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "electron-installer-debian": { @@ -3225,7 +3260,7 @@ "get-folder-size": "2.0.1", "lodash": "4.17.11", "pify": "4.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "word-wrap": "1.2.3", "yargs": "12.0.5" }, @@ -3235,6 +3270,12 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true } } }, @@ -3334,7 +3375,7 @@ "rcedit": "1.1.1", "resolve": "1.8.1", "sanitize-filename": "1.6.1", - "semver": "5.6.0", + "semver": "5.7.0", "yargs-parser": "13.0.0" }, "dependencies": { @@ -3350,6 +3391,12 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "yargs-parser": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", @@ -3640,7 +3687,7 @@ "path-is-inside": "1.0.2", "progress": "2.0.3", "regexpp": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", "table": "5.2.2", @@ -3693,7 +3740,7 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" } @@ -3716,6 +3763,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -4091,7 +4144,7 @@ "ignore": "5.0.5", "minimatch": "3.0.4", "resolve": "1.8.1", - "semver": "5.6.0" + "semver": "5.7.0" }, "dependencies": { "ignore": { @@ -4099,6 +4152,12 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz", "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==", "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true } } }, @@ -6074,9 +6133,16 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "execa": { @@ -6122,9 +6188,16 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "execa": { @@ -6735,8 +6808,7 @@ "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" }, "is-zip": { "version": "1.0.0", @@ -7702,8 +7774,15 @@ "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", "requires": { "bin-version": "3.0.0", - "semver": "5.6.0", + "semver": "5.7.0", "semver-truncate": "1.1.2" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "bin-wrapper": { @@ -7840,9 +7919,16 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "decompress": { @@ -8267,7 +8353,14 @@ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.7.1.tgz", "integrity": "sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw==", "requires": { - "semver": "5.6.0" + "semver": "5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "node-gyp": { @@ -8331,8 +8424,15 @@ "npmlog": "4.1.2", "rc": "1.2.8", "rimraf": "2.6.3", - "semver": "5.6.0", + "semver": "5.7.0", "tar": "4.4.8" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "node-status-codes": { @@ -8356,8 +8456,15 @@ "requires": { "hosted-git-info": "2.7.1", "is-builtin-module": "1.0.0", - "semver": "5.6.0", + "semver": "5.7.0", "validate-npm-package-license": "3.0.4" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "normalize-path": { @@ -8592,9 +8699,12 @@ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" }, "open": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", - "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.0.0.tgz", + "integrity": "sha512-/yb5mVZBz7mHLySMiSj2DcLtMBbFPJk5JBKEkHVZFxZAPzeg3L026O0T+lbdz1B2nyDnkClRSwRQJdeVUIF7zw==", + "requires": { + "is-wsl": "1.1.0" + } }, "open-editor": { "version": "1.2.0", @@ -8824,7 +8934,7 @@ "got": "6.7.1", "registry-auth-token": "3.3.2", "registry-url": "3.1.0", - "semver": "5.6.0" + "semver": "5.7.0" }, "dependencies": { "got": { @@ -8846,6 +8956,12 @@ "url-parse-lax": "1.0.0" } }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -9310,9 +9426,16 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "execa": { @@ -9345,8 +9468,15 @@ "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", "requires": { "bin-version": "3.0.0", - "semver": "5.6.0", + "semver": "5.7.0", "semver-truncate": "1.1.2" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "bin-wrapper": { @@ -9615,9 +9745,16 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "get-stream": { @@ -10561,9 +10698,9 @@ } }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==" }, "semver-diff": { "version": "2.1.0", @@ -10571,7 +10708,15 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "5.6.0" + "semver": "5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "semver-regex": { @@ -10584,7 +10729,14 @@ "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", "integrity": "sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g=", "requires": { - "semver": "5.6.0" + "semver": "5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "send": { @@ -12414,9 +12566,9 @@ } }, "ws": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz", - "integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "requires": { "async-limiter": "1.0.0" } @@ -12507,7 +12659,7 @@ "prettier": "1.16.3", "resolve-cwd": "2.0.0", "resolve-from": "4.0.0", - "semver": "5.6.0", + "semver": "5.7.0", "slash": "2.0.0", "update-notifier": "2.5.0", "xo-init": "0.7.0" @@ -12691,6 +12843,12 @@ "strip-indent": "2.0.0" } }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, "slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -12878,9 +13036,17 @@ "requires": { "nice-try": "1.0.5", "path-key": "2.0.1", - "semver": "5.6.0", + "semver": "5.7.0", "shebang-command": "1.2.0", "which": "1.3.1" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "execa": { diff --git a/package.json b/package.json index 92e3adda5..b3a6691c5 100644 --- a/package.json +++ b/package.json @@ -27,14 +27,14 @@ "cls-hooked": "4.2.2", "commonmark": "0.28.1", "cookie-parser": "1.4.4", - "csurf": "^1.9.0", + "csurf": "1.9.0", "dayjs": "1.8.11", "debug": "4.1.1", "ejs": "2.6.1", "electron-debug": "2.1.0", "electron-dl": "1.13.0", "electron-in-page-search": "1.3.2", - "electron-window-state": "^5.0.3", + "electron-window-state": "5.0.3", "express": "4.16.4", "express-session": "1.15.6", "file-type": "10.9.0", @@ -42,7 +42,7 @@ "get-port": "4.2.0", "helmet": "3.16.0", "html": "1.0.0", - "html2plaintext": "^2.1.2", + "html2plaintext": "2.1.2", "image-type": "4.0.0", "imagemin": "6.1.0", "imagemin-giflossy": "5.1.10", @@ -50,17 +50,17 @@ "imagemin-pngquant": "7.0.0", "ini": "1.3.5", "jimp": "0.6.0", - "mime-types": "^2.1.22", + "mime-types": "2.1.22", "moment": "2.24.0", "multer": "1.4.1", "node-abi": "2.7.1", - "open": "0.0.5", + "open": "6.0.0", "rand-token": "0.4.0", "rcedit": "1.1.1", "rimraf": "2.6.3", "sanitize-filename": "1.6.1", - "sax": "^1.2.4", - "semver": "^5.6.0", + "sax": "1.2.4", + "semver": "6.0.0", "serve-favicon": "2.5.0", "session-file-store": "1.2.0", "simple-node-logger": "18.12.22", @@ -68,7 +68,7 @@ "tar-stream": "2.0.1", "turndown": "5.0.3", "unescape": "1.0.1", - "ws": "6.2.0", + "ws": "6.2.1", "xml2js": "0.4.19" }, "devDependencies": { @@ -76,7 +76,7 @@ "electron": "4.0.3", "electron-builder": "20.39.0", "electron-compile": "6.4.4", - "electron-installer-debian": "^1.1.1", + "electron-installer-debian": "1.1.1", "electron-packager": "13.1.1", "electron-rebuild": "1.8.4", "lorem-ipsum": "2.0.0", diff --git a/src/routes/routes.js b/src/routes/routes.js index 4b7837cd2..3ad6c5037 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -172,10 +172,10 @@ function register(app) { apiRoute(POST, '/api/sync/fill-sync-rows', syncApiRoute.fillSyncRows); apiRoute(POST, '/api/sync/force-full-sync', syncApiRoute.forceFullSync); apiRoute(POST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync); - apiRoute(GET, '/api/sync/changed', syncApiRoute.getChanged); - apiRoute(PUT, '/api/sync/update', syncApiRoute.update); + route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler); + route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler); + route(POST, '/api/sync/finished', [auth.checkApiAuth], syncApiRoute.syncFinished, apiResultHandler); route(GET, '/api/sync/stats', [], syncApiRoute.getStats, apiResultHandler); - apiRoute(POST, '/api/sync/finished', syncApiRoute.syncFinished); apiRoute(GET, '/api/event-log', eventLogRoute.getEventLog); diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index 8a351873d..208fbc8a5 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -357,13 +357,6 @@ async function findLogicIssues() { logFix(`Removed link ${linkId} because target note ${targetNoteId} is also deleted.`); }); - - await findIssues(` - SELECT noteId - FROM notes - JOIN note_contents USING(noteId) - WHERE notes.isDeleted = 0 AND notes.isProtected != note_contents.isProtected`, - ({noteId}) => `Note ${noteId} has inconsistent isProtected in notes and note_contents tables`); } async function runSyncRowChecks(entityName, key) { diff --git a/src/services/sync.js b/src/services/sync.js index a216f97c9..6036e24fe 100644 --- a/src/services/sync.js +++ b/src/services/sync.js @@ -58,7 +58,7 @@ async function sync() { }; } else { - log.info("sync failed: " + e.message); + log.info("sync failed: " + e.message + e.stack); return { success: false, @@ -262,6 +262,12 @@ async function getEntityRow(entityName, entityId) { const entity = await sql.getRow(`SELECT * FROM ${entityName} WHERE ${primaryKey} = ?`, [entityId]); + if (!entity) { + console.log(entityName, entityId); + + console.log(`SELECT * FROM ${entityName} WHERE ${primaryKey} = '${entityId}'`); + } + if (['note_contents', 'note_revisions'].includes(entityName) && entity.content !== null) { if (typeof entity.content === 'string') { entity.content = Buffer.from(entity.content, 'UTF-8'); diff --git a/src/services/sync_table.js b/src/services/sync_table.js index 1aa2c2c7f..050815111 100644 --- a/src/services/sync_table.js +++ b/src/services/sync_table.js @@ -88,7 +88,7 @@ async function fillAllSyncRows() { await sql.execute("DELETE FROM sync"); await fillSyncRows("notes", "noteId"); - await fillSyncRows("note_contents", "noteContentId"); + await fillSyncRows("note_contents", "noteId"); await fillSyncRows("branches", "branchId"); await fillSyncRows("note_revisions", "noteRevisionId"); await fillSyncRows("recent_notes", "branchId");