* Adjusted theme colors to add more contrast on white theme while making black more OLED friendly
* Links now get an underline on hover * Cleaned up CSS variable names, added another theme color for more control * Cleaned up unused CSS, removed scrollbars popping up, tons of other little UI tweaks * Renamed shared notes to inbox * Tweaked form display, seperated login and create accouts * Put login/sign up form on home page * Created more legitimate marketing for home page * Tons up updates to note page and note input panel * Better support for two users editing a note * MUCH better diff handling, web sockets restore notes with unsaved diffs * Moved all squire text modifier functions into a mixin class * It now says saving when closing a note * Lots of cleanup and better handiling of events on mount and destroy * Scroll behavior modified to load notes when closer to bottom of page * Pretty decent shared notes and sharable link support * Updated help text * Search now includes tag suggestions and attachment suggestions * Cleaned up scratch pad a ton, allow for users to create new scratch pads * Created a 404 Page and a Shared note page * So many other small improvements. Oh my god, what is wrong with me, not doing commits!?
This commit is contained in:
110
server/index.js
110
server/index.js
@@ -40,11 +40,10 @@ var io = require('socket.io')(http, {
|
||||
//Set socket IO as a global in the app
|
||||
global.SocketIo = io
|
||||
|
||||
let noteDiffs = {}
|
||||
|
||||
io.on('connection', function(socket){
|
||||
|
||||
// console.log('New user ', socket.id)
|
||||
|
||||
//When a user connects, add them to their own room
|
||||
// This allows the server to emit events to that specific user
|
||||
// access socket.io in the controller with SocketIo global
|
||||
@@ -58,14 +57,43 @@ io.on('connection', function(socket){
|
||||
})
|
||||
})
|
||||
|
||||
socket.on('join_room', roomId => {
|
||||
// console.log('Join room ', roomId)
|
||||
socket.join(roomId)
|
||||
socket.on('join_room', rawTextId => {
|
||||
// Join user to rawtextid room when they enter
|
||||
socket.join(rawTextId)
|
||||
|
||||
const usersInRoom = io.sockets.adapter.rooms[roomId]
|
||||
//If there are past diffs for this note, send them to the user
|
||||
if(noteDiffs[rawTextId] != undefined){
|
||||
|
||||
//Sort all note diffs by when they were created.
|
||||
noteDiffs[rawTextId].sort((a,b) => { return a.time - b.time })
|
||||
|
||||
//Emit all sorted diffs to user
|
||||
socket.emit('past_diffs', noteDiffs[rawTextId])
|
||||
} else {
|
||||
socket.emit('past_diffs', null)
|
||||
}
|
||||
|
||||
const usersInRoom = io.sockets.adapter.rooms[rawTextId]
|
||||
if(usersInRoom){
|
||||
// console.log('Users in room', usersInRoom.length)
|
||||
io.to(roomId).emit('update_user_count', usersInRoom.length)
|
||||
//Update users in room count
|
||||
io.to(rawTextId).emit('update_user_count', usersInRoom.length)
|
||||
|
||||
//Debugging text
|
||||
console.log('Note diff object')
|
||||
console.log(noteDiffs)
|
||||
|
||||
|
||||
let noteDiffKeys = Object.keys(noteDiffs)
|
||||
let totalDiffs = 0
|
||||
noteDiffKeys.forEach(diffSetKey => {
|
||||
if(noteDiffs[diffSetKey]){
|
||||
totalDiffs += noteDiffs[diffSetKey].length
|
||||
}
|
||||
})
|
||||
|
||||
console.log('Total notes in limbo -> ', noteDiffKeys.length)
|
||||
console.log('Total Diffs for all notes -> ', totalDiffs)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@@ -83,13 +111,41 @@ io.on('connection', function(socket){
|
||||
|
||||
socket.on('note_diff', data => {
|
||||
|
||||
//Log each diff for note
|
||||
const noteId = data.id
|
||||
delete data.id
|
||||
if(noteDiffs[noteId] == undefined){ noteDiffs[noteId] = [] }
|
||||
data.time = +new Date
|
||||
|
||||
noteDiffs[noteId].push(data)
|
||||
|
||||
//Remove duplicate diffs if they exist
|
||||
for (var i = noteDiffs[noteId].length - 1; i >= 0; i--) {
|
||||
|
||||
let pastDiff = noteDiffs[noteId][i]
|
||||
|
||||
for (var j = noteDiffs[noteId].length - 1; j >= 0; j--) {
|
||||
let currentDiff = noteDiffs[noteId][j]
|
||||
|
||||
if(i == j){
|
||||
continue
|
||||
}
|
||||
|
||||
if(currentDiff.diff == pastDiff.diff || currentDiff.time == pastDiff.time){
|
||||
console.log('Removing Duplicate')
|
||||
noteDiffs[noteId].splice(i,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Each user joins a room when they open the app.
|
||||
io.in(data.id).clients((error, clients) => {
|
||||
io.in(noteId).clients((error, clients) => {
|
||||
if (error) throw error;
|
||||
|
||||
//Go through each client in note room and send them the diff
|
||||
clients.forEach(socketId => {
|
||||
if(socketId != socket.id){
|
||||
io.to(socketId).emit('incoming_diff', data.diff)
|
||||
io.to(socketId).emit('incoming_diff', data)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -97,6 +153,38 @@ io.on('connection', function(socket){
|
||||
|
||||
})
|
||||
|
||||
socket.on('truncate_diffs_at_save', checkpoint => {
|
||||
|
||||
let diffSet = noteDiffs[checkpoint.rawTextId]
|
||||
if(diffSet && diffSet.length > 0){
|
||||
|
||||
//Make sure all diffs are sorted before cleaning
|
||||
noteDiffs[checkpoint.rawTextId].sort((a,b) => { return a.time - b.time })
|
||||
|
||||
// Remove all diffs until it reaches the current hash
|
||||
let sliceTo = 0
|
||||
for (var i = 0; i < diffSet.length; i++) {
|
||||
if(diffSet[i].hash == checkpoint){
|
||||
sliceTo = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
noteDiffs[checkpoint.rawTextId] = diffSet.slice(0, sliceTo)
|
||||
|
||||
if(noteDiffs[checkpoint.rawTextId].length == 0){
|
||||
delete noteDiffs[checkpoint.rawTextId]
|
||||
}
|
||||
//Debugging
|
||||
else {
|
||||
console.log('Diffset after save')
|
||||
console.log(noteDiffs[checkpoint.rawTextId])
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function(){
|
||||
// console.log('user disconnected');
|
||||
});
|
||||
@@ -139,7 +227,7 @@ app.use(function(req, res, next){
|
||||
const printResults = false
|
||||
let UserTest = require('@models/User')
|
||||
let NoteTest = require('@models/Note')
|
||||
UserTest.keyPairTest('genMan2', '1', printResults)
|
||||
UserTest.keyPairTest('genMan12', '1', printResults)
|
||||
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
||||
.then( message => {
|
||||
if(printResults) console.log(message)
|
||||
|
@@ -280,9 +280,7 @@ Attachment.scanTextForWebsites = (io, userId, noteId, noteText) => {
|
||||
Attachment.scrapeUrlsCreateAttachments(userId, noteId, foundUrls).then( freshlyScrapedText => {
|
||||
|
||||
//Once everything is done being scraped, emit new attachment events
|
||||
if(io){
|
||||
io.to(userId).emit('update_counts')
|
||||
}
|
||||
SocketIo.to(userId).emit('update_counts')
|
||||
|
||||
solrAttachmentText += freshlyScrapedText
|
||||
resolve(solrAttachmentText)
|
||||
|
@@ -72,7 +72,7 @@ Note.test = (userId, masterKey, printResults) => {
|
||||
if(printResults) console.log('Test: Normal Note Search Index - Pass')
|
||||
} else { console.log('Test: Search Index - Fail') }
|
||||
|
||||
return ShareNote.migrateNoteToShared(userId, testNoteId, shareUserId, masterKey)
|
||||
return ShareNote.addUserToSharedNote(userId, testNoteId, shareUserId, masterKey)
|
||||
})
|
||||
.then(({success, shareUserId, sharedUserNoteId}) => {
|
||||
|
||||
@@ -134,13 +134,13 @@ Note.test = (userId, masterKey, printResults) => {
|
||||
|
||||
if(printResults) console.log('Test: Delete Shared Note - Pass')
|
||||
|
||||
return ShareNote.migrateNoteToShared(userId, testNoteId2, shareUserId, masterKey)
|
||||
return ShareNote.addUserToSharedNote(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)
|
||||
return ShareNote.removeUserFromSharedNote(userId, testNoteId2, sharedUserNoteId, masterKey)
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
@@ -162,75 +162,6 @@ Note.test = (userId, masterKey, printResults) => {
|
||||
return resolve('Test: Complete ---')
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//User doesn't have an encrypted note set. Encrypt all notes
|
||||
Note.encryptEveryNote = (userId, masterKey) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//Select all the user notes
|
||||
db.promise().query(`
|
||||
SELECT * FROM note
|
||||
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
||||
WHERE salt IS NULL AND user_id = ? AND shared = 0`, [userId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
let foundNotes = rows[0]
|
||||
console.log('Encrypting user notes ',rows[0].length)
|
||||
|
||||
// return resolve(true)
|
||||
|
||||
let allTheUpdates = []
|
||||
let timeoutAdder = 0
|
||||
foundNotes.forEach(note => {
|
||||
timeoutAdder += 100
|
||||
const newUpdate = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
console.log('Encrypting Note ', note.id)
|
||||
|
||||
const created = Math.round((+new Date)/1000)
|
||||
const salt = cs.createSmallSalt()
|
||||
|
||||
const noteText = note.text
|
||||
const noteTitle = note.title
|
||||
|
||||
const snippet = JSON.stringify([noteTitle, noteText.substring(0, 500)])
|
||||
const noteSnippet = cs.encrypt(masterKey, salt, snippet)
|
||||
|
||||
const textObject = JSON.stringify([noteTitle, noteText])
|
||||
const encryptedText = cs.encrypt(masterKey, salt, textObject)
|
||||
|
||||
db.promise()
|
||||
.query('UPDATE note_raw_text SET title = ?, text = ?, snippet = ?, salt = ? WHERE id = ?',
|
||||
[null, encryptedText, noteSnippet, salt, note.note_raw_text_id])
|
||||
.then(() => {
|
||||
resolve(true)
|
||||
})
|
||||
}, timeoutAdder)
|
||||
})
|
||||
allTheUpdates.push(newUpdate)
|
||||
})
|
||||
|
||||
Promise.all(allTheUpdates).then(done => {
|
||||
|
||||
console.log('Indexing first 100')
|
||||
return Note.reindex(userId, masterKey)
|
||||
|
||||
}).then(results => {
|
||||
|
||||
console.log('Done')
|
||||
resolve(true)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -490,7 +421,8 @@ Note.reindex = (userId, masterKey, removeId = null) => {
|
||||
Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, hash, masterKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const now = Math.round((+new Date)/1000)
|
||||
// const now = Math.round((+new Date)/1000)
|
||||
const now = +new Date
|
||||
|
||||
let noteSnippet = ''
|
||||
|
||||
@@ -691,10 +623,6 @@ Note.delete = (userId, noteId, masterKey = null) => {
|
||||
// delete tags
|
||||
return db.promise()
|
||||
.query('DELETE FROM note_tag WHERE note_tag.note_id = ? AND note_tag.user_id = ?', [noteId,userId])
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
@@ -706,6 +634,8 @@ Note.delete = (userId, noteId, masterKey = null) => {
|
||||
}
|
||||
})
|
||||
|
||||
SocketIo.to(userId).emit('update_counts')
|
||||
|
||||
if(masterKey){
|
||||
//Remove note ID from index
|
||||
Note.reindex(userId, masterKey, [noteId])
|
||||
@@ -803,6 +733,7 @@ Note.get = (userId, noteId, masterKey) => {
|
||||
note.archived,
|
||||
note.trashed,
|
||||
note.color,
|
||||
note.shared,
|
||||
note.encrypted_share_password_key,
|
||||
count(distinct attachment.id) as attachment_count,
|
||||
note.note_raw_text_id as rawTextId,
|
||||
@@ -845,8 +776,8 @@ Note.get = (userId, noteId, masterKey) => {
|
||||
db.promise().query(`UPDATE note SET opened = ? WHERE (id = ?)`, [nowTime, noteId])
|
||||
|
||||
//Return note data
|
||||
delete noteData.salt //remove salt from return data
|
||||
delete noteData.encrypted_share_password_key
|
||||
// delete noteData.salt //remove salt from return data
|
||||
// delete noteData.encrypted_share_password_key
|
||||
noteData.lockedOut = noteLockedOut
|
||||
resolve(noteData)
|
||||
|
||||
@@ -859,17 +790,48 @@ Note.get = (userId, noteId, masterKey) => {
|
||||
}
|
||||
|
||||
//Public note share action -> may not be used
|
||||
Note.getShared = (noteId) => {
|
||||
Note.getShared = (noteId, sharedKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.promise()
|
||||
.query('SELECT text, color FROM note WHERE id = ? AND shared = 1 LIMIT 1', [noteId])
|
||||
.query(`
|
||||
SELECT
|
||||
note_raw_text.text,
|
||||
note_raw_text.salt,
|
||||
note_raw_text.updated as updated,
|
||||
note.id,
|
||||
note.user_id,
|
||||
note.created,
|
||||
note.pinned,
|
||||
note.archived,
|
||||
note.trashed,
|
||||
note.color,
|
||||
note.shared,
|
||||
note.encrypted_share_password_key,
|
||||
note.note_raw_text_id as rawTextId
|
||||
FROM note
|
||||
JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
||||
WHERE note.id = ? LIMIT 1`, [noteId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Return note data
|
||||
resolve(rows[0][0])
|
||||
let noteData = rows[0][0]
|
||||
|
||||
const decipheredText = cs.decrypt(sharedKey, noteData.salt, noteData.text)
|
||||
if(decipheredText == null){
|
||||
throw new Error('Unable to decropt note text')
|
||||
}
|
||||
|
||||
const success = true
|
||||
const noteObject = JSON.parse(decipheredText)
|
||||
const title = noteObject[0]
|
||||
const text = noteObject[1]
|
||||
const styleObject = JSON.parse(noteData.color)
|
||||
|
||||
return resolve({success, title, text, styleObject})
|
||||
|
||||
})
|
||||
.catch(console.log)
|
||||
.catch(error => {
|
||||
resolve({'success':false})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -12,19 +12,19 @@ let ShareNote = module.exports = {}
|
||||
const crypto = require('crypto')
|
||||
const cs = require('@helpers/CryptoString')
|
||||
|
||||
ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
ShareNote.addUserToSharedNote = (userId, noteId, shareUserId, masterKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const Note = require('@models/Note')
|
||||
const User = require('@models/User')
|
||||
|
||||
//generate new random salts and password
|
||||
const sharedNoteMasterKey = cs.createSmallSalt()
|
||||
let sharedNoteMasterKey = null
|
||||
|
||||
let encryptedSharedKey = null //new key for note encrypted with shared users pubic key
|
||||
|
||||
//Current note object
|
||||
let note = null
|
||||
let noteObject = null
|
||||
let publicKey = null
|
||||
|
||||
db.promise().query('SELECT id FROM user WHERE id = ?', [shareUserId])
|
||||
@@ -34,20 +34,16 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
throw new Error('User Does Not Exist')
|
||||
}
|
||||
|
||||
return Note.get(userId, noteId, masterKey)
|
||||
return ShareNote.migrateToShared(userId, noteId, masterKey)
|
||||
|
||||
})
|
||||
.then( noteObject => {
|
||||
.then(({note, sharedNoteKey})=> {
|
||||
|
||||
if(!noteObject){
|
||||
throw new Error('Note Not Found')
|
||||
}
|
||||
|
||||
note = noteObject
|
||||
sharedNoteMasterKey = sharedNoteKey
|
||||
noteObject = note
|
||||
|
||||
return db.promise()
|
||||
.query('SELECT id FROM note WHERE user_id = ? AND note_raw_text_id = ?', [shareUserId, note.rawTextId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
@@ -55,46 +51,6 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
throw new Error('User Already has this note shared with them')
|
||||
}
|
||||
|
||||
//All check pass, proceed with sharing note
|
||||
return User.getPublicKey(userId)
|
||||
})
|
||||
.then( userPublicKey => {
|
||||
|
||||
//Get users public key
|
||||
publicKey = userPublicKey
|
||||
|
||||
//
|
||||
// Modify note to have a shared password, encrypt text with this password
|
||||
//
|
||||
const sharedNoteSalt = cs.createSmallSalt()
|
||||
|
||||
//Encrypt note text with new password
|
||||
const textObject = JSON.stringify([note.title, note.text])
|
||||
const encryptedText = cs.encrypt(sharedNoteMasterKey, sharedNoteSalt, textObject)
|
||||
|
||||
//Update note raw text with new data
|
||||
return db.promise()
|
||||
.query("UPDATE `application`.`note_raw_text` SET `text` = ?, `salt` = ? WHERE (`id` = ?)",
|
||||
[encryptedText, sharedNoteSalt, note.rawTextId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
//New Encrypted snippet, using new shared password
|
||||
const sharedNoteSnippetSalt = cs.createSmallSalt()
|
||||
const snippet = JSON.stringify([note.title, note.text.substring(0, 500)])
|
||||
const encryptedSnippet = cs.encrypt(sharedNoteMasterKey, sharedNoteSnippetSalt, snippet)
|
||||
|
||||
//Encrypt shared password for this user
|
||||
const encryptedSharedKey = crypto.publicEncrypt(publicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64')
|
||||
|
||||
//Update note snippet for current user with public key encoded snippet
|
||||
return db.promise().query('UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 2 WHERE id = ? AND user_id = ?',
|
||||
[encryptedSnippet, sharedNoteSnippetSalt, encryptedSharedKey, noteId, userId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
return User.getPublicKey(shareUserId)
|
||||
|
||||
})
|
||||
@@ -102,7 +58,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
|
||||
//New Encrypted snippet, using new shared password
|
||||
const newSnippetSalt = cs.createSmallSalt()
|
||||
const snippet = JSON.stringify([note.title, note.text.substring(0, 500)])
|
||||
const snippet = JSON.stringify([noteObject.title, noteObject.text.substring(0, 500)])
|
||||
const encryptedSnippet = cs.encrypt(sharedNoteMasterKey, newSnippetSalt, snippet)
|
||||
|
||||
//Encrypt shared password for this user
|
||||
@@ -111,7 +67,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
//Insert new note for shared user
|
||||
return db.promise().query(`
|
||||
INSERT INTO note (user_id, note_raw_text_id, created, color, share_user_id, snippet, snippet_salt, encrypted_share_password_key) VALUES (?,?,?,?,?,?,?,?);
|
||||
`, [shareUserId, note.rawTextId, note.created, note.color, userId, encryptedSnippet, newSnippetSalt, encryptedSharedKey])
|
||||
`, [shareUserId, noteObject.rawTextId, noteObject.created, noteObject.color, userId, encryptedSnippet, newSnippetSalt, encryptedSharedKey])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
@@ -119,7 +75,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
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')
|
||||
SocketIo.to(shareUserId).emit('update_counts')
|
||||
|
||||
let success = true
|
||||
return resolve({success, shareUserId, sharedUserNoteId})
|
||||
@@ -133,7 +89,7 @@ ShareNote.migrateNoteToShared = (userId, noteId, shareUserId, masterKey) => {
|
||||
})
|
||||
}
|
||||
|
||||
ShareNote.removeUserFromShared = (userId, noteId, shareNoteUserId, masterKey) => {
|
||||
ShareNote.removeUserFromSharedNote = (userId, noteId, shareNoteUserId, masterKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let rawTextId = null
|
||||
@@ -160,32 +116,9 @@ ShareNote.removeUserFromShared = (userId, noteId, shareNoteUserId, masterKey) =>
|
||||
//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)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
return ShareNote.migrateToNormal(userId, noteId, masterKey)
|
||||
.then(results => {
|
||||
resolve(true)
|
||||
})
|
||||
|
||||
} else {
|
||||
@@ -196,9 +129,163 @@ ShareNote.removeUserFromShared = (userId, noteId, shareNoteUserId, masterKey) =>
|
||||
})
|
||||
}
|
||||
|
||||
// Get users who see a shared note
|
||||
ShareNote.getUsers = (userId, rawTextId) => {
|
||||
//Encrypt note with private shared key
|
||||
ShareNote.migrateToShared = (userId, noteId, masterKey) => {
|
||||
|
||||
const User = require('@models/User')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//generate new random password
|
||||
const sharedNoteMasterKey = cs.createSmallSalt()
|
||||
let userPublicKey = null
|
||||
let userPrivateKey = null
|
||||
let note = null
|
||||
|
||||
User.generateKeypair(userId, masterKey)
|
||||
.then( ({publicKey, privateKey}) => {
|
||||
|
||||
//Get users public key
|
||||
userPublicKey = publicKey
|
||||
userPrivateKey = privateKey
|
||||
|
||||
return Note.get(userId, noteId, masterKey)
|
||||
})
|
||||
.then(noteObject => {
|
||||
|
||||
note = noteObject
|
||||
|
||||
if(note.shared == 2){
|
||||
|
||||
//Note is already shared, decrypt sharedKey
|
||||
let sharedNoteKey = null
|
||||
const encryptedShareKey = note.encrypted_share_password_key
|
||||
if(encryptedShareKey != null){
|
||||
sharedNoteKey = crypto.privateDecrypt(userPrivateKey,
|
||||
Buffer.from(encryptedShareKey, 'base64') )
|
||||
}
|
||||
|
||||
return resolve({note, sharedNoteKey})
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
//
|
||||
// Update raw_text to have a shared password, encrypt text with this password
|
||||
//
|
||||
const sharedNoteSalt = cs.createSmallSalt()
|
||||
|
||||
//Encrypt note text with new password
|
||||
const textObject = JSON.stringify([note.title, note.text])
|
||||
const encryptedText = cs.encrypt(sharedNoteMasterKey, sharedNoteSalt, textObject)
|
||||
|
||||
//Update note raw text with new data
|
||||
db.promise()
|
||||
.query("UPDATE `application`.`note_raw_text` SET `text` = ?, `salt` = ? WHERE (`id` = ?)",
|
||||
[encryptedText, sharedNoteSalt, note.rawTextId])
|
||||
.then((rows, fields) => {
|
||||
|
||||
//
|
||||
// Update snippet using new shared password
|
||||
// + Save shared password (encrypted with public key)
|
||||
//
|
||||
const sharedNoteSnippetSalt = cs.createSmallSalt()
|
||||
const snippet = JSON.stringify([note.title, note.text.substring(0, 500)])
|
||||
const encryptedSnippet = cs.encrypt(sharedNoteMasterKey, sharedNoteSnippetSalt, snippet)
|
||||
|
||||
//Encrypt shared password for this user
|
||||
const encryptedSharedKey = crypto.publicEncrypt(userPublicKey, Buffer.from(sharedNoteMasterKey, 'utf8')).toString('base64')
|
||||
|
||||
//Update note snippet for current user with public key encoded snippet
|
||||
return db.promise().query('UPDATE note SET snippet = ?, snippet_salt = ?, encrypted_share_password_key = ?, shared = 2 WHERE id = ? AND user_id = ?',
|
||||
[encryptedSnippet, sharedNoteSnippetSalt, encryptedSharedKey, noteId, userId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
return resolve({note, 'sharedNoteKey':sharedNoteMasterKey})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
//Remove private shared key, encrypt note with users master password
|
||||
ShareNote.migrateToNormal = (userId, noteId, masterKey) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ShareNote.decryptSharedKey = (userId, noteId, masterKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let userPrivateKey = null
|
||||
|
||||
const User = require('@models/User')
|
||||
User.generateKeypair(userId, masterKey)
|
||||
.then( ({publicKey, privateKey}) => {
|
||||
|
||||
userPrivateKey = privateKey
|
||||
|
||||
return Note.get(userId, noteId, masterKey)
|
||||
})
|
||||
.then(noteObject => {
|
||||
|
||||
//Shared notes use encrypted key - decrypt key then return it.
|
||||
const encryptedShareKey = noteObject.encrypted_share_password_key
|
||||
|
||||
if(encryptedShareKey != null && userPrivateKey != null){
|
||||
const currentNoteKey = crypto.privateDecrypt(userPrivateKey,
|
||||
Buffer.from(encryptedShareKey, 'base64') )
|
||||
|
||||
return resolve(currentNoteKey)
|
||||
|
||||
} else {
|
||||
return resolve(null)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Get users who see a shared note
|
||||
ShareNote.getShareInfo = (userId, noteId, rawTextId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let shareUsers = []
|
||||
let shareStatus = 0
|
||||
|
||||
db.promise()
|
||||
.query(`
|
||||
SELECT username, note.id as noteId
|
||||
@@ -211,47 +298,14 @@ ShareNote.getUsers = (userId, rawTextId) => {
|
||||
.then((rows, fields) => {
|
||||
|
||||
//Return a list of user names
|
||||
return resolve (rows[0])
|
||||
shareUsers = rows[0]
|
||||
return db.promise().query('SELECT shared FROM note WHERE id = ? AND user_id = ?', [noteId, userId])
|
||||
})
|
||||
})
|
||||
}
|
||||
.then((rows, fields) => {
|
||||
|
||||
// Remove a user from a shared note
|
||||
ShareNote.removeUser = (userId, noteId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const Note = require('@models/Note')
|
||||
|
||||
let rawTextId = null
|
||||
let removeUserId = null
|
||||
|
||||
//note.id = noteId, share_user_id = userId
|
||||
db.promise()
|
||||
.query('SELECT note_raw_text_id, user_id FROM note WHERE id = ? AND share_user_id = ?', [noteId, 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
|
||||
if(removeUserId && Number.isInteger(removeUserId)){
|
||||
//Delete this users access to the note
|
||||
return Note.delete(removeUserId, noteId, masterKey)
|
||||
|
||||
} else {
|
||||
|
||||
return new Promise((resolve, reject) => { resolve(true) })
|
||||
}
|
||||
shareStatus = rows[0][0]['shared']
|
||||
|
||||
return resolve({ shareStatus, shareUsers })
|
||||
})
|
||||
.then(stuff => {
|
||||
resolve(true)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
resolve(false)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
}
|
@@ -19,15 +19,6 @@ User.login = (username, password) => {
|
||||
.query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
|
||||
.then((rows, fields) => {
|
||||
|
||||
// Create New Account
|
||||
//
|
||||
if(rows[0].length == 0){
|
||||
User.create(lowerName, password)
|
||||
.then( ({token, userId}) => {
|
||||
return resolve({ token, userId })
|
||||
})
|
||||
}
|
||||
|
||||
// Login User
|
||||
//
|
||||
if(rows[0].length == 1){
|
||||
@@ -60,6 +51,8 @@ User.login = (username, password) => {
|
||||
reject('Password does not match database')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return reject('Incorrect Username or Password')
|
||||
}
|
||||
})
|
||||
.catch(console.log)
|
||||
@@ -69,7 +62,7 @@ User.login = (username, password) => {
|
||||
|
||||
//Create user account
|
||||
//Issues login token
|
||||
User.create = (username, password) => {
|
||||
User.register = (username, password) => {
|
||||
|
||||
//For some reason, username won't get into the promise. But password will @TODO figure this out
|
||||
const lowerName = username.toLowerCase().trim()
|
||||
@@ -152,8 +145,8 @@ User.getCounts = (userId) => {
|
||||
SUM(archived = 1 && share_user_id IS NULL && trashed = 0) AS archivedNotes,
|
||||
SUM(trashed = 1) AS trashedNotes,
|
||||
SUM(share_user_id IS NULL && trashed = 0) AS totalNotes,
|
||||
SUM(share_user_id != ? && trashed = 0) AS sharedToNotes,
|
||||
SUM( (share_user_id != ? && opened IS null && trashed = 0) || (share_user_id != ? && note_raw_text.updated > opened && trashed = 0) ) AS unreadNotes
|
||||
SUM(share_user_id IS NOT null && opened IS null && trashed = 0) AS youGotMailCount,
|
||||
SUM(share_user_id != ? && trashed = 0) AS sharedToNotes
|
||||
FROM note
|
||||
LEFT JOIN note_raw_text ON (note_raw_text.id = note.note_raw_text_id)
|
||||
WHERE user_id = ?`, [userId, userId, userId, userId])
|
||||
@@ -406,12 +399,12 @@ User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => {
|
||||
|
||||
const randomUsername = Math.random().toString(36).substring(2, 15);
|
||||
const randomPassword = '1'
|
||||
|
||||
User.login(testUserName, password)
|
||||
|
||||
User.register(testUserName, password)
|
||||
.then( ({ token, userId }) => {
|
||||
testUserId = userId
|
||||
|
||||
if(printResults) console.log('Test: Create/Login User '+testUserName+' - Pass')
|
||||
if(printResults) console.log('Test: Register User '+testUserName+' - Pass')
|
||||
|
||||
return User.getMasterKey(testUserId, password)
|
||||
})
|
||||
@@ -439,6 +432,12 @@ User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => {
|
||||
//Convert it back to string
|
||||
if(printResults) console.log(publicDeccryptMessage.toString('utf8'))
|
||||
|
||||
return User.login(testUserName, password)
|
||||
})
|
||||
.then( ({token, userId}) => {
|
||||
|
||||
if(printResults) console.log('Test: Login New User - Pass')
|
||||
|
||||
resolve({testUserId, masterKey})
|
||||
})
|
||||
})
|
||||
|
@@ -23,8 +23,13 @@ router.use(function setUserId (req, res, next) {
|
||||
//
|
||||
router.post('/get', function (req, res) {
|
||||
Note.get(userId, req.body.noteId, masterKey)
|
||||
.then( data => {
|
||||
res.send(data)
|
||||
.then( noteObject => {
|
||||
|
||||
delete noteObject.snippet_salt
|
||||
delete noteObject.salt
|
||||
delete noteObject.encrypted_share_password_key
|
||||
|
||||
res.send(noteObject)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -91,8 +96,8 @@ router.post('/settrashed', function (req, res) {
|
||||
//
|
||||
// Share Note Actions
|
||||
//
|
||||
router.post('/getshareusers', function (req, res) {
|
||||
ShareNote.getUsers(userId, req.body.rawTextId)
|
||||
router.post('/getshareinfo', function (req, res) {
|
||||
ShareNote.getShareInfo(userId, req.body.noteId, req.body.rawTextId)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
@@ -100,7 +105,7 @@ router.post('/shareadduser', function (req, res) {
|
||||
// ShareNote.addUser(userId, req.body.noteId, req.body.rawTextId, req.body.username, masterKey)
|
||||
User.getByUserName(req.body.username)
|
||||
.then( user => {
|
||||
return ShareNote.migrateNoteToShared(userId, req.body.noteId, user.id, masterKey)
|
||||
return ShareNote.addUserToSharedNote(userId, req.body.noteId, user.id, masterKey)
|
||||
})
|
||||
.then( ({success, shareUserId}) => {
|
||||
|
||||
@@ -110,10 +115,28 @@ router.post('/shareadduser', function (req, res) {
|
||||
|
||||
router.post('/shareremoveuser', function (req, res) {
|
||||
// (userId, noteId, shareNoteUserId, shareUserId, masterKey)
|
||||
ShareNote.removeUserFromShared(userId, req.body.noteId, req.body.shareUserNoteId, masterKey)
|
||||
ShareNote.removeUserFromSharedNote(userId, req.body.noteId, req.body.shareUserNoteId, masterKey)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
router.post('/enableshare', function (req, res) {
|
||||
//Create Shared Encryption Key for Note
|
||||
ShareNote.migrateToShared(userId, req.body.noteId, masterKey)
|
||||
.then(results => res.send(true))
|
||||
})
|
||||
router.post('/getsharekey', function (req, res) {
|
||||
//Get Shared Key for a note
|
||||
ShareNote.decryptSharedKey(userId, req.body.noteId, masterKey)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
router.post('/disableshare', function (req, res) {
|
||||
//Removed shared encryption key from note
|
||||
ShareNote.migrateToNormal(userId, req.body.noteId, masterKey)
|
||||
.then(results => res.send(true))
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Testing Action
|
||||
|
@@ -1,12 +1,15 @@
|
||||
var express = require('express')
|
||||
var router = express.Router()
|
||||
|
||||
let Notes = require('@models/Note')
|
||||
let Note = require('@models/Note')
|
||||
|
||||
router.post('/note', function (req, res) {
|
||||
|
||||
Notes.getShared(req.body.noteId)
|
||||
.then( data => res.send(data) )
|
||||
//
|
||||
// Public Note action
|
||||
//
|
||||
router.post('/opensharednote', function (req, res) {
|
||||
|
||||
Note.getShared(req.body.noteId, req.body.sharedKey)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
|
||||
|
@@ -18,34 +18,31 @@ router.get('/about', function (req, res) {
|
||||
User.getUsername(req.headers.userId)
|
||||
.then( data => res.send(data) )
|
||||
})
|
||||
// define the login route
|
||||
// Login User
|
||||
router.post('/login', function (req, res) {
|
||||
|
||||
//Pull out variables we want
|
||||
const username = req.body.username
|
||||
const password = req.body.password
|
||||
|
||||
let returnData = {
|
||||
success: false,
|
||||
token: '',
|
||||
username: ''
|
||||
}
|
||||
|
||||
User.login(username, password)
|
||||
.then( ({token, userId}) => {
|
||||
|
||||
returnData['username'] = username
|
||||
returnData['token'] = token
|
||||
returnData['success'] = true
|
||||
User.login(req.body.username, req.body.password)
|
||||
.then( returnData => {
|
||||
|
||||
res.send(returnData)
|
||||
return
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
res.send(returnData)
|
||||
res.send(false)
|
||||
})
|
||||
})
|
||||
// Login User
|
||||
router.post('/register', function (req, res) {
|
||||
|
||||
User.register(req.body.username, req.body.password)
|
||||
.then( returnData => {
|
||||
|
||||
res.send(returnData)
|
||||
})
|
||||
.catch(e => {
|
||||
res.send(false)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// fetch counts of users notes
|
||||
router.post('/totals', function (req, res) {
|
||||
|
Reference in New Issue
Block a user