* Added error display to every axios server call

* Added better destroy of login token if invalid
* Block users from opening notes they don't own, note closes automatically
* Beefed up login and home page a little to make them more appealing
This commit is contained in:
Max G 2020-04-14 05:09:19 +00:00
parent 278b204b3b
commit 2b8f70b5fa
19 changed files with 114 additions and 23 deletions

View File

@ -203,6 +203,7 @@
}, 600) }, 600)
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to delete attachment') })
}, },
saveIt(){ saveIt(){
@ -221,6 +222,7 @@
//Save it, and don't think about it. //Save it, and don't think about it.
axios.post('/api/attachment/update', data) axios.post('/api/attachment/update', data)
.catch(error => { this.$bus.$emit('notification', 'Failed to Save') })
}, },
} }

View File

@ -73,6 +73,7 @@
}) })
.catch(results => { .catch(results => {
this.uploadStatusText = 0 this.uploadStatusText = 0
this.$bus.$emit('notification', 'Failed to Upload')
}) })
}, },
handleFileUpload() { handleFileUpload() {

View File

@ -257,7 +257,7 @@
}, },
data: function(){ data: function(){
return { return {
version: '1.0.2', version: '1.0.3',
username: '', username: '',
collapsed: false, collapsed: false,
mobile: false, mobile: false,
@ -334,6 +334,7 @@
this.disableNewNote = false this.disableNewNote = false
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
}, },
destroyLoginToken() { destroyLoginToken() {
this.$bus.$emit('notification', 'Logged Out') this.$bus.$emit('notification', 'Logged Out')

View File

@ -33,6 +33,7 @@
this.$store.dispatch('fetchAndUpdateUserTotals') this.$store.dispatch('fetchAndUpdateUserTotals')
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Delete Note') })
}, },
reset(){ reset(){
this.click = 0 this.click = 0

View File

@ -862,6 +862,13 @@
axios.post('/api/note/get', { 'noteId': this.noteid, 'password':this.hashedPass }) axios.post('/api/note/get', { 'noteId': this.noteid, 'password':this.hashedPass })
.then(response => { .then(response => {
//Block notes you don't have access to from opening
if(response.data === false){
this.$bus.$emit('notification', 'Invalid Note')
this.close(true)
return
}
//Set up local data //Set up local data
this.currentNoteId = this.noteid this.currentNoteId = this.noteid
this.rawTextId = response.data.rawTextId this.rawTextId = response.data.rawTextId
@ -918,6 +925,7 @@
}) })
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Open Note') })
} else { } else {
console.log('Could not fetch note') console.log('Could not fetch note')
} }
@ -1155,6 +1163,7 @@
this.startAutolockTimer() this.startAutolockTimer()
return resolve(true) return resolve(true)
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Save Note') })
}) })
}, },
checkForUpdatedNote(){ checkForUpdatedNote(){
@ -1183,6 +1192,7 @@
this.updated = data.updated this.updated = data.updated
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to get diff') })
} }
//Track visibility state //Track visibility state
@ -1208,7 +1218,14 @@
return hash; return hash;
}, },
close(){ close(force = false){
if(force){
this.$bus.$emit('close_active_note', {
position: this.position, noteId: this.noteid, modified: this.modified
})
return
}
this.save().then( result => { this.save().then( result => {

View File

@ -91,6 +91,7 @@
this.allTags = data.allTags this.allTags = data.allTags
this.noteTagIds = data.noteTagIds this.noteTagIds = data.noteTagIds
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Get Tags') })
}, },
isTagOnNote(id){ isTagOnNote(id){
for (let i = 0; i < this.noteTagIds.length; i++) { for (let i = 0; i < this.noteTagIds.length; i++) {
@ -170,6 +171,7 @@
vm.suggestions = response.data vm.suggestions = response.data
vm.selection = -1 //Nothing selected vm.selection = -1 //Nothing selected
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Get Suggested Tags') })
} }
}, 300) }, 300)
}, },
@ -226,6 +228,7 @@
//Trigger focus event to reload tag suggestions //Trigger focus event to reload tag suggestions
vm.onFocus() vm.onFocus()
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Add Tag') })
}, },
onFocus(){ onFocus(){
return return
@ -240,6 +243,7 @@
vm.suggestions = response.data vm.suggestions = response.data
vm.selection = -1 //Nothing selected vm.selection = -1 //Nothing selected
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Fetch Latest Tags') })
}, },
onKeyup(){ onKeyup(){
@ -268,6 +272,7 @@
.then(response => { .then(response => {
this.getTags() this.getTags()
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Remove Tag') })
}, },
clearSuggestions(){ clearSuggestions(){
this.suggestions = [] this.suggestions = []

View File

@ -172,6 +172,7 @@
.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') })
}, },
archiveNote(){ //toggleArchived() <- old name archiveNote(){ //toggleArchived() <- old name
let postData = {'archived': !this.note.archived, 'noteId':this.note.id} let postData = {'archived': !this.note.archived, 'noteId':this.note.id}
@ -187,6 +188,7 @@
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') })
}, },
toggleTags(state){ toggleTags(state){

View File

@ -78,6 +78,7 @@
.then( ({data}) => { .then( ({data}) => {
this.sharedWithUsers = data this.sharedWithUsers = data
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Load Shared') })
}, },
onRevokeAccess(noteId){ onRevokeAccess(noteId){
axios.post('/api/note/shareremoveuser', {'noteId':noteId}) axios.post('/api/note/shareremoveuser', {'noteId':noteId})
@ -87,6 +88,7 @@
this.loadShareList() this.loadShareList()
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Remove Share User') })
}, },
onKeyup(event){ onKeyup(event){
if(event.keyCode == 13){ if(event.keyCode == 13){
@ -105,6 +107,7 @@
this.$bus.$emit('notification', 'User not found') this.$bus.$emit('notification', 'User not found')
} }
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Add User') })
}, },
} }

View File

@ -100,6 +100,7 @@
} }
}) })
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Load Attachments') })
}, },
methods: { methods: {
onFileClick(file){ onFileClick(file){

View File

@ -194,6 +194,7 @@
//Grab the next batch //Grab the next batch
this.loadedAttachmentsOffset += results.data.length this.loadedAttachmentsOffset += results.data.length
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Attachments') })
}, },
} }
} }

View File

@ -11,7 +11,7 @@
<!-- Content copied from note --> <!-- Content copied from note -->
<!-- https://www.solidscribe.com/#/notes/open/552 --> <!-- https://www.solidscribe.com/#/notes/open/552 -->
<p><b>Quick Note</b><br></p><p>The Quick note feature was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p><p>All data pushed to the quick note can still be edited like a normal note.<br></p><p><br></p><p><b>Dark Theme</b><br></p><p>Dark theme was designed to minimize the amount of blue. Less blue entering your eyes is supposed to help you fall asleep.<br></p><p>Most things turn sepia and a filter is applied to images to make them more sepia.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p><p><br></p><p><b>Password Protected Notes</b><br></p><p>Note protected with a password are encrypted. This means the data is scrambled and unreadable unless the correct password is used to decrypt them.<br></p><p>If a password is forgotten, it can never be recovered. Passwords are not saved for encrypted notes. If you lose the password to a protected note, that note text is lost. <br></p><p>Only the text of the note is protected. Tags, Files attached to the note, and the title of the note are still visible without a password. You can not search text in a password protected note. But you can search by the title.<br></p><p><br></p><p><b>Links in notes</b><br></p><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. <br></p><p><br></p><p><b>Files in notes</b><br></p><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have the text pulled out so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time.<br></p><p><br></p><p><b>Deleting notes</b><br></p><p>When<b> </b>notes are deleted, none of the files related to the note are deleted. <br></p><p><br></p><p><b>Daily Backups</b><br></p><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or restoration a restoration procedure. <br></p> <p><b>Quick Note</b><br></p><p>The Quick note feature was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p><p>All data pushed to the quick note can still be edited like a normal note.<br></p><p><br></p><p><b>Dark Theme</b><br></p><p>Dark theme was designed to minimize the amount of blue. Less blue entering your eyes is supposed to help you fall asleep.<br></p><p>Most things turn sepia and a filter is applied to images to make them more sepia.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p><p><br></p><p><b>Password Protected Notes</b><br></p><p>Note protected with a password are encrypted. This means the data is scrambled and unreadable unless the correct password is used to decrypt them.<br></p><p>If a password is forgotten, it can never be recovered. Passwords are not saved for encrypted notes. If you lose the password to a protected note, that note text is lost. <br></p><p>Only the text of the note is protected. Tags, Files attached to the note, and the title of the note are still visible without a password. You can not search text in a password protected note. But you can search by the title.<br></p><p><br></p><p><b>Links in notes</b><br></p><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. <br></p><p><br></p><p><b>Files in notes</b><br></p><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have the text pulled out so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time.<br></p><p><br></p><p><b>Deleting notes</b><br></p><p>When<b> </b>notes are deleted, none of the files related to the note are deleted. <br></p><p><br></p><p><b>Daily Backups</b><br></p><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or a restoration procedure. <br></p>
<!-- content copied from note --> <!-- content copied from note -->
</div> </div>

View File

@ -13,6 +13,7 @@
} }
.logo-display { .logo-display {
width: 50%; width: 50%;
max-width: 450px;
} }
.lightly-padded { .lightly-padded {
margin-top: 10px; margin-top: 10px;
@ -75,10 +76,14 @@
margin-left: 0 !important; margin-left: 0 !important;
} }
.home-main img {
max-height: 400px !important;
}
</style> </style>
<template> <template>
<div class="lightly-padded"> <div class="lightly-padded home-main">
<div class="ui centered vertically divided stackable grid"> <div class="ui centered vertically divided stackable grid">
<div class="row hero fadeBg" :style="{ 'height':(height+'px') }"> <div class="row hero fadeBg" :style="{ 'height':(height+'px') }">
@ -128,9 +133,21 @@
</div> </div>
<div class="row">
<div class="eight wide middle aligned column">
<h2>Get Started. Only a username is required.</h2>
</div>
<div class="four wide center aligned column">
<router-link class="ui huge green labeled icon button" to="/login">
<i class="plug icon"></i>Register
</router-link>
</div>
</div>
<!-- set --> <!-- set -->
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<h2>Everyone has knowledge that need to be expressed</h2> <h2>Everyone has knowledge that need to be expressed</h2>
<h3>Utilize action potential to create notes by encoding raw brainwaves converted to written language</h3> <h3>Utilize action potential to create notes by encoding raw brainwaves converted to written language</h3>
</div> </div>
@ -140,7 +157,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/gardening.svg" alt="Pruning the mind garden"> <img loading="lazy" width="100%" src="/api/static/assets/marketing/gardening.svg" alt="Pruning the mind garden">
</div> </div>
<div class="six wide column"> <div class="six wide column">
@ -161,7 +178,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/solution.svg" alt="Hypercube of Solutions"> <img loading="lazy" width="100%" src="/api/static/assets/marketing/solution.svg" alt="Hypercube of Solutions">
</div> </div>
<div class="six wide column"> <div class="six wide column">
@ -172,7 +189,7 @@
<!-- set --> <!-- set -->
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<h2>Search your data</h2> <h2>Search your data</h2>
<h3>Type in a word and find that same word but somewhere else</h3> <h3>Type in a word and find that same word but somewhere else</h3>
</div> </div>
@ -182,7 +199,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/plan.svg" alt="Scheme for planetary destruction"> <img loading="lazy" width="100%" src="/api/static/assets/marketing/plan.svg" alt="Scheme for planetary destruction">
</div> </div>
<div class="six wide column"> <div class="six wide column">
@ -193,7 +210,7 @@
<!-- set --> <!-- set -->
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<h2>Space for Growth</h2> <h2>Space for Growth</h2>
<h3>Groom a clear path for new expressions and innovations. Elevate your being and lower your cholesterol</h3> <h3>Groom a clear path for new expressions and innovations. Elevate your being and lower your cholesterol</h3>
</div> </div>
@ -203,7 +220,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/onboarding.svg" alt="Shrunken man near giant tablet"> <img loading="lazy" width="100%" src="/api/static/assets/marketing/onboarding.svg" alt="Shrunken man near giant tablet">
</div> </div>
<div class="six wide column"> <div class="six wide column">
@ -214,7 +231,7 @@
<!-- set --> <!-- set -->
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<h2>Ice Cream</h2> <h2>Ice Cream</h2>
<h3>Get excited without all the screaming</h3> <h3>Get excited without all the screaming</h3>
</div> </div>
@ -224,7 +241,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<img loading="lazy" width="100%" src="/api/static/assets/marketing/secure.svg" alt="marketing mumbo jumbo"> <img loading="lazy" width="100%" src="/api/static/assets/marketing/secure.svg" alt="marketing mumbo jumbo">
</div> </div>
<div class="six wide column"> <div class="six wide column">
@ -234,7 +251,7 @@
</div> </div>
<div class="middle aligned centered row"> <div class="middle aligned centered row">
<div class="six wide column"> <div class="six wide right aligned column">
<h2>Freedom to unleash yourself</h2> <h2>Freedom to unleash yourself</h2>
<h3>Imagine an awakening of what could be</h3> <h3>Imagine an awakening of what could be</h3>
</div> </div>
@ -314,7 +331,7 @@ export default {
//Don't change hero banner on mobile //Don't change hero banner on mobile
if(!this.$store.getters.getIsUserOnMobile){ if(!this.$store.getters.getIsUserOnMobile){
let windowHeight = window.innerHeight let windowHeight = window.innerHeight
this.height = windowHeight - (windowHeight * 0.10) this.height = windowHeight - (windowHeight * 0.15)
} }
}, },

View File

@ -1,11 +1,26 @@
<template> <template>
<div class="ui basic segment no-fluf-segment"> <div class="ui basic segment no-fluf-segment">
<div class="ui grid"> <div class="ui stackable grid">
<div class="ui sixteen wide column">
<div class="sixteen wide center aligned column">
<h2>Login / Register</h2>
</div>
<div class="ui sixteen wide column">
<div class="ui text container">
<p><b>Create an account:</b> type in the username you want to use followed by the password.</p>
<div class="ui segment" v-on:keyup.enter="submit"> <div class="ui segment" v-on:keyup.enter="submit">
<h4 class="ui header">
<i class="plug icon"></i>
<div class="content">
To Register
<div class="sub header">Choose Any Username & password</div>
</div>
</h4>
<div class="ui large form"> <div class="ui large form">
<div class="field"> <div class="field">
<div class="ui input"> <div class="ui input">
@ -21,8 +36,8 @@
</div> </div>
</div> </div>
<p>You will remain logged in on this browser, until you log out.</p> <p>You will remain logged in on this browser, for 30 days or until you log out.</p>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -290,6 +290,14 @@
window.removeEventListener('hashchange', this.hashChangeAction) window.removeEventListener('hashchange', this.hashChangeAction)
document.removeEventListener('visibilitychange', this.visibiltyChangeAction) document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
this.$bus.$off('note_reload')
this.$bus.$off('close_active_note')
this.$bus.$off('update_single_note')
this.$bus.$off('note_deleted')
this.$bus.$off('update_fast_filters')
this.$bus.$off('update_search_term')
this.$bus.$off('open_note')
//We want to remove event listeners, but something here is messing them up and preventing ALL event listeners from working //We want to remove event listeners, but something here is messing them up and preventing ALL event listeners from working
// this.$off() // Remove all event listeners // this.$off() // Remove all event listeners
// this.$bus.$off() // this.$bus.$off()
@ -529,12 +537,14 @@
//Trigger section rebuild //Trigger section rebuild
this.rebuildNoteCategorise() this.rebuildNoteCategorise()
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Notes') })
}, },
searchAttachments(){ searchAttachments(){
axios.post('/api/attachment/textsearch', {'searchTerm':this.searchTerm}) axios.post('/api/attachment/textsearch', {'searchTerm':this.searchTerm})
.then(results => { .then(results => {
this.foundAttachments = results.data this.foundAttachments = results.data
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Attachments') })
}, },
search(showLoading = true, notesInNextLoad = null, mergeExisting = false){ search(showLoading = true, notesInNextLoad = null, mergeExisting = false){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -593,6 +603,7 @@
return resolve(true) return resolve(true)
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Notes') })
}) })
}, },
rebuildNoteCategorise(){ rebuildNoteCategorise(){
@ -692,6 +703,7 @@
this.commonTags = data this.commonTags = data
resolve(data) resolve(data)
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Fetch Tags') })
}) })
}, },
updateFastFilters(index){ updateFastFilters(index){

View File

@ -145,6 +145,7 @@
this.savedQuickNoteText = results.data.text this.savedQuickNoteText = results.data.text
this.quickNoteId = results.data.id this.quickNoteId = results.data.id
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Update Quick Note') })
}, },
getQuickNote (){ getQuickNote (){
axios.post('/api/quick-note/get') axios.post('/api/quick-note/get')
@ -152,6 +153,7 @@
this.savedQuickNoteText = results.data.text this.savedQuickNoteText = results.data.text
this.quickNoteId = results.data.id this.quickNoteId = results.data.id
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Fetch Quick Note') })
}, },
onPaste(event){ onPaste(event){

View File

@ -42,6 +42,7 @@
this.noteText = response.data.text this.noteText = response.data.text
}) })
.catch(error => { this.$bus.$emit('notification', 'Failed to Open Public Note') })
} }
} }
} }

View File

@ -134,8 +134,10 @@ export default new Vuex.Store({
commit('setUserTotals', data) commit('setUserTotals', data)
}) })
.catch( error => { .catch( error => {
commit('destroyLoginToken') if(error.response && error.response.status == 400){
location.reload() commit('destroyLoginToken')
location.reload()
}
}) })
} }
} }

View File

@ -55,7 +55,8 @@ io.on('connection', function(socket){
.then(userData => { .then(userData => {
socket.join(userData.id) socket.join(userData.id)
}).catch(error => { }).catch(error => {
console.log(error) //Don't add user to room if they are not logged in
// console.log(error)
}) })
}) })

View File

@ -404,6 +404,8 @@ Note.get = (userId, noteId, password = '') => {
note_raw_text.updated as updated, note_raw_text.updated as updated,
note_raw_text.decrypt_attempts_count, note_raw_text.decrypt_attempts_count,
note_raw_text.last_decrypted_date, note_raw_text.last_decrypted_date,
note.id,
note.user_id,
note.created, note.created,
note.pinned, note.pinned,
note.archived, note.archived,
@ -425,6 +427,11 @@ Note.get = (userId, noteId, password = '') => {
const rawTextId = noteData['rawTextId'] const rawTextId = noteData['rawTextId']
noteData.decrypted = true noteData.decrypted = true
//Block access to notes if invalid or user doesn't have access
if(!noteData || !noteData['user_id'] || noteData['user_id'] != userId || noteData['id'] != noteId){
return resolve(false)
}
//If this is not and encrypted note, pass decrypted true, skip encryption stuff //If this is not and encrypted note, pass decrypted true, skip encryption stuff
if(noteData.encrypted == 1){ if(noteData.encrypted == 1){