mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
better transaction handling with rollback on exception
This commit is contained in:
parent
afadd6ec06
commit
de3d1b3e39
@ -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
|
||||
});
|
||||
|
@ -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({});
|
||||
});
|
||||
|
@ -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({});
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user