-
-
{{ tag }}
@@ -90,24 +81,43 @@
-
-
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -185,7 +195,7 @@
let postData = {'pinned': !this.note.pinned, 'noteId':this.note.id}
axios.post('/api/note/setpinned', postData)
.then(data => {
- this.$bus.$emit('update_single_note', this.note.id)
+ // this.$bus.$emit('update_single_note', this.note.id)
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Pin Note') })
},
@@ -197,20 +207,35 @@
//Show message so no one worries where note went
let message = 'Moved to Archive'
if(postData.archived != 1){
- message = 'Move to main list'
+ message = 'Moved to main list'
}
this.$bus.$emit('notification', message)
- this.$bus.$emit('update_single_note', this.note.id)
+ // this.$bus.$emit('update_single_note', this.note.id)
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Archive Note') })
},
+ trashNote(){ //toggleArchived() <- old name
+ let postData = {'trashed': !this.note.trashed, 'noteId':this.note.id}
+ axios.post('/api/note/settrashed', postData)
+ .then(data => {
+
+ //Show message so no one worries where note went
+ let message = 'Moved to Trash'
+ if(postData.trashed == 0){
+ message = 'Moved to main list'
+ }
+ this.$bus.$emit('notification', message)
+
+ })
+ .catch(error => { this.$bus.$emit('notification', 'Failed to Trash Note') })
+ },
toggleTags(state){
this.showTagSlideMenu = state
if(state == false){
- this.$bus.$emit('update_single_note', this.note.id)
+ // this.$bus.$emit('update_single_note', this.note.id)
}
},
@@ -330,6 +355,10 @@
color: var(--text_color);
background-color: var(--background_color);
}
+ .subtext {
+ display: inline-block;
+ width: 100%;
+ }
/*Strict font sizes for card display*/
.small-text {
diff --git a/client/src/components/ShareNoteComponent.vue b/client/src/components/ShareNoteComponent.vue
index 963699b..fbe8ed5 100644
--- a/client/src/components/ShareNoteComponent.vue
+++ b/client/src/components/ShareNoteComponent.vue
@@ -80,8 +80,14 @@
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Load Shared') })
},
- onRevokeAccess(noteId){
- axios.post('/api/note/shareremoveuser', {'noteId':noteId})
+ onRevokeAccess(sharedNoteId){
+
+ const postData = {
+ 'noteId': this.noteId,
+ 'shareUserNoteId': sharedNoteId
+ }
+
+ axios.post('/api/note/shareremoveuser', postData)
.then( ({data}) => {
console.log(data)
if(data == true){
diff --git a/client/src/components/TagDisplayComponent.vue b/client/src/components/TagDisplayComponent.vue
index 8de15f6..0f154cd 100644
--- a/client/src/components/TagDisplayComponent.vue
+++ b/client/src/components/TagDisplayComponent.vue
@@ -25,6 +25,9 @@
+
+ Tags added to Notes will appear here.
+
diff --git a/client/src/pages/LoginPage.vue b/client/src/pages/LoginPage.vue
index f53cbbf..494057d 100644
--- a/client/src/pages/LoginPage.vue
+++ b/client/src/pages/LoginPage.vue
@@ -82,10 +82,13 @@
const username = response.data.username
const masterKey = response.data.masterKey
- vm.$store.commit('setLoginToken', {token, username, masterKey})
+ this.$store.commit('setLoginToken', {token, username, masterKey})
+
+ //Setup socket io after user logs in
+ this.$io.emit('user_connect', token)
//Redirect user to notes section after login
- vm.$router.push('/notes')
+ this.$router.push('/notes')
} else {
// this.password = ''
this.$bus.$emit('notification', 'Incorrect Username or Password')
diff --git a/client/src/pages/NotesPage.vue b/client/src/pages/NotesPage.vue
index 5cc7bba..9b7cdfe 100644
--- a/client/src/pages/NotesPage.vue
+++ b/client/src/pages/NotesPage.vue
@@ -26,6 +26,10 @@
+
+
+
+
-
Notes with Links
-
Notes with Tags
-
Archived Notes
-
Shared Notes
-
Password Protected - No longer supported
+
+
Archived Notes
+
+
+
+
Trash
+ ({{ $store.getters.totals['trashedNotes'] }})
+
+
+ Empty Trash
+
+
+
+
+
+
Shared Notes
+
@@ -124,15 +140,11 @@
-
@@ -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) )
})