* 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
This commit is contained in:
parent
e87e8513bc
commit
5096e74a60
@ -70,9 +70,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="popup-body slide-in-bottom" v-on:click="dismiss" v-if="notifications.length > 0">
|
<div class="popup-body slide-in-bottom" v-on:click="dismiss" v-if="notifications.length > 0">
|
||||||
<div class="popup-row color-fade" v-for="item in notifications">
|
<div class="popup-row color-fade" v-for="item in notifications">
|
||||||
<i class="disabled angle left icon"></i>
|
|
||||||
<span>{{ item }}</span>
|
<span>{{ item }}</span>
|
||||||
<i class="disabled angle right icon"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -257,7 +257,7 @@
|
|||||||
},
|
},
|
||||||
data: function(){
|
data: function(){
|
||||||
return {
|
return {
|
||||||
version: '2.1.2',
|
version: '2.2.2',
|
||||||
username: '',
|
username: '',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
mobile: false,
|
mobile: false,
|
||||||
@ -329,7 +329,7 @@
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
||||||
if(response.data && response.data.id){
|
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.$bus.$emit('open_note', response.data.id)
|
||||||
this.disableNewNote = false
|
this.disableNewNote = false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span class="clickable" @click="confirmDelete()" v-if="click == 0" data-tooltip="Delete" data-inverted="" data-position="top right">
|
<span class="clickable" @click="confirmDelete()" v-if="click == 0" data-tooltip="Delete Forever" data-inverted="" data-position="top right">
|
||||||
<i class="trash alternate icon"></i>
|
<i class="trash alternate icon"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="clickable" @click="actuallyDelete()" @mouseleave="reset" v-if="click == 1" data-tooltip="Click again to delete." data-position="top right" data-inverted="">
|
<span class="clickable" @click="actuallyDelete()" @mouseleave="reset" v-if="click == 1" data-tooltip="Click again to delete." data-position="top right" data-inverted="">
|
||||||
|
@ -265,7 +265,7 @@
|
|||||||
updated: '',
|
updated: '',
|
||||||
shareUsername: null,
|
shareUsername: null,
|
||||||
diffNoteText: '',
|
diffNoteText: '',
|
||||||
statusText: 'Saved',
|
statusText: 'Saved.',
|
||||||
lastNoteHash: null,
|
lastNoteHash: null,
|
||||||
saveDebounce: null, //Prevent save from being called numerous times quickly
|
saveDebounce: null, //Prevent save from being called numerous times quickly
|
||||||
updated: 'Never',
|
updated: 'Never',
|
||||||
@ -306,16 +306,16 @@
|
|||||||
watch: {
|
watch: {
|
||||||
noteid:function(newVal, oldVal){
|
noteid:function(newVal, oldVal){
|
||||||
|
|
||||||
if(newVal == this.currentNoteId){
|
// if(newVal == this.currentNoteId){
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(newVal == oldVal){
|
// if(newVal == oldVal){
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.currentNoteId = newVal
|
// this.currentNoteId = newVal
|
||||||
this.loadNote(this.currentNoteId)
|
// this.loadNote(this.currentNoteId)
|
||||||
|
|
||||||
},
|
},
|
||||||
urlData(newVal, oldVal){
|
urlData(newVal, oldVal){
|
||||||
@ -388,17 +388,10 @@
|
|||||||
this.lastNoteHash = this.hashString(this.getText())
|
this.lastNoteHash = this.hashString(this.getText())
|
||||||
// console.log('hash on load', this.lastNoteHash)
|
// console.log('hash on load', this.lastNoteHash)
|
||||||
|
|
||||||
//focus on open, not on mobile, thats annoying
|
//focus on open, not on mobile, it causes the keyboard to pop up, thats annoying
|
||||||
if(!this.$store.getters.getIsUserOnMobile){
|
if(!this.$store.getters.getIsUserOnMobile){
|
||||||
// this.editor.focus()
|
this.editor.focus()
|
||||||
|
this.editor.moveCursorToEnd()
|
||||||
if(this.noteTitle.length == 0){
|
|
||||||
this.$refs.titleTextarea.focus()
|
|
||||||
} else {
|
|
||||||
this.editor.focus()
|
|
||||||
this.editor.moveCursorToEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Change button states on editor when element is active
|
//Change button states on editor when element is active
|
||||||
@ -812,11 +805,14 @@
|
|||||||
loadNote(noteId){
|
loadNote(noteId){
|
||||||
|
|
||||||
//Generate a random loading message
|
//Generate a random loading message
|
||||||
|
let mod = ['Gently','Calmly','Lovingly','Quickly','','','','','','','','','','','','','']
|
||||||
let doing = ['Loading','Loading','Getting','Fetching','Grabbing','Sequencing','Organizing','Untangling','Processing','Refining','Extracting','Fusing','Pruning','Expanding','Enlarging','Transfiguring','Quantizing','Ingratiating','Lumping']
|
let doing = ['Loading','Loading','Getting','Fetching','Grabbing','Sequencing','Organizing','Untangling','Processing','Refining','Extracting','Fusing','Pruning','Expanding','Enlarging','Transfiguring','Quantizing','Ingratiating','Lumping']
|
||||||
let thing = ['Note','Note','Note','Note','Data','Text','Document','Algorithm','Buffer','Client','Download','File','Frame','Graphics','Hardware','HTML','Interface','Logic','Mainframe','Memory','Media','Nodes','Network','Chaos']
|
let thing = ['Note','Note','Note','Note','Data','Text','Document','Algorithm','Buffer','Client','Download','File','Frame','Graphics','Hardware','HTML','Interface','Logic','Mainframe','Memory','Media','Nodes','Network','Chaos']
|
||||||
let p1 = doing[Math.floor(Math.random() * doing.length)]
|
|
||||||
let p2 = thing[Math.floor(Math.random() * thing.length)]
|
let p1 = mod[Math.floor(Math.random() * mod.length)]
|
||||||
this.loadingMessage = p1 + ' ' + p2
|
let p2 = doing[Math.floor(Math.random() * doing.length)]
|
||||||
|
let p3 = thing[Math.floor(Math.random() * thing.length)]
|
||||||
|
this.loadingMessage = `${p1} ${p2} ${p3}`
|
||||||
|
|
||||||
//Component is activated with NoteId in place, lookup text with associated ID
|
//Component is activated with NoteId in place, lookup text with associated ID
|
||||||
if(this.$store.getters.getLoggedIn){
|
if(this.$store.getters.getLoggedIn){
|
||||||
@ -1039,7 +1035,7 @@
|
|||||||
},
|
},
|
||||||
onKeyup(){
|
onKeyup(){
|
||||||
|
|
||||||
this.statusText = ''
|
this.statusText = 'Modded'
|
||||||
|
|
||||||
// this.diffText()
|
// this.diffText()
|
||||||
|
|
||||||
@ -1067,7 +1063,7 @@
|
|||||||
const currentNoteText = this.getText()
|
const currentNoteText = this.getText()
|
||||||
const currentHash = this.hashString( currentNoteText )
|
const currentHash = this.hashString( currentNoteText )
|
||||||
if( this.lastNoteHash == currentHash){
|
if( this.lastNoteHash == currentHash){
|
||||||
this.statusText = 'Saved'
|
this.statusText = 'Saved.'
|
||||||
return resolve(true)
|
return resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1090,7 +1086,7 @@
|
|||||||
|
|
||||||
this.statusText = 'Saving'
|
this.statusText = 'Saving'
|
||||||
axios.post('/api/note/update', postData).then( response => {
|
axios.post('/api/note/update', postData).then( response => {
|
||||||
this.statusText = 'Saved'
|
this.statusText = 'Saved.'
|
||||||
this.updated = Math.round((+new Date)/1000)
|
this.updated = Math.round((+new Date)/1000)
|
||||||
this.modified = true
|
this.modified = true
|
||||||
|
|
||||||
|
@ -29,12 +29,10 @@
|
|||||||
|
|
||||||
<!-- Title display -->
|
<!-- Title display -->
|
||||||
<span v-if="note.title.length > 0"
|
<span v-if="note.title.length > 0"
|
||||||
data-test-id="title"
|
|
||||||
class="big-text"><p>{{ note.title }}</p></span>
|
class="big-text"><p>{{ note.title }}</p></span>
|
||||||
|
|
||||||
<!-- Sub text display -->
|
<!-- Sub text display -->
|
||||||
<span v-if="note.subtext.length > 0 && !isShowingSearchResults()"
|
<span v-if="note.subtext.length > 0 && !isShowingSearchResults()"
|
||||||
data-test-id="subtext"
|
|
||||||
class="small-text"
|
class="small-text"
|
||||||
v-html="note.subtext"></span>
|
v-html="note.subtext"></span>
|
||||||
|
|
||||||
@ -71,13 +69,6 @@
|
|||||||
<!-- Toolbar on the bottom -->
|
<!-- Toolbar on the bottom -->
|
||||||
<div class="tool-bar" @click.self="cardClicked" v-if="!titleView">
|
<div class="tool-bar" @click.self="cardClicked" v-if="!titleView">
|
||||||
<div class="icon-bar">
|
<div class="icon-bar">
|
||||||
|
|
||||||
<!-- <span v-if="note.pinned == 1" data-position="top left" data-tooltip="Pinned" data-inverted>
|
|
||||||
<i class="green pin icon"></i>
|
|
||||||
</span>
|
|
||||||
<span v-if="note.archived == 1" data-position="top left" data-tooltip="Archived" data-inverted>
|
|
||||||
<i class="green archive icon"></i>
|
|
||||||
</span> -->
|
|
||||||
|
|
||||||
<span class="tags" v-if="note.tags">
|
<span class="tags" v-if="note.tags">
|
||||||
<span v-for="tag in (note.tags.split(','))" class="little-tag">{{ tag }}</span>
|
<span v-for="tag in (note.tags.split(','))" class="little-tag">{{ tag }}</span>
|
||||||
@ -90,24 +81,43 @@
|
|||||||
|
|
||||||
<span class="teeny-buttons" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
<span class="teeny-buttons" :class="{ 'hover-hide':(!$store.getters.getIsUserOnMobile) }">
|
||||||
|
|
||||||
<i class="teeny-button" data-tooltip="Tags" data-inverted v-on:click="toggleTags(true)">
|
<span v-if="!note.trashed">
|
||||||
<i class="tags icon"></i>
|
|
||||||
</i>
|
|
||||||
|
|
||||||
<i class="teeny-button"
|
<i class="teeny-button" data-tooltip="Tags" data-inverted v-on:click="toggleTags(true)">
|
||||||
data-tooltip="Archive"
|
<i class="tags icon"></i>
|
||||||
:data-tooltip="note.archived ? 'Un-Archive':'Archive' "
|
</i>
|
||||||
data-inverted v-on:click="archiveNote">
|
|
||||||
<i class="archive icon" :class="{'green':note.archived}"></i>
|
|
||||||
</i>
|
|
||||||
|
|
||||||
<i class="teeny-button"
|
<i class="teeny-button"
|
||||||
:data-tooltip="note.pinned ? 'Un-Pin':'Pin' "
|
data-tooltip="Archive"
|
||||||
data-inverted v-on:click="pinNote">
|
:data-tooltip="note.archived ? 'Un-Archive':'Archive' "
|
||||||
<i class="pin icon" :class="{'green':note.pinned}"></i>
|
data-inverted v-on:click="archiveNote">
|
||||||
</i>
|
<i class="archive icon" :class="{'green':note.archived}"></i>
|
||||||
|
</i>
|
||||||
|
|
||||||
<delete-button class="teeny-button" :note-id="note.id" />
|
<i class="teeny-button"
|
||||||
|
:data-tooltip="note.pinned ? 'Un-Pin':'Pin' "
|
||||||
|
data-inverted v-on:click="pinNote">
|
||||||
|
<i class="pin icon" :class="{'green':note.pinned}"></i>
|
||||||
|
</i>
|
||||||
|
|
||||||
|
<i class="teeny-button"
|
||||||
|
data-tooltip="Move to Trash"
|
||||||
|
data-inverted v-on:click="trashNote()">
|
||||||
|
<i class="trash icon"></i>
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Trash note options -->
|
||||||
|
<span v-if="note.trashed">
|
||||||
|
<i class="teeny-button"
|
||||||
|
data-tooltip="Un-Trash"
|
||||||
|
data-inverted v-on:click="trashNote()">
|
||||||
|
<i class="reply icon"></i>
|
||||||
|
</i>
|
||||||
|
<delete-button class="teeny-button" :note-id="note.id" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -185,7 +195,7 @@
|
|||||||
let postData = {'pinned': !this.note.pinned, 'noteId':this.note.id}
|
let postData = {'pinned': !this.note.pinned, 'noteId':this.note.id}
|
||||||
axios.post('/api/note/setpinned', postData)
|
axios.post('/api/note/setpinned', postData)
|
||||||
.then(data => {
|
.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') })
|
.catch(error => { this.$bus.$emit('notification', 'Failed to Pin Note') })
|
||||||
},
|
},
|
||||||
@ -197,20 +207,35 @@
|
|||||||
//Show message so no one worries where note went
|
//Show message so no one worries where note went
|
||||||
let message = 'Moved to Archive'
|
let message = 'Moved to Archive'
|
||||||
if(postData.archived != 1){
|
if(postData.archived != 1){
|
||||||
message = 'Move to main list'
|
message = 'Moved to main list'
|
||||||
}
|
}
|
||||||
this.$bus.$emit('notification', message)
|
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') })
|
.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){
|
toggleTags(state){
|
||||||
|
|
||||||
this.showTagSlideMenu = state
|
this.showTagSlideMenu = state
|
||||||
|
|
||||||
if(state == false){
|
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);
|
color: var(--text_color);
|
||||||
background-color: var(--background_color);
|
background-color: var(--background_color);
|
||||||
}
|
}
|
||||||
|
.subtext {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/*Strict font sizes for card display*/
|
/*Strict font sizes for card display*/
|
||||||
.small-text {
|
.small-text {
|
||||||
|
@ -80,8 +80,14 @@
|
|||||||
})
|
})
|
||||||
.catch(error => { this.$bus.$emit('notification', 'Failed to Load Shared') })
|
.catch(error => { this.$bus.$emit('notification', 'Failed to Load Shared') })
|
||||||
},
|
},
|
||||||
onRevokeAccess(noteId){
|
onRevokeAccess(sharedNoteId){
|
||||||
axios.post('/api/note/shareremoveuser', {'noteId':noteId})
|
|
||||||
|
const postData = {
|
||||||
|
'noteId': this.noteId,
|
||||||
|
'shareUserNoteId': sharedNoteId
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.post('/api/note/shareremoveuser', postData)
|
||||||
.then( ({data}) => {
|
.then( ({data}) => {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
if(data == true){
|
if(data == true){
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
<div class="two wide middle aligned center aligned column" v-on:click="menuOpen = false">
|
<div class="two wide middle aligned center aligned column" v-on:click="menuOpen = false">
|
||||||
<i class="grey close icon"></i>
|
<i class="grey close icon"></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sixteen wide middle aligned column" v-if="loadedTags.length == 0">
|
||||||
|
Tags added to Notes will appear here.
|
||||||
|
</div>
|
||||||
<div class="row hover-row" v-for="tag in loadedTags" v-on:click="onClick(tag.id)" :class="{'green':(activeTags[0] == tag.id)}">
|
<div class="row hover-row" v-for="tag in loadedTags" v-on:click="onClick(tag.id)" :class="{'green':(activeTags[0] == tag.id)}">
|
||||||
<div class="two wide center aligned column">
|
<div class="two wide center aligned column">
|
||||||
<i class="grey tag icon"></i>
|
<i class="grey tag icon"></i>
|
||||||
|
@ -82,10 +82,13 @@
|
|||||||
const username = response.data.username
|
const username = response.data.username
|
||||||
const masterKey = response.data.masterKey
|
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
|
//Redirect user to notes section after login
|
||||||
vm.$router.push('/notes')
|
this.$router.push('/notes')
|
||||||
} else {
|
} else {
|
||||||
// this.password = ''
|
// this.password = ''
|
||||||
this.$bus.$emit('notification', 'Incorrect Username or Password')
|
this.$bus.$emit('notification', 'Incorrect Username or Password')
|
||||||
|
@ -26,6 +26,10 @@
|
|||||||
<!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> -->
|
<!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="ui basic icon button shrinking" v-on:click="updateFastFilters(4)" v-if="$store.getters.totals && $store.getters.totals['trashedNotes'] > 0">
|
||||||
|
<i class="trash alternate outline icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<tag-display
|
<tag-display
|
||||||
:active-tags="searchTags"
|
:active-tags="searchTags"
|
||||||
v-on:tagClick="tagId => toggleTagFilter(tagId)"
|
v-on:tagClick="tagId => toggleTagFilter(tagId)"
|
||||||
@ -66,11 +70,23 @@
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 v-if="fastFilters['withLinks'] == 1">Notes with Links</h2>
|
<div v-if="fastFilters['onlyArchived'] == 1" class="sixteen wide column">
|
||||||
<h2 v-if="fastFilters['withTags'] == 1">Notes with Tags</h2>
|
<h2>Archived Notes</h2>
|
||||||
<h2 v-if="fastFilters['onlyArchived'] == 1">Archived Notes</h2>
|
</div>
|
||||||
<h2 v-if="fastFilters['onlyShowSharedNotes'] == 1">Shared Notes</h2>
|
|
||||||
<h2 v-if="fastFilters['onlyShowEncrypted'] == 1">Password Protected - No longer supported</h2>
|
<div class="sixteen wide column" v-if="fastFilters['onlyShowTrashed'] == 1">
|
||||||
|
<h2 >Trash
|
||||||
|
<span>({{ $store.getters.totals['trashedNotes'] }})</span>
|
||||||
|
<div class="ui right floated basic button" data-tooltip="This doesn't work yet">
|
||||||
|
<i class="poo storm icon"></i>
|
||||||
|
Empty Trash
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sixteen wide column" v-if="fastFilters['onlyShowSharedNotes'] == 1">
|
||||||
|
<h2>Shared Notes</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Note title card display -->
|
<!-- Note title card display -->
|
||||||
<div class="sixteen wide column">
|
<div class="sixteen wide column">
|
||||||
@ -124,15 +140,11 @@
|
|||||||
|
|
||||||
<input-notes
|
<input-notes
|
||||||
v-if="activeNoteId1 != null"
|
v-if="activeNoteId1 != null"
|
||||||
|
:key="'active_note_'+activeNoteId1"
|
||||||
:noteid="activeNoteId1"
|
:noteid="activeNoteId1"
|
||||||
:position="activeNote1Position"
|
:position="activeNote1Position"
|
||||||
:url-data="$route.params"
|
:url-data="$route.params"
|
||||||
ref="note1" />
|
ref="note1" />
|
||||||
<input-notes
|
|
||||||
v-if="activeNoteId2 != null"
|
|
||||||
:noteid="activeNoteId2"
|
|
||||||
:position="activeNote2Position"
|
|
||||||
ref="note2" />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -148,7 +160,7 @@
|
|||||||
'input-notes': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
|
'input-notes': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
|
||||||
|
|
||||||
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
|
'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,
|
'search-input': require('@/components/SearchInput.vue').default,
|
||||||
'attachment-display': require('@/components/AttachmentDisplayCard').default,
|
'attachment-display': require('@/components/AttachmentDisplayCard').default,
|
||||||
'counter':require('@/components/AnimatedCounterComponent.vue').default,
|
'counter':require('@/components/AnimatedCounterComponent.vue').default,
|
||||||
@ -205,7 +217,8 @@
|
|||||||
'shared': ['envelope outline', 'Received Notes'],
|
'shared': ['envelope outline', 'Received Notes'],
|
||||||
'sent': ['paper plane outline', 'Shared Notes'],
|
'sent': ['paper plane outline', 'Shared Notes'],
|
||||||
'notes': ['file','Notes'],
|
'notes': ['file','Notes'],
|
||||||
'highlights': ['paragraph', 'Found In Text']
|
'highlights': ['paragraph', 'Found In Text'],
|
||||||
|
'trashed': ['poop', 'Trashed Notes']
|
||||||
},
|
},
|
||||||
noteSections: {
|
noteSections: {
|
||||||
pinned: [],
|
pinned: [],
|
||||||
@ -213,7 +226,8 @@
|
|||||||
shared:[],
|
shared:[],
|
||||||
sent:[],
|
sent:[],
|
||||||
notes: [],
|
notes: [],
|
||||||
highlights: []
|
highlights: [],
|
||||||
|
trashed: []
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -223,35 +237,47 @@
|
|||||||
this.$parent.loginGateway()
|
this.$parent.loginGateway()
|
||||||
|
|
||||||
this.$io.on('new_note_created', noteId => {
|
this.$io.on('new_note_created', noteId => {
|
||||||
|
|
||||||
//Do not update note if its open
|
//Do not update note if its open
|
||||||
if(this.activeNoteId1 != noteId){
|
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
|
//Update title cards when new note text is saved
|
||||||
this.$io.on('new_note_text_saved', ({noteId, hash}) => {
|
this.$io.on('new_note_text_saved', ({noteId, hash}) => {
|
||||||
|
|
||||||
//Do not update note if its open
|
//Do not update note if its open
|
||||||
if(this.activeNoteId1 != noteId){
|
if(this.activeNoteId1 != noteId){
|
||||||
this.updateSingleNote(parseInt(noteId))
|
this.updateSingleNote(noteId)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//Update totals for app
|
//Update totals for app
|
||||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
|
|
||||||
|
//Close note event
|
||||||
this.$bus.$on('close_active_note', ({position, noteId, modified}) => {
|
this.$bus.$on('close_active_note', ({position, noteId, modified}) => {
|
||||||
|
|
||||||
this.closeNote(position)
|
this.closeNote()
|
||||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
|
||||||
if(modified){
|
if(modified){
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
this.updateSingleNote(parseInt(noteId))
|
this.updateSingleNote(parseInt(noteId))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.$bus.$on('update_single_note', (noteId) => {
|
// this.$bus.$on('update_single_note', (noteId) => {
|
||||||
this.updateSingleNote(noteId)
|
// this.updateSingleNote(noteId)
|
||||||
})
|
// })
|
||||||
|
|
||||||
this.$bus.$on('note_deleted', (noteId) => {
|
this.$bus.$on('note_deleted', (noteId) => {
|
||||||
//Remove deleted note from set, its deleted
|
//Remove deleted note from set, its deleted
|
||||||
@ -320,7 +346,7 @@
|
|||||||
|
|
||||||
this.$bus.$off('note_reload')
|
this.$bus.$off('note_reload')
|
||||||
this.$bus.$off('close_active_note')
|
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('note_deleted')
|
||||||
this.$bus.$off('update_fast_filters')
|
this.$bus.$off('update_fast_filters')
|
||||||
this.$bus.$off('update_search_term')
|
this.$bus.$off('update_search_term')
|
||||||
@ -364,30 +390,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
closeNote(position){
|
closeNote(position){
|
||||||
//One note open, close that note
|
this.activeNoteId1 = null
|
||||||
if(position == 0){
|
this.$router.push('/notes')
|
||||||
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
|
|
||||||
|
|
||||||
},
|
},
|
||||||
toggleTagFilter(tagId){
|
toggleTagFilter(tagId){
|
||||||
|
|
||||||
@ -479,6 +483,8 @@
|
|||||||
// @TODO Don't even trigger this if the note wasn't changed
|
// @TODO Don't even trigger this if the note wasn't changed
|
||||||
updateSingleNote(noteId){
|
updateSingleNote(noteId){
|
||||||
|
|
||||||
|
noteId = parseInt(noteId)
|
||||||
|
|
||||||
//Find local note, if it exists; continue
|
//Find local note, if it exists; continue
|
||||||
|
|
||||||
|
|
||||||
@ -656,33 +662,58 @@
|
|||||||
//Sort notes into defined sections
|
//Sort notes into defined sections
|
||||||
notes.forEach(note => {
|
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
|
//Show archived notes
|
||||||
if(note.archived == 1 && this.fastFilters.onlyArchived == 1){
|
if(this.fastFilters.onlyArchived == 1){
|
||||||
this.noteSections.archived.push(note)
|
|
||||||
|
if(note.pinned == 1 && note.archived == 1){
|
||||||
|
this.noteSections.pinned.push(note)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(note.archived == 1){
|
||||||
|
this.noteSections.archived.push(note)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(note.shareUsername != null){
|
if(note.archived == 1){
|
||||||
this.noteSections.shared.push(note)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Only show sent notes section if shared is selected
|
//Only show sent notes section if shared is selected
|
||||||
if(note.shared == 2 && this.fastFilters.onlyShowSharedNotes == 1){
|
if(this.fastFilters.onlyShowSharedNotes == 1){
|
||||||
this.noteSections.sent.push(note)
|
|
||||||
return
|
if(note.shared == 2){
|
||||||
}
|
this.noteSections.sent.push(note)
|
||||||
if(note.note_highlights.length > 0){
|
}
|
||||||
this.noteSections.highlights.push(note)
|
if(note.shareUsername != null){
|
||||||
|
this.noteSections.shared.push(note)
|
||||||
|
}
|
||||||
return
|
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
|
// Pinned notes are always first, they can appear in the archive
|
||||||
if(note.pinned == 1){
|
if(note.pinned == 1){
|
||||||
this.noteSections.pinned.push(note)
|
this.noteSections.pinned.push(note)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//If the note is not archived, push it.
|
|
||||||
if(note.archived != 1 && this.fastFilters.onlyArchived != 1){
|
//Push to default note section
|
||||||
this.noteSections.notes.push(note)
|
this.noteSections.notes.push(note)
|
||||||
}
|
|
||||||
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -692,14 +723,13 @@
|
|||||||
this.searchTerm = ''
|
this.searchTerm = ''
|
||||||
this.searchTags = []
|
this.searchTags = []
|
||||||
this.fastFilters = {}
|
this.fastFilters = {}
|
||||||
|
this.updateFastFilters(5)
|
||||||
this.foundAttachments = [] //Remove all attachments
|
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
|
//Load initial batch, then tags, then other batch
|
||||||
this.search(true, this.firstLoadBatchSize)
|
this.search(true, this.firstLoadBatchSize)
|
||||||
.then( () => {
|
.then( () => {
|
||||||
|
|
||||||
|
|
||||||
//Load a larger batch once first batch has loaded
|
//Load a larger batch once first batch has loaded
|
||||||
return this.search(false, this.batchSize, true)
|
return this.search(false, this.batchSize, true)
|
||||||
})
|
})
|
||||||
@ -711,6 +741,7 @@
|
|||||||
|
|
||||||
//clear out tags
|
//clear out tags
|
||||||
this.searchTags = []
|
this.searchTags = []
|
||||||
|
this.loadingInProgress = false
|
||||||
|
|
||||||
//A little hacky, brings user to notes page then filters on click
|
//A little hacky, brings user to notes page then filters on click
|
||||||
if(this.$route.name != 'Note Page'){
|
if(this.$route.name != 'Note Page'){
|
||||||
@ -725,7 +756,8 @@
|
|||||||
'withTags', // 'Only Show Notes with Tags'
|
'withTags', // 'Only Show Notes with Tags'
|
||||||
'onlyArchived', //'Only Show Archived Notes'
|
'onlyArchived', //'Only Show Archived Notes'
|
||||||
'onlyShowSharedNotes', //Only show shared notes
|
'onlyShowSharedNotes', //Only show shared notes
|
||||||
'onlyShowEncrypted',
|
'onlyShowTrashed',
|
||||||
|
'notesHome',
|
||||||
]
|
]
|
||||||
|
|
||||||
let filter = {}
|
let filter = {}
|
||||||
|
@ -123,6 +123,7 @@ export default new Vuex.Store({
|
|||||||
//Save all the totals for the user
|
//Save all the totals for the user
|
||||||
state.userTotals = totalsObject
|
state.userTotals = totalsObject
|
||||||
|
|
||||||
|
// console.log('-------------')
|
||||||
// Object.keys(totalsObject).forEach( key => {
|
// Object.keys(totalsObject).forEach( key => {
|
||||||
// console.log(key + ' -- ' + totalsObject[key])
|
// console.log(key + ' -- ' + totalsObject[key])
|
||||||
// })
|
// })
|
||||||
|
@ -37,11 +37,9 @@ var io = require('socket.io')(http, {
|
|||||||
path:'/socket'
|
path:'/socket'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make io accessible to our router
|
//Set socket IO as a global in the app
|
||||||
app.use(function(req,res,next){
|
global.SocketIo = io
|
||||||
req.io = io;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
io.on('connection', function(socket){
|
io.on('connection', function(socket){
|
||||||
|
|
||||||
@ -49,7 +47,7 @@ io.on('connection', function(socket){
|
|||||||
|
|
||||||
//When a user connects, add them to their own room
|
//When a user connects, add them to their own room
|
||||||
// This allows the server to emit events to that specific user
|
// 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 => {
|
socket.on('user_connect', token => {
|
||||||
Auth.decodeToken(token)
|
Auth.decodeToken(token)
|
||||||
.then(userData => {
|
.then(userData => {
|
||||||
@ -138,12 +136,14 @@ app.use(function(req, res, next){
|
|||||||
|
|
||||||
|
|
||||||
// Test Area
|
// Test Area
|
||||||
// -> right here
|
const printResults = true
|
||||||
// let UserTest = require('@models/User')
|
let UserTest = require('@models/User')
|
||||||
// let NoteTest = require('@models/Note')
|
let NoteTest = require('@models/Note')
|
||||||
// UserTest.keyPairTest()
|
UserTest.keyPairTest('genMan2', '1', printResults)
|
||||||
// .then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey))
|
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
||||||
// .then( message => { console.log(message) })
|
.then( message => {
|
||||||
|
if(printResults) console.log(message)
|
||||||
|
})
|
||||||
// Test Area
|
// Test Area
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +117,9 @@ Attachment.update = (userId, attachmentId, updatedText, noteId) => {
|
|||||||
|
|
||||||
Attachment.delete = (userId, attachmentId, urlDelete = false) => {
|
Attachment.delete = (userId, attachmentId, urlDelete = false) => {
|
||||||
|
|
||||||
|
let attachment = null
|
||||||
|
let noteExists = true
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
db.promise()
|
db.promise()
|
||||||
.query('SELECT * FROM attachment WHERE id = ? AND user_id = ? LIMIT 1', [attachmentId, userId])
|
.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)
|
return resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Pull data we want out of
|
attachment = rows[0][0]
|
||||||
let row = rows[0][0]
|
|
||||||
let url = row.url
|
return db.promise().query('SELECT count(id) as `exists` FROM note WHERE id = ?', [attachment.note_id])
|
||||||
const noteId = row.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 to delete file and thumbnail
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(filePath+row.file_location)
|
fs.unlinkSync(filePath+attachment.file_location)
|
||||||
} catch(err) { console.error('File Does not exist') }
|
} catch(err) { console.error('File Does not exist') }
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(filePath+'thumb_'+row.file_location)
|
fs.unlinkSync(filePath+'thumb_'+attachment.file_location)
|
||||||
} catch(err) { console.error('Thumbnail Does not exist') }
|
} catch(err) { console.error('Thumbnail Does not exist') }
|
||||||
|
|
||||||
//Do not delete link attachments, just hide them. They will be deleted if removed from note
|
//Do not delete link attachments, just hide them. They will be deleted if removed from note or if note is deleted
|
||||||
if(row.attachment_type == 1 && !urlDelete){
|
if(attachment.attachment_type == 1 && !urlDelete && noteExists){
|
||||||
db.promise()
|
db.promise()
|
||||||
.query(`UPDATE attachment SET visible = 0 WHERE id = ?`, [attachmentId])
|
.query(`UPDATE attachment SET visible = 0 WHERE id = ?`, [attachmentId])
|
||||||
.then((rows, fields) => { })
|
.then((rows, fields) => { })
|
||||||
|
@ -14,81 +14,152 @@ const crypto = require('crypto')
|
|||||||
const cs = require('@helpers/CryptoString')
|
const cs = require('@helpers/CryptoString')
|
||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const gm = require('gm')
|
const gm = require('gm')
|
||||||
|
|
||||||
Note.test = (userId, masterKey) => {
|
Note.test = (userId, masterKey, printResults) => {
|
||||||
return new Promise((resolve, reject) => {
|
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 => {
|
.then(newNoteId => {
|
||||||
|
|
||||||
console.log('Test: Create Note - Pass')
|
if(printResults) console.log('Test: Created Note -> ', newNoteId)
|
||||||
testNoteId = newNoteId
|
testNoteId = newNoteId
|
||||||
|
|
||||||
return Note.update
|
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(() => {
|
.then(() => {
|
||||||
|
|
||||||
console.log('Test: Update Note - Pass')
|
if(printResults) console.log('Test: Update Note '+testNoteId+' - 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')
|
|
||||||
|
|
||||||
return Note.get(userId, testNoteId, masterKey)
|
return Note.get(userId, testNoteId, masterKey)
|
||||||
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
||||||
console.log('Test: Open Shared Note - Pass')
|
if(printResults) console.log('Test: Open Updated Note - Pass')
|
||||||
|
|
||||||
return Note.update
|
|
||||||
(null, userId, testNoteId, 'Shared Update', 'Test Note beans Title', 0, 0, 0, 'hash', masterKey)
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
|
|
||||||
console.log('Test: Update Shared Note - Pass')
|
|
||||||
|
|
||||||
return Note.reindex(userId, masterKey)
|
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)
|
return Note.encryptedIndexSearch(userId, 'beans', null, masterKey)
|
||||||
|
|
||||||
})
|
})
|
||||||
.then(textSearchResults => {
|
.then(textSearchResults => {
|
||||||
|
|
||||||
if(textSearchResults['ids'] && textSearchResults['ids'].length >= 1){
|
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') }
|
} 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 => {
|
.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(`
|
db.promise().query(`
|
||||||
SELECT * FROM note
|
SELECT * FROM note
|
||||||
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
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) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
let foundNotes = rows[0]
|
let foundNotes = rows[0]
|
||||||
@ -164,7 +235,7 @@ Note.encryptEveryNote = (userId, masterKey) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Returns insertedId of new note
|
//Returns insertedId of new note
|
||||||
Note.create = (io, userId, noteTitle = '', noteText = '', masterKey) => {
|
Note.create = (userId, noteTitle = '', noteText = '', masterKey) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
if(userId == null || userId < 10){ reject('User Id required to create note') }
|
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
|
const rawTextId = rows[0].insertId
|
||||||
|
|
||||||
return db.promise()
|
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])
|
[userId, rawTextId, created, 0, snippet, snippetSalt])
|
||||||
})
|
})
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
if(io){
|
if(SocketIo){
|
||||||
io.to(userId).emit('new_note_created', rows[0].insertId)
|
SocketIo.to(userId).emit('new_note_created', rows[0].insertId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexing is done on save
|
// Indexing is done on save
|
||||||
@ -205,7 +276,8 @@ Note.create = (io, userId, noteTitle = '', noteText = '', masterKey) => {
|
|||||||
// Called when a note is close
|
// Called when a note is close
|
||||||
// Will attempt to reindex all notes that are flagged in database as not indexed
|
// Will attempt to reindex all notes that are flagged in database as not indexed
|
||||||
// Limit to 100 notes per batch
|
// 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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
if(!masterKey || masterKey.length == 0){
|
if(!masterKey || masterKey.length == 0){
|
||||||
@ -216,17 +288,37 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
let searchIndex = null
|
let searchIndex = null
|
||||||
let searchIndexSalt = null
|
let searchIndexSalt = null
|
||||||
let foundNotes = null
|
let foundNotes = null
|
||||||
|
let userPrivateKey = null
|
||||||
|
|
||||||
//First check if we have any notes to index
|
let User = require('@models/User')
|
||||||
db.promise().query(`
|
User.generateKeypair(userId, masterKey)
|
||||||
SELECT note.id, text, salt FROM note
|
.then(({publicKey, privateKey}) => {
|
||||||
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
|
userPrivateKey = privateKey
|
||||||
AND user_id = ? LIMIT 100`, [userId])
|
//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) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
//Halt execution if there are no new notes
|
//Halt execution if there are no new notes
|
||||||
foundNotes = rows[0]
|
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){
|
if(foundNotes.length == 0){
|
||||||
throw new Error('No new notes to index')
|
throw new Error('No new notes to index')
|
||||||
}
|
}
|
||||||
@ -244,9 +336,9 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
|
|
||||||
//Select all user notes to recreate index
|
//Select all user notes to recreate index
|
||||||
return db.promise().query(`
|
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
|
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) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
foundNotes = rows[0]
|
foundNotes = rows[0]
|
||||||
@ -276,6 +368,10 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
})
|
})
|
||||||
.then(rawSearchIndex => {
|
.then(rawSearchIndex => {
|
||||||
|
|
||||||
|
if(rawSearchIndex == null){
|
||||||
|
throw new Error('Search Index Not Found/Decrypted')
|
||||||
|
}
|
||||||
|
|
||||||
searchIndex = rawSearchIndex
|
searchIndex = rawSearchIndex
|
||||||
|
|
||||||
//Remove all instances of IDs from text
|
//Remove all instances of IDs from text
|
||||||
@ -288,7 +384,6 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
const removeDoubles = new RegExp(',,',"g")
|
const removeDoubles = new RegExp(',,',"g")
|
||||||
// const removeTrail = new RegExp(',]',"g")
|
// const removeTrail = new RegExp(',]',"g")
|
||||||
|
|
||||||
|
|
||||||
searchIndex = searchIndex
|
searchIndex = searchIndex
|
||||||
.replace(removeId, '')
|
.replace(removeId, '')
|
||||||
.replace(removeDoubles, ',')
|
.replace(removeDoubles, ',')
|
||||||
@ -319,7 +414,17 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
return resolve(true)
|
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 =
|
const rawText =
|
||||||
ProcessText.removeHtml(noteHtml) //Remove HTML
|
ProcessText.removeHtml(noteHtml) //Remove HTML
|
||||||
@ -356,8 +461,6 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
})
|
})
|
||||||
.then(rawSearchIndex => {
|
.then(rawSearchIndex => {
|
||||||
|
|
||||||
// console.log('All notes indexed')
|
|
||||||
|
|
||||||
const created = Math.round((+new Date)/1000)
|
const created = Math.round((+new Date)/1000)
|
||||||
const jsonSearchIndex = JSON.stringify(searchIndex)
|
const jsonSearchIndex = JSON.stringify(searchIndex)
|
||||||
const encryptedJsonIndex = cs.encrypt(masterKey, searchIndexSalt, jsonSearchIndex)
|
const encryptedJsonIndex = cs.encrypt(masterKey, searchIndexSalt, jsonSearchIndex)
|
||||||
@ -377,58 +480,14 @@ Note.reindex = (userId, masterKey) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.log('Reindex Error')
|
console.log('Reindex Error:')
|
||||||
console.log(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
|
// 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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const now = Math.round((+new Date)/1000)
|
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
|
//Re-encrypt for other user
|
||||||
const updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet)
|
const updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet)
|
||||||
db.promise().query('UPDATE note SET snippet = ? WHERE id = ?', [updatedSnippet, otherNote.id])
|
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) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
if(io){
|
if(SocketIo){
|
||||||
io.to(userId).emit('new_note_text_saved', {noteId, hash})
|
SocketIo.to(userId).emit('new_note_text_saved', {noteId, hash})
|
||||||
}
|
}
|
||||||
|
|
||||||
//Async attachment reindex
|
//Async attachment reindex
|
||||||
Attachment.scanTextForWebsites(io, userId, noteId, noteText)
|
Attachment.scanTextForWebsites(SocketIo, userId, noteId, noteText)
|
||||||
|
|
||||||
//Send back updated response
|
//Send back updated response
|
||||||
resolve(rows[0])
|
resolve(rows[0])
|
||||||
@ -519,12 +580,14 @@ Note.setPinned = (userId, noteId, pinnedBoolean) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const pinned = pinnedBoolean ? 1:0
|
const pinned = pinnedBoolean ? 1:0
|
||||||
|
const now = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
//Update other note attributes
|
//Update other note attributes
|
||||||
return db.promise()
|
return db.promise()
|
||||||
.query('UPDATE note SET pinned = ? WHERE id = ? AND user_id = ? LIMIT 1',
|
.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, noteId, userId])
|
[pinned, now, noteId, userId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
SocketIo.to(userId).emit('note_attribute_modified', noteId)
|
||||||
resolve(true)
|
resolve(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -534,12 +597,31 @@ Note.setArchived = (userId, noteId, archivedBoolead) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const archived = archivedBoolead ? 1:0
|
const archived = archivedBoolead ? 1:0
|
||||||
|
const now = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
//Update other note attributes
|
//Update other note attributes
|
||||||
return db.promise()
|
return db.promise()
|
||||||
.query('UPDATE note SET archived = ? WHERE id = ? AND user_id = ? LIMIT 1',
|
.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, noteId, userId])
|
[archived, now, noteId, userId])
|
||||||
.then((rows, fields) => {
|
.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)
|
resolve(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -548,7 +630,7 @@ Note.setArchived = (userId, noteId, archivedBoolead) => {
|
|||||||
//
|
//
|
||||||
// Delete a note and all its remaining parts
|
// Delete a note and all its remaining parts
|
||||||
//
|
//
|
||||||
Note.delete = (userId, noteId) => {
|
Note.delete = (userId, noteId, masterKey = null) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -591,10 +673,10 @@ Note.delete = (userId, noteId) => {
|
|||||||
return db.promise()
|
return db.promise()
|
||||||
.query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId, userId])
|
.query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId, userId])
|
||||||
})
|
})
|
||||||
.then((rows, fields) => {
|
.then( results => {
|
||||||
// Delete search index
|
// Delete hidden attachments for this note (files for attachment are already gone)
|
||||||
return db.promise()
|
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) => {
|
.then((rows, fields) => {
|
||||||
// delete tags
|
// delete tags
|
||||||
@ -603,7 +685,11 @@ Note.delete = (userId, noteId) => {
|
|||||||
})
|
})
|
||||||
.then((rows, fields) => {
|
.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])
|
db.promise().query('SELECT id FROM note WHERE note_raw_text_id = ?',[rawTextId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
if(rows[0].length == 1){
|
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.created,
|
||||||
note.pinned,
|
note.pinned,
|
||||||
note.archived,
|
note.archived,
|
||||||
|
note.trashed,
|
||||||
note.color,
|
note.color,
|
||||||
note.encrypted_share_password_key,
|
note.encrypted_share_password_key,
|
||||||
count(distinct attachment.id) as attachment_count,
|
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,
|
count(distinct attachment.id) as attachment_count,
|
||||||
note.pinned,
|
note.pinned,
|
||||||
note.archived,
|
note.archived,
|
||||||
note.encrypted,
|
note.trashed,
|
||||||
GROUP_CONCAT(DISTINCT tag.text) as tags,
|
GROUP_CONCAT(DISTINCT tag.text) as tags,
|
||||||
GROUP_CONCAT(DISTINCT attachment.file_location) as thumbs,
|
GROUP_CONCAT(DISTINCT attachment.file_location) as thumbs,
|
||||||
shareUser.username as shareUsername,
|
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)
|
LEFT JOIN user as shareUser ON (note.share_user_id = shareUser.id)
|
||||||
WHERE note.user_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 text search returned results, limit search to those ids
|
||||||
if(textSearchIds.length > 0){
|
if(textSearchIds.length > 0){
|
||||||
@ -921,18 +1006,13 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
|
|||||||
searchAllNotes = true
|
searchAllNotes = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If Specific ID's are being searched, search ALL notes
|
||||||
if(fastFilters.noteIdSet && fastFilters.noteIdSet.length > 0){
|
if(fastFilters.noteIdSet && fastFilters.noteIdSet.length > 0){
|
||||||
searchParams.push(fastFilters.noteIdSet)
|
searchParams.push(fastFilters.noteIdSet)
|
||||||
noteSearchQuery += ' AND note.id IN (?)'
|
noteSearchQuery += ' AND note.id IN (?)'
|
||||||
searchAllNotes = true
|
searchAllNotes = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//Encrypted Note
|
|
||||||
if(fastFilters.onlyShowEncrypted == 1){
|
|
||||||
noteSearchQuery += ' AND encrypted = 1'
|
|
||||||
searchAllNotes = true
|
|
||||||
}
|
|
||||||
|
|
||||||
//If tags are passed, use those tags in search
|
//If tags are passed, use those tags in search
|
||||||
if(searchTags.length > 0){
|
if(searchTags.length > 0){
|
||||||
searchParams.push(searchTags)
|
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
|
//Show archived notes, only if fast filter is set, default to not archived
|
||||||
if(searchAllNotes == false){
|
if(searchAllNotes == false){
|
||||||
if(fastFilters.onlyArchived == 1){
|
|
||||||
noteSearchQuery += ' AND note.archived = 1' //Show Archived
|
if(fastFilters.notesHome == 1){
|
||||||
} else {
|
noteSearchQuery += ' AND note.archived = 0 AND note.trashed = 0 AND note.share_user_id IS NULL'
|
||||||
noteSearchQuery += ' AND note.archived = 0' //Exclude archived
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
//Finish up note query
|
||||||
noteSearchQuery += ' GROUP BY note.id'
|
noteSearchQuery += ' GROUP BY note.id'
|
||||||
|
|
||||||
//Only show notes with Tags
|
//Only show notes with Tags
|
||||||
if(fastFilters.withTags == 1){
|
if(fastFilters.withTags == 1){
|
||||||
returnTagResults = true
|
|
||||||
noteSearchQuery += ' HAVING tag_count > 0'
|
noteSearchQuery += ' HAVING tag_count > 0'
|
||||||
}
|
}
|
||||||
//Only show notes with links
|
//Only show notes with links
|
||||||
if(fastFilters.withLinks == 1){
|
if(fastFilters.withLinks == 1){
|
||||||
returnTagResults = true
|
|
||||||
noteSearchQuery += ' HAVING attachment_count > 0'
|
noteSearchQuery += ' HAVING attachment_count > 0'
|
||||||
}
|
}
|
||||||
//Only show archived notes
|
//Only show archived notes
|
||||||
if(fastFilters.onlyArchived == 1){
|
// if(fastFilters.onlyArchived == 1){
|
||||||
returnTagResults = true
|
// noteSearchQuery += ' HAVING note.archived = 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.
|
// 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 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
|
const limitOffset = parseInt(fastFilters.limitOffset, 10) || 0 //Either parse int, or use zero
|
||||||
|
|
||||||
|
|
||||||
// console.log(` LIMIT ${limitOffset}, ${limitSize}`)
|
|
||||||
noteSearchQuery += ` LIMIT ${limitOffset}, ${limitSize}`
|
noteSearchQuery += ` LIMIT ${limitOffset}, ${limitSize}`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,19 +1100,14 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
|
|||||||
.query(noteSearchQuery, searchParams)
|
.query(noteSearchQuery, searchParams)
|
||||||
.then((noteRows, noteFields) => {
|
.then((noteRows, noteFields) => {
|
||||||
|
|
||||||
//Current note key may change, default to master key
|
|
||||||
let currentNoteKey = masterKey
|
|
||||||
|
|
||||||
//Push all notes
|
//Push all notes
|
||||||
returnData['notes'] = noteRows[0]
|
returnData['notes'] = noteRows[0]
|
||||||
|
|
||||||
//pull out all note ids so we can fetch all tags for those notes
|
//pull out all note ids so we can fetch all tags for those notes
|
||||||
let noteIds = []
|
|
||||||
returnData['notes'].forEach(note => {
|
returnData['notes'].forEach(note => {
|
||||||
|
|
||||||
//Grab note ID for finding tags
|
//Current note key may change, default to master key
|
||||||
noteIds.push(note.id)
|
let currentNoteKey = masterKey
|
||||||
|
|
||||||
|
|
||||||
//Shared notes use encrypted key - decrypt key then decrypt note
|
//Shared notes use encrypted key - decrypt key then decrypt note
|
||||||
const encryptedShareKey = note.encrypted_share_password_key
|
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
|
//Clear out note.text before sending it to front end, its being used in title and subtext
|
||||||
delete note.snippet
|
delete note.snippet
|
||||||
delete note.salt
|
delete note.salt
|
||||||
|
delete note.encrypted_share_password_key
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ QuickNote.makeUrlLink = (inputText) => {
|
|||||||
return replacedText;
|
return replacedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuickNote.update = (io, userId, pushText, masterKey) => {
|
QuickNote.update = (userId, pushText, masterKey) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
let finalId = null
|
let finalId = null
|
||||||
@ -91,7 +91,7 @@ QuickNote.update = (io, userId, pushText, masterKey) => {
|
|||||||
|
|
||||||
finalText += broken
|
finalText += broken
|
||||||
|
|
||||||
return Note.create(io, userId, 'Quick Note', finalText, masterKey)
|
return Note.create(userId, 'Quick Note', finalText, masterKey)
|
||||||
.then(insertedId => {
|
.then(insertedId => {
|
||||||
finalId = insertedId
|
finalId = insertedId
|
||||||
return db.promise().query('UPDATE note SET quick_note = 1 WHERE id = ? AND user_id = ?',[insertedId, userId])
|
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)
|
finalText += (broken + noteObject.text)
|
||||||
finalId = noteObject.id
|
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 => {
|
.then( saveResults => {
|
||||||
|
@ -89,7 +89,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
|||||||
const encryptedSharedKey = crypto.publicEncrypt(publicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64')
|
const encryptedSharedKey = crypto.publicEncrypt(publicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64')
|
||||||
|
|
||||||
//Update note snippet for current user with public key encoded snippet
|
//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])
|
[encryptedSnippet, sharedNoteSnippetSalt, encryptedSharedKey, noteId, userId])
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -116,8 +116,13 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
|||||||
})
|
})
|
||||||
.then((rows, fields) => {
|
.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
|
let success = true
|
||||||
return resolve({success, shareUserId})
|
return resolve({success, shareUserId, sharedUserNoteId})
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.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
|
// Get users who see a shared note
|
||||||
ShareNote.getUsers = (userId, rawTextId) => {
|
ShareNote.getUsers = (userId, rawTextId) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -168,7 +236,7 @@ ShareNote.removeUser = (userId, noteId) => {
|
|||||||
//Delete note entry for other user - remove users access
|
//Delete note entry for other user - remove users access
|
||||||
if(removeUserId && Number.isInteger(removeUserId)){
|
if(removeUserId && Number.isInteger(removeUserId)){
|
||||||
//Delete this users access to the note
|
//Delete this users access to the note
|
||||||
return Note.delete(removeUserId, noteId)
|
return Note.delete(removeUserId, noteId, masterKey)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -19,10 +19,8 @@ User.login = (username, password) => {
|
|||||||
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
//Pull out user data from database results
|
// Create New Account
|
||||||
const lookedUpUser = rows[0][0];
|
//
|
||||||
|
|
||||||
//User not found, create a new account with set data
|
|
||||||
if(rows[0].length == 0){
|
if(rows[0].length == 0){
|
||||||
User.create(lowerName, password)
|
User.create(lowerName, password)
|
||||||
.then( ({token, userId}) => {
|
.then( ({token, userId}) => {
|
||||||
@ -30,7 +28,13 @@ User.login = (username, password) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Login User
|
||||||
|
//
|
||||||
if(rows[0].length == 1){
|
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
|
//hash the password and check for a match
|
||||||
// const salt = new Buffer(lookedUpUser.salt, 'binary')
|
// const salt = new Buffer(lookedUpUser.salt, 'binary')
|
||||||
const salt = Buffer.from(lookedUpUser.salt, 'binary')
|
const salt = Buffer.from(lookedUpUser.salt, 'binary')
|
||||||
@ -102,37 +106,33 @@ User.create = (username, password) => {
|
|||||||
created: currentDate
|
created: currentDate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let userId = null
|
||||||
|
let newMasterKey = null
|
||||||
|
|
||||||
db.promise()
|
db.promise()
|
||||||
.query('INSERT INTO user SET ?', new_user)
|
.query('INSERT INTO user SET ?', new_user)
|
||||||
.then((rows, fields) => {
|
.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)
|
const token = Auth.createToken(userId, newMasterKey)
|
||||||
.then( result => User.getMasterKey(userId, password))
|
return resolve({token, userId})
|
||||||
.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')
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
reject('Username already in use.')
|
return reject('Username already in use.')
|
||||||
}//END user create
|
}//END user create
|
||||||
})
|
})
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
@ -149,22 +149,24 @@ User.getCounts = (userId) => {
|
|||||||
|
|
||||||
db.promise().query(
|
db.promise().query(
|
||||||
`SELECT
|
`SELECT
|
||||||
SUM(pinned = 1 && archived = 0 && share_user_id IS NULL) AS pinnedNotes,
|
SUM(archived = 1 && share_user_id IS NULL && trashed = 0) AS archivedNotes,
|
||||||
SUM(archived = 1 && share_user_id IS NULL) AS archivedNotes,
|
SUM(trashed = 1) AS trashedNotes,
|
||||||
SUM(encrypted = 1) AS encryptedNotes,
|
SUM(share_user_id IS NULL && trashed = 0) AS totalNotes,
|
||||||
SUM(share_user_id IS NULL) AS totalNotes,
|
SUM(share_user_id != ? && trashed = 0) AS sharedToNotes,
|
||||||
SUM(share_user_id != ?) AS sharedToNotes,
|
SUM( (share_user_id != ? && opened IS null && trashed = 0) || (share_user_id != ? && note_raw_text.updated > opened && trashed = 0) ) AS unreadNotes
|
||||||
SUM( (share_user_id != ? && opened IS null) || (share_user_id != ? && note_raw_text.updated > opened) ) AS unreadNotes
|
|
||||||
FROM note
|
FROM note
|
||||||
LEFT JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
LEFT JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
||||||
WHERE user_id = ?`, [userId, userId, userId, userId])
|
WHERE user_id = ?`, [userId, userId, userId, userId])
|
||||||
.then( (rows, fields) => {
|
.then( (rows, fields) => {
|
||||||
|
|
||||||
Object.assign(countTotals, rows[0][0]) //combine results
|
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(
|
return db.promise().query(
|
||||||
`SELECT count(id) AS sharedFromNotes
|
`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) => {
|
.then( (rows, fields) => {
|
||||||
@ -228,18 +230,12 @@ User.generateMasterKey = (userId, password) => {
|
|||||||
'INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);',
|
'INSERT INTO user_key (`user_id`, `salt`, `key`, `created`) VALUES (?, ?, ?, ?);',
|
||||||
[userId, salt, encryptedMasterPassword, created]
|
[userId, salt, encryptedMasterPassword, created]
|
||||||
)
|
)
|
||||||
.then((rows, fields)=>{
|
|
||||||
return Note.encryptEveryNote(userId, masterPassword)
|
|
||||||
})
|
|
||||||
.then(results => {
|
.then(results => {
|
||||||
return new Promise((resolve, reject) => { resolve(true) })
|
return resolve(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.then((rows, fields) => {
|
|
||||||
return resolve(true)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log('Create Master Password Error')
|
console.log('Create Master Password Error')
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@ -261,6 +257,10 @@ User.getMasterKey = (userId, password) => {
|
|||||||
|
|
||||||
const row = rows[0][0]
|
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'])
|
const masterKey = cs.decrypt(password, row['salt'], row['key'])
|
||||||
|
|
||||||
if(masterKey == null){
|
if(masterKey == null){
|
||||||
@ -370,25 +370,56 @@ User.getByUserName = (username) => {
|
|||||||
User.deleteUser = (userId, password) => {
|
User.deleteUser = (userId, password) => {
|
||||||
|
|
||||||
//Verify user is correct by decryptig master key with 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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
let masterKey = null
|
let masterKey = null
|
||||||
let testUserId = null
|
let testUserId = null
|
||||||
|
|
||||||
|
|
||||||
|
const randomUsername = Math.random().toString(36).substring(2, 15);
|
||||||
|
const randomPassword = '1'
|
||||||
|
|
||||||
User.login(testUserName, password)
|
User.login(testUserName, password)
|
||||||
.then( ({ token, userId }) => {
|
.then( ({ token, userId }) => {
|
||||||
testUserId = 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)
|
return User.getMasterKey(testUserId, password)
|
||||||
})
|
})
|
||||||
.then(newMasterKey => {
|
.then(newMasterKey => {
|
||||||
masterKey = 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)
|
return User.generateKeypair(testUserId, masterKey)
|
||||||
})
|
})
|
||||||
.then(({publicKey, privateKey}) => {
|
.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 privateKeyEncrypted = crypto.privateEncrypt(privateKey, Buffer.from(privateKeyMessage, 'utf8')).toString('base64')
|
||||||
const decryptedPrivate = crypto.publicDecrypt(publicKey, Buffer.from(privateKeyEncrypted, 'base64'))
|
const decryptedPrivate = crypto.publicDecrypt(publicKey, Buffer.from(privateKeyEncrypted, 'base64'))
|
||||||
//Conver back to a string
|
//Conver back to a string
|
||||||
console.log(decryptedPrivate.toString('utf8'))
|
if(printResults) console.log(decryptedPrivate.toString('utf8'))
|
||||||
|
|
||||||
//Encrypt with public key
|
//Encrypt with public key
|
||||||
const pubEncrMsc = crypto.publicEncrypt(publicKey, Buffer.from(publicKeyMessage, 'utf8')).toString('base64')
|
const pubEncrMsc = crypto.publicEncrypt(publicKey, Buffer.from(publicKeyMessage, 'utf8')).toString('base64')
|
||||||
const publicDeccryptMessage = crypto.privateDecrypt(privateKey, Buffer.from(pubEncrMsc, 'base64') )
|
const publicDeccryptMessage = crypto.privateDecrypt(privateKey, Buffer.from(pubEncrMsc, 'base64') )
|
||||||
//Convert it back to string
|
//Convert it back to string
|
||||||
console.log(publicDeccryptMessage.toString('utf8'))
|
if(printResults) console.log(publicDeccryptMessage.toString('utf8'))
|
||||||
|
|
||||||
resolve({testUserId, masterKey})
|
resolve({testUserId, masterKey})
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var express = require('express')
|
var express = require('express')
|
||||||
var router = express.Router()
|
var router = express.Router()
|
||||||
|
|
||||||
let Notes = require('@models/Note')
|
let Note = require('@models/Note')
|
||||||
let User = require('@models/User')
|
let User = require('@models/User')
|
||||||
let ShareNote = require('@models/ShareNote')
|
let ShareNote = require('@models/ShareNote')
|
||||||
|
|
||||||
@ -22,36 +22,36 @@ router.use(function setUserId (req, res, next) {
|
|||||||
// Note actions
|
// Note actions
|
||||||
//
|
//
|
||||||
router.post('/get', function (req, res) {
|
router.post('/get', function (req, res) {
|
||||||
Notes.get(userId, req.body.noteId, masterKey)
|
Note.get(userId, req.body.noteId, masterKey)
|
||||||
.then( data => {
|
.then( data => {
|
||||||
res.send(data)
|
res.send(data)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/delete', function (req, res) {
|
router.post('/delete', function (req, res) {
|
||||||
Notes.delete(userId, req.body.noteId)
|
Note.delete(userId, req.body.noteId)
|
||||||
.then( data => res.send(data) )
|
.then( data => res.send(data) )
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/create', function (req, res) {
|
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}) )
|
.then( id => res.send({id}) )
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/update', function (req, res) {
|
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}) )
|
.then( id => res.send({id}) )
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/search', function (req, res) {
|
router.post('/search', function (req, res) {
|
||||||
Notes.search(userId, req.body.searchQuery, req.body.searchTags, req.body.fastFilters, masterKey)
|
Note.search(userId, req.body.searchQuery, req.body.searchTags, req.body.fastFilters, masterKey)
|
||||||
.then( notesAndTags => {
|
.then( NoteAndTags => {
|
||||||
res.send(notesAndTags)
|
res.send(NoteAndTags)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/difftext', function (req, res) {
|
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 => {
|
.then( fullDiffText => {
|
||||||
//Response should be full diff text
|
//Response should be full diff text
|
||||||
res.send(fullDiffText)
|
res.send(fullDiffText)
|
||||||
@ -59,7 +59,7 @@ router.post('/difftext', function (req, res) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.post('/reindex', function (req, res) {
|
router.post('/reindex', function (req, res) {
|
||||||
Notes.reindex(userId, masterKey)
|
Note.reindex(userId, masterKey)
|
||||||
.then( data => {
|
.then( data => {
|
||||||
res.send(data)
|
res.send(data)
|
||||||
})
|
})
|
||||||
@ -70,13 +70,19 @@ router.post('/reindex', function (req, res) {
|
|||||||
// Update single note attributes
|
// Update single note attributes
|
||||||
//
|
//
|
||||||
router.post('/setpinned', function (req, res) {
|
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 => {
|
.then( results => {
|
||||||
res.send(results)
|
res.send(results)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
router.post('/setarchived', function (req, res) {
|
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 => {
|
.then( results => {
|
||||||
res.send(results)
|
res.send(results)
|
||||||
})
|
})
|
||||||
@ -98,15 +104,13 @@ router.post('/shareadduser', function (req, res) {
|
|||||||
})
|
})
|
||||||
.then( ({success, shareUserId}) => {
|
.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)
|
res.send(success)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/shareremoveuser', function (req, res) {
|
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))
|
.then(results => res.send(results))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -114,10 +118,10 @@ router.post('/shareremoveuser', function (req, res) {
|
|||||||
//
|
//
|
||||||
// Testing Action
|
// 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) {
|
router.get('/reindex5yu43prchuj903mrc', function (req, res) {
|
||||||
|
|
||||||
Notes.migrateNoteTextToNewTable().then(status => {
|
Note.migrateNoteTextToNewTable().then(status => {
|
||||||
return res.send(status)
|
return res.send(status)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ router.post('/get', function (req, res) {
|
|||||||
|
|
||||||
//Push text to quick note
|
//Push text to quick note
|
||||||
router.post('/update', function (req, res) {
|
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) )
|
.then( data => res.send(data) )
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user