index.js 10.4 KB
var express = require('express');
var app = express();
var nunjucks = require('nunjucks');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var crypto = require('crypto');
//var sha256sum = crypto.createHash('sha256');
var fsextra = require('fs-extra');
var strftime = require('strftime');
var fs = require('fs');
var ini = require('ini');
var os = require('os');
var naturalSort = require('javascript-natural-sort');
var pkginfo = require('pkginfo')(module, 'version');

var config;
var aaa;
var matrix;
var view_path = __dirname + '/views';

function loggedIn(req, res, next) {
    if (req.user) {
        next();
    } else {
        res.redirect('/login');
    }
}

function matchedPasswordAndHash(password, hash) {
    var sha256sum = crypto.createHash('sha256');
    sha256sum.update(password);
    return (hash == sha256sum.digest('hex'))
}

function renderDashboard(req, res) {
    res.render('dashboard.html', {
        gateway_name: config.globals.gateway_name,
        title: 'Dashboard',
        config: config,
        matrix: matrix,
    });
}

function renderConfigIndex(req, res) {

    masked = ['config.expresso.password'];

    res.render('config.index.html', {
        gateway_name: config.globals.gateway_name,
        title: 'CONFIG', configs: config,
        config_dirty: matrix.config_dirty,
        isMask: function(scope, keyword) {
            return masked.indexOf('config.' + scope + '.' + keyword) >= 0;
        },
    });
}

function renderConfigEdit(req, res) {
    var template = "config.edit.html";

    if (req.scope == 'expresso' && req.keyword == 'password') {
        template = "config.edit.expresso.password.html";
    }

    res.render(template, {
        gateway_name: config.globals.gateway_name,
        title: 'Edit Konfigurasi',
        scope: req.scope,
        keyword: req.keyword,
        old_value: config[req.scope][req.keyword]
    });
}

function renderConfigAdd(req, res){

    var template = 'config.add.html';
    if (req.scope == 'products') {
        template = 'config.add.products.html';
    }

    res.render(template, {
        gateway_name: config.globals.gateway_name,
        title: 'Tambah Item Konfigurasi',
        scope: req.scope
    });
}

function submitPause(req, res) {
    matrix.pause = 1;
    res.redirect('/');
    return;
}

function submitResume(req, res) {
    matrix.pause = 0;
    res.redirect('/');
    return;
}

function submitTerminate(req, res) {
    res.end('Terminating....', function() {
        setTimeout(process.exit, 2 * 1000);
    });
}

function submitConfigEdit(req, res) {

    if (req.scope == 'expresso' && req.keyword == 'password') {
        if (req.body.newValue != req.body.newValue2) {
            res.redirect('/config/edit/' + req.scope + '/' + req.keyword);
            return;
        }

        var sha256sum = crypto.createHash('sha256');
        sha256sum.update(req.body.newValue);
        req.body.newValue = sha256sum.digest('hex');

    } else if (req.scope == 'globals' && req.keyword == 'products' && req.body.newValue.trim()) {

        try {
            /*
            var unsortedString = req.body.newValue.toUpperCase().replace(/ /g, '').trim();
            var unsortedProducts = unsortedString.split(',');
            var sortedProducts = aaa.sortProductsArray(unsortedProducts);
            req.body.newValue = sortedProducts.join(',');
            */

            req.body.newValue = req.body.newValue.trim().toUpperCase().split(/\W+/).sort(naturalSort).join(',');
        }
        catch(e) {
            console.log('Error sorting products');
        }
    }

    if (config[req.body.scope][req.body.keyword] != req.body.newValue.trim()) {
        config[req.body.scope][req.body.keyword] = req.body.newValue.trim();
        matrix.config_dirty = 1;
    }

    res.redirect('/config');
}

function submitConfigAdd(req, res) {

    if (!req.body.newKeyword.trim()) {
        res.redirect('/config');
        return;
    }

    if (config[req.body.scope] === undefined) {
        config[req.body.scope] = {};
    }

    config[req.body.scope][req.body.newKeyword.trim()] = req.body.newValue.trim();
    matrix.config_dirty = 1;

    res.redirect('/config#config.' + req.body.scope + '.' + req.body.newKeyword);
}

function submitConfigDelete(req, res) {

    matrix.config_dirty = 1;
    delete config[req.scope][req.keyword];

    res.redirect('/config');
}

function submitConfigSave(req, res) {
    fsextra.copy('config.ini', 'config.ini.backup_' + strftime('%F_%H%M%S', new Date()), function(err) {
        fs.writeFileSync('./config.ini', ini.stringify(config));
        matrix.config_dirty = 0;
        res.redirect('/config');
    });
}

function renderLoginPage(req, res) {
    res.render('signin.html', {title: config.globals.gateway_name});
}

function renderConfigAskDelete(req, res) {
    res.render('config.ask.delete.html', {
        gateway_name: config.globals.gateway_name,
        title: 'Konfirmasi Penghapusan',
        scope: req.scope,
        keyword: req.keyword,
        value: config[req.scope][req.keyword],
    });
}

function renderAskTerminate(req, res) {
    res.render('ask.terminate.html', {
        gateway_name: config.globals.gateway_name,
        title: 'Konfirmasi Terminasi'
    });
}


function renderRuntimeInfo(req, res) {
    var template = "runtime-info.html";

    var os_info = {
        uptime: os.uptime(),
        loadavg: os.loadavg(),
        hostname: os.hostname(),
        type: os.type(),
        platform: os.platform(),
        arch: os.arch(),
        release: os.release(),
        totalmem: os.totalmem(),
    }

    var modules_versions = {
        'expresso': module.exports.version
    }

    try {
        modules_versions.sate24 = aaa.version;
    } catch (e) {}

    var pendings;
    try {
        pendings = JSON.stringify(aaa.getPendingList(), null, 2);
    }
    catch(err) {
        pendings = "{}";
    }

    res.render(template, {
        gateway_name: config.globals.gateway_name,
        title: 'Runtime Info',
        matrix: JSON.stringify(matrix, null, 2),
        config: JSON.stringify(config, null, 2),
        nodejs_versions: JSON.stringify(process.versions, null, 2),
        pendings: pendings,
        memory_usage: JSON.stringify(process.memoryUsage(), null, 2),
        uptime: process.uptime(),
        os_info: JSON.stringify(os_info, null, 2),
        net_ifaces: JSON.stringify(os.networkInterfaces(), null, 2),
        modules_versions: JSON.stringify(modules_versions, null, 2)
    });
}

function isNoTemplateCache() {
    var retval = false;
    try {
        retval = config.expresso.no_template_cache.toUpperCase() == 'YES';
    }
    catch(err) {
        return retval;
    }

    return retval;
}

function createServer() {
    if (!config.expresso || !config.expresso.listen_port) {
        console.log('Not starting expresso admin UI');
        return;
    }

    nunjucks.configure(view_path, {
        autoescape: true,
        noCache: isNoTemplateCache(),
        express: app
    });

    app.use(express.static(__dirname + '/public'));
    app.use(require('cookie-parser')());
    app.use(require('body-parser').urlencoded({ extended: true }));

    var express_session_opts = {
        secret: 'keyboard cat',
        resave: false,
        saveUninitialized: false
    };

    if (config.expresso && config.expresso.session_name) {
        express_session_opts.name = config.expresso.session_name;
    }

    app.use(require('express-session')(express_session_opts));
    app.use(passport.initialize());
    app.use(passport.session());

    passport.use(new LocalStrategy(
        function(username, password, done) {

            if (username == 'admin' && matchedPasswordAndHash(password, config.expresso.password)) {
                var user = {
                    username: username,
                    last_login: Date.now() / 1000 | 0
                }

                return done(null, user);
            }

            return done(null, false, { message: 'Incorrect password.' });
        }
    ));

    passport.serializeUser(function(user, done) {
        done(null, user.username);
    });

    passport.deserializeUser(function(username, done) {
        var user = {
            username: username
        };
        done(null, user);
    });

    app.get('/', loggedIn, function(req, res) { res.redirect('/runtime-info'); });
    app.get('/dashboard', loggedIn, renderDashboard);
    app.get('/config', loggedIn, renderConfigIndex);
    app.get('/login', renderLoginPage);
    app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }));
    app.get('/logout', function(req, res){
        req.logout();
        res.redirect('/login');
    });

    app.param('scope', function(req, res, next, value) {
        req.scope = value;
        next();
    });

    app.param('keyword', function(req, res, next, value) {
        req.keyword = value;
        next();
    });

    app.get('/config/edit/:scope/:keyword', loggedIn, renderConfigEdit);
    app.post('/config/edit/:scope/:keyword', loggedIn, submitConfigEdit);

    app.get('/config/ask-delete/:scope/:keyword', loggedIn, renderConfigAskDelete);
    app.get('/config/delete/:scope/:keyword', loggedIn, submitConfigDelete);

    app.get('/config/add/:scope', loggedIn, renderConfigAdd);
    app.post('/config/add/:scope', loggedIn, submitConfigAdd);

    app.get('/config/save', loggedIn, submitConfigSave);

    app.get('/pause', loggedIn, submitPause);
    app.get('/resume', loggedIn, submitResume);
    app.get('/terminate', loggedIn, submitTerminate);
    app.get('/ask-terminate', loggedIn, renderAskTerminate);

    app.get('/runtime-info', loggedIn, renderRuntimeInfo);

    var server = app.listen(config.expresso.listen_port, function () {
        var host = server.address().address;
        var port = server.address().port;

        console.log('Expresso admin UI listening at http://%s:%s', host, port);
    });
}

function sanitizeMatrix(matrix) {
    if (matrix === undefined) {
        matrix = {};
    }
    if (matrix.config_dirty === undefined) {
        matrix.config_dirty = 0;
    }
}

function start(options) {
    if (options['config']) {
        config = options['config'];
    }

    if (options['matrix']) {
        matrix = options['matrix'];
    }
    sanitizeMatrix(matrix);

    if (options['aaa']) {
        aaa = options['aaa'];
    }

    if (options['expresso_views']) {
        view_path = options['expresso_views'];
    }

    createServer();
}

exports.start = start;