| 
									
										
										
										
											2017-10-21 21:10:33 -04:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | const db = require('sqlite'); | 
					
						
							|  |  |  | const utils = require('./utils'); | 
					
						
							| 
									
										
										
										
											2017-10-24 22:17:48 -04:00
										 |  |  | const log = require('./log'); | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | const SOURCE_ID = require('./source_id'); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  | async function insert(table_name, rec, replace = false) { | 
					
						
							|  |  |  |     const keys = Object.keys(rec); | 
					
						
							|  |  |  |     if (keys.length === 0) { | 
					
						
							|  |  |  |         log.error("Can't insert empty object into table " + table_name); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const columns = keys.join(", "); | 
					
						
							|  |  |  |     const questionMarks = keys.map(p => "?").join(", "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const query = "INSERT " + (replace ? "OR REPLACE" : "") + " INTO " + table_name + "(" + columns + ") VALUES (" + questionMarks + ")"; | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  |     const res = await execute(query, Object.values(rec)); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return res.lastID; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  | async function replace(table_name, rec) { | 
					
						
							|  |  |  |     return await insert(table_name, rec, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | async function beginTransaction() { | 
					
						
							|  |  |  |     return await db.run("BEGIN"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function commit() { | 
					
						
							|  |  |  |     return await db.run("COMMIT"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  | async function rollback() { | 
					
						
							|  |  |  |     return await db.run("ROLLBACK"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | async function getSingleResult(query, params = []) { | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     return await wrap(async () => db.get(query, ...params)); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-28 19:55:55 -04:00
										 |  |  | async function getSingleResultOrNull(query, params = []) { | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     const all = await wrap(async () => db.all(query, ...params)); | 
					
						
							| 
									
										
										
										
											2017-10-28 19:55:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  |     return all.length > 0 ? all[0] : null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getSingleValue(query, params = []) { | 
					
						
							|  |  |  |     const row = await getSingleResultOrNull(query, params); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!row) { | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return row[Object.keys(row)[0]]; | 
					
						
							| 
									
										
										
										
											2017-10-28 19:55:55 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | async function getResults(query, params = []) { | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     return await wrap(async () => db.all(query, ...params)); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 23:14:26 -04:00
										 |  |  | async function getFlattenedResults(key, query, params = []) { | 
					
						
							|  |  |  |     const list = []; | 
					
						
							|  |  |  |     const result = await getResults(query, params); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const row of result) { | 
					
						
							|  |  |  |         list.push(row[key]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return list; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | async function execute(query, params = []) { | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     return await wrap(async () => db.run(query, ...params)); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-15 17:31:49 -04:00
										 |  |  | async function executeScript(query) { | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     return await wrap(async () => db.exec(query)); | 
					
						
							| 
									
										
										
										
											2017-10-15 17:31:49 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | async function remove(tableName, noteId) { | 
					
						
							|  |  |  |     return await execute("DELETE FROM " + tableName + " WHERE note_id = ?", [noteId]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addAudit(category, req=null, noteId=null, changeFrom=null, changeTo=null, comment=null) { | 
					
						
							|  |  |  |     const browserId = req == null ? null : req.get('x-browser-id'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 22:36:26 -04:00
										 |  |  |     await addAuditWithBrowserId(category, browserId, noteId, changeFrom, changeTo, comment); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addSyncAudit(category, sourceId, noteId) { | 
					
						
							|  |  |  |     await addAuditWithBrowserId(category, sourceId, noteId); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addAuditWithBrowserId(category, browserId=null, noteId=null, changeFrom=null, changeTo=null, comment=null) { | 
					
						
							|  |  |  |     const now = utils.nowTimestamp(); | 
					
						
							| 
									
										
										
										
											2017-10-24 22:17:48 -04:00
										 |  |  |     log.info("audit: " + category + ", browserId=" + browserId + ", noteId=" + noteId + ", from=" + changeFrom | 
					
						
							|  |  |  |         + ", to=" + changeTo + ", comment=" + comment); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-28 12:12:20 -04:00
										 |  |  |     const id = utils.randomString(14); | 
					
						
							| 
									
										
										
										
											2017-10-26 23:21:31 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     await execute("INSERT INTO audit_log (id, date_modified, category, browser_id, note_id, change_from, change_to, comment)" | 
					
						
							| 
									
										
										
										
											2017-11-01 22:36:26 -04:00
										 |  |  |         + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, now, category, browserId, noteId, changeFrom, changeTo, comment]); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function deleteRecentAudits(category, req, noteId) { | 
					
						
							|  |  |  |     const browserId = req.get('x-browser-id'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const deleteCutoff = utils.nowTimestamp() - 10 * 60; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await execute("DELETE FROM audit_log WHERE category = ? AND browser_id = ? AND note_id = ? AND date_modified > ?", | 
					
						
							|  |  |  |             [category, browserId, noteId, deleteCutoff]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | async function addNoteSync(noteId, sourceId) { | 
					
						
							|  |  |  |     await addEntitySync("notes", noteId, sourceId) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addNoteTreeSync(noteId, sourceId) { | 
					
						
							|  |  |  |     await addEntitySync("notes_tree", noteId, sourceId) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addNoteHistorySync(noteHistoryId, sourceId) { | 
					
						
							|  |  |  |     await addEntitySync("notes_history", noteHistoryId, sourceId); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 20:48:02 -04:00
										 |  |  | async function addOptionsSync(optName, sourceId) { | 
					
						
							|  |  |  |     await addEntitySync("options", optName, sourceId); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | async function addEntitySync(entityName, entityId, sourceId) { | 
					
						
							|  |  |  |     await replace("sync", { | 
					
						
							|  |  |  |         entity_name: entityName, | 
					
						
							|  |  |  |         entity_id: entityId, | 
					
						
							|  |  |  |         sync_date: utils.nowTimestamp(), | 
					
						
							|  |  |  |         source_id: sourceId || SOURCE_ID | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  | async function wrap(func) { | 
					
						
							|  |  |  |     const error = new Error(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |         return await func(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     catch (e) { | 
					
						
							|  |  |  |         log.error("Error executing transaction, executing rollback. Inner exception: " + e.stack + error.stack); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         throw e; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | async function doInTransaction(func) { | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  |     const error = new Error(); // to capture correct stack trace in case of exception
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  |         await beginTransaction(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await func(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await commit(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     catch (e) { | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  |         log.error("Error executing transaction, executing rollback. Inner exception: " + e.stack + error.stack); | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         await rollback(); | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         throw e; | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | module.exports = { | 
					
						
							|  |  |  |     insert, | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  |     replace, | 
					
						
							|  |  |  |     getSingleValue, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     getSingleResult, | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  |     getSingleResultOrNull, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     getResults, | 
					
						
							| 
									
										
										
										
											2017-10-24 23:14:26 -04:00
										 |  |  |     getFlattenedResults, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     execute, | 
					
						
							| 
									
										
										
										
											2017-10-15 17:31:49 -04:00
										 |  |  |     executeScript, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     addAudit, | 
					
						
							| 
									
										
										
										
											2017-11-01 22:36:26 -04:00
										 |  |  |     addSyncAudit, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     deleteRecentAudits, | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  |     remove, | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  |     doInTransaction, | 
					
						
							|  |  |  |     addNoteSync, | 
					
						
							|  |  |  |     addNoteTreeSync, | 
					
						
							| 
									
										
										
										
											2017-11-02 20:48:02 -04:00
										 |  |  |     addNoteHistorySync, | 
					
						
							|  |  |  |     addOptionsSync | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | }; |