index.js 9.48 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 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 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....');
    process.exit();
}

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(',');
        }
        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 renderDashboardIndex(req, res) {
    var template = "dashboard.index.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(),
    }
    
    res.render(template, { 
        gateway_name: config.globals.gateway_name,
        title: 'Dashboard',
        matrix: JSON.stringify(matrix, null, 2),
        config: JSON.stringify(config, null, 2),
        nodejs_versions: JSON.stringify(process.versions, null, 2),
        pendings: JSON.stringify(aaa.getPendingList(), null, 2),
        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),
    });
}

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('/dashboard'); });
    app.get('/config', loggedIn, renderConfigIndex);
    app.get('/login', renderLoginPage);
    app.post('/login', passport.authenticate('local', { successRedirect: '/dashboard', 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('/dashboard', loggedIn, renderDashboardIndex);
    
    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;