PlanHubMe/PlanHub

View on GitHub
app.js

Summary

Maintainability
F
3 days
Test Coverage
/*
    MyHomeworkSpace
    https://myhomework.space/
    https://github.com/MyHomeworkSpace/MyHomeworkSpace
    Licensed under the MIT License.
*/

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var KnexSessionStore = require('connect-session-knex')(session);

var app = express();

var env = "development";
global.env = ((app.get("env") == "production") ? app.get("env") : env);

global.config = require("./config");

global.basePath = config.basePath;
global.staticPath = config.staticPath;
global.mysqlConnection = config.dbConnection;

global.knex = require('knex')({
    client: 'mysql',
    connection: global.mysqlConnection,
    pool: {
        min: 2,
        max: 10
    }
});

global.sessionStore = new KnexSessionStore({
    knex: global.knex
});

global.transporter = require("nodemailer").createTransport(config.emails.smtpConfig);

if (global.basePath[global.basePath.length - 1] == "/") {
    global.basePath = global.basePath.slice(0, -1); // remove trailing slash
}

global.isApiPath = function(path) {
    return path.indexOf("api") > -1;
};

global.requireUser = function(req, res, next) {
    if (req.session.loggedIn) {
        next();
    } else {
        if (res.locals.apiCall) {
            res.json({
                status: "auth_required",
                message: "Authentication is required."
            });
        } else {
            res.redirect(global.basePath + "/login");
        }
    }
};

global.getUserRecord = function(req, res, next) {
    // TODO: errors on api pages?
    global.knex("users").select("*").where({ username: req.session.username }).then(function(obj) {
        if (obj.length > 1) {
            res.render("error", { title: "Error", msg: "A database error has occurred, and there is a duplicate user record. Please contact us for assistance." });
            return;
        }
        if (obj.length == 0) {
            res.render("error", { title: "Error", msg: "A database error has occurred. Your user record is missing. Please log out and log back in. If you have lost data, please contact us as soon as possible."});
            return;
        }
        res.locals.user = obj[0];
        next();
    });/*.catch(function() {
        res.render("error", { title: "Error", msg: "A database communication error has occurred. Please try to log out and log back in. If you have lost data, please contact us as soon as possible."});
    });*/
};

global.requireNonZeroLevel = function(req, res, next) {
    if (res.locals.user.level < 0) {
        if (res.locals.apiCall) {
            res.json({
                status: "forbidden",
                message: "You are not permitted to access this resource."
            });
        } else {
            res.redirect(global.basePath + "/login");
        }
    } else {
        next();
    }
};

global.requireViewFeedback = function(req, res, next) {
    if (res.locals.user.canFeedback != 1) {
        if (res.locals.apiCall) {
            res.json({
                status: "forbidden",
                message: "You are not permitted to access this resource."
            });
        } else {
            res.redirect(global.basePath + "/login");
        }
    } else {
        next();
    }
};

global.requireEditAnnouncements = function(req, res, next) {
    if (res.locals.user.canAnnouncements != 1) {
        if (res.locals.apiCall) {
            res.json({
                status: "forbidden",
                message: "You are not permitted to modify this resource."
            });
        } else {
            res.redirect(global.basePath + "/login");
        }
    } else {
        next();
    }
};

global.apiCall = function(req, res, next) {
    res.locals.apiCall = true;
    if (req.param("nonce") && req.session.nonce && req.param("nonce") === req.session.nonce) {
        next();
    } else {
        res.json({
            status: "error",
            error: "The nonce is invalid."
        });
    }
};

global.getOptionalUserRecord = function(req, res, next) {
    if (!req.session.loggedIn) {
        next();
        return;
    }
    global.knex("users").select("*").where({ username: req.session.username }).then(function(obj) {
        if (obj.length > 1) {
            res.render("error", { title: "Error", msg: "A database error has occurred, and there is a duplicate user record. Please contact us for assistance." });
            return;
        }
        if (obj.length == 0) {
            res.render("error", { title: "Error", msg: "A database error has occurred. Your user record is missing. Please log out and log back in. If you have lost data, please contact us as soon as possible."});
            return;
        }
        res.locals.user = obj[0];
        next();
    });
};

global.dbErrorHandler = function(err, req, res, next) {
        if (res.locals.apiCall) {
            res.json({
                status: "error",
                error: "Database error"
            });
        } else {
            res.render("error", { title: "Error", msg: "Database error occured. Please email hello@myhomework.space for assistance. Mention what you were trying to do."});
        }
};

if (!String.prototype.encodeHTML) {
    String.prototype.encodeHTML = function () {
        return this.replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                    .replace(/"/g, '&quot;')
                    .replace(/'/g, '&apos;');
    };
}

// require routes here so they can access globals
var routes = require('./routes/index');
var appRouter = require('./routes/app');

// TODO: make this betterified
var api_main = require('./routes/api_main');
var api_ext = require('./routes/api_ext');
var api_labs = require('./routes/api_labs');
var api_hwView = require('./routes/api_hwView');
var api_myDay = require('./routes/api_myDay');
var api_overview = require('./routes/api_overview');
var api_planner = require('./routes/api_planner');
var api_prefs = require('./routes/api_prefs');
var api_admin = require('./routes/api_admin');
var api_groups = require('./routes/api_groups');
var api_connected = require('./routes/api_connected');
var api_pageHandler = require('./routes/api_pageHandler');
var api_sms = require('./routes/api_sms');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false, limit: "500kb" }));
app.use(cookieParser());

// sessions
var sess = {
    store: global.sessionStore,
    secret: 'keyboard cat',
    cookie: {}
};

/*
if (global.env == "production") {
    sess.cookie.secure = true; // serve secure cookies
}
*/

app.use(session(sess));

app.use(function(req, res, next) {
    if (req.host == "staging.myhomework.space") {
        res.locals.isStaging = true;
    } else {
        res.locals.isStaging = false;
    }
    next();
});

app.use(basePath + '/static', express.static(path.join(__dirname, 'public')));

app.use(basePath + '/', routes);
app.use(basePath + '/app', appRouter);

app.use(basePath + '/api/v1/', api_main);
app.use(basePath + '/api/v1/ext', api_ext);
app.use(basePath + '/api/v1/labs', api_labs);
app.use(basePath + '/api/v1/hwView', api_hwView);
app.use(basePath + '/api/v1/myDay', api_myDay);
app.use(basePath + '/api/v1/overview', api_overview);
app.use(basePath + '/api/v1/planner', api_planner);
app.use(basePath + '/api/v1/prefs', api_prefs);
app.use(basePath + '/api/v1/admin', api_admin);
app.use(basePath + '/api/v1/groups', api_groups);
app.use(basePath + '/api/v1/connected', api_connected);
app.use(basePath + '/api/v1/pageHandler', api_pageHandler);
app.use(basePath + '/api/v1/sms', api_sms);

// catch 404 and forward to error handler
app.use(global.getOptionalUserRecord); // get record to show logged in on 404

app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers
app.use(function(err, req, res, next) {
    // logging
    if (err) {
        var status = (err.status || 500);
        var message = (err.message || "");
        var name = (err.name || "");
        var stack = (err.stack || "");
        var username = (req.session ? (req.session.loggedIn ? req.session.username : undefined) : undefined);
        var url = req.url;
        var headers = JSON.stringify(req.headers);
        knex("errors").insert({
            username: username,
            status: status,
            stack: stack,
            msg: message,
            url: url,
            headers: headers
        }).then(function() {
            res.locals.errorLogged = true;
            next(err);
        }).catch(function() {
            res.locals.errorLogged = false;
            next(err);
        });
    } else {
        next(err);
    }
});

app.use(function(err, req, res, next) {
    // 404
    if (err.status == 404) {
        res.status(404);
        res.render("404");
    } else {
        next(err);
    }
});

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;