* 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:
		@@ -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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -72,13 +70,6 @@
 | 
				
			|||||||
			<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>
 | 
				
			||||||
						<br>
 | 
											<br>
 | 
				
			||||||
@@ -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 class="teeny-button"
 | 
				
			||||||
						</i>
 | 
													data-tooltip="Archive"
 | 
				
			||||||
 | 
													:data-tooltip="note.archived ? 'Un-Archive':'Archive' " 
 | 
				
			||||||
 | 
													data-inverted v-on:click="archiveNote">
 | 
				
			||||||
 | 
													<i class="archive icon" :class="{'green':note.archived}"></i>
 | 
				
			||||||
 | 
												</i>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												<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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<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>
 | 
					 | 
				
			||||||
						
 | 
											
 | 
				
			||||||
						<delete-button class="teeny-button" :note-id="note.id" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					</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,63 +14,54 @@ 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,17 +69,97 @@ Note.test = (userId, 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,
 | 
				
			||||||
@@ -904,16 +999,6 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
 | 
				
			|||||||
				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){
 | 
				
			||||||
				searchParams.push(textSearchIds)
 | 
									searchParams.push(textSearchIds)
 | 
				
			||||||
@@ -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){
 | 
				
			||||||
@@ -371,24 +371,55 @@ User.deleteUser = (userId, password) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	//Verify user is correct by decryptig master key with password
 | 
						//Verify user is correct by decryptig master key with password
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	//Delete user, all notes, all keys
 | 
						let deletePromises = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) )
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user