SolidScribe/server/index.js
Max G 6bb856689d * 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!?
2020-06-07 20:57:35 +00:00

271 lines
6.7 KiB
JavaScript

//Set up environmental variables, pulled from .env file used as process.env.DB_HOST
const os = require('os') //Used to get path of home directory
const result = require('dotenv').config({ path:(os.homedir()+'/.env') })
//Allow user of @ in in require calls. Config in package.json
require('module-alias/register')
//Auth helper, used for decoding users web token
let Auth = require('@helpers/Auth')
//Helmet adds additional security to express server
const helmet = require('helmet')
//Setup express server
const express = require('express')
const app = express()
app.use( helmet() )
const port = 3000
//
// Request Rate Limiter
//
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 10 * 60 * 1000, // minutes
max: 1000 // limit each IP to 100 requests per windowMs
});
// apply to all requests
app.use(limiter);
var http = require('http').createServer(app);
var io = require('socket.io')(http, {
path:'/socket'
});
//Set socket IO as a global in the app
global.SocketIo = io
let noteDiffs = {}
io.on('connection', function(socket){
//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
socket.on('user_connect', token => {
Auth.decodeToken(token)
.then(userData => {
socket.join(userData.id)
}).catch(error => {
//Don't add user to room if they are not logged in
// console.log(error)
})
})
socket.on('join_room', rawTextId => {
// Join user to rawtextid room when they enter
socket.join(rawTextId)
//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){
//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)
}
})
socket.on('leave_room', roomId => {
socket.leave(roomId)
// console.log('User Left room')
const usersInRoom = io.sockets.adapter.rooms[roomId]
if(usersInRoom){
// console.log('Users in room', usersInRoom.length)
io.to(roomId).emit('update_user_count', usersInRoom.length)
}
})
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(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)
}
})
});
})
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');
});
});
http.listen(3001, function(){
// console.log('socket.io liseting on port 3001');
});
//Enable json body parsing in requests. Allows me to post data in ajax calls
app.use(express.json({limit: '5mb'}))
//Prefix defied by route in nginx config
const prefix = '/api'
//App Auth, all requests will come in with a token, decode the token and set global var
app.use(function(req, res, next){
//auth token set by axios in headers
let token = req.headers.authorizationtoken
if(token && token != null && typeof token === 'string'){
Auth.decodeToken(token)
.then(userData => {
req.headers.userId = userData.id //Update headers for the rest of the application
req.headers.masterKey = userData.masterKey
next()
}).catch(error => {
res.statusMessage = error //Throw 400 error if token is bad
res.status(400).end()
})
} else {
next() //No token. Move along.
}
})
// Test Area
const printResults = false
let UserTest = require('@models/User')
let NoteTest = require('@models/Note')
UserTest.keyPairTest('genMan12', '1', printResults)
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
.then( message => {
if(printResults) console.log(message)
})
// Test Area
//Test
app.get(prefix, (req, res) => res.send('The api is running'))
//Serve up uploaded files
app.use(prefix+'/static', express.static( __dirname+'/../staticFiles' ))
//Public routes
var public = require('@routes/publicController')
app.use(prefix+'/public', public)
//user endpoint
var user = require('@routes/userController')
app.use(prefix+'/user', user)
//notes endpoint
var notes = require('@routes/noteController')
app.use(prefix+'/note', notes)
//tags endpoint
var tags = require('@routes/tagController')
app.use(prefix+'/tag', tags)
//notes endpoint
var attachment = require('@routes/attachmentController')
app.use(prefix+'/attachment', attachment)
//quick notes endpoint
var quickNote = require('@routes/quicknoteController')
app.use(prefix+'/quick-note', quickNote)
//Output running status
app.listen(port, () => {
// console.log(`Listening on port ${port}!`)
})