diff --git a/client/src/App.vue b/client/src/App.vue
index 06caf51..7624c89 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -23,22 +23,76 @@ export default {
data: function(){
return {
// loggedIn:
+ fetchingInProgress: false, //Prevent start getting token while fetch is in progress
+ blockUntilNextRequest: false //If token was just renewed, don't fetch more until next request
}
},
+
+ //Axios response interceptor
+ // - Gets new session tokens from server and uses them in app
beforeCreate: function(){
+ //Before all requests going out
+ axios.interceptors.request.use(
+ (config) => {
+
+ //Enable token fetching after another request is made
+ if(this.blockUntilNextRequest){
+ this.fetchingInProgress = false
+ this.blockUntilNextRequest = false
+ }
+
+ return config
+ },
+ (error) => {
+ return Promise.reject(error)
+ }
+ )
+
+ // Add a response interceptor, token can be renewed on every response
+ axios.interceptors.response.use(
+ (response) => {
+
+ if(typeof response.headers.remaininguses !== 'undefined'){
+
+ // console.log(response.headers.remaininguses)
+ //Look at remaining uses of token, if its less than five, request a new one
+ if(response.headers.remaininguses < 10 && !this.fetchingInProgress && !this.blockUntilNextRequest){
+ this.fetchingInProgress = true
+ const currentToken = localStorage.getItem('loginToken')
+ this.$io.emit('renew_session_token', currentToken)
+ }
+ }
+
+ return response
+ },
+ (error) => {
+ return Promise.reject(error)
+ }
+ )
+
//Puts token into state on page load
let token = localStorage.getItem('loginToken')
let username = localStorage.getItem('username')
- // const socket = io({ path:'/socket' });
- const socket = this.$io
- socket.on('connect', () => {
+ //
+ if(token && token.length > 0){
- this.$store.commit('setSocketIoSocket', socket.id)
+ //setup username display
+ this.$store.commit('setUsername', username)
- this.$io.emit('user_connect', token)
- })
+ //Set session token on every request if set
+ axios.defaults.headers.common['authorizationtoken'] = token
+
+ //Setup websockets into vue instance
+ const socket = this.$io
+ socket.on('connect', () => {
+
+ //Put user into personal event room for live note updates, etc
+ this.$io.emit('user_connect', token)
+ })
+ }
+
//Detect if user is on a mobile browser and set a flag in store
this.$store.commit('detectIsUserOnMobile')
@@ -49,11 +103,6 @@ export default {
this.$store.commit('toggleNightMode', themeNumber)
}
- //Put user data into global store on load
- if(token){
- this.$store.commit('setLoginToken', {token, username})
- }
-
},
mounted: function(){
@@ -63,6 +112,17 @@ export default {
this.$store.dispatch('fetchAndUpdateUserTotals')
})
+ this.$io.on('recievend_new_token', newToken => {
+
+ // console.log('Got a new token')
+
+ axios.defaults.headers.common['authorizationtoken'] = newToken
+ localStorage.setItem('loginToken', newToken)
+
+ //Disable getting new tokens until next request
+ this.blockUntilNextRequest = true
+ })
+
},
computed: {
loggedIn () {
diff --git a/client/src/assets/semantic-helper.css b/client/src/assets/semantic-helper.css
index bf28844..a538bf6 100644
--- a/client/src/assets/semantic-helper.css
+++ b/client/src/assets/semantic-helper.css
@@ -17,6 +17,10 @@
:root {
+
+ --main-accent: #16ab39;
+
+ /*theme colors */
--body_bg_color: #f5f6f7;
--small_element_bg_color: #fff;
--text_color: #3d3d3d;
diff --git a/client/src/components/GlobalSiteMenu.vue b/client/src/components/GlobalSiteMenu.vue
index 6a9d3e9..d699082 100644
--- a/client/src/components/GlobalSiteMenu.vue
+++ b/client/src/components/GlobalSiteMenu.vue
@@ -248,7 +248,7 @@
-
+
{{ version }}
@@ -267,7 +267,7 @@
},
data: function(){
return {
- version: '2.3.4',
+ version: '0',
username: '',
collapsed: false,
mobile: false,
@@ -277,6 +277,7 @@
}
},
beforeCreate: function(){
+
},
mounted: function(){
this.mobile = this.$store.getters.getIsUserOnMobile
@@ -288,6 +289,7 @@
if(this.loggedIn){
this.$store.dispatch('fetchAndUpdateUserTotals')
+ this.version = localStorage.getItem('currentVersion')
}
},
@@ -347,11 +349,12 @@
.catch(error => { this.$bus.$emit('notification', 'Failed to create note') })
},
destroyLoginToken() {
- axios.post('/api/user/logout').then( response => {
+ axios.post('/api/user/logout')
+ setTimeout(() => {
this.$bus.$emit('notification', 'Logged Out')
this.$store.commit('destroyLoginToken')
this.$router.push('/')
- })
+ }, 200)
},
toggleNightMode(){
this.$store.commit('toggleNightMode')
diff --git a/client/src/components/LoginFormComponent.vue b/client/src/components/LoginFormComponent.vue
index 9932e32..86117a2 100644
--- a/client/src/components/LoginFormComponent.vue
+++ b/client/src/components/LoginFormComponent.vue
@@ -98,13 +98,15 @@
//Login user if we have a valid token
if(data && data.token && data.token.length > 0){
- const token = data.token
- const username = this.username
+ //Set username to local session
+ this.$store.commit('setUsername', this.username)
- this.$store.commit('setLoginToken', {token, username})
+ const token = data.token
//Setup socket io after user logs in
+ axios.defaults.headers.common['authorizationtoken'] = token
this.$io.emit('user_connect', token)
+ localStorage.setItem('loginToken', token)
//Redirect user to notes section after login
this.$router.push('/notes')
@@ -113,7 +115,7 @@
register(){
if( this.username.length == 0 || this.password.length == 0 ){
- this.$bus.$emit('notification', 'Username and Password Required')
+ this.$bus.$emit('notification', 'Unable to Sign Up - Username and Password Required')
return
}
@@ -121,19 +123,19 @@
.then(({data}) => {
if(data == false){
- this.$bus.$emit('notification', 'Username already in use')
+ this.$bus.$emit('notification', 'Unable to Sign Up - Username already in use')
}
this.finalizeLogin(data)
})
.catch(error => {
- this.$bus.$emit('notification', 'Username already in use')
+ this.$bus.$emit('notification', 'Unable to Sign Up - Username already in use')
})
},
login(){
if( this.username.length == 0 || this.password.length == 0 ){
- this.$bus.$emit('notification', 'Username and Password Required')
+ this.$bus.$emit('notification', 'Unable to Login - Username and Password Required')
return
}
@@ -141,13 +143,13 @@
.then(({data}) => {
if(data == false){
- this.$bus.$emit('notification', 'Incorrect Username or Password')
+ this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
}
this.finalizeLogin(data)
})
.catch(error => {
- this.$bus.$emit('notification', 'Incorrect Username or Password')
+ this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
})
}
}
diff --git a/client/src/components/NoteInputPanel.vue b/client/src/components/NoteInputPanel.vue
index 204016a..4b54b09 100644
--- a/client/src/components/NoteInputPanel.vue
+++ b/client/src/components/NoteInputPanel.vue
@@ -170,13 +170,18 @@
-
+
+
{{ tag.text }}
+
{{ tag.text }}
+
+ Add Tag
+
@@ -195,7 +200,7 @@
/>
-
+
@@ -254,10 +259,10 @@
@@ -335,10 +340,6 @@
diffTextTimeout: null,
diffsApplied: null,
- //Fake Caret position and visibility
- caretShow: false,
- caretLeft: null,
- caretTop: null,
//Used to restore caret position
lastRange: null,
startOffset: 0,
@@ -541,9 +542,6 @@
if(!this.$store.getters.getIsUserOnMobile){
this.editor.focus()
this.editor.moveCursorToEnd()
- this.caretShow = true
- this.moveArtificialCaret()
-
this.fetchNoteTags() //Don't load tags on mobile
}
@@ -615,14 +613,7 @@
})
this.editor.addEventListener('keydown', event => {
- setTimeout(() => {
- if(event.keyCode == 32){
- this.caretLeft += 3
- }
- if(event.keyCode == 8){
- // this.caretLeft -= 3
- }
- }, 10)
+
})
//Bind event handlers
@@ -633,29 +624,10 @@
//Show and hide additional toolbars
this.editor.addEventListener('focus', e => {
- // this.caretShow = true
})
this.editor.addEventListener('blur', e => {
- // this.caretShow = false
})
},
- moveArtificialCaret(rect = null){
-
- //Lets not use the artificial caret for now
- return
-
- //If rect isn't present, grab by selection
- if(!rect || rect.left == 0){ //Left should always be greater than 0, because of a margin
- rect = this.editor.getCursorPosition()
- //Another way to get range
- // window.getSelection().getRangeAt(0)
- }
-
- const textArea = document.getElementById('text-box-container').getBoundingClientRect()
-
- this.caretLeft = (rect.left - textArea.left - 1)
- this.caretTop = (rect.top - textArea.top - 1 )
- },
openEditAttachment(){
this.$router.push('/attachments/note/'+this.currentNoteId)
@@ -845,7 +817,7 @@
// clearTimeout(this.editDebounce)
if(this.statusText == 'saving'){
- return reject(false)
+ return resolve(true)
}
//Don't save note if its hash doesn't change
@@ -1081,38 +1053,45 @@
.note-mini-tag-area {
position: fixed;
- width: 100px;
- left: calc(15% - 100px);
+ width: 120px;
+ left: calc(15% - 125px);
top: 46px;
bottom: 0;
- height: 500px;
+ height: calc(100vh - 55px);
z-index: 1000;
overflow-y: scroll;
scrollbar-width: none;
scrollbar-color: transparent transparent;
}
+ .note-mini-tag-area {
+ scrollbar-width: auto;
+ scrollbar-color: inherit inherit;
+ }
.subtle-tag {
display: inline-block;
width: 100%;
- padding: 2px 1px 2px 4px;
- margin: 0 0 2px;
+ padding: 1px 1px 1px 5px;
+ margin: 0 0 0;
border: 1px solid transparent;
border-right: none;
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
- color: var(--text_color);
+ border-radius: 3px;
background-color: transparent;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: color ease 0.3s, background ease 0.3s;
- font-size: 12px;
+ font-size: 11px;
cursor: pointer;
opacity: 0;
+ text-transform:capitalize;
}
.note-mini-tag-area:hover .subtle-tag {
opacity: 1;
- }
+ }
+ .note-mini-tag-area:hover .active-mini-tag {
+ background-color: var(--main-accent);
+ color: white;
+ }
.note-mini-tag-area:hover .subtle-tag:not(.active-mini-tag) {
border-right: none;
color: var(--text_color);
@@ -1120,9 +1099,9 @@
opacity: 1;
}
.active-mini-tag {
- opacity: 0.6;
- background-color: #16ab39;
- color: white;
+ opacity: 0.7;
+ background-color: var(--small_element_bg_color);
+ color: var(--text_color)
}
diff --git a/client/src/components/NoteTitleDisplayCard.vue b/client/src/components/NoteTitleDisplayCard.vue
index 7afe537..a011c0d 100644
--- a/client/src/components/NoteTitleDisplayCard.vue
+++ b/client/src/components/NoteTitleDisplayCard.vue
@@ -32,7 +32,7 @@
class="big-text">{{ note.title }}
-
@@ -49,15 +49,6 @@
-
-
-
-
-
@@ -179,12 +170,6 @@
return updated
},
- isShowingSearchResults(){
- if(this.note.note_highlights.length > 0 || this.note.attachment_highlights.length > 0 || this.note.tag_highlights.length > 0){
- return true
- }
- return false
- },
splitTags(text){
return text.split(',')
},
diff --git a/client/src/components/ShareNoteComponent.vue b/client/src/components/ShareNoteComponent.vue
index 6fb61c1..65a1eea 100644
--- a/client/src/components/ShareNoteComponent.vue
+++ b/client/src/components/ShareNoteComponent.vue
@@ -8,8 +8,12 @@
-
Enable Shared
-
Shared notes are different and junk.
+
Enable Sharing
+
+ - Shared notes can be read and edited by you and all shared users.
+ - Shared notes can only be shared by the creator of the note.
+
+
@@ -17,14 +21,10 @@
Remove Shared
Get Shareable URL
-
-
-
+
+
+
Public Link - this link can be disabled by turning off sharing
+
{{ sharedUrl }}
diff --git a/client/src/pages/NotesPage.vue b/client/src/pages/NotesPage.vue
index 5595bb3..08855ef 100644
--- a/client/src/pages/NotesPage.vue
+++ b/client/src/pages/NotesPage.vue
@@ -117,7 +117,7 @@
:data="note"
:title-view="titleView"
:currently-open="(activeNoteId1 == note.id || activeNoteId2 == note.id)"
- :key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length + '-' + note.tag_count + note.updated"
+ :key="note.id + note.color + '-' +note.title.length + '-' +note.subtext.length + '-' + note.tag_count + note.updated"
/>
diff --git a/client/src/stores/mainStore.js b/client/src/stores/mainStore.js
index 0a697df..4ea2105 100644
--- a/client/src/stores/mainStore.js
+++ b/client/src/stores/mainStore.js
@@ -6,30 +6,16 @@ Vue.use(Vuex);
export default new Vuex.Store({
state: {
- token: null,
username: null,
nightMode: false,
isUserOnMobile: false,
- isNoteSettingsOpen: false, //Little note settings pane
- socket: null,
userTotals: null,
},
mutations: {
- setLoginToken(state, userData){
-
- const username = userData.username
- const token = userData.token
-
- localStorage.removeItem('loginToken') //We only want one login token per computer
- localStorage.setItem('loginToken', token)
+ setUsername(state, username){
localStorage.removeItem('username') //We only want one login token per computer
localStorage.setItem('username', username)
-
- //Set default token to axios, every request will have header
- axios.defaults.headers.common['authorizationtoken'] = token
-
- state.token = token
state.username = username
},
destroyLoginToken(state){
@@ -37,8 +23,8 @@ export default new Vuex.Store({
//Remove login token from local storage and from headers
localStorage.removeItem('loginToken')
localStorage.removeItem('username')
+ localStorage.removeItem('currentVersion')
delete axios.defaults.headers.common['authorizationtoken']
- state.token = null
state.username = null
},
toggleNightMode(state, pastTheme){
@@ -125,6 +111,20 @@ export default new Vuex.Store({
//Save all the totals for the user
state.userTotals = totalsObject
+ //Set computer version from server
+ const currentVersion = localStorage.getItem('currentVersion')
+ if(currentVersion == null){
+ localStorage.setItem('currentVersion', totalsObject.currentVersion)
+ return
+ }
+
+ //If version is already set and it doesn't match the server, reload app
+ if(currentVersion != totalsObject.currentVersion){
+ localStorage.setItem('currentVersion', totalsObject.currentVersion)
+ location.reload(true)
+ }
+
+
// console.log('-------------')
// Object.keys(totalsObject).forEach( key => {
// console.log(key + ' -- ' + totalsObject[key])
@@ -135,11 +135,8 @@ export default new Vuex.Store({
getUsername: state => {
return state.username
},
- getLoginToken: state => {
- return state.token
- },
getLoggedIn: state => {
- let weIn = (state.token !== null && state.token != undefined && state.token.length > 0)
+ let weIn = (state.username && state.username.length > 0)
return weIn
},
getIsNightMode: state => {
diff --git a/configs/nginx/default b/configs/nginx/default
deleted file mode 100644
index 336a3bd..0000000
--- a/configs/nginx/default
+++ /dev/null
@@ -1,38 +0,0 @@
-##
-#
-# This is just a mock config file, describing what is needed to run the app
-# The app currently only needs two paths / and /api
-#
-##
-
-#
-# This is needed to define any ports the app may use from node
-#
-upstream expressapp {
- server 127.0.0.1:3000;
- keepalive 8;
-}
-
-server {
-
- #
- # Needed to server up static, compiled JS files and index.html
- #
- location / {
- autoindex on;
- }
-
- #
- # define the api route to connect to the backend and serve up static files
- #
- location /api {
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- proxy_set_header X-NginX-Proxy true;
-
- proxy_pass http://expressapp;
- proxy_redirect off;
- }
-
-}
\ No newline at end of file
diff --git a/server/helpers/Auth.js b/server/helpers/Auth.js
index d9cfc09..0038bac 100644
--- a/server/helpers/Auth.js
+++ b/server/helpers/Auth.js
@@ -6,26 +6,33 @@ let Auth = {}
const tokenSecretKey = process.env.JSON_KEY
-Auth.createToken = (userId, masterKey, request = null) => {
+Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) => {
return new Promise((resolve, reject) => {
- const created = Math.floor(+new Date/1000)
+ const created = pastCreatedDate ? pastCreatedDate : Math.floor(+new Date/1000)
const userHash = cs.hash(String(userId)).toString('base64')
//Encrypt Master Password and save it to the server
+ const sessionId = pastId ? pastId : cs.createSmallSalt().slice(0,9) //Use existing session id
const salt = cs.createSmallSalt()
const tempPass = cs.createSmallSalt()
const encryptedMasterPass = cs.encrypt(tempPass, salt, masterKey)
-
- db.promise().query(
-
- 'INSERT INTO user_active_session (salt, encrypted_master_password, created, uses, user_hash) VALUES (?,?,?,?,?)',
- [salt, encryptedMasterPass, created, 1, userHash])
+ //Deactivate all other session keys, they delete after 30 seconds
+ db.promise().query('UPDATE user_active_session SET active = 0 WHERE session_id = ?', [sessionId])
.then((r,f) => {
+ return db.promise().query(
+ 'INSERT INTO user_active_session (salt, encrypted_master_password, created, uses, user_hash, session_id) VALUES (?,?,?,?,?,?)',
+ [salt, encryptedMasterPass, created, 40, userHash, sessionId])
+
+ })
+ .then((r,f) => {
+
+ const sessionNum = r[0].insertId
+
//Required Data for JWT payload
- const tokenPayload = {userId, tempPass, salt}
+ const tokenPayload = {userId, tempPass, sessionNum}
//Return token
const token = jwt.sign(tokenPayload, tokenSecretKey)
@@ -33,50 +40,85 @@ Auth.createToken = (userId, masterKey, request = null) => {
})
})
}
+
Auth.decodeToken = (token, request = null) => {
return new Promise((resolve, reject) => {
let decodedToken = null
+ //Delete all tokens older than 20 days before continuing or inacive and older than 1 minute
+ const now = (Math.floor((+new Date)/1000))
+ const twentyDays = (Math.floor((+new Date)/1000)) - (86400 * 20)
+ const thirtySeconds = (Math.floor((+new Date)/1000)) - (30)
+
//Decode Json web token
- jwt.verify(token, tokenSecretKey, function(err, decoded){
- if(err || decoded.tempPass == undefined || decoded.tempPass.length < 5 || decoded.salt == undefined || decoded.salt.length < 5){
- return reject('Bad Token')
- }
+ jwt.verify(token, tokenSecretKey, function(err, decoded){
+ if(err || decoded.tempPass == undefined || decoded.tempPass.length < 5){
+ throw new Error('Bad Token')
+ }
- decodedToken = decoded
+ decodedToken = decoded
- //Lookup session data in database
- return db.promise().query('SELECT * FROM user_active_session WHERE salt = ? LIMIT 1', [decodedToken.salt])
- })
- .then((r,f) => {
+ db.promise().query('DELETE from user_active_session WHERE (created < ?) OR (active = false AND last_used < ?)', [twentyDays, thirtySeconds])
+ .then((r,f) => {
+
+ //Lookup session data in database
+ db.promise().query('SELECT * FROM user_active_session WHERE id = ? LIMIT 1', [decodedToken.sessionNum])
+ .then((r,f) => {
- const row = r[0][0]
- if(row == undefined || row.length == 0){
- return reject(false)
- }
+ if(r == undefined || r[0].length == 0){
+ throw new Error('Active Session not found for token')
+ }
- //Decrypt master key from database
- const masterKey = cs.decrypt(decodedToken.tempPass, decodedToken.salt, row.encrypted_master_password)
- if(masterKey == null){
- return reject (false)
- }
+ const row = r[0][0]
- const userData = {
- userId: decodedToken.userId, masterKey, tokenId: row.id
- }
+ // console.log(decodedToken.sessionNum + ' uses -> ' + row.uses)
- //Async update DB counts
- db.promise().query('UPDATE user_active_session SET uses = uses + 1 WHERE salt = ? LIMIT 1', [decodedToken.salt])
+ if(row.uses <= 0){
+ throw new Error('Token is used up')
+ }
- return resolve(userData)
+ //Decrypt master key from lookup
+ const masterKey = cs.decrypt(decodedToken.tempPass, row.salt, row.encrypted_master_password)
+ if(masterKey == null){
+ // console.log('Deleting invalid session')
+ Auth.terminateSession(row.session_id)
+ throw new Error ('Unable to decrypt password for session')
+ }
+ //Async update DB counts and disable session if needed
+ db.promise().query('UPDATE user_active_session SET uses = uses -1, last_used = ? WHERE id = ? LIMIT 1', [now, decodedToken.sessionNum])
+ .then((r,f) => {
+
+ let userData = {
+ 'userId': decodedToken.userId,
+ 'masterKey': masterKey,
+ 'sessionId': row.session_id,
+ 'created': row.created,
+ 'remainingUses':(row.uses--),
+ 'active': row.active
+ }
+
+ //Return token Data
+ return resolve(userData)
+
+ })
+ })
+ .catch(error => {
+ //Token errors result in having sessions deleted
+ // console.log('-- Auth Token Error --')
+ // console.log(error)
+ reject(error)
+ })
+ })
})
})
}
-Auth.reissueToken = () => {
- //If token has more than 200 uses, renew it
+
+Auth.terminateSession = (sessionId) => {
+ return db.promise().query('DELETE from user_active_session WHERE session_id = ?', [sessionId])
}
+
Auth.deletAllLoginKeys = (userId) => {
const userHash = cs.hash(String(userId)).toString('base64')
@@ -86,8 +128,6 @@ Auth.deletAllLoginKeys = (userId) => {
Auth.test = () => {
- // return Auth.deletAllLoginKeys(testUserId)
-
const testUserId = 22
const testPass = cs.createSmallSalt()
Auth.createToken(testUserId, testPass)
@@ -100,7 +140,6 @@ Auth.test = () => {
.then(userData => {
console.log('Test: Decrypted key Match -> ' + (testPass == userData.masterKey))
-
return Auth.deletAllLoginKeys(testUserId)
})
.then(results => {
@@ -108,16 +147,6 @@ Auth.test = () => {
console.log('Test: Remove user Json Web Tokens - Pass')
})
-
- //create token with userId and master key
- // Auth.createToken()
-
- //Thirt days ago
- // const thirtyDays = (Math.floor((+new Date)/1000)) - (86400 * 30)
- // const created = Math.floor(decoded.date/1000)
- // if(created < thirtyDays){
- // return reject('Token Expired')
- // }
}
module.exports = Auth
\ No newline at end of file
diff --git a/server/helpers/CryptoString.js b/server/helpers/CryptoString.js
index ea50503..e80635b 100644
--- a/server/helpers/CryptoString.js
+++ b/server/helpers/CryptoString.js
@@ -31,6 +31,9 @@ CryptoString.encrypt = (password, salt64, rawText) => {
//Decrypt base64 string cipher text,
CryptoString.decrypt = (password, salt64, cipherTextString) => {
+ if(!password || !salt64 || !cipherTextString){ return '' }
+ if(password.length == 0 || salt64.length == 0 || cipherTextString == 0){ return '' }
+
let cipherText = Buffer.from(cipherTextString, 'base64')
const salt = Buffer.from(salt64, 'base64')
diff --git a/server/helpers/ProcessText.js b/server/helpers/ProcessText.js
index 9f63b80..f071080 100644
--- a/server/helpers/ProcessText.js
+++ b/server/helpers/ProcessText.js
@@ -69,7 +69,7 @@ ProcessText.deduceNoteTitle = (inTitle, inString) => {
//Remove inline styles that may be added by editor
// inString = inString.replace(/style=".*?"/g,'')
- const tagFreeLength = ProcessText.removeHtml(inString).length
+ // const tagFreeLength = ProcessText.removeHtml(inString).length
//
// Simplified attempt!
@@ -80,7 +80,7 @@ ProcessText.deduceNoteTitle = (inTitle, inString) => {
// if(tagFreeLength > 200){
// sub += '... '
// }
- inString += ''
+ // inString += ''
return {title, sub}
diff --git a/server/index.js b/server/index.js
index 118dbd5..52ee28d 100644
--- a/server/index.js
+++ b/server/index.js
@@ -50,13 +50,37 @@ io.on('connection', function(socket){
socket.on('user_connect', token => {
Auth.decodeToken(token)
.then(userData => {
- socket.join(userData.id)
+ socket.join(userData.userId)
}).catch(error => {
//Don't add user to room if they are not logged in
// console.log(error)
})
})
+ //Renew Session tokens when users request a new one
+ socket.on('renew_session_token', token => {
+
+ //Decode the token they currently have
+ Auth.decodeToken(token)
+ .then(userData => {
+
+ console.log('Is active -> ', userData.active)
+
+ if(userData.active == 1){
+ //Create a new one using credentials and session keys from current
+ Auth.createToken(userData.userId, userData.masterKey, userData.sessionId, userData.created)
+ .then(newToken => {
+
+ //Emit new token only to user on socket
+ socket.emit('recievend_new_token', newToken)
+ })
+ } else {
+ //Attempting to reactivate disabled session, kills it all
+ Auth.terminateSession(userData.sessionId)
+ }
+ })
+ })
+
socket.on('join_room', rawTextId => {
// Join user to rawtextid room when they enter
socket.join(rawTextId)
@@ -78,11 +102,7 @@ io.on('connection', function(socket){
//Update users in room count
io.to(rawTextId).emit('update_user_count', usersInRoom.length)
- //Debugging text
- console.log('Note diff object')
- console.log(noteDiffs)
-
-
+ //Debugging text - prints out notes in limbo
let noteDiffKeys = Object.keys(noteDiffs)
let totalDiffs = 0
noteDiffKeys.forEach(diffSetKey => {
@@ -90,9 +110,11 @@ io.on('connection', function(socket){
totalDiffs += noteDiffs[diffSetKey].length
}
})
-
- console.log('Total notes in limbo -> ', noteDiffKeys.length)
- console.log('Total Diffs for all notes -> ', totalDiffs)
+ //Debugging Text
+ if(noteDiffKeys.length > 0){
+ console.log('Total notes in limbo -> ', noteDiffKeys.length)
+ console.log('Total Diffs for all notes -> ', totalDiffs)
+ }
}
})
@@ -203,18 +225,27 @@ app.use(function(req, res, next){
//Always null out master key, never allow it set from outside
req.headers.masterKey = null
- req.headers.tokenId = null
+ req.headers.sessionId = null
//auth token set by axios in headers
let token = req.headers.authorizationtoken
- if(token && token != null && typeof token === 'string'){
+ if(token !== undefined && token.length > 0){
Auth.decodeToken(token, req)
.then(userData => {
- req.headers.userId = userData.userId //Update headers for the rest of the application
+
+ //Update headers for the rest of the application
+ req.headers.userId = userData.userId
req.headers.masterKey = userData.masterKey
- req.headers.tokenId = userData.tokenId
+ req.headers.sessionId = userData.sessionId
+
+ //Tell front end remaining uses on current token
+ res.set('remainingUses', userData.remainingUses)
+
next()
- }).catch(error => {
+ })
+ .catch(error => {
+
+ console.log(error)
res.statusMessage = error //Throw 400 error if token is bad
res.status(400).end()
@@ -231,7 +262,7 @@ let UserTest = require('@models/User')
let NoteTest = require('@models/Note')
let AuthTest = require('@helpers/Auth')
Auth.test()
-UserTest.keyPairTest('genMan12', '1', printResults)
+UserTest.keyPairTest('genMan15', '1', printResults)
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
.then( message => {
if(printResults) console.log(message)
diff --git a/server/models/Note.js b/server/models/Note.js
index 6e15a9d..f23f5e3 100644
--- a/server/models/Note.js
+++ b/server/models/Note.js
@@ -454,9 +454,12 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has
}
let encryptedNoteText = ''
- //Create encrypted snippet
- const snippet = JSON.stringify([noteTitle, noteText.substring(0, 500)])
- noteSnippet = cs.encrypt(masterKey, snippetSalt, snippet)
+ //Create encrypted snippet if its a long note
+ let snippet = ''
+ if(noteText.length > 500){
+ snippet = JSON.stringify([noteTitle, noteText.substring(0, 500)])
+ noteSnippet = cs.encrypt(masterKey, snippetSalt, snippet)
+ }
//Encrypt note text
const textObject = JSON.stringify([noteTitle, noteText])
@@ -946,9 +949,11 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
let searchParams = [userId]
let noteSearchQuery = `
SELECT note.id,
- note.snippet as snippet,
- note.snippet_salt as salt,
- note_raw_text.updated as updated,
+ note.snippet as snippetText,
+ note.snippet_salt as snippetSalt,
+ note_raw_text.text as noteText,
+ note_raw_text.salt as noteSalt,
+ note_raw_text.updated as updated,
opened,
color,
count(distinct note_tag.id) as tag_count,
@@ -1092,26 +1097,39 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
}
- //Decrypt note text
- if(note.snippet && note.salt){
- const decipheredText = cs.decrypt(currentNoteKey, note.salt, note.snippet)
- const textObject = JSON.parse(decipheredText)
- if(textObject != null && textObject.length == 2){
- note.title = textObject[0]
- note.text = textObject[1]
- }
+ //Only long notes have snippets, decipher it if present
+ let displayTitle = ''
+ let displayText = ''
+
+ let encryptedText = note.noteText
+ let relatedSalt = note.noteSalt
+
+ //Default to note text, use snippet if set
+ if(note.snippetSalt && note.snippetText && note.snippetSalt.length > 0 && note.snippetText.length > 0){
+ encryptedText = note.snippetText
+ relatedSalt = note.snippetSalt
}
- //Deduce note title
- const textData = ProcessText.deduceNoteTitle(note.title, note.text)
-
- note.title = textData.title
- note.subtext = textData.sub
+ try {
+ const decipheredText = cs.decrypt(currentNoteKey, relatedSalt, encryptedText)
+ const textObject = JSON.parse(decipheredText)
+ if(textObject != null && textObject.length == 2){
+ if(textObject[0] && textObject[0] != null && textObject[0].length > 0){
+ displayTitle = textObject[0]
+ }
+ if(textObject[1] && textObject[1] != null && textObject[1].length > 0){
+ displayText = textObject[1]
+ }
+ }
+ } catch(err) {
+ console.log('Error opening note id -> ', note.id)
+ console.log(err)
+ }
- //Remove these variables
- note.note_highlights = []
- note.attachment_highlights = []
- note.tag_highlights = []
+
+
+ note.title = displayTitle
+ note.subtext = ProcessText.stripDoubleBlankLines(displayText)
//Limit number of attachment thumbs to 4
if(note.thumbs){
@@ -1123,9 +1141,12 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
}
//Clear out note.text before sending it to front end, its being used in title and subtext
- delete note.snippet
- delete note.salt
+ delete note.snippetText
+ delete note.snippetSalt
+ delete note.noteText
+ delete note.noteSalt
delete note.encrypted_share_password_key
+ delete note.text //Passed back as title and subtext
})
diff --git a/server/models/User.js b/server/models/User.js
index 44f1574..c2da73d 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -143,6 +143,7 @@ User.getCounts = (userId) => {
return new Promise((resolve, reject) => {
let countTotals = {}
+ const userHash = cs.hash(String(userId)).toString('base64')
db.promise().query(
`SELECT
@@ -169,8 +170,6 @@ User.getCounts = (userId) => {
.then( (rows, fields) => {
Object.assign(countTotals, rows[0][0]) //combine results
-
- const userHash = cs.hash(String(userId)).toString('base64')
return db.promise().query(
`SELECT count(id) as activeSessions FROM user_active_session WHERE user_hash = ?`, [userHash]
@@ -199,6 +198,8 @@ User.getCounts = (userId) => {
countTotals[key] = count ? count : 0
})
+ countTotals['currentVersion'] = '3.0.0'
+
resolve(countTotals)
})
@@ -206,8 +207,9 @@ User.getCounts = (userId) => {
}
//Log out user by deleting login token for that active session
-User.logout = (tokenId) => {
- return db.promise().query('DELETE FROM user_active_session WHERE (id = ?)', [tokenId])
+User.logout = (sessionId) => {
+ console.log('Terminate Session -> ', sessionId)
+ return db.promise().query('DELETE FROM user_active_session WHERE (session_id = ?)', [sessionId])
}
User.generateMasterKey = (userId, password) => {
diff --git a/server/routes/noteController.js b/server/routes/noteController.js
index b6093de..b458e92 100644
--- a/server/routes/noteController.js
+++ b/server/routes/noteController.js
@@ -136,19 +136,4 @@ router.post('/disableshare', function (req, res) {
})
-
-
-//
-// Testing Action
-//
-//Reindex all Note. Not a very good function, not public
-router.get('/reindex5yu43prchuj903mrc', function (req, res) {
-
- Note.migrateNoteTextToNewTable().then(status => {
- return res.send(status)
- })
-
-})
-
-
module.exports = router
\ No newline at end of file
diff --git a/server/routes/userController.js b/server/routes/userController.js
index 6102cac..b68b05a 100644
--- a/server/routes/userController.js
+++ b/server/routes/userController.js
@@ -33,7 +33,7 @@ router.post('/login', function (req, res) {
// Logout User
router.post('/logout', function (req, res) {
- User.logout(req.headers.tokenId)
+ User.logout(req.headers.sessionId)
.then( returnData => {
res.send(true)
})