forked from flanchan/doushio
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.
204 lines
4.8 KiB
204 lines
4.8 KiB
var _ = require('../lib/underscore'),
|
|
authcommon = require('./common'),
|
|
caps = require('../server/caps'),
|
|
common = require('../common'),
|
|
okyaku = require('../server/okyaku'),
|
|
STATE = require('../server/state');
|
|
|
|
var ADDRS = STATE.dbCache.addresses;
|
|
authcommon.modCache.addresses = ADDRS;
|
|
var ip_key = authcommon.ip_key;
|
|
|
|
function on_client_ip(ip, clients) {
|
|
var addr = {key: ip_key(ip), ip: ip, count: clients.length};
|
|
// This will leak 0-count clients.
|
|
// I want them to expire after a delay, really. Should reduce churn.
|
|
this.send([0, common.COLLECTION_ADD, 'addrs', addr]);
|
|
}
|
|
|
|
function on_refresh(info) {
|
|
this.send([0, common.MODEL_SET, 'adminState', info]);
|
|
}
|
|
|
|
function connect() {
|
|
return global.redis;
|
|
}
|
|
|
|
function address_view(addr) {
|
|
addr = _.extend({}, addr);
|
|
addr.shallow = false;
|
|
var clients = STATE.clientsByIP[addr.ip];
|
|
if (clients) {
|
|
addr.count = clients.length;
|
|
addr.country = clients[0] && clients[0].country || '';
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
okyaku.dispatcher[authcommon.FETCH_ADDRESS] = function (msg, client) {
|
|
if (!caps.can_moderate(client.ident))
|
|
return false;
|
|
var ip = msg[0];
|
|
if (!authcommon.is_valid_ip(ip))
|
|
return false;
|
|
var key = ip_key(ip);
|
|
var addr = ADDRS[key];
|
|
if (addr) {
|
|
client.send([0, common.COLLECTION_ADD, 'addrs', address_view(addr)]);
|
|
return true;
|
|
}
|
|
|
|
// Cache miss
|
|
ADDRS[key] = addr = {ip, key, shallow: true};
|
|
var r = connect();
|
|
r.hgetall('ip:'+key, function (err, info) {
|
|
if (err) {
|
|
if (ADDRS[key] === addr)
|
|
delete ADDRS[key];
|
|
return client.kotowaru(err);
|
|
}
|
|
if (ADDRS[key] !== addr)
|
|
return;
|
|
|
|
_.extend(addr, info);
|
|
client.send([0, common.COLLECTION_ADD, 'addrs', address_view(addr)]);
|
|
});
|
|
return true;
|
|
}
|
|
|
|
okyaku.dispatcher[authcommon.SET_ADDRESS_NAME] = function (msg, client) {
|
|
if (!caps.can_moderate(client.ident))
|
|
return false;
|
|
var ip = msg[0], name = msg[1];
|
|
if (!authcommon.is_valid_ip(ip) || typeof name != 'string')
|
|
return false;
|
|
var key = ip_key(ip);
|
|
name = name.trim().slice(0, 30);
|
|
var m = connect().multi();
|
|
if (!name) {
|
|
m.hdel('ip:' + key, 'name');
|
|
m.srem('namedIPs', key);
|
|
}
|
|
else {
|
|
m.hset('ip:' + key, 'name', name);
|
|
m.sadd('namedIPs', key);
|
|
}
|
|
|
|
m.exec(function (err) {
|
|
if (err)
|
|
return client.kotowaru(err);
|
|
|
|
// should observe a publication for this cache update
|
|
var addr = ADDRS[key];
|
|
if (!addr)
|
|
addr = ADDRS[key] = {key: key, ip: ip};
|
|
addr.name = name;
|
|
|
|
var amend = {name: name};
|
|
client.send([0, common.MODEL_SET, ['addrs', key], amend]);
|
|
});
|
|
return true;
|
|
};
|
|
|
|
var panelListeners = 0, panelInterval = 0;
|
|
|
|
function listen_panel(client) {
|
|
STATE.emitter.on('change:clientsByIP', client.on_client_ip);
|
|
STATE.emitter.on('refresh', client.on_refresh);
|
|
|
|
panelListeners++;
|
|
if (panelListeners == 1) {
|
|
panelInterval = setInterval(refresh_panel_state, 10*1000);
|
|
}
|
|
}
|
|
|
|
function unlisten_panel(client) {
|
|
STATE.emitter.removeListener('change:clientsByIP',client.on_client_ip);
|
|
STATE.emitter.removeListener('refresh', client.on_refresh);
|
|
|
|
panelListeners--;
|
|
if (panelListeners == 0) {
|
|
clearInterval(panelInterval);
|
|
panelInterval = 0;
|
|
}
|
|
}
|
|
|
|
function snapshot_panel() {
|
|
var addrCount = 0;
|
|
for (var key in ADDRS)
|
|
addrCount++;
|
|
|
|
var ranges = STATE.dbCache.ranges;
|
|
var banCount = ranges.bans ? ranges.bans.length : 0;
|
|
|
|
return {
|
|
memoryUsage: process.memoryUsage(),
|
|
uptime: process.uptime(),
|
|
addrs: addrCount,
|
|
bans: banCount,
|
|
};
|
|
}
|
|
|
|
function refresh_panel_state() {
|
|
STATE.emitter.emit('refresh', snapshot_panel());
|
|
}
|
|
|
|
function subscribe() {
|
|
if (this.on_client_ip)
|
|
return false;
|
|
|
|
this.on_client_ip = on_client_ip.bind(this);
|
|
this.on_refresh = on_refresh.bind(this);
|
|
this.unsubscribe_admin_state = unsubscribe.bind(this);
|
|
this.once('close', this.unsubscribe_admin_state);
|
|
listen_panel(this);
|
|
|
|
var state = snapshot_panel();
|
|
state.visible = true;
|
|
this.send([0, common.MODEL_SET, 'adminState', state]);
|
|
|
|
var ips = [];
|
|
for (var ip in STATE.clientsByIP) {
|
|
var key = ip_key(ip);
|
|
var a = ADDRS[key];
|
|
ips.push(a ? address_view(a) : {
|
|
key: key, ip: ip, shallow: true,
|
|
count: STATE.clientsByIP[ip].length
|
|
});
|
|
}
|
|
this.send([0, common.COLLECTION_RESET, 'addrs', ips]);
|
|
|
|
return true;
|
|
}
|
|
|
|
function unsubscribe() {
|
|
if (!this.on_client_ip)
|
|
return false;
|
|
|
|
unlisten_panel(this);
|
|
this.removeListener('close', this.unsubscribe_admin_state);
|
|
this.on_client_ip = null;
|
|
this.on_refresh = null;
|
|
this.unsubscribe_admin_state = null;
|
|
|
|
this.send([0, common.MODEL_SET, 'adminState', {visible: false}]);
|
|
return true;
|
|
}
|
|
|
|
okyaku.dispatcher[common.SUBSCRIBE] = function (msg, client) {
|
|
if (!caps.can_administrate(client.ident))
|
|
return false;
|
|
if (msg[0] != 'adminState')
|
|
return false;
|
|
|
|
return subscribe.call(client);
|
|
};
|
|
|
|
okyaku.dispatcher[common.UNSUBSCRIBE] = function (msg, client) {
|
|
if (!caps.can_administrate(client.ident))
|
|
return false;
|
|
if (msg[0] != 'adminState')
|
|
return false;
|
|
return unsubscribe.call(client);
|
|
};
|