You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
4.5 KiB
211 lines
4.5 KiB
6 years ago
|
var _ = require('../lib/underscore'),
|
||
|
async = require('async'),
|
||
|
config = require('../config'),
|
||
|
crypto = require('crypto'),
|
||
|
fs = require('fs'),
|
||
|
hooks = require('../hooks'),
|
||
|
path = require('path'),
|
||
|
pipeline = require('../pipeline'),
|
||
|
vm = require('vm');
|
||
|
|
||
|
_.templateSettings = {
|
||
|
interpolate: /\{\{(.+?)\}\}/g
|
||
|
};
|
||
|
|
||
|
exports.emitter = new (require('events').EventEmitter);
|
||
|
|
||
|
exports.dbCache = {
|
||
|
OPs: {},
|
||
|
opTags: {},
|
||
|
threadSubs: {},
|
||
|
YAKUMAN: 0,
|
||
|
funThread: 0,
|
||
|
addresses: {},
|
||
|
ranges: {},
|
||
|
connTokenSecretKey: null,
|
||
|
};
|
||
|
|
||
|
var HOT = exports.hot = {};
|
||
|
var RES = exports.resources = {};
|
||
|
exports.clients = {};
|
||
|
exports.clientsByIP = {};
|
||
|
|
||
|
function reload_hot_config(cb) {
|
||
|
fs.readFile('hot.js', 'UTF-8', function (err, js) {
|
||
|
if (err)
|
||
|
cb(err);
|
||
|
var hot = {};
|
||
|
try {
|
||
|
vm.runInNewContext(js, hot);
|
||
|
}
|
||
|
catch (e) {
|
||
|
return cb(e);
|
||
|
}
|
||
|
if (!hot || !hot.hot)
|
||
|
return cb('Bad hot config.');
|
||
|
|
||
|
// Overwrite the original object just in case
|
||
|
Object.keys(HOT).forEach(function (k) {
|
||
|
delete HOT[k];
|
||
|
});
|
||
|
_.extend(HOT, hot.hot);
|
||
|
read_exits('exits.txt', function () {
|
||
|
hooks.trigger('reloadHot', HOT, cb);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// load the encryption key for connToken
|
||
|
hooks.hook('reloadHot', function (hot, cb) {
|
||
|
var r = global.redis;
|
||
|
var key = 'ctoken-secret-key';
|
||
|
r.get(key, function (err, secretHex) {
|
||
|
if (err) return cb(err);
|
||
|
if (secretHex) {
|
||
|
var secretBytes = new Buffer(secretHex, 'hex');
|
||
|
if (secretBytes.length != 32)
|
||
|
return cb('ctoken secret key is invalid');
|
||
|
HOT.connTokenSecretKey = secretBytes;
|
||
|
return cb(null);
|
||
|
}
|
||
|
// generate a new one
|
||
|
var secretKey = crypto.randomBytes(32);
|
||
|
r.setnx(key, secretKey.toString('hex'), function (err, wasSet) {
|
||
|
if (err) return cb(err);
|
||
|
if (wasSet)
|
||
|
HOT.connTokenSecretKey = secretKey;
|
||
|
else
|
||
|
assert(!!HOT.connTokenSecretKey);
|
||
|
cb(null);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
function reload_scripts(cb) {
|
||
|
var json = path.join('state', 'scripts.json');
|
||
|
fs.readFile(json, 'UTF-8', function (err, json) {
|
||
|
if (err)
|
||
|
cb(err);
|
||
|
var js;
|
||
|
try {
|
||
|
js = JSON.parse(json);
|
||
|
}
|
||
|
catch (e) {
|
||
|
return cb(e);
|
||
|
}
|
||
|
if (!js || !js.vendor || !js.client)
|
||
|
return cb('Bad state/scripts.json.');
|
||
|
|
||
|
HOT.VENDOR_JS = js.vendor;
|
||
|
HOT.CLIENT_JS = js.client;
|
||
|
|
||
|
var modJs = path.join('state', js.mod);
|
||
|
fs.readFile(modJs, 'UTF-8', function (err, modSrc) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
RES.modJs = modSrc;
|
||
|
cb(null);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function reload_resources(cb) {
|
||
|
|
||
|
var deps = require('../deps');
|
||
|
|
||
|
read_templates(function (err, tmpls) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
|
||
|
_.extend(RES, expand_templates(tmpls));
|
||
|
|
||
|
hooks.trigger('reloadResources', RES, cb);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function read_templates(cb) {
|
||
|
function read(dir, file) {
|
||
|
return fs.readFile.bind(fs, path.join(dir, file), 'UTF-8');
|
||
|
}
|
||
|
|
||
|
async.parallel({
|
||
|
index: read('tmpl', 'index.html'),
|
||
|
filter: read('tmpl', 'filter.html'),
|
||
|
curfew: read('tmpl', 'curfew.html'),
|
||
|
suspension: read('tmpl', 'suspension.html'),
|
||
|
notFound: read('www', '404.html'),
|
||
|
serverError: read('www', '50x.html'),
|
||
|
}, cb);
|
||
|
}
|
||
|
|
||
|
function expand_templates(res) {
|
||
|
var templateVars = _.clone(HOT);
|
||
|
_.extend(templateVars, require('../imager/config'));
|
||
|
_.extend(templateVars, config);
|
||
|
|
||
|
function tmpl(data) {
|
||
|
var expanded = _.template(data, templateVars);
|
||
|
return {tmpl: expanded.split(/\$[A-Z]+/),
|
||
|
src: expanded};
|
||
|
}
|
||
|
|
||
|
var ex = {
|
||
|
navigationHtml: make_navigation_html(),
|
||
|
filterTmpl: tmpl(res.filter).tmpl,
|
||
|
curfewTmpl: tmpl(res.curfew).tmpl,
|
||
|
suspensionTmpl: tmpl(res.suspension).tmpl,
|
||
|
notFoundHtml: res.notFound,
|
||
|
serverErrorHtml: res.serverError,
|
||
|
};
|
||
|
|
||
|
var index = tmpl(res.index);
|
||
|
ex.indexTmpl = index.tmpl;
|
||
|
var hash = crypto.createHash('md5').update(index.src);
|
||
|
ex.indexHash = hash.digest('hex').slice(0, 8);
|
||
|
|
||
|
return ex;
|
||
|
}
|
||
|
|
||
|
exports.reload_hot_resources = function (cb) {
|
||
|
pipeline.refresh_deps();
|
||
|
|
||
|
async.series([
|
||
|
reload_hot_config,
|
||
|
pipeline.rebuild,
|
||
|
reload_scripts,
|
||
|
reload_resources,
|
||
|
], cb);
|
||
|
}
|
||
|
|
||
|
function make_navigation_html() {
|
||
|
if (!HOT.INTER_BOARD_NAVIGATION)
|
||
|
return '';
|
||
|
var bits = ['<nav>['];
|
||
|
config.BOARDS.forEach(function (board, i) {
|
||
|
if (board == config.STAFF_BOARD)
|
||
|
return;
|
||
|
if (i > 0)
|
||
|
bits.push(' / ');
|
||
|
bits.push('<a href="../'+board+'/">'+board+'</a>');
|
||
|
});
|
||
|
bits.push(']</nav>');
|
||
|
return bits.join('');
|
||
|
}
|
||
|
|
||
|
function read_exits(file, cb) {
|
||
|
fs.readFile(file, 'UTF-8', function (err, lines) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
var exits = [], dest = HOT.BANS;
|
||
|
lines.split(/\n/g).forEach(function (line) {
|
||
|
var m = line.match(/^(?:^#\d)*(\d+\.\d+\.\d+\.\d+)/);
|
||
|
if (!m)
|
||
|
return;
|
||
|
var exit = m[1];
|
||
|
if (dest.indexOf(exit) < 0)
|
||
|
dest.push(exit);
|
||
|
});
|
||
|
cb(null);
|
||
|
});
|
||
|
}
|