//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') }) const ports = { express: 3000, socketIo: 3001 } //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() ) // allow for the parsing of url encoded forms app.use(express.urlencoded({ extended: true })); // // Request Rate Limiter // const rateLimit = require('express-rate-limit') //Limiter for the entire app const limiter = rateLimit({ windowMs: 10 * 60 * 1000, // 10 minutes max: 1000 // limit each IP to 1000 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.userId) //Track active logged in user accounts const usersInRoom = io.sockets.adapter.rooms[userData.userId] io.to(userData.userId).emit('update_active_user_count', usersInRoom.length) }).catch(error => { //Don't add user to room if they are not logged in // console.log(error) }) }) socket.on('get_active_user_count', token => { Auth.decodeToken(token) .then(userData => { socket.join(userData.userId) //Track active logged in user accounts const usersInRoom = io.sockets.adapter.rooms[userData.userId] io.to(userData.userId).emit('update_active_user_count', usersInRoom.length) }).catch(error => { // 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 => { 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) //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]) } const usersInRoom = io.sockets.adapter.rooms[rawTextId] if(usersInRoom){ //Update users in room count io.to(rawTextId).emit('update_user_count', usersInRoom.length) } }) 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) // Go over each user in this note-room 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 => { // only send off diff if user 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(socket){ // console.log('user disconnected'); }); }); http.listen(ports.socketIo, function(){ console.log(`Socke.io: Listening on port ${ports.socketIo}`) }); //Enable json body parsing in requests. Allows me to post data in ajax calls app.use(express.json({limit: '5mb'})) //App Auth, all requests will come in with a token, decode the token and set global var app.use(function(req, res, next){ //Always null out master key, never allow it set from outside req.headers.userId = null req.headers.masterKey = null req.headers.sessionId = null //auth token set by axios in headers let token = req.headers.authorizationtoken if(token !== undefined && token.length > 0){ Auth.decodeToken(token, req) .then(userData => { //Update headers for the rest of the application req.headers.userId = userData.userId req.headers.masterKey = userData.masterKey req.headers.sessionId = userData.sessionId //Tell front end remaining uses on current token res.set('remainingUses', userData.remainingUses) next() }) .catch(error => { next('Unauthorized') }) } else { next() //No token. Move along. } }) // Test Area // const printResults = true // let UserTest = require('@models/User') // let NoteTest = require('@models/Note') // let AuthTest = require('@helpers/Auth') // Auth.test() // UserTest.keyPairTest('genMan30', '1', printResults) // .then( ({testUserId, masterKey}) => // NoteTest.test(testUserId, masterKey, printResults)) // .then( message => { // if(printResults) console.log(message) // Auth.testTwoFactor() // }) // .catch((error) => { // console.log(error) // }) //Test app.get('/api', (req, res) => res.send('Solidscribe /API is up and running')) //Serve up uploaded files app.use('/api/static', express.static( __dirname+'/../staticFiles' )) //Public routes var public = require('@routes/publicController') app.use('/api/public', public) //user endpoint var user = require('@routes/userController') app.use('/api/user', user) //notes endpoint var notes = require('@routes/noteController') app.use('/api/note', notes) //tags endpoint var tags = require('@routes/tagController') app.use('/api/tag', tags) //notes endpoint var attachment = require('@routes/attachmentController') app.use('/api/attachment', attachment) //quick notes endpoint var quickNote = require('@routes/quicknoteController') app.use('/api/quick-note', quickNote) //cycle tracking endpoint var metricTracking = require('@routes/metrictrackingController') app.use('/api/metric-tracking', metricTracking) //Output running status app.listen(ports.express, () => { console.log(`Express: Listening on port ${ports.express}!`) }) // //Error handlers // //Default error handler just say unauthorized for everything app.use(function (err, req, res, next) { if (err) { res.status(401).send('Unauthorized') return } next() })