diff --git a/src/entities/branch.js b/src/entities/branch.js index b84c99289..61be42e53 100644 --- a/src/entities/branch.js +++ b/src/entities/branch.js @@ -1,10 +1,15 @@ "use strict"; const Entity = require('./entity'); +const utils = require('../services/utils'); class Branch extends Entity { static get tableName() { return "branches"; } static get primaryKeyName() { return "branchId"; } + + beforeSaving() { + this.dateModified = utils.nowDate() + } } module.exports = Branch; \ No newline at end of file diff --git a/src/entities/entity_constructor.js b/src/entities/entity_constructor.js index 93b5dfc5c..b439c854a 100644 --- a/src/entities/entity_constructor.js +++ b/src/entities/entity_constructor.js @@ -1,5 +1,6 @@ const Note = require('../entities/note'); const NoteRevision = require('../entities/note_revision'); +const NoteImage = require('../entities/note_image'); const Branch = require('../entities/branch'); const Label = require('../entities/label'); const repository = require('../services/repository'); @@ -13,6 +14,9 @@ function createEntityFromRow(row) { else if (row.noteRevisionId) { entity = new NoteRevision(row); } + else if (row.noteImageId) { + entity = new NoteImage(row); + } else if (row.branchId) { entity = new Branch(row); } diff --git a/src/entities/label.js b/src/entities/label.js index f9ae9cb34..da6dab27a 100644 --- a/src/entities/label.js +++ b/src/entities/label.js @@ -2,6 +2,7 @@ const Entity = require('./entity'); const repository = require('../services/repository'); +const utils = require('../services/utils'); class Label extends Entity { static get tableName() { return "labels"; } @@ -10,6 +11,14 @@ class Label extends Entity { async getNote() { return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); } + + beforeSaving() { + if (!this.dateCreated) { + this.dateCreated = utils.nowDate(); + } + + this.dateModified = utils.nowDate(); + } } module.exports = Label; \ No newline at end of file diff --git a/src/entities/note.js b/src/entities/note.js index 68f9affad..1d4896f3c 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -3,6 +3,7 @@ const Entity = require('./entity'); const protected_session = require('../services/protected_session'); const repository = require('../services/repository'); +const utils = require('../services/utils'); class Note extends Entity { static get tableName() { return "notes"; } @@ -79,6 +80,10 @@ class Note extends Entity { return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); } + async getNoteImages() { + return await repository.getEntities("SELECT * FROM note_images WHERE noteId = ? AND isDeleted = 0", [this.noteId]); + } + async getTrees() { return await repository.getEntities("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); } @@ -127,11 +132,19 @@ class Note extends Entity { } beforeSaving() { - this.content = JSON.stringify(this.jsonContent, null, '\t'); + if (this.isJson()) { + this.content = JSON.stringify(this.jsonContent, null, '\t'); + } if (this.isProtected) { protected_session.encryptNote(this); } + + if (!this.dateCreated) { + this.dateCreated = utils.nowDate(); + } + + this.dateModified = utils.nowDate(); } } diff --git a/src/entities/note_image.js b/src/entities/note_image.js new file mode 100644 index 000000000..434a27070 --- /dev/null +++ b/src/entities/note_image.js @@ -0,0 +1,15 @@ +"use strict"; + +const Entity = require('./entity'); +const repository = require('../services/repository'); + +class NoteImage extends Entity { + static get tableName() { return "note_images"; } + static get primaryKeyName() { return "noteImageId"; } + + async getNote() { + return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); + } +} + +module.exports = NoteImage; \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index 15cd5ce71..00793b8ea 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -177,7 +177,7 @@ function register(app) { apiRoute(POST, '/api/cleanup/vacuum-database', cleanupRoute.vacuumDatabase); route(GET, '/api/images/:imageId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage); - route(POST, '/api/images', [auth.checkApiAuthOrElectron], imageRoute.uploadImage, apiResultHandler); + route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware], imageRoute.uploadImage, apiResultHandler); apiRoute(POST, '/api/script/exec', scriptRoute.exec); apiRoute(POST, '/api/script/run/:noteId', scriptRoute.run); diff --git a/src/services/notes.js b/src/services/notes.js index fdc8d35e3..2c8b05a05 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -5,6 +5,8 @@ const sync_table = require('./sync_table'); const labels = require('./labels'); const protected_session = require('./protected_session'); const repository = require('./repository'); +const NoteImage = require('../entities/note_image'); +const NoteRevision = require('../entities/note_revision'); async function createNewNote(parentNoteId, noteOpts) { const noteId = utils.newNoteId(); @@ -127,110 +129,46 @@ async function protectNoteRecursively(note, protect) { } async function protectNote(note, protect) { - let changed = false; + if (protect !== note.isProtected) { + note.isProtected = protect; - if (protect && !note.isProtected) { - note.isProtected = true; - - changed = true; - } - else if (!protect && note.isProtected) { - note.isProtected = false; - - changed = true; - } - - if (changed) { await repository.updateEntity(note); - - await sync_table.addNoteSync(note.noteId); } - await protectNoteRevisions(note, protect); + await protectNoteRevisions(note); } -async function protectNoteRevisions(note, protect) { +async function protectNoteRevisions(note) { for (const revision of await note.getRevisions()) { - let changed = false; + if (note.isProtected !== revision.isProtected) { + revision.isProtected = note.isProtected; - if (protect && !revision.isProtected) { - revision.isProtected = true; - - changed = true; - } - else if(!protect && revision.isProtected) { - revision.isProtected = false; - - changed = true; - } - - if (changed) { await repository.updateEntity(revision); } - - await sync_table.addNoteRevisionSync(revision.noteRevisionId); } } -async function saveNoteRevision(noteId, nowStr) { - const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); - - if (oldNote.type === 'file') { +async function saveNoteImages(note) { + if (note.type !== 'text') { return; } - if (oldNote.isProtected) { - protected_session.decryptNote(oldNote); - - oldNote.isProtected = false; - } - - const newNoteRevisionId = utils.newNoteRevisionId(); - - await sql.insert('note_revisions', { - noteRevisionId: newNoteRevisionId, - noteId: noteId, - // title and text should be decrypted now - title: oldNote.title, - content: oldNote.content, - isProtected: 0, // will be fixed in the protectNoteRevisions() call - dateModifiedFrom: oldNote.dateModified, - dateModifiedTo: nowStr - }); - - await sync_table.addNoteRevisionSync(newNoteRevisionId); -} - -async function saveNoteImages(noteId, noteText) { - const existingNoteImages = await sql.getRows("SELECT * FROM note_images WHERE noteId = ?", [noteId]); + const existingNoteImages = await note.getNoteImages(); const foundImageIds = []; - const now = utils.nowDate(); const re = /src="\/api\/images\/([a-zA-Z0-9]+)\//g; let match; - while (match = re.exec(noteText)) { + while (match = re.exec(note.content)) { const imageId = match[1]; const existingNoteImage = existingNoteImages.find(ni => ni.imageId === imageId); if (!existingNoteImage) { - const noteImageId = utils.newNoteImageId(); - - await sql.insert("note_images", { - noteImageId: noteImageId, - noteId: noteId, + await repository.updateEntity(new NoteImage({ + noteImageId: utils.newNoteImageId(), + noteId: note.noteId, imageId: imageId, - isDeleted: 0, - dateModified: now, - dateCreated: now - }); - - await sync_table.addNoteImageSync(noteImageId); - } - else if (existingNoteImage.isDeleted) { - await sql.execute("UPDATE note_images SET isDeleted = 0, dateModified = ? WHERE noteImageId = ?", - [now, existingNoteImage.noteImageId]); - - await sync_table.addNoteImageSync(existingNoteImage.noteImageId); + isDeleted: 0 + })); } // else we don't need to do anything @@ -241,67 +179,62 @@ async function saveNoteImages(noteId, noteText) { const unusedNoteImages = existingNoteImages.filter(ni => !foundImageIds.includes(ni.imageId)); for (const unusedNoteImage of unusedNoteImages) { - await sql.execute("UPDATE note_images SET isDeleted = 1, dateModified = ? WHERE noteImageId = ?", - [now, unusedNoteImage.noteImageId]); + unusedNoteImage.isDeleted = 1; - await sync_table.addNoteImageSync(unusedNoteImage.noteImageId); + await repository.updateEntity(unusedNoteImage); } } -async function loadFile(noteId, newNote) { - const oldNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); - - if (oldNote.isProtected) { - await protected_session.decryptNote(oldNote); - } - - newNote.content = oldNote.content; -} - -async function updateNote(noteId, newNote) { - if (newNote.type === 'file') { - await loadFile(noteId, newNote); - } - - if (newNote.isProtected) { - await protected_session.encryptNote(newNote); - } - - const labelsMap = await labels.getNoteLabelMap(noteId); +async function saveNoteRevision(note) { + const labelsMap = await note.getLabelMap(); const now = new Date(); - const nowStr = utils.nowDate(); - const noteRevisionSnapshotTimeInterval = parseInt(await options.getOption('note_revision_snapshot_time_interval')); const revisionCutoff = utils.dateStr(new Date(now.getTime() - noteRevisionSnapshotTimeInterval * 1000)); const existingnoteRevisionId = await sql.getValue( - "SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND dateModifiedTo >= ?", [noteId, revisionCutoff]); + "SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND dateModifiedTo >= ?", [note.noteId, revisionCutoff]); - await sql.doInTransaction(async () => { - const msSinceDateCreated = now.getTime() - utils.parseDateTime(newNote.dateCreated).getTime(); + const msSinceDateCreated = now.getTime() - utils.parseDateTime(note.dateCreated).getTime(); - if (labelsMap.disable_versioning !== 'true' - && !existingnoteRevisionId - && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { + if (note.type !== 'file' + && labelsMap.disable_versioning !== 'true' + && !existingnoteRevisionId + && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { - await saveNoteRevision(noteId, nowStr); - } + await repository.updateEntity(new NoteRevision({ + noteRevisionId: utils.newNoteRevisionId(), + noteId: note.noteId, + // title and text should be decrypted now + title: note.title, + content: note.content, + isProtected: 0, // will be fixed in the protectNoteRevisions() call + dateModifiedFrom: note.dateModified, + dateModifiedTo: utils.nowDate() + })); + } +} - await saveNoteImages(noteId, newNote.content); +async function updateNote(noteId, noteUpdates) { + const note = await repository.getNote(noteId); - await protectNoteRevisions(await repository.getNote(noteId), newNote.isProtected); + if (note.type === 'file') { + // for update file, newNote doesn't contain file payloads + noteUpdates.content = note.content; + } - await sql.execute("UPDATE notes SET title = ?, content = ?, isProtected = ?, dateModified = ? WHERE noteId = ?", [ - newNote.title, - newNote.content, - newNote.isProtected, - nowStr, - noteId]); + await saveNoteRevision(note); - await sync_table.addNoteSync(noteId); - }); + note.title = noteUpdates.title; + note.content = noteUpdates.content; + note.isProtected = noteUpdates.isProtected; + + await repository.updateEntity(note); + + await saveNoteImages(note); + + await protectNoteRevisions(note); } async function deleteNote(branchId) {