From 5096e74a6085a776730b399d9395005a49631821 Mon Sep 17 00:00:00 2001 From: Max G Date: Mon, 18 May 2020 07:45:35 +0000 Subject: [PATCH] * Removed arrows from notification * Added trash can function * Tweaked status text to always be the same * Removed some open second note code * Edior always focuses on text now * Added some extra loading note messages * Notes are now removed from search index when deleted * Lots more things happen and update in real time on multiple machines * Shared notes can be reverted * WAY more tests * Note Categories are much more reliable * Lots of code is much cleaner --- .../GlobalNotificationComponent.vue | 2 - client/src/components/GlobalSiteMenu.vue | 4 +- .../components/NoteDeleteButtonComponent.vue | 2 +- client/src/components/NoteInputPanel.vue | 46 +- .../src/components/NoteTitleDisplayCard.vue | 85 ++-- client/src/components/ShareNoteComponent.vue | 10 +- client/src/components/TagDisplayComponent.vue | 3 + client/src/pages/LoginPage.vue | 7 +- client/src/pages/NotesPage.vue | 158 ++++--- client/src/stores/mainStore.js | 1 + server/index.js | 24 +- server/models/Attachment.js | 30 +- server/models/Note.js | 395 +++++++++++------- server/models/QuickNote.js | 6 +- server/models/ShareNote.js | 74 +++- server/models/User.js | 123 ++++-- server/routes/noteController.js | 40 +- server/routes/quicknoteController.js | 2 +- 18 files changed, 644 insertions(+), 368 deletions(-) diff --git a/client/src/components/GlobalNotificationComponent.vue b/client/src/components/GlobalNotificationComponent.vue index 53e9880..462b568 100644 --- a/client/src/components/GlobalNotificationComponent.vue +++ b/client/src/components/GlobalNotificationComponent.vue @@ -70,9 +70,7 @@ diff --git a/client/src/components/GlobalSiteMenu.vue b/client/src/components/GlobalSiteMenu.vue index 0c45193..64b6b06 100644 --- a/client/src/components/GlobalSiteMenu.vue +++ b/client/src/components/GlobalSiteMenu.vue @@ -257,7 +257,7 @@ }, data: function(){ return { - version: '2.1.2', + version: '2.2.2', username: '', collapsed: false, mobile: false, @@ -329,7 +329,7 @@ .then(response => { if(response.data && response.data.id){ - // this.$router.push('/notes/open/'+response.data.id) + //Redirect to note page if user is not on it this.$bus.$emit('open_note', response.data.id) this.disableNewNote = false } diff --git a/client/src/components/NoteDeleteButtonComponent.vue b/client/src/components/NoteDeleteButtonComponent.vue index c8d401d..c2725e0 100644 --- a/client/src/components/NoteDeleteButtonComponent.vue +++ b/client/src/components/NoteDeleteButtonComponent.vue @@ -1,6 +1,6 @@ @@ -148,7 +160,7 @@ 'input-notes': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'), 'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default, - 'fast-filters': require('@/components/FastFilters.vue').default, + // 'fast-filters': require('@/components/FastFilters.vue').default, 'search-input': require('@/components/SearchInput.vue').default, 'attachment-display': require('@/components/AttachmentDisplayCard').default, 'counter':require('@/components/AnimatedCounterComponent.vue').default, @@ -205,7 +217,8 @@ 'shared': ['envelope outline', 'Received Notes'], 'sent': ['paper plane outline', 'Shared Notes'], 'notes': ['file','Notes'], - 'highlights': ['paragraph', 'Found In Text'] + 'highlights': ['paragraph', 'Found In Text'], + 'trashed': ['poop', 'Trashed Notes'] }, noteSections: { pinned: [], @@ -213,7 +226,8 @@ shared:[], sent:[], notes: [], - highlights: [] + highlights: [], + trashed: [] }, } @@ -223,35 +237,47 @@ this.$parent.loginGateway() this.$io.on('new_note_created', noteId => { + //Do not update note if its open if(this.activeNoteId1 != noteId){ - this.updateSingleNote(parseInt(noteId)) + this.$store.dispatch('fetchAndUpdateUserTotals') + this.updateSingleNote(noteId) } }) + this.$io.on('note_attribute_modified', noteId => { + //Do not update note if its open + if(this.activeNoteId1 != noteId){ + this.$store.dispatch('fetchAndUpdateUserTotals') + this.updateSingleNote(noteId) + } + }) + //Update title cards when new note text is saved this.$io.on('new_note_text_saved', ({noteId, hash}) => { + //Do not update note if its open if(this.activeNoteId1 != noteId){ - this.updateSingleNote(parseInt(noteId)) + this.updateSingleNote(noteId) } }) //Update totals for app this.$store.dispatch('fetchAndUpdateUserTotals') + //Close note event this.$bus.$on('close_active_note', ({position, noteId, modified}) => { - this.closeNote(position) - this.$store.dispatch('fetchAndUpdateUserTotals') + this.closeNote() if(modified){ + this.$store.dispatch('fetchAndUpdateUserTotals') this.updateSingleNote(parseInt(noteId)) } }) - this.$bus.$on('update_single_note', (noteId) => { - this.updateSingleNote(noteId) - }) + // this.$bus.$on('update_single_note', (noteId) => { + // this.updateSingleNote(noteId) + // }) this.$bus.$on('note_deleted', (noteId) => { //Remove deleted note from set, its deleted @@ -320,7 +346,7 @@ this.$bus.$off('note_reload') this.$bus.$off('close_active_note') - this.$bus.$off('update_single_note') + // this.$bus.$off('update_single_note') this.$bus.$off('note_deleted') this.$bus.$off('update_fast_filters') this.$bus.$off('update_search_term') @@ -364,30 +390,8 @@ } }, closeNote(position){ - //One note open, close that note - if(position == 0){ - this.activeNoteId1 = null - this.activeNoteId2 = null - } - //Right note closed, thats 1 - if(position == 1){ - this.activeNoteId1 = null - } - if(position == 2){ - this.activeNoteId2 = null - } - - //IF two notes get opened, update ID of open note - if(this.activeNoteId1 || this.activeNoteId2){ - this.$router.push('/notes/open/'+Math.max(this.activeNoteId1, this.activeNoteId2)) - } else { - //No notes are open, just show notes page - this.$router.push('/notes') - } - - this.activeNote1Position = 0 - this.activeNote2Position = 0 - + this.activeNoteId1 = null + this.$router.push('/notes') }, toggleTagFilter(tagId){ @@ -479,6 +483,8 @@ // @TODO Don't even trigger this if the note wasn't changed updateSingleNote(noteId){ + noteId = parseInt(noteId) + //Find local note, if it exists; continue @@ -656,33 +662,58 @@ //Sort notes into defined sections notes.forEach(note => { + //Only show trashed notes when trashed + if(this.fastFilters.onlyShowTrashed == 1){ + + if(note.trashed == 1){ + this.noteSections.trashed.push(note) + } + return + } + if(note.trashed == 1){ + return + } + //Show archived notes - if(note.archived == 1 && this.fastFilters.onlyArchived == 1){ - this.noteSections.archived.push(note) + if(this.fastFilters.onlyArchived == 1){ + + if(note.pinned == 1 && note.archived == 1){ + this.noteSections.pinned.push(note) + return + } + if(note.archived == 1){ + this.noteSections.archived.push(note) + } return } - if(note.shareUsername != null){ - this.noteSections.shared.push(note) + if(note.archived == 1){ return } + //Only show sent notes section if shared is selected - if(note.shared == 2 && this.fastFilters.onlyShowSharedNotes == 1){ - this.noteSections.sent.push(note) - return - } - if(note.note_highlights.length > 0){ - this.noteSections.highlights.push(note) + if(this.fastFilters.onlyShowSharedNotes == 1){ + + if(note.shared == 2){ + this.noteSections.sent.push(note) + } + if(note.shareUsername != null){ + this.noteSections.shared.push(note) + } return } + //Show shared notes on main list but not notes shared with you + if(note.shareUsername != null){ return } + // Pinned notes are always first, they can appear in the archive if(note.pinned == 1){ this.noteSections.pinned.push(note) return } - //If the note is not archived, push it. - if(note.archived != 1 && this.fastFilters.onlyArchived != 1){ - this.noteSections.notes.push(note) - } + + //Push to default note section + this.noteSections.notes.push(note) + + return }) }, @@ -692,14 +723,13 @@ this.searchTerm = '' this.searchTags = [] this.fastFilters = {} + this.updateFastFilters(5) this.foundAttachments = [] //Remove all attachments - this.$bus.$emit('reset_fast_filters') + // this.$bus.$emit('reset_fast_filters') //Load initial batch, then tags, then other batch this.search(true, this.firstLoadBatchSize) .then( () => { - - //Load a larger batch once first batch has loaded return this.search(false, this.batchSize, true) }) @@ -711,6 +741,7 @@ //clear out tags this.searchTags = [] + this.loadingInProgress = false //A little hacky, brings user to notes page then filters on click if(this.$route.name != 'Note Page'){ @@ -725,7 +756,8 @@ 'withTags', // 'Only Show Notes with Tags' 'onlyArchived', //'Only Show Archived Notes' 'onlyShowSharedNotes', //Only show shared notes - 'onlyShowEncrypted', + 'onlyShowTrashed', + 'notesHome', ] let filter = {} diff --git a/client/src/stores/mainStore.js b/client/src/stores/mainStore.js index 1532e0f..f4e9dfe 100644 --- a/client/src/stores/mainStore.js +++ b/client/src/stores/mainStore.js @@ -123,6 +123,7 @@ export default new Vuex.Store({ //Save all the totals for the user state.userTotals = totalsObject + // console.log('-------------') // Object.keys(totalsObject).forEach( key => { // console.log(key + ' -- ' + totalsObject[key]) // }) diff --git a/server/index.js b/server/index.js index d6e5ea0..a20ac03 100644 --- a/server/index.js +++ b/server/index.js @@ -37,11 +37,9 @@ var io = require('socket.io')(http, { path:'/socket' }); -// Make io accessible to our router -app.use(function(req,res,next){ - req.io = io; - next(); -}); +//Set socket IO as a global in the app +global.SocketIo = io + io.on('connection', function(socket){ @@ -49,7 +47,7 @@ io.on('connection', function(socket){ //When a user connects, add them to their own room // This allows the server to emit events to that specific user - // access socket.io in the controller with req.io + // access socket.io in the controller with SocketIo global socket.on('user_connect', token => { Auth.decodeToken(token) .then(userData => { @@ -138,12 +136,14 @@ app.use(function(req, res, next){ // Test Area -// -> right here -// let UserTest = require('@models/User') -// let NoteTest = require('@models/Note') -// UserTest.keyPairTest() -// .then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey)) -// .then( message => { console.log(message) }) +const printResults = true +let UserTest = require('@models/User') +let NoteTest = require('@models/Note') +UserTest.keyPairTest('genMan2', '1', printResults) +.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults)) +.then( message => { + if(printResults) console.log(message) +}) // Test Area diff --git a/server/models/Attachment.js b/server/models/Attachment.js index 0699839..c3ae14a 100644 --- a/server/models/Attachment.js +++ b/server/models/Attachment.js @@ -117,6 +117,9 @@ Attachment.update = (userId, attachmentId, updatedText, noteId) => { Attachment.delete = (userId, attachmentId, urlDelete = false) => { + let attachment = null + let noteExists = true + return new Promise((resolve, reject) => { db.promise() .query('SELECT * FROM attachment WHERE id = ? AND user_id = ? LIMIT 1', [attachmentId, userId]) @@ -127,21 +130,32 @@ Attachment.delete = (userId, attachmentId, urlDelete = false) => { return resolve(true) } - //Pull data we want out of - let row = rows[0][0] - let url = row.url - const noteId = row.note_id + attachment = rows[0][0] + + return db.promise().query('SELECT count(id) as `exists` FROM note WHERE id = ?', [attachment.note_id]) + + }) + + .then((rows, fields) => { + + noteExists = (rows[0]['exists'] > 0) + + + + + let url = attachment.url + const noteId = attachment.note_id //Try to delete file and thumbnail try { - fs.unlinkSync(filePath+row.file_location) + fs.unlinkSync(filePath+attachment.file_location) } catch(err) { console.error('File Does not exist') } try { - fs.unlinkSync(filePath+'thumb_'+row.file_location) + fs.unlinkSync(filePath+'thumb_'+attachment.file_location) } catch(err) { console.error('Thumbnail Does not exist') } - //Do not delete link attachments, just hide them. They will be deleted if removed from note - if(row.attachment_type == 1 && !urlDelete){ + //Do not delete link attachments, just hide them. They will be deleted if removed from note or if note is deleted + if(attachment.attachment_type == 1 && !urlDelete && noteExists){ db.promise() .query(`UPDATE attachment SET visible = 0 WHERE id = ?`, [attachmentId]) .then((rows, fields) => { }) diff --git a/server/models/Note.js b/server/models/Note.js index e61e83a..5f42d2e 100644 --- a/server/models/Note.js +++ b/server/models/Note.js @@ -14,81 +14,152 @@ const crypto = require('crypto') const cs = require('@helpers/CryptoString') const rp = require('request-promise'); const fs = require('fs') - - - const gm = require('gm') -Note.test = (userId, masterKey) => { +Note.test = (userId, masterKey, printResults) => { return new Promise((resolve, reject) => { - let testNoteId = 0 - Note.create(null, userId, '','', masterKey) + let testNoteId = 0 + let testNoteId2 = 0 + let sharedNoteId = 0 //ID of note shared with user + const shareUserId = 61 + + Note.create(userId, 'Random Note','With Random Text dogs', masterKey) + .then( newNoteId => { + if(printResults) console.log('Test: Created Note -> ', newNoteId) + return Note.create(userId, 'Yo, note','a second test note cheese mate', masterKey) + }) + .then( newNoteId => { + if(printResults) console.log('Test: Created Note -> ', newNoteId) + testNoteId2 = newNoteId + //Create a blank note to test updating note and reindexing it + return Note.create(userId, '','', masterKey) + }) .then(newNoteId => { - console.log('Test: Create Note - Pass') + if(printResults) console.log('Test: Created Note -> ', newNoteId) testNoteId = newNoteId return Note.update - (null, userId, testNoteId, 'Note text', 'Test Note beans Title', 0, 0, 0, 'hash', masterKey) + (userId, testNoteId, 'Note text', 'Test Note beans Title', 0, 0, 0, 'hash', masterKey) }) .then(() => { - console.log('Test: Update Note - Pass') - - return Note.get(userId, testNoteId, masterKey) - - }) - .then(updatedText => { - - console.log('Test: Open Updated Note - Pass') - - const shareUserId = 61 - return ShareNote.migrateNoteToShared(userId, testNoteId, shareUserId, masterKey) - }) - .then(shareResults => { - - console.log('Test: Set Note To Shared - Pass') + if(printResults) console.log('Test: Update Note '+testNoteId+' - Pass') return Note.get(userId, testNoteId, masterKey) }) .then(() => { - console.log('Test: Open Shared Note - Pass') - - return Note.update - (null, userId, testNoteId, 'Shared Update', 'Test Note beans Title', 0, 0, 0, 'hash', masterKey) - }) - .then(() => { + if(printResults) console.log('Test: Open Updated Note - Pass') - console.log('Test: Update Shared Note - Pass') - return Note.reindex(userId, masterKey) - }) - .then( reindexResults => { - console.log(`Test: Reindex Notes - ${reindexResults?'Pass':'Fail'}`) + }) + .then(() => { + + if(printResults) console.log('Test: Reindex normal note - Pass') return Note.encryptedIndexSearch(userId, 'beans', null, masterKey) }) .then(textSearchResults => { - + if(textSearchResults['ids'] && textSearchResults['ids'].length >= 1){ - console.log('Test: Search Index - Pass') + if(printResults) console.log('Test: Normal Note Search Index - Pass') } else { console.log('Test: Search Index - Fail') } - return Note.delete(userId, testNoteId) + return ShareNote.migrateNoteToShared(userId, testNoteId, shareUserId, masterKey) + }) + .then(({success, shareUserId, sharedUserNoteId}) => { + + if(printResults) console.log('Test: Set Note To Shared - Pass') + + sharedNoteId = sharedUserNoteId + + return Note.get(userId, testNoteId, masterKey) + + }) + .then(() => { + + if(printResults) console.log('Test: Open Shared Note - Pass') + + return Note.update + (userId, testNoteId, 'Shared Update', 'Test Note yarnsmarf Title', 0, 0, 0, 'hash', masterKey) + }) + .then(() => { + + if(printResults) console.log('Test: Update Shared Note - Pass') + + return Note.reindex(userId, masterKey) + }) + .then( reindexResults => { + + if(printResults) console.log(`Test: Reindex Notes - ${reindexResults?'Pass':'Fail'}`) + + return Note.encryptedIndexSearch(userId, 'yarnsmarf', null, masterKey) + + }) + .then(textSearchResults => { + + if(textSearchResults['ids'] && textSearchResults['ids'].length >= 1){ + + if(printResults) console.log('Test: Search Index - Pass') + + } else { console.log('Test: Search Index - Fail') } + + return Note.delete(userId, testNoteId, masterKey) }) .then(results => { - console.log('Test: Delete Note - Pass') + if(printResults) console.log('Test: Delete Note - Pass') - return resolve('Test: Complete') + return Note.encryptedIndexSearch(userId, 'yarnsmarf', null, masterKey) + + }) + .then(textSearchResults => { + + if(textSearchResults['ids'] && textSearchResults['ids'].length == 0){ + if(printResults) console.log('Test: Search Deleted Note Text - Pass') + } else { console.log('Test: Search Deleted Note Text - Fail') } + + return Note.delete(shareUserId, sharedNoteId) + + }) + .then(() => { + + if(printResults) console.log('Test: Delete Shared Note - Pass') + + return ShareNote.migrateNoteToShared(userId, testNoteId2, shareUserId, masterKey) + }) + .then(({success, shareUserId, sharedUserNoteId}) => { + + if(printResults) console.log('Test: Created Another New Shared Note - pass') + + return ShareNote.removeUserFromShared(userId, testNoteId2, sharedUserNoteId, masterKey) + }) + .then(() => { + + if(printResults) console.log('Test: Unshared New Shared Note -> convert back to normal note - pass') + + return Note.get(userId, testNoteId2, masterKey) + }) + .then(() => { + + if(printResults) console.log('Test: Decrypt unshared note - pass') + + let User = require('@models/User') + return User.deleteUser(userId, '1') + }) + .then(results => { + + if(printResults) console.log('Test: Delete User Account - Pass') + + return resolve('Test: Complete ---') }) }) @@ -103,7 +174,7 @@ Note.encryptEveryNote = (userId, masterKey) => { db.promise().query(` SELECT * FROM note JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id) - WHERE salt IS NULL AND user_id = ? AND encrypted = 0 AND shared = 0`, [userId]) + WHERE salt IS NULL AND user_id = ? AND shared = 0`, [userId]) .then((rows, fields) => { let foundNotes = rows[0] @@ -164,7 +235,7 @@ Note.encryptEveryNote = (userId, masterKey) => { } //Returns insertedId of new note -Note.create = (io, userId, noteTitle = '', noteText = '', masterKey) => { +Note.create = (userId, noteTitle = '', noteText = '', masterKey) => { return new Promise((resolve, reject) => { if(userId == null || userId < 10){ reject('User Id required to create note') } @@ -186,13 +257,13 @@ Note.create = (io, userId, noteTitle = '', noteText = '', masterKey) => { const rawTextId = rows[0].insertId return db.promise() - .query('INSERT INTO note (user_id, note_raw_text_id, created, quick_note, snippet, snippet_salt) VALUES (?,?,?,?,?,?)', + .query('INSERT INTO note (user_id, note_raw_text_id, created, quick_note, snippet, snippet_salt, indexed) VALUES (?,?,?,?,?,?,0)', [userId, rawTextId, created, 0, snippet, snippetSalt]) }) .then((rows, fields) => { - if(io){ - io.to(userId).emit('new_note_created', rows[0].insertId) + if(SocketIo){ + SocketIo.to(userId).emit('new_note_created', rows[0].insertId) } // Indexing is done on save @@ -205,7 +276,8 @@ Note.create = (io, userId, noteTitle = '', noteText = '', masterKey) => { // Called when a note is close // Will attempt to reindex all notes that are flagged in database as not indexed // Limit to 100 notes per batch -Note.reindex = (userId, masterKey) => { +// removeId = array of Ids to remove, [122,223] +Note.reindex = (userId, masterKey, removeId = null) => { return new Promise((resolve, reject) => { if(!masterKey || masterKey.length == 0){ @@ -216,17 +288,37 @@ Note.reindex = (userId, masterKey) => { let searchIndex = null let searchIndexSalt = null let foundNotes = null + let userPrivateKey = null - //First check if we have any notes to index - db.promise().query(` - SELECT note.id, text, salt FROM note - JOIN note_raw_text ON note.note_raw_text_id = note_raw_text.id - WHERE indexed = 0 AND encrypted = 0 AND salt IS NOT NULL - AND user_id = ? LIMIT 100`, [userId]) + let User = require('@models/User') + User.generateKeypair(userId, masterKey) + .then(({publicKey, privateKey}) => { + + userPrivateKey = privateKey + //First check if we have any notes to index + return db.promise().query(` + SELECT note.id, text, salt, encrypted_share_password_key FROM note + JOIN note_raw_text ON note.note_raw_text_id = note_raw_text.id + WHERE indexed = 0 AND salt IS NOT NULL + AND user_id = ? LIMIT 100`, [userId]) + + }) .then((rows, fields) => { //Halt execution if there are no new notes foundNotes = rows[0] + + //Remove ID from index but don't reindex text + if(removeId != null){ + removeId.forEach(removeId => { + foundNotes.push({ + id:removeId, + text:'', + salt: null, + }) + }) + } + if(foundNotes.length == 0){ throw new Error('No new notes to index') } @@ -244,9 +336,9 @@ Note.reindex = (userId, masterKey) => { //Select all user notes to recreate index return db.promise().query(` - SELECT note.id, text, salt FROM note + SELECT note.id, text, salt, encrypted_share_password_key FROM note JOIN note_raw_text ON note.note_raw_text_id = note_raw_text.id - WHERE encrypted = 0 AND user_id = ?`, [userId]) + WHERE user_id = ?`, [userId]) .then((rows, fields) => { foundNotes = rows[0] @@ -276,6 +368,10 @@ Note.reindex = (userId, masterKey) => { }) .then(rawSearchIndex => { + if(rawSearchIndex == null){ + throw new Error('Search Index Not Found/Decrypted') + } + searchIndex = rawSearchIndex //Remove all instances of IDs from text @@ -288,7 +384,6 @@ Note.reindex = (userId, masterKey) => { const removeDoubles = new RegExp(',,',"g") // const removeTrail = new RegExp(',]',"g") - searchIndex = searchIndex .replace(removeId, '') .replace(removeDoubles, ',') @@ -319,7 +414,17 @@ Note.reindex = (userId, masterKey) => { return resolve(true) } - const noteHtml = cs.decrypt(masterKey, note.salt, note.text) + let currentNoteKey = masterKey + + //Decrypt shared key if it exists + const encryptedShareKey = note.encrypted_share_password_key + if(encryptedShareKey != null){ + currentNoteKey = crypto.privateDecrypt(userPrivateKey, + Buffer.from(encryptedShareKey, 'base64') ) + } + + //Decrypt text with proper key + const noteHtml = cs.decrypt(currentNoteKey, note.salt, note.text) const rawText = ProcessText.removeHtml(noteHtml) //Remove HTML @@ -356,8 +461,6 @@ Note.reindex = (userId, masterKey) => { }) .then(rawSearchIndex => { - // console.log('All notes indexed') - const created = Math.round((+new Date)/1000) const jsonSearchIndex = JSON.stringify(searchIndex) const encryptedJsonIndex = cs.encrypt(masterKey, searchIndexSalt, jsonSearchIndex) @@ -377,58 +480,14 @@ Note.reindex = (userId, masterKey) => { }) }).catch(error => { - console.log('Reindex Error') + console.log('Reindex Error:') console.log(error) }) - - - - - - - //Find all note Ids that need to be reindexed - - // return resolve(true) - return - - Note.get(userId, noteId) - .then(note => { - - let noteText = note.text - if(note.encrypted == 1){ - noteText = '' //Don't put note text in encrypted notes - } - - - // - // Update Solr index - // - Tags.string(userId, noteId) - .then(tagString => { - - const fullText = note.title + ' ' + ProcessText.removeHtml(noteText) +' '+ tagString - - db.promise() - .query(` - - INSERT INTO note_text_index (note_id, user_id, text) - VALUES (?,?,?) - ON DUPLICATE KEY UPDATE text = ? - - `, [noteId, userId, fullText, fullText]) - .then((rows, fields) => { - resolve(true) - }) - .catch(console.log) - - }) - - }) }) } // Returns updated note text -Note.update = (io, userId, noteId, noteText, noteTitle, color, pinned, archived, hash, masterKey) => { +Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, hash, masterKey) => { return new Promise((resolve, reject) => { const now = Math.round((+new Date)/1000) @@ -483,6 +542,8 @@ Note.update = (io, userId, noteId, noteText, noteTitle, color, pinned, archived, //Re-encrypt for other user const updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet) db.promise().query('UPDATE note SET snippet = ? WHERE id = ?', [updatedSnippet, otherNote.id]) + + SocketIo.to(otherNote['user_id']).emit('new_note_text_saved', {'noteId':otherNote.id, hash}) } }) @@ -501,12 +562,12 @@ Note.update = (io, userId, noteId, noteText, noteTitle, color, pinned, archived, }) .then((rows, fields) => { - if(io){ - io.to(userId).emit('new_note_text_saved', {noteId, hash}) + if(SocketIo){ + SocketIo.to(userId).emit('new_note_text_saved', {noteId, hash}) } //Async attachment reindex - Attachment.scanTextForWebsites(io, userId, noteId, noteText) + Attachment.scanTextForWebsites(SocketIo, userId, noteId, noteText) //Send back updated response resolve(rows[0]) @@ -519,12 +580,14 @@ Note.setPinned = (userId, noteId, pinnedBoolean) => { return new Promise((resolve, reject) => { const pinned = pinnedBoolean ? 1:0 + const now = Math.round((+new Date)/1000) //Update other note attributes return db.promise() - .query('UPDATE note SET pinned = ? WHERE id = ? AND user_id = ? LIMIT 1', - [pinned, noteId, userId]) + .query('UPDATE note JOIN note_raw_text ON note_raw_text_id = note_raw_text.id SET pinned = ?, updated = ? WHERE note.id = ? AND user_id = ?', + [pinned, now, noteId, userId]) .then((rows, fields) => { + SocketIo.to(userId).emit('note_attribute_modified', noteId) resolve(true) }) }) @@ -534,12 +597,31 @@ Note.setArchived = (userId, noteId, archivedBoolead) => { return new Promise((resolve, reject) => { const archived = archivedBoolead ? 1:0 + const now = Math.round((+new Date)/1000) //Update other note attributes return db.promise() - .query('UPDATE note SET archived = ? WHERE id = ? AND user_id = ? LIMIT 1', - [archived, noteId, userId]) + .query('UPDATE note JOIN note_raw_text ON note_raw_text_id = note_raw_text.id SET archived = ?, updated = ? WHERE note.id = ? AND user_id = ?', + [archived, now, noteId, userId]) .then((rows, fields) => { + SocketIo.to(userId).emit('note_attribute_modified', noteId) + resolve(true) + }) + }) +} + +Note.setTrashed = (userId, noteId, trashedBoolean) => { + return new Promise((resolve, reject) => { + + const trashed = trashedBoolean ? 1:0 + const now = Math.round((+new Date)/1000) + + //Update other note attributes + return db.promise() + .query('UPDATE note JOIN note_raw_text ON note_raw_text_id = note_raw_text.id SET trashed = ?, updated = ? WHERE note.id = ? AND user_id = ?', + [trashed, now, noteId, userId]) + .then((rows, fields) => { + SocketIo.to(userId).emit('note_attribute_modified', noteId) resolve(true) }) }) @@ -548,7 +630,7 @@ Note.setArchived = (userId, noteId, archivedBoolead) => { // // Delete a note and all its remaining parts // -Note.delete = (userId, noteId) => { +Note.delete = (userId, noteId, masterKey = null) => { return new Promise((resolve, reject) => { // @@ -591,10 +673,10 @@ Note.delete = (userId, noteId) => { return db.promise() .query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId, userId]) }) - .then((rows, fields) => { - // Delete search index + .then( results => { + // Delete hidden attachments for this note (files for attachment are already gone) return db.promise() - .query('DELETE FROM note_text_index WHERE note_text_index.note_id = ? AND note_text_index.user_id = ?', [noteId,userId]) + .query('DELETE FROM attachment WHERE visible = 0 AND note_id = ? AND user_id = ?', [noteId, userId]) }) .then((rows, fields) => { // delete tags @@ -603,7 +685,11 @@ Note.delete = (userId, noteId) => { }) .then((rows, fields) => { - //IF there are nots with a matching raw text id, we want to under their share status + + }) + .then((rows, fields) => { + + //IF there are notes with a matching raw text id, we want to update their share status db.promise().query('SELECT id FROM note WHERE note_raw_text_id = ?',[rawTextId]) .then((rows, fields) => { if(rows[0].length == 1){ @@ -611,7 +697,15 @@ Note.delete = (userId, noteId) => { } }) - resolve(true) + if(masterKey){ + //Remove note ID from index + Note.reindex(userId, masterKey, [noteId]) + .then(results => { + return resolve(true) + }) + } else { + return resolve(true) + } }) }) } @@ -698,6 +792,7 @@ Note.get = (userId, noteId, masterKey) => { note.created, note.pinned, note.archived, + note.trashed, note.color, note.encrypted_share_password_key, count(distinct attachment.id) as attachment_count, @@ -889,7 +984,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { count(distinct attachment.id) as attachment_count, note.pinned, note.archived, - note.encrypted, + note.trashed, GROUP_CONCAT(DISTINCT tag.text) as tags, GROUP_CONCAT(DISTINCT attachment.file_location) as thumbs, shareUser.username as shareUsername, @@ -903,16 +998,6 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { LEFT JOIN user as shareUser ON (note.share_user_id = shareUser.id) WHERE note.user_id = ? ` - - //Show shared notes - if(fastFilters.onlyShowSharedNotes == 1){ - //share_user_id means your shared them, a note with a shared user id filled in means it was shared - noteSearchQuery += ` AND share_user_id IS NOT NULL OR (note.shared = 2 AND note.user_id = ?)` - searchParams.push(userId) - //Show notes shared with you - } else { - noteSearchQuery += ' AND note.share_user_id IS NULL' - } //If text search returned results, limit search to those ids if(textSearchIds.length > 0){ @@ -921,18 +1006,13 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { searchAllNotes = true } + //If Specific ID's are being searched, search ALL notes if(fastFilters.noteIdSet && fastFilters.noteIdSet.length > 0){ searchParams.push(fastFilters.noteIdSet) noteSearchQuery += ' AND note.id IN (?)' searchAllNotes = true } - //Encrypted Note - if(fastFilters.onlyShowEncrypted == 1){ - noteSearchQuery += ' AND encrypted = 1' - searchAllNotes = true - } - //If tags are passed, use those tags in search if(searchTags.length > 0){ searchParams.push(searchTags) @@ -941,31 +1021,48 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { //Show archived notes, only if fast filter is set, default to not archived if(searchAllNotes == false){ - if(fastFilters.onlyArchived == 1){ - noteSearchQuery += ' AND note.archived = 1' //Show Archived - } else { - noteSearchQuery += ' AND note.archived = 0' //Exclude archived + + if(fastFilters.notesHome == 1){ + noteSearchQuery += ' AND note.archived = 0 AND note.trashed = 0 AND note.share_user_id IS NULL' } + + if(fastFilters.onlyShowSharedNotes == 1){ + //share_user_id means your shared them, a note with a shared user id filled in means it was shared + noteSearchQuery += ` AND share_user_id IS NOT NULL OR (note.shared = 2 AND note.user_id = ?) AND note.trashed = 0` + searchParams.push(userId) + //Show notes shared with you + } + if(fastFilters.onlyArchived == 1){ + noteSearchQuery += ' AND note.archived = 1 AND note.trashed = 0' //Show Archived + } + + if(fastFilters.onlyShowTrashed == 1){ + noteSearchQuery += ' AND note.trashed = 1' //Show Exclude + } + } + + //Finish up note query noteSearchQuery += ' GROUP BY note.id' //Only show notes with Tags if(fastFilters.withTags == 1){ - returnTagResults = true noteSearchQuery += ' HAVING tag_count > 0' } //Only show notes with links if(fastFilters.withLinks == 1){ - returnTagResults = true noteSearchQuery += ' HAVING attachment_count > 0' } //Only show archived notes - if(fastFilters.onlyArchived == 1){ - returnTagResults = true - noteSearchQuery += ' HAVING note.archived = 1' - } + // if(fastFilters.onlyArchived == 1){ + // noteSearchQuery += ' HAVING note.archived = 1' + // } + // //Only show trashed notes + // if(fastFilters.onlyShowTrashed == 1){ + // noteSearchQuery += ' HAVING note.trashed = 1' + // } // // Always prioritize pinned notes in searches. @@ -992,8 +1089,6 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { const limitSize = parseInt(fastFilters.limitSize, 10) || 10 //Use int or default to 10 const limitOffset = parseInt(fastFilters.limitOffset, 10) || 0 //Either parse int, or use zero - - // console.log(` LIMIT ${limitOffset}, ${limitSize}`) noteSearchQuery += ` LIMIT ${limitOffset}, ${limitSize}` } @@ -1005,19 +1100,14 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { .query(noteSearchQuery, searchParams) .then((noteRows, noteFields) => { - //Current note key may change, default to master key - let currentNoteKey = masterKey - //Push all notes returnData['notes'] = noteRows[0] //pull out all note ids so we can fetch all tags for those notes - let noteIds = [] returnData['notes'].forEach(note => { - //Grab note ID for finding tags - noteIds.push(note.id) - + //Current note key may change, default to master key + let currentNoteKey = masterKey //Shared notes use encrypted key - decrypt key then decrypt note const encryptedShareKey = note.encrypted_share_password_key @@ -1060,6 +1150,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => { //Clear out note.text before sending it to front end, its being used in title and subtext delete note.snippet delete note.salt + delete note.encrypted_share_password_key }) diff --git a/server/models/QuickNote.js b/server/models/QuickNote.js index a9bfcd8..27e7d73 100644 --- a/server/models/QuickNote.js +++ b/server/models/QuickNote.js @@ -58,7 +58,7 @@ QuickNote.makeUrlLink = (inputText) => { return replacedText; } -QuickNote.update = (io, userId, pushText, masterKey) => { +QuickNote.update = (userId, pushText, masterKey) => { return new Promise((resolve, reject) => { let finalId = null @@ -91,7 +91,7 @@ QuickNote.update = (io, userId, pushText, masterKey) => { finalText += broken - return Note.create(io, userId, 'Quick Note', finalText, masterKey) + return Note.create(userId, 'Quick Note', finalText, masterKey) .then(insertedId => { finalId = insertedId return db.promise().query('UPDATE note SET quick_note = 1 WHERE id = ? AND user_id = ?',[insertedId, userId]) @@ -101,7 +101,7 @@ QuickNote.update = (io, userId, pushText, masterKey) => { finalText += (broken + noteObject.text) finalId = noteObject.id - return Note.update(io, userId, noteObject.id, finalText, noteObject.title, noteObject.color, noteObject.pinned, noteObject.archived, null, masterKey) + return Note.update(userId, noteObject.id, finalText, noteObject.title, noteObject.color, noteObject.pinned, noteObject.archived, null, masterKey) } }) .then( saveResults => { diff --git a/server/models/ShareNote.js b/server/models/ShareNote.js index 9e398a7..8a62873 100644 --- a/server/models/ShareNote.js +++ b/server/models/ShareNote.js @@ -89,7 +89,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => { const encryptedSharedKey = crypto.publicEncrypt(publicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64') //Update note snippet for current user with public key encoded snippet - return db.promise().query('UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ? WHERE id = ? AND user_id = ?', + return db.promise().query('UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 2 WHERE id = ? AND user_id = ?', [encryptedSnippet, sharedNoteSnippetSalt, encryptedSharedKey, noteId, userId]) }) @@ -116,8 +116,13 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => { }) .then((rows, fields) => { + const sharedUserNoteId = rows[0]['insertId'] + + //Emit update count event to user shared with - so they see the note in real time + SocketIo.to(sharedUserNoteId).emit('update_counts') + let success = true - return resolve({success, shareUserId}) + return resolve({success, shareUserId, sharedUserNoteId}) }) .catch(error => { @@ -128,6 +133,69 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => { }) } +ShareNote.removeUserFromShared = (userId, noteId, shareNoteUserId, masterKey) => { + return new Promise((resolve, reject) => { + + let rawTextId = null + let removeUserId = null + + db.promise() + .query('SELECT note_raw_text_id, user_id FROM note WHERE id = ? AND share_user_id = ?', [shareNoteUserId, userId]) + .then( (rows, fields) => { + + rawTextId = rows[0][0]['note_raw_text_id'] + removeUserId = rows[0][0]['user_id'] + + //Delete note entry for other user - remove users access + //@TODO - this won't remove the note from their search index, it needs to + return Note.delete(removeUserId, shareNoteUserId) + + }) + .then(results => { + + return db.promise().query('SELECT count(*) as count FROM note WHERE note_raw_text_id = ?', [rawTextId]) + }) + .then((rows, fields) => { + + //Convert back to normal note if there is only one person with this note + if(rows[0][0]['count'] == 1){ + + Note.get(userId, noteId, masterKey) + .then(noteObject => { + + const salt = cs.createSmallSalt() + const snippetSalt = cs.createSmallSalt() + + const snippetObj = JSON.stringify([noteObject.title, noteObject.text.substring(0, 500)]) + const snippet = cs.encrypt(masterKey, snippetSalt, snippetObj) + + const textObject = JSON.stringify([noteObject.title, noteObject.text]) + const encryptedText = cs.encrypt(masterKey, salt, textObject) + + db.promise() + .query(`UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 0 WHERE id = ? AND user_id = ?`, + [snippet, snippetSalt, null, noteId, userId]) + .then((r,f) => { + + db.promise() + .query('UPDATE note_raw_text SET text = ?, salt = ? WHERE id = ?', + [encryptedText, salt, noteObject.rawTextId]) + .then(() => { + return resolve(true) + }) + + }) + + }) + + } else { + //Keep note shared + return resolve(true) + } + }) + }) +} + // Get users who see a shared note ShareNote.getUsers = (userId, rawTextId) => { return new Promise((resolve, reject) => { @@ -168,7 +236,7 @@ ShareNote.removeUser = (userId, noteId) => { //Delete note entry for other user - remove users access if(removeUserId && Number.isInteger(removeUserId)){ //Delete this users access to the note - return Note.delete(removeUserId, noteId) + return Note.delete(removeUserId, noteId, masterKey) } else { diff --git a/server/models/User.js b/server/models/User.js index b664702..9e251b3 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -19,10 +19,8 @@ User.login = (username, password) => { .query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName]) .then((rows, fields) => { - //Pull out user data from database results - const lookedUpUser = rows[0][0]; - - //User not found, create a new account with set data + // Create New Account + // if(rows[0].length == 0){ User.create(lowerName, password) .then( ({token, userId}) => { @@ -30,7 +28,13 @@ User.login = (username, password) => { }) } + // Login User + // if(rows[0].length == 1){ + + //Pull out user data from database results + const lookedUpUser = rows[0][0] + //hash the password and check for a match // const salt = new Buffer(lookedUpUser.salt, 'binary') const salt = Buffer.from(lookedUpUser.salt, 'binary') @@ -102,37 +106,33 @@ User.create = (username, password) => { created: currentDate }; + let userId = null + let newMasterKey = null + db.promise() .query('INSERT INTO user SET ?', new_user) .then((rows, fields) => { - if(rows[0].affectedRows == 1){ + userId = rows[0].insertId + return User.generateMasterKey(userId, password) + }) + .then( result => { - const userId = rows[0].insertId + return User.getMasterKey(userId, password) + }) + .then(masterKey => { + newMasterKey = masterKey + return User.generateKeypair(userId, newMasterKey) + }) + .then(({publicKey, privateKey}) => { - User.generateMasterKey(userId, password) - .then( result => User.getMasterKey(userId, password)) - .then(masterKey => { - - User.generateKeypair(userId, masterKey) - .then(({publicKey, privateKey}) => { - - const token = Auth.createToken(userId, masterKey) - return resolve({token, userId}) - }) - - - }) - - } else { - //Emit Error to user - reject('New user could not be created') - } + const token = Auth.createToken(userId, newMasterKey) + return resolve({token, userId}) }) .catch(console.log) }) } else { - reject('Username already in use.') + return reject('Username already in use.') }//END user create }) .catch(console.log) @@ -149,22 +149,24 @@ User.getCounts = (userId) => { db.promise().query( `SELECT - SUM(pinned = 1 && archived = 0 && share_user_id IS NULL) AS pinnedNotes, - SUM(archived = 1 && share_user_id IS NULL) AS archivedNotes, - SUM(encrypted = 1) AS encryptedNotes, - SUM(share_user_id IS NULL) AS totalNotes, - SUM(share_user_id != ?) AS sharedToNotes, - SUM( (share_user_id != ? && opened IS null) || (share_user_id != ? && note_raw_text.updated > opened) ) AS unreadNotes + SUM(archived = 1 && share_user_id IS NULL && trashed = 0) AS archivedNotes, + SUM(trashed = 1) AS trashedNotes, + SUM(share_user_id IS NULL && trashed = 0) AS totalNotes, + SUM(share_user_id != ? && trashed = 0) AS sharedToNotes, + SUM( (share_user_id != ? && opened IS null && trashed = 0) || (share_user_id != ? && note_raw_text.updated > opened && trashed = 0) ) AS unreadNotes FROM note LEFT JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id) WHERE user_id = ?`, [userId, userId, userId, userId]) .then( (rows, fields) => { Object.assign(countTotals, rows[0][0]) //combine results - + // + // @TODO - Figured out if this is useful + // We want, notes shared with user and note user has shared + // return db.promise().query( `SELECT count(id) AS sharedFromNotes - FROM note WHERE share_user_id = ?`, [userId] + FROM note WHERE shared = 2 AND user_id = ? AND trashed = 0`, [userId] ) }) .then( (rows, fields) => { @@ -228,18 +230,12 @@ User.generateMasterKey = (userId, password) => { 'INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);', [userId, salt, encryptedMasterPassword, created] ) - .then((rows, fields)=>{ - return Note.encryptEveryNote(userId, masterPassword) - }) .then(results => { - return new Promise((resolve, reject) => { resolve(true) }) + return resolve(true) }) } }) - .then((rows, fields) => { - return resolve(true) - }) .catch(error => { console.log('Create Master Password Error') console.log(error) @@ -261,6 +257,10 @@ User.getMasterKey = (userId, password) => { const row = rows[0][0] + if(!rows[0] || rows[0].length == 0 || rows[0][0] == undefined){ + return reject('Row or salt or something not set') + } + const masterKey = cs.decrypt(password, row['salt'], row['key']) if(masterKey == null){ @@ -370,25 +370,56 @@ User.getByUserName = (username) => { User.deleteUser = (userId, password) => { //Verify user is correct by decryptig master key with password + + let deletePromises = [] - //Delete user, all notes, all keys + let noteDelete = db.promise().query(` + DELETE note, note_raw_text + FROM note + JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id) + WHERE note.user_id = ? + `,[userId]) + deletePromises.push(noteDelete) + + let userDelete = db.promise().query(` + DELETE FROM user WHERE id = ? + `,[userId]) + deletePromises.push(userDelete) + + let tables = ['user_key', 'user_encrypted_search_index', 'attachment'] + tables.forEach(tableName => { + + const query = `DELETE FROM ${tableName} WHERE user_id = ?` + const deleteQuery = db.promise().query(query, [userId]) + deletePromises.push(deleteQuery) + }) + + return Promise.all(deletePromises) } -User.keyPairTest = (testUserName = 'genMan', password = '1') => { +User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => { return new Promise((resolve, reject) => { let masterKey = null let testUserId = null + + const randomUsername = Math.random().toString(36).substring(2, 15); + const randomPassword = '1' + User.login(testUserName, password) .then( ({ token, userId }) => { testUserId = userId - console.log('Test: Create/Login User - Pass') + + if(printResults) console.log('Test: Create/Login User '+testUserName+' - Pass') + return User.getMasterKey(testUserId, password) }) .then(newMasterKey => { masterKey = newMasterKey - console.log('Test: Generate/Decrypt Master Key - Pass') + + if(printResults) console.log('Test: Generate/Decrypt Master Key - Pass') + return User.generateKeypair(testUserId, masterKey) }) .then(({publicKey, privateKey}) => { @@ -400,13 +431,13 @@ User.keyPairTest = (testUserName = 'genMan', password = '1') => { const privateKeyEncrypted = crypto.privateEncrypt(privateKey, Buffer.from(privateKeyMessage, 'utf8')).toString('base64') const decryptedPrivate = crypto.publicDecrypt(publicKey, Buffer.from(privateKeyEncrypted, 'base64')) //Conver back to a string - console.log(decryptedPrivate.toString('utf8')) + if(printResults) console.log(decryptedPrivate.toString('utf8')) //Encrypt with public key const pubEncrMsc = crypto.publicEncrypt(publicKey, Buffer.from(publicKeyMessage, 'utf8')).toString('base64') const publicDeccryptMessage = crypto.privateDecrypt(privateKey, Buffer.from(pubEncrMsc, 'base64') ) //Convert it back to string - console.log(publicDeccryptMessage.toString('utf8')) + if(printResults) console.log(publicDeccryptMessage.toString('utf8')) resolve({testUserId, masterKey}) }) diff --git a/server/routes/noteController.js b/server/routes/noteController.js index cefb72e..d024045 100644 --- a/server/routes/noteController.js +++ b/server/routes/noteController.js @@ -1,7 +1,7 @@ var express = require('express') var router = express.Router() -let Notes = require('@models/Note') +let Note = require('@models/Note') let User = require('@models/User') let ShareNote = require('@models/ShareNote') @@ -22,36 +22,36 @@ router.use(function setUserId (req, res, next) { // Note actions // router.post('/get', function (req, res) { - Notes.get(userId, req.body.noteId, masterKey) + Note.get(userId, req.body.noteId, masterKey) .then( data => { res.send(data) }) }) router.post('/delete', function (req, res) { - Notes.delete(userId, req.body.noteId) + Note.delete(userId, req.body.noteId) .then( data => res.send(data) ) }) router.post('/create', function (req, res) { - Notes.create(req.io, userId, req.body.title, req.body.text, masterKey) + Note.create(userId, req.body.title, req.body.text, masterKey) .then( id => res.send({id}) ) }) router.post('/update', function (req, res) { - Notes.update(req.io, userId, req.body.noteId, req.body.text, req.body.title, req.body.color, req.body.pinned, req.body.archived, req.body.hash, masterKey) + Note.update(userId, req.body.noteId, req.body.text, req.body.title, req.body.color, req.body.pinned, req.body.archived, req.body.hash, masterKey) .then( id => res.send({id}) ) }) router.post('/search', function (req, res) { - Notes.search(userId, req.body.searchQuery, req.body.searchTags, req.body.fastFilters, masterKey) - .then( notesAndTags => { - res.send(notesAndTags) + Note.search(userId, req.body.searchQuery, req.body.searchTags, req.body.fastFilters, masterKey) + .then( NoteAndTags => { + res.send(NoteAndTags) }) }) router.post('/difftext', function (req, res) { - Notes.getDiffText(userId, req.body.noteId, req.body.text, req.body.updated) + Note.getDiffText(userId, req.body.noteId, req.body.text, req.body.updated) .then( fullDiffText => { //Response should be full diff text res.send(fullDiffText) @@ -59,7 +59,7 @@ router.post('/difftext', function (req, res) { }) router.post('/reindex', function (req, res) { - Notes.reindex(userId, masterKey) + Note.reindex(userId, masterKey) .then( data => { res.send(data) }) @@ -70,13 +70,19 @@ router.post('/reindex', function (req, res) { // Update single note attributes // router.post('/setpinned', function (req, res) { - Notes.setPinned(userId, req.body.noteId, req.body.pinned) + Note.setPinned(userId, req.body.noteId, req.body.pinned) .then( results => { res.send(results) }) }) router.post('/setarchived', function (req, res) { - Notes.setArchived(userId, req.body.noteId, req.body.archived) + Note.setArchived(userId, req.body.noteId, req.body.archived) + .then( results => { + res.send(results) + }) +}) +router.post('/settrashed', function (req, res) { + Note.setTrashed(userId, req.body.noteId, req.body.trashed) .then( results => { res.send(results) }) @@ -98,15 +104,13 @@ router.post('/shareadduser', function (req, res) { }) .then( ({success, shareUserId}) => { - //Emit update count event to user shared with - so they see the note in real time - req.io.to(shareUserId).emit('update_counts') - res.send(success) }) }) router.post('/shareremoveuser', function (req, res) { - ShareNote.removeUser(userId, req.body.noteId) + // (userId, noteId, shareNoteUserId, shareUserId, masterKey) + ShareNote.removeUserFromShared(userId, req.body.noteId, req.body.shareUserNoteId, masterKey) .then(results => res.send(results)) }) @@ -114,10 +118,10 @@ router.post('/shareremoveuser', function (req, res) { // // Testing Action // -//Reindex all notes. Not a very good function, not public +//Reindex all Note. Not a very good function, not public router.get('/reindex5yu43prchuj903mrc', function (req, res) { - Notes.migrateNoteTextToNewTable().then(status => { + Note.migrateNoteTextToNewTable().then(status => { return res.send(status) }) diff --git a/server/routes/quicknoteController.js b/server/routes/quicknoteController.js index c4687d7..bbf5008 100644 --- a/server/routes/quicknoteController.js +++ b/server/routes/quicknoteController.js @@ -24,7 +24,7 @@ router.post('/get', function (req, res) { //Push text to quick note router.post('/update', function (req, res) { - QuickNote.update(req.io, userId, req.body.pushText, masterKey) + QuickNote.update(userId, req.body.pushText, masterKey) .then( data => res.send(data) ) })