diff --git a/public/javascripts/init.js b/public/javascripts/init.js index babd2e320..ab36b2441 100644 --- a/public/javascripts/init.js +++ b/public/javascripts/init.js @@ -1,9 +1,5 @@ "use strict"; -const glob = { - activeDialog: null -}; - // hot keys are active also inside inputs and content editables jQuery.hotkeys.options.filterInputAcceptingElements = true; jQuery.hotkeys.options.filterContentEditable = true; diff --git a/public/javascripts/messaging.js b/public/javascripts/messaging.js index 6ee4e47b9..8419110f2 100644 --- a/public/javascripts/messaging.js +++ b/public/javascripts/messaging.js @@ -19,23 +19,27 @@ const messaging = (function() { function messageHandler(event) { const message = JSON.parse(event.data); + if (message.data.length > 0) { + console.log(message); + } + if (message.type === 'sync') { lastPingTs = new Date().getTime(); - const data = message.data; + const syncData = message.data.filter(sync => sync.source_id !== glob.sourceId); - if (data.notes_tree) { + if (syncData.some(sync => sync.entity_name === 'notes_tree')) { console.log("Reloading tree because of background changes"); noteTree.reload(); } - if (data.notes && data.notes.includes(noteEditor.getCurrentNoteId())) { + if (syncData.some(sync => sync.entity_name === 'notes' && sync.entity_id === noteEditor.getCurrentNoteId())) { showMessage('Reloading note because background change'); noteEditor.reload(); } - if (data.recent_notes) { + if (syncData.some(sync => sync.entity_name === 'recent_notes')) { console.log("Reloading recent notes because of background changes"); recentNotes.reload(); diff --git a/public/javascripts/server.js b/public/javascripts/server.js index ee8d47bfd..f1173bb7f 100644 --- a/public/javascripts/server.js +++ b/public/javascripts/server.js @@ -8,7 +8,8 @@ const server = (function() { catch(e) {} return { - 'x-protected-session-id': protectedSessionId + protected_session_id: protectedSessionId, + source_id: glob.sourceId }; } diff --git a/routes/api/note_history.js b/routes/api/note_history.js index f1a5201cd..c39b83d43 100644 --- a/routes/api/note_history.js +++ b/routes/api/note_history.js @@ -25,10 +25,12 @@ router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { }); router.put('', auth.checkApiAuth, async (req, res, next) => { + const sourceId = req.headers.source_id; + await sql.doInTransaction(async () => { await sql.replace("notes_history", req.body); - await sync_table.addNoteHistorySync(req.body.note_history_id); + await sync_table.addNoteHistorySync(req.body.note_history_id, sourceId); }); res.send(); diff --git a/routes/api/notes.js b/routes/api/notes.js index e87813571..704caaf47 100644 --- a/routes/api/notes.js +++ b/routes/api/notes.js @@ -35,10 +35,11 @@ router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { }); router.post('/:parentNoteId/children', async (req, res, next) => { + const sourceId = req.headers.source_id; const parentNoteId = req.params.parentNoteId; const note = req.body; - const { noteId, noteTreeId } = await notes.createNewNote(parentNoteId, note); + const { noteId, noteTreeId } = await notes.createNewNote(parentNoteId, note, sourceId); res.send({ 'note_id': noteId, @@ -58,7 +59,7 @@ router.put('/:noteId', async (req, res, next) => { router.delete('/:noteTreeId', async (req, res, next) => { await sql.doInTransaction(async () => { - await notes.deleteNote(req.params.noteTreeId); + await notes.deleteNote(req.params.noteTreeId, req.headers.source_id); }); res.send({}); diff --git a/routes/api/notes_move.js b/routes/api/notes_move.js index 1c02cdc6a..7559107b0 100644 --- a/routes/api/notes_move.js +++ b/routes/api/notes_move.js @@ -10,6 +10,7 @@ const sync_table = require('../../services/sync_table'); router.put('/:noteTreeId/move-to/:parentNoteId', auth.checkApiAuth, async (req, res, next) => { const noteTreeId = req.params.noteTreeId; const parentNoteId = req.params.parentNoteId; + const sourceId = req.headers.source_id; const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]); const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1; @@ -20,7 +21,7 @@ router.put('/:noteTreeId/move-to/:parentNoteId', auth.checkApiAuth, async (req, await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?", [parentNoteId, newNotePos, now, noteTreeId]); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); }); res.send({}); @@ -29,6 +30,7 @@ router.put('/:noteTreeId/move-to/:parentNoteId', auth.checkApiAuth, async (req, router.put('/:noteTreeId/move-before/:beforeNoteTreeId', async (req, res, next) => { const noteTreeId = req.params.noteTreeId; const beforeNoteTreeId = req.params.beforeNoteTreeId; + const sourceId = req.headers.source_id; const beforeNote = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [beforeNoteTreeId]); @@ -38,14 +40,14 @@ router.put('/:noteTreeId/move-before/:beforeNoteTreeId', async (req, res, next) await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos >= ? AND is_deleted = 0", [beforeNote.note_pid, beforeNote.note_pos]); - await sync_table.addNoteReorderingSync(beforeNote.note_pid); + await sync_table.addNoteReorderingSync(beforeNote.note_pid, sourceId); const now = utils.nowDate(); await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?", [beforeNote.note_pid, beforeNote.note_pos, now, noteTreeId]); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); }); res.send({}); @@ -58,6 +60,7 @@ router.put('/:noteTreeId/move-before/:beforeNoteTreeId', async (req, res, next) router.put('/:noteTreeId/move-after/:afterNoteTreeId', async (req, res, next) => { const noteTreeId = req.params.noteTreeId; const afterNoteTreeId = req.params.afterNoteTreeId; + const sourceId = req.headers.source_id; const afterNote = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [afterNoteTreeId]); @@ -67,12 +70,12 @@ router.put('/:noteTreeId/move-after/:afterNoteTreeId', async (req, res, next) => await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0", [afterNote.note_pid, afterNote.note_pos]); - await sync_table.addNoteReorderingSync(afterNote.note_pid); + await sync_table.addNoteReorderingSync(afterNote.note_pid, sourceId); await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?", [afterNote.note_pid, afterNote.note_pos + 1, utils.nowDate(), noteTreeId]); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); }); res.send({}); @@ -85,6 +88,7 @@ router.put('/:noteTreeId/move-after/:afterNoteTreeId', async (req, res, next) => router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, async (req, res, next) => { const parentNoteId = req.params.parentNoteId; const childNoteId = req.params.childNoteId; + const sourceId = req.headers.source_id; const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [childNoteId, parentNoteId]); @@ -118,7 +122,7 @@ router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, async (req await sql.replace("notes_tree", noteTree); - await sync_table.addNoteTreeSync(noteTree.note_tree_id); + await sync_table.addNoteTreeSync(noteTree.note_tree_id, sourceId); res.send({ success: true @@ -129,6 +133,7 @@ router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, async (req router.put('/:noteId/clone-after/:afterNoteTreeId', async (req, res, next) => { const noteId = req.params.noteId; const afterNoteTreeId = req.params.afterNoteTreeId; + const sourceId = req.headers.source_id; const afterNote = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [afterNoteTreeId]); @@ -157,7 +162,7 @@ router.put('/:noteId/clone-after/:afterNoteTreeId', async (req, res, next) => { await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0", [afterNote.note_pid, afterNote.note_pos]); - await sync_table.addNoteReorderingSync(afterNote.note_pid); + await sync_table.addNoteReorderingSync(afterNote.note_pid, sourceId); const noteTree = { note_tree_id: utils.newNoteTreeId(), @@ -171,7 +176,7 @@ router.put('/:noteId/clone-after/:afterNoteTreeId', async (req, res, next) => { await sql.replace("notes_tree", noteTree); - await sync_table.addNoteTreeSync(noteTree.note_tree_id); + await sync_table.addNoteTreeSync(noteTree.note_tree_id, sourceId); res.send({ success: true diff --git a/routes/api/recent_notes.js b/routes/api/recent_notes.js index 58de6f137..a79505c94 100644 --- a/routes/api/recent_notes.js +++ b/routes/api/recent_notes.js @@ -15,6 +15,7 @@ router.get('', auth.checkApiAuth, async (req, res, next) => { router.put('/:noteTreeId/:notePath', auth.checkApiAuth, async (req, res, next) => { const noteTreeId = req.params.noteTreeId; const notePath = req.params.notePath; + const sourceId = req.headers.source_id; await sql.doInTransaction(async () => { await sql.replace('recent_notes', { @@ -24,9 +25,9 @@ router.put('/:noteTreeId/:notePath', auth.checkApiAuth, async (req, res, next) = is_deleted: 0 }); - await sync_table.addRecentNoteSync(noteTreeId); + await sync_table.addRecentNoteSync(noteTreeId, sourceId); - await options.setOption('start_note_path', notePath); + await options.setOption('start_note_path', notePath, sourceId); }); res.send(await getRecentNotes()); diff --git a/routes/api/settings.js b/routes/api/settings.js index 0257bbd15..dd26ea7fb 100644 --- a/routes/api/settings.js +++ b/routes/api/settings.js @@ -25,12 +25,13 @@ router.get('/', auth.checkApiAuth, async (req, res, next) => { router.post('/', async (req, res, next) => { const body = req.body; + const sourceId = req.headers.source_id; if (ALLOWED_OPTIONS.includes(body['name'])) { const optionName = await options.getOption(body['name']); await sql.doInTransaction(async () => { - await options.setOption(body['name'], body['value']); + await options.setOption(body['name'], body['value'], sourceId); }); res.send({}); diff --git a/routes/api/tree.js b/routes/api/tree.js index 1b3a53a8a..14fdb909e 100644 --- a/routes/api/tree.js +++ b/routes/api/tree.js @@ -39,9 +39,10 @@ router.put('/:noteId/protect-sub-tree/:isProtected', auth.checkApiAuth, async (r const noteId = req.params.noteId; const isProtected = !!parseInt(req.params.isProtected); const dataKey = protected_session.getDataKey(req); + const sourceId = req.headers.source_id; await sql.doInTransaction(async () => { - await notes.protectNoteRecursively(noteId, dataKey, isProtected); + await notes.protectNoteRecursively(noteId, dataKey, isProtected, sourceId); }); res.send({}); @@ -49,12 +50,13 @@ router.put('/:noteId/protect-sub-tree/:isProtected', auth.checkApiAuth, async (r router.put('/:noteTreeId/set-prefix', auth.checkApiAuth, async (req, res, next) => { const noteTreeId = req.params.noteTreeId; + const sourceId = req.headers.source_id; const prefix = utils.isEmptyOrWhitespace(req.body.prefix) ? null : req.body.prefix; await sql.doInTransaction(async () => { await sql.execute("UPDATE notes_tree SET prefix = ?, date_modified = ? WHERE note_tree_id = ?", [prefix, utils.nowDate(), noteTreeId]); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); }); res.send({}); diff --git a/routes/index.js b/routes/index.js index d279d303f..ee326b404 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,10 +3,12 @@ const express = require('express'); const router = express.Router(); const auth = require('../services/auth'); -const utils = require('../services/utils'); +const source_id = require('../services/source_id'); router.get('', auth.checkAuth, async (req, res, next) => { - res.render('index', {}); + res.render('index', { + sourceId: await source_id.generateSourceId() + }); }); module.exports = router; diff --git a/services/notes.js b/services/notes.js index 9718751ff..521601320 100644 --- a/services/notes.js +++ b/services/notes.js @@ -5,7 +5,7 @@ const notes = require('./notes'); const data_encryption = require('./data_encryption'); const sync_table = require('./sync_table'); -async function createNewNote(parentNoteId, note) { +async function createNewNote(parentNoteId, note, sourceId) { const noteId = utils.newNoteId(); const noteTreeId = utils.newNoteTreeId(); @@ -25,7 +25,7 @@ async function createNewNote(parentNoteId, note) { await sql.execute('UPDATE notes_tree SET note_pos = note_pos + 1, date_modified = ? WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0', [utils.nowDate(), parentNoteId, afterNote.note_pos]); - await sync_table.addNoteReorderingSync(parentNoteId); + await sync_table.addNoteReorderingSync(parentNoteId, sourceId); } else { throw new Error('Unknown target: ' + note.target); @@ -42,7 +42,7 @@ async function createNewNote(parentNoteId, note) { is_protected: note.is_protected }); - await sync_table.addNoteSync(noteId); + await sync_table.addNoteSync(noteId, sourceId); await sql.insert("notes_tree", { note_tree_id: noteTreeId, @@ -54,7 +54,7 @@ async function createNewNote(parentNoteId, note) { is_deleted: 0 }); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); }); return { @@ -68,15 +68,15 @@ async function encryptNote(note, ctx) { note.detail.note_text = data_encryption.encrypt(ctx.getDataKey(), data_encryption.noteTextIv(note.detail.note_id), note.detail.note_text); } -async function protectNoteRecursively(noteId, dataKey, protect) { +async function protectNoteRecursively(noteId, dataKey, protect, sourceId) { const note = await sql.getSingleResult("SELECT * FROM notes WHERE note_id = ?", [noteId]); - await protectNote(note, dataKey, protect); + await protectNote(note, dataKey, protect, sourceId); const children = await sql.getFlattenedResults("SELECT note_id FROM notes_tree WHERE note_pid = ?", [noteId]); for (const childNoteId of children) { - await protectNoteRecursively(childNoteId, dataKey, protect); + await protectNoteRecursively(childNoteId, dataKey, protect, sourceId); } } @@ -86,7 +86,7 @@ function decryptNote(note, dataKey) { note.is_protected = false; } -async function protectNote(note, dataKey, protect) { +async function protectNote(note, dataKey, protect, sourceId) { let changed = false; if (protect && !note.is_protected) { @@ -108,13 +108,13 @@ async function protectNote(note, dataKey, protect) { await sql.execute("UPDATE notes SET note_title = ?, note_text = ?, is_protected = ? WHERE note_id = ?", [note.note_title, note.note_text, note.is_protected, note.note_id]); - await sync_table.addNoteSync(note.note_id); + await sync_table.addNoteSync(note.note_id, sourceId); } - await protectNoteHistory(note.note_id, dataKey, protect); + await protectNoteHistory(note.note_id, dataKey, protect, sourceId); } -async function protectNoteHistory(noteId, dataKey, protect) { +async function protectNoteHistory(noteId, dataKey, protect, sourceId) { const historyToChange = await sql.getResults("SELECT * FROM notes_history WHERE note_id = ? AND is_protected != ?", [noteId, protect]); for (const history of historyToChange) { @@ -132,7 +132,7 @@ async function protectNoteHistory(noteId, dataKey, protect) { await sql.execute("UPDATE notes_history SET note_title = ?, note_text = ?, is_protected = ? WHERE note_history_id = ?", [history.note_title, history.note_text, history.is_protected, history.note_history_id]); - await sync_table.addNoteHistorySync(history.note_history_id); + await sync_table.addNoteHistorySync(history.note_history_id, sourceId); } } @@ -174,7 +174,7 @@ async function updateNote(noteId, newNote, ctx) { date_modified_to: nowStr }); - await sync_table.addNoteHistorySync(newNoteHistoryId); + await sync_table.addNoteHistorySync(newNoteHistoryId, ctx.sourceId); } await protectNoteHistory(noteId, ctx.getDataKeyOrNull(), newNote.detail.is_protected); @@ -186,15 +186,15 @@ async function updateNote(noteId, newNote, ctx) { nowStr, noteId]); - await sync_table.addNoteSync(noteId); + await sync_table.addNoteSync(noteId, ctx.sourceId); }); } -async function deleteNote(noteTreeId) { +async function deleteNote(noteTreeId, sourceId) { const now = utils.nowDate(); await sql.execute("UPDATE notes_tree SET is_deleted = 1, date_modified = ? WHERE note_tree_id = ?", [now, noteTreeId]); - await sync_table.addNoteTreeSync(noteTreeId); + await sync_table.addNoteTreeSync(noteTreeId, sourceId); const noteId = await sql.getSingleValue("SELECT note_id FROM notes_tree WHERE note_tree_id = ?", [noteTreeId]); @@ -202,12 +202,12 @@ async function deleteNote(noteTreeId) { if (!notDeletedNoteTreesCount) { await sql.execute("UPDATE notes SET is_deleted = 1, date_modified = ? WHERE note_id = ?", [now, noteId]); - await sync_table.addNoteSync(noteId); + await sync_table.addNoteSync(noteId, sourceId); const children = await sql.getResults("SELECT note_tree_id FROM notes_tree WHERE note_pid = ? AND is_deleted = 0", [noteId]); for (const child of children) { - await deleteNote(child.note_tree_id); + await deleteNote(child.note_tree_id, sourceId); } } } diff --git a/services/options.js b/services/options.js index 2e08f400d..5bacbcdee 100644 --- a/services/options.js +++ b/services/options.js @@ -16,9 +16,9 @@ async function getOption(optName) { return row['opt_value']; } -async function setOption(optName, optValue) { +async function setOption(optName, optValue, sourceId) { if (SYNCED_OPTIONS.includes(optName)) { - await sync_table.addOptionsSync(optName); + await sync_table.addOptionsSync(optName, sourceId); } await sql.replace("options", { diff --git a/services/ping_job.js b/services/ping_job.js index a7621e269..2faaf87ab 100644 --- a/services/ping_job.js +++ b/services/ping_job.js @@ -1,5 +1,4 @@ const sql = require('./sql'); -const source_id = require('./source_id'); const utils = require('./utils'); const messaging = require('./messaging'); const options = require('./options'); @@ -9,24 +8,10 @@ let startTime = utils.nowDate(); let sentSyncId = []; async function sendPing() { - const syncs = await sql.getResults("SELECT * FROM sync WHERE sync_date >= ? AND source_id != ?", [startTime, source_id.currentSourceId]); + const syncs = await sql.getResults("SELECT * FROM sync WHERE sync_date >= ?", [startTime]); startTime = utils.nowDate(); - const data = {}; - const syncIds = []; - - for (const sync of syncs) { - if (sentSyncId.includes(sync.id)) { - continue; - } - - if (!data[sync.entity_name]) { - data[sync.entity_name] = []; - } - - data[sync.entity_name].push(sync.entity_id); - syncIds.push(sync.id); - } + const syncData = syncs.filter(sync => !sentSyncId.includes(sync.id)); const lastSyncedPush = await options.getOption('last_synced_push'); @@ -34,12 +19,12 @@ async function sendPing() { messaging.sendMessage({ type: 'sync', - data: data, + data: syncData, changesToPushCount: sync_setup.isSyncSetup ? changesToPushCount : 0 }); - for (const syncId of syncIds) { - sentSyncId.push(syncId); + for (const sync of syncData) { + sentSyncId.push(sync.id); } } diff --git a/services/protected_session.js b/services/protected_session.js index c32ded505..31b9e82ba 100644 --- a/services/protected_session.js +++ b/services/protected_session.js @@ -11,7 +11,7 @@ function setDataKey(req, decryptedDataKey) { } function getProtectedSessionId(req) { - return req.headers['x-protected-session-id']; + return req.headers.protected_session_id; } function getDataKey(req) { diff --git a/services/request_context.js b/services/request_context.js index bb12d6ff7..be0fb3e4f 100644 --- a/services/request_context.js +++ b/services/request_context.js @@ -3,6 +3,8 @@ const protected_session = require('./protected_session'); module.exports = function(req) { + const sourceId = req.headers.source_id; + function isProtectedSessionAvailable() { return protected_session.isProtectedSessionAvailable(req); } @@ -24,6 +26,7 @@ module.exports = function(req) { } return { + sourceId, isProtectedSessionAvailable, getDataKey, getDataKeyOrNull diff --git a/services/source_id.js b/services/source_id.js index 2ab6ae149..0e74cd5e8 100644 --- a/services/source_id.js +++ b/services/source_id.js @@ -2,31 +2,39 @@ const utils = require('./utils'); const log = require('./log'); const sql = require('./sql'); -const currentSourceId = utils.randomString(12); +async function generateSourceId() { + const sourceId = utils.randomString(12); -log.info("Using sourceId=" + currentSourceId); + log.info("Generated sourceId=" + sourceId); + + await sql.doInTransaction(async () => { + await sql.insert("source_ids", { + source_id: sourceId, + date_created: utils.nowDate() + }); + }); + + await refreshSourceIds(); + + return sourceId; +} + +async function refreshSourceIds() { + allSourceIds = await sql.getFlattenedResults("SELECT source_id FROM source_ids ORDER BY date_created DESC"); +} let allSourceIds = []; -sql.dbReady.then(async () => { - try { - await sql.doInTransaction(async () => { - await sql.insert("source_ids", { - source_id: currentSourceId, - date_created: utils.nowDate() - }); - }); - - allSourceIds = await sql.getFlattenedResults("SELECT source_id FROM source_ids ORDER BY date_created DESC"); - } - catch (e) {} -}); +sql.dbReady.then(refreshSourceIds); function isLocalSourceId(srcId) { return allSourceIds.includes(srcId); } +const currentSourceId = generateSourceId(); + module.exports = { + generateSourceId, currentSourceId, isLocalSourceId }; \ No newline at end of file diff --git a/views/index.ejs b/views/index.ejs index b06943808..905faa9e7 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -291,6 +291,10 @@