UNPKG

cabmin

Version:

Simple control panel for Node.js based on OrangeBox

387 lines (297 loc) 8.32 kB
var crypto = require('crypto'); var fs = require('fs'); var extend = require('util')._extend; var marked = require('marked'); var swig = require('swig'); var markCache = {}; var swigCache = {}; var fakeUser = {upd: 0}; marked.setOptions({ highlight: function (code) { return require('highlight.js').highlightAuto(code).value; }}); exports.sha512 = function (text, secret, salt) { secret = secret || 'r4ya6u7i8eu254t'; var sha = crypto.createHmac('sha512', secret); sha.update(String(text)); if (salt) { sha.update(salt); } return sha.digest('hex'); }; exports.md5 = function (text, salt) { var md5 = crypto.createHash('md5'); md5.update(String(text)); if (salt) { md5.update(salt); } return md5.digest('hex'); }; exports.hex2rgba = function (hex, opacity) { hex = hex.replace('#', ''); var r = parseInt(hex.substring(0, 2), 16); var g = parseInt(hex.substring(2, 4), 16); var b = parseInt(hex.substring(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity / 100 + ')'; }; exports.arrayTest = function (arr1, arr2) { return arr1.filter(function (i) { return (arr2.indexOf(i) > -1); }); }; exports.wrap = function (options, callback) { return function (request, response, next) { var viewsPath = options.views || response.getVariable('views'); swig.setDefaults({ autoescape: response.variables['view autoescape'] }); swig.setDefaultTZOffset(response.variables['view timezone']); var res = extend({cook: response.cook}, response); if (request.options) { extend(request.options, options); } else { extend(request, {options: extend({}, options)}); } res.render = function (view, data, cabView, obj) { if (arguments.length === 1) { data = view; view = false; } if ( typeof view === 'object' && typeof data === 'string') { var swith = view; view = data; data = swith; } if (view) { if (view.indexOf('.html') === -1) { // файл в viewsPath view = viewsPath + '/' + view + '.html'; } if (!swigCache[view]) { swigCache[view] = swig.compileFile(view); } var d = extend(extend({}, request.options), typeof data === 'object' ? data : {data: data}); data = swigCache[view](d); } response.status(res.statusCode); response.render( cabView || 'index', extend(extend({data: data}, request.options), obj || {})); }; res.json = function (data) { response.status(res.statusCode); response.type('json').send(JSON.stringify(data, '\t', 4)); }; res.mark = function (data) { var hash = exports.md5(data); if (!markCache[hash]) { markCache[hash] = marked(data, true); setTimeout(function () { if (markCache[hash]) { delete markCache[hash]; } }, 10000); } res.render('<div class="markdown">' + markCache[hash] + '</div>'); }; res.dump = function (data) { res.mark('```\n' + JSON.stringify(data, '\t', 4) + '\n```'); }; callback(request, res, next); }; }; exports.run = function (req, res, next) { var step = true; var pg404 = false; if (!req.params.module || typeof req.options.modules[req.params.module] === 'undefined') { if (!req.options.modules['404']) { res.redirect(req.options.home); step = false; next(); } else { pg404 = true; } } if (step) { var page = req.options.modules[req.params.module]; var user = req.options.user || false; var u = user ? req.options.users[user.login] : fakeUser; var updated = false; if (page && page.tabs) { var firstTab; var activeTab; for (var tabKey in page.tabs) { if (!firstTab) { firstTab = tabKey; } if (tabKey === 'default' && !activeTab) { activeTab = 'default'; } if (req.query.tab && tabKey === req.query.tab) { activeTab = tabKey; } } page.activeTab = activeTab || firstTab; } if (pg404) { req.options.page = page = req.options.modules['404']; } if (user || req.options.noAuth) { var sec = parseInt(Date.now() / 1000, 10); if (!u.upd || Number(u.upd) !== sec) { u.upd = sec; req.options.menu.forEach(function (el, i) { var info = req.options.reinfo[req.options.menu[i].route](req.options.noAuth ? false : user); exports.add(req.options, info); }); } updated = true; } req.options.menu.forEach(function (el, i) { if (updated) extend(req.options.menu[i], req.options.modules[req.options.menu[i].r]); el.active = req.options.baseUrl + '/' + page.route.toLowerCase() === el.url.toLowerCase(); }); req.options.page = page; req.current = page; var pg = req.options.page.groups.split(',').map(function (el) { return String(el.trim()); }); var ug = (user && user.groups) ? user.groups.split(',').map(function (el) { return String(el.trim()); }) : []; ug.push('all'); if (user && user.groups) { req.options.menu = req.options.menu.filter(function (el) { if (!el.groups) return false; var g = el.groups.split(',').map(function (el) {return String(el.trim());}); if (!exports.arrayTest(ug, g).length) { return false; } return true; }); } if (!exports.arrayTest(ug, pg).length) { if (req.options.modules['access-denied']) { req.options.page = page = req.options.modules['access-denied']; } else { page.showTitle = false; page.method = function access(req, res) { res.render(__dirname + '/../views/access-denied.html', {}); }; } } var submenus = {}; req.options.menu.forEach(function (el, i) { if (el.submenu && el.submenu.length) { if (!submenus[el.submenu]) { submenus[el.submenu] = { items : [], order : 1 }; } var sub = submenus[el.submenu]; sub.items.push(el); if (el.subOrder) { sub.order = el.subOrder; } if (el.active) { sub.open = true; } req.options.menu[i].showInMenu = false; } }); req.options.submenus = sortByProp(submenus, 'order'); var p = req.path.replace(req.options.page.url + '/', '').split('/'); extend(req.params, p); req.length = p.length; page.method(req, res, next); } }; function sortByProp(obj, prop) { var sortObj = {}; var after = {}; var r = {}; Object.keys(obj).forEach(function (key) { var el = obj[key]; if (!el[prop]) { after[key] = el; return; } var k = el[prop]; if (!sortObj[k]) { sortObj[k] = {}; } sortObj[k][key] = el; }); Object.keys(sortObj).sort(function (a, b) { return a - b; }).forEach(function (k) { var el = sortObj[k]; Object.keys(el).forEach(function (k) { r[k] = el[k]; }); }); return extend(r, after); } exports.load = function (options) { var path = options.path; var controllers = {}; var reinfo = {}; fs.readdirSync(path).forEach(function (el) { // Require only .js files if (/.*\.js$/gi.test(el)) { var module = require(path + '/' + el); if (typeof module === 'function') { var info = module(); if (typeof info === 'object') { for (var r in info) { reinfo[r] = module; } extend(controllers, info); } } } }); for (var r in controllers) { controllers[r] = extend({ title: '', route: r, showTitle: true, showInMenu: true, parentMenu: '/', order: 5, count: 0, groups: 'all', url: (options.baseUrl + '/' + r), note: '', active: 0 }, controllers[r]); options.menu.push(controllers[r]); } options.menu.sort(function (a, b) {return a.order > b.order ? 1 : (a.order < b.order ? -1 : 0);}); exports.add(options, controllers); if (options.reinfo) { extend(options.reinfo, reinfo); } else { options.reinfo = reinfo; } if (options.loadDefaults) { options.loadDefaults = false; options.path = __dirname + '/../default-pages'; exports.load(options); } }; exports.add = function (options, obj) { if (typeof obj === 'object') { if (options.modules) { for (var el in obj) { if (options.modules[el]) { extend(options.modules[el], obj[el]); } else { options.modules[el] = obj[el]; } } } else { options.modules = obj; } } }; exports.MINUTE = 60 * 1000; exports.HOUR = 60 * exports.MINUTE; exports.DAY = 24 * exports.HOUR; exports.WEEK = 7 * exports.DAY; exports.MONTH = 30 * exports.DAY; exports.YEAR = 365 * exports.DAY;