Thunder Backend

The backend of Thunder is a monolithic Nodejs application implemented with the Koajs 1.x framework. It has the following features:

  • Receives HTTP requests with JSON
  • Authenticates with JWT
  • Broadcasts updates with socket.io
  • MongoDB database

Overview

app.js is the starting point of the server, and serves as a directory for the HTTP endpoints.

Most functionality is split into task specific modules. Generally this is a 1 to 1 relationship with collections in the database. These module files exist in the root directory of the repo (little messy). The modules contain the REST endpoints and most of the business logic / database interactions.

Any outgoing information besides HTTP responses goes through the Broadcast module which sends messages through socket.io to all logged in users.

Installation

After cloning:

npm i

Create a serverConfig.js in the root folder with the contents:

module.exports = {
	facilities: ['CPS'],
	database: "mongodb://thunderdbcps:bluedogredclown5@104.131.96.255:27111/thunder",
	port: 80
}

Then run.

node app

You can update the frontend with gulp:

npx gulp

Module endpoints

Typically,

  • incoming data exists in the this.data object (for PUT & POST), and/or in the this.params object (for GET & DELETE).
  • this.decodedToken also may have relevant data. Some user specific parameters - username, facility, role, flags, etc. are inside the JWT and are passed with each authenticated request.
  • Outgoing data for the response is placed in the this.body with the HTTP status in this.status

Auth

Auth typically follows this flow:

  • User logs in via a JSON HTTP request, the password is checked with their hash and salt and if successful, they are issued a JWT
  • The JWT carries their username, permission flags (flags in the JWT and database) and an expiration date.
  • The client is in charge of making sure they refresh their JWT before expiration occurs
  • The client sends the JWT with every request (while logged in) in the Authorization header (without the Bearer syntax)
  • Permission is checked as Koa middleware before reaching the endpoint. For example:
app.get('/user/:username', authed, User.getUser);

The authed here will make sure that the JWT with the request is both valid (issued by the server) and not expired. So any logged in user will be able to access a user via a username.

app.post('/user/resetPassword', has('managePasswords'), User.resetPassword);

Here, the has middleware will check that the JWT both is valid, and contains the managePasswords flag. So only users with the managePasswords permision can reset a password.

The authentication code exists mostly in auth.js and user.js.

Broadcasting

The idea behind broadcasting would that only certain people would want to listen to certain messages. To this end, messages sent from the server would have a structure like:

var Broadcast = require('./broadcast');

Broadcast.send({
    for: {admins: true},
    type: 'adminalert'
    data: {...}
});

but generally in practice the for field was ignored and was often just {}.

On the client side, a module would listen for broadcasts with:

var Message = require('message');

Message.listen({for: {admins: true}, type: 'adminalert'}, function(data){

});