diff --git a/routes/api/notes.js b/routes/api/notes.js index 72de979cd..ea7b399d1 100644 --- a/routes/api/notes.js +++ b/routes/api/notes.js @@ -44,73 +44,70 @@ router.put('/:noteId', async (req, res, next) => { const history = await sql.getSingleResult("select id from notes_history where note_id = ? and date_modified_from >= ?", [noteId, historyCutoff]); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + if (history) { + await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [ + note['detail']['note_title'], + note['detail']['note_text'], + note['detail']['encryption'], + now, + history['id'] + ]); + } + else { + await sql.execute("insert into notes_history (note_id, note_title, note_text, encryption, date_modified_from, date_modified_to) values (?, ?, ?, ?, ?, ?)", [ + noteId, + note['detail']['note_title'], + note['detail']['note_text'], + note['detail']['encryption'], + now, + now + ]); + } - if (history) { - await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [ + if (note['detail']['note_title'] !== detail['note_title']) { + await sql.deleteRecentAudits(audit_category.UPDATE_TITLE, req, noteId); + await sql.addAudit(audit_category.UPDATE_TITLE, req, noteId); + } + + if (note['detail']['note_text'] !== detail['note_text']) { + await sql.deleteRecentAudits(audit_category.UPDATE_CONTENT, req, noteId); + await sql.addAudit(audit_category.UPDATE_CONTENT, req, noteId); + } + + if (note['detail']['encryption'] !== detail['encryption']) { + await sql.addAudit(audit_category.ENCRYPTION, req, noteId, detail['encryption'], note['detail']['encryption']); + } + + await sql.execute("update notes set note_title = ?, note_text = ?, encryption = ?, date_modified = ? where note_id = ?", [ note['detail']['note_title'], note['detail']['note_text'], note['detail']['encryption'], now, - history['id'] - ]); - } - else { - await sql.execute("insert into notes_history (note_id, note_title, note_text, encryption, date_modified_from, date_modified_to) values (?, ?, ?, ?, ?, ?)", [ - noteId, - note['detail']['note_title'], - note['detail']['note_text'], - note['detail']['encryption'], - now, - now - ]); - } + noteId]); - if (note['detail']['note_title'] !== detail['note_title']) { - await sql.deleteRecentAudits(audit_category.UPDATE_TITLE, req, noteId); - await sql.addAudit(audit_category.UPDATE_TITLE, req, noteId); - } + await sql.remove("images", noteId); - if (note['detail']['note_text'] !== detail['note_text']) { - await sql.deleteRecentAudits(audit_category.UPDATE_CONTENT, req, noteId); - await sql.addAudit(audit_category.UPDATE_CONTENT, req, noteId); - } + for (const img of note['images']) { + img['image_data'] = atob(img['image_data']); - if (note['detail']['encryption'] !== detail['encryption']) { - await sql.addAudit(audit_category.ENCRYPTION, req, noteId, detail['encryption'], note['detail']['encryption']); - } + await sql.insert("images", img); + } - await sql.execute("update notes set note_title = ?, note_text = ?, encryption = ?, date_modified = ? where note_id = ?", [ - note['detail']['note_title'], - note['detail']['note_text'], - note['detail']['encryption'], - now, - noteId]); + await sql.remove("links", noteId); - await sql.remove("images", noteId); - - for (const img of note['images']) { - img['image_data'] = atob(img['image_data']); - - await sql.insert("images", img); - } - - await sql.remove("links", noteId); - - for (const link in note['links']) - await sql.insert("links", link); - - await sql.commit(); + for (const link in note['links']) { + await sql.insert("links", link); + } + }); res.send({}); }); router.delete('/:noteId', async (req, res, next) => { - await sql.beginTransaction(); - - await deleteNote(req.params.noteId, req); - - await sql.commit(); + await sql.doInTransaction(async () => { + await deleteNote(req.params.noteId, req); + }); res.send({}); }); @@ -165,33 +162,31 @@ router.post('/:parentNoteId/children', async (req, res, next) => { throw new ('Unknown target: ' + note['target']); } - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.addAudit(audit_category.CREATE_NOTE, req, noteId); - await sql.addAudit(audit_category.CREATE_NOTE, req, noteId); + const now = utils.nowTimestamp(); - const now = utils.nowTimestamp(); + await sql.insert("notes", { + 'note_id': noteId, + 'note_title': note['note_title'], + 'note_text': '', + 'note_clone_id': '', + 'date_created': now, + 'date_modified': now, + 'encryption': note['encryption'] + }); - await sql.insert("notes", { - 'note_id': noteId, - 'note_title': note['note_title'], - 'note_text': '', - 'note_clone_id': '', - 'date_created': now, - 'date_modified': now, - 'encryption': note['encryption'] + await sql.insert("notes_tree", { + 'note_id': noteId, + 'note_pid': parentNoteId, + 'note_pos': newNotePos, + 'is_expanded': 0, + 'date_modified': utils.nowTimestamp(), + 'is_deleted': 0 + }); }); - await sql.insert("notes_tree", { - 'note_id': noteId, - 'note_pid': parentNoteId, - 'note_pos': newNotePos, - 'is_expanded': 0, - 'date_modified': utils.nowTimestamp(), - 'is_deleted': 0 - }); - - await sql.commit(); - res.send({ 'note_id': noteId }); diff --git a/routes/api/notes_move.js b/routes/api/notes_move.js index e3205a344..8659f1f3b 100644 --- a/routes/api/notes_move.js +++ b/routes/api/notes_move.js @@ -22,14 +22,12 @@ router.put('/:noteId/moveTo/:parentId', auth.checkApiAuth, async (req, res, next const now = utils.nowTimestamp(); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", + [parentId, newNotePos, now, noteId]); - await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", - [parentId, newNotePos, now, noteId]); - - await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId); - - await sql.commit(); + await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId); + }); res.send({}); }); @@ -43,16 +41,14 @@ router.put('/:noteId/moveBefore/:beforeNoteId', async (req, res, next) => { if (beforeNote) { const now = utils.nowTimestamp(); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_id = ?", [now, beforeNoteId]); - await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_id = ?", [now, beforeNoteId]); + await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", + [beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]); - await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", - [beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]); - - await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); - - await sql.commit(); + await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); + }); } res.send({}); @@ -67,17 +63,15 @@ router.put('/:noteId/moveAfter/:afterNoteId', async (req, res, next) => { if (afterNote) { const now = utils.nowTimestamp(); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_pid = ? and note_pos > ? and is_deleted = 0", + [now, afterNote['note_pid'], afterNote['note_pos']]); - await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_pid = ? and note_pos > ? and is_deleted = 0", - [now, afterNote['note_pid'], afterNote['note_pos']]); + await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", + [afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]); - await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", - [afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]); - - await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); - - await sql.commit(); + await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); + }); } res.send({}); @@ -88,9 +82,11 @@ router.put('/:noteId/expanded/:expanded', async (req, res, next) => { const expanded = req.params.expanded; const now = utils.nowTimestamp(); - await sql.execute("update notes_tree set is_expanded = ?, date_modified = ? where note_id = ?", [expanded, now, noteId]); + await sql.doInTransaction(async () => { + await sql.execute("update notes_tree set is_expanded = ?, date_modified = ? where note_id = ?", [expanded, now, noteId]); - // no audit here, not really important + await sql.addAudit(audit_category.CHANGE_EXPANDED, req, noteId, null, expanded); + }); res.send({}); }); diff --git a/routes/api/settings.js b/routes/api/settings.js index fd1ff8051..a1cd6d610 100644 --- a/routes/api/settings.js +++ b/routes/api/settings.js @@ -27,13 +27,11 @@ router.post('/', async (req, res, next) => { if (ALLOWED_OPTIONS.includes(body['name'])) { const optionName = await sql.getOption(body['name']); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.addAudit(audit_category.SETTINGS, req, null, optionName, body['value'], body['name']); - await sql.addAudit(audit_category.SETTINGS, req, null, optionName, body['value'], body['name']); - - await sql.setOption(body['name'], body['value']); - - await sql.commit(); + await sql.setOption(body['name'], body['value']); + }); res.send({}); } diff --git a/services/audit_category.js b/services/audit_category.js index b36906ce4..cc939969f 100644 --- a/services/audit_category.js +++ b/services/audit_category.js @@ -4,6 +4,7 @@ module.exports = { UPDATE_CONTENT: 'CONTENT', UPDATE_TITLE: 'TITLE', CHANGE_POSITION: 'POSITION', + CHANGE_EXPANDED: 'EXPANDED', CREATE_NOTE: 'CREATE', DELETE_NOTE: 'DELETE', CHANGE_PARENT: 'PARENT', diff --git a/services/change_password.js b/services/change_password.js index 6eb1e6bc3..46c21bfd1 100644 --- a/services/change_password.js +++ b/services/change_password.js @@ -55,15 +55,13 @@ async function changePassword(currentPassword, newPassword, req = null) { const newEncryptedDataKey = encrypt(decryptedDataKey); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.setOption('encrypted_data_key', newEncryptedDataKey); - await sql.setOption('encrypted_data_key', newEncryptedDataKey); + await sql.setOption('password_verification_hash', newPasswordVerificationKey); - await sql.setOption('password_verification_hash', newPasswordVerificationKey); - - await sql.addAudit(audit_category.CHANGE_PASSWORD, req); - - await sql.commit(); + await sql.addAudit(audit_category.CHANGE_PASSWORD, req); + }); return { 'success': true, diff --git a/services/migration.js b/services/migration.js index 935019ea4..b3a48211d 100644 --- a/services/migration.js +++ b/services/migration.js @@ -42,13 +42,11 @@ async function migrate() { try { log.info("Attempting migration to version " + mig.dbVersion + " with script: " + migrationSql); - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await sql.executeScript(migrationSql); - await sql.executeScript(migrationSql); - - await sql.setOption("db_version", mig.dbVersion); - - await sql.commit(); + await sql.setOption("db_version", mig.dbVersion); + }); log.info("Migration to version " + mig.dbVersion + " has been successful."); diff --git a/services/sql.js b/services/sql.js index 1c0df1a3b..3ba03ffea 100644 --- a/services/sql.js +++ b/services/sql.js @@ -109,6 +109,21 @@ async function deleteRecentAudits(category, req, noteId) { [category, browserId, noteId, deleteCutoff]) } +async function doInTransaction(func) { + try { + await beginTransaction(); + + await func(); + + await commit(); + } + catch (e) { + log.error("Error executing transaction, executing rollback. Inner exception: " + e.stack); + + await rollback(); + } +} + module.exports = { insert, getSingleResult, @@ -118,10 +133,8 @@ module.exports = { executeScript, getOption, setOption, - beginTransaction, - commit, - rollback, addAudit, deleteRecentAudits, - remove + remove, + doInTransaction }; \ No newline at end of file diff --git a/services/sync.js b/services/sync.js index f0893ecfd..bbb2aea46 100644 --- a/services/sync.js +++ b/services/sync.js @@ -34,41 +34,37 @@ async function pullSync(cookieJar, syncLog) { } try { - await sql.beginTransaction(); + await sql.doInTransaction(async () => { + await putChanged(resp, syncLog); - await putChanged(resp, syncLog); + for (const noteId of resp.notes) { + let note; - for (const noteId of resp.notes) { - let note; + try { + note = await rp({ + uri: SYNC_SERVER + "/api/sync/note/" + noteId + "/" + lastSyncedPull, + headers: { + auth: 'sync' + }, + json: true, + jar: cookieJar + }); + } + catch (e) { + throw new Error("Can't pull note " + noteId + ", inner exception: " + e.stack); + } - try { - note = await rp({ - uri: SYNC_SERVER + "/api/sync/note/" + noteId + "/" + lastSyncedPull, - headers: { - auth: 'sync' - }, - json: true, - jar: cookieJar - }); - } - catch (e) { - throw new Error("Can't pull note " + noteId + ", inner exception: " + e.stack); + await putNote(note, syncLog); } - await putNote(note, syncLog); - } + if (resp.notes.length > 0) { + await sql.addAudit(audit_category.SYNC); + } - if (resp.notes.length > 0) { - await sql.addAudit(audit_category.SYNC); - } - - await sql.setOption('last_synced_pull', resp.syncTimestamp); - - await sql.commit(); + await sql.setOption('last_synced_pull', resp.syncTimestamp); + }); } catch (e) { - await sql.rollback(); - throw e; } }