return cross-shard requests only to shard that requested

make api/banme POST request
This commit is contained in:
HF 2022-09-22 16:25:03 +02:00
parent 279819eac0
commit 2707fb6123
6 changed files with 76 additions and 31 deletions

View File

@ -56,7 +56,7 @@ const RegUser = sequelize.define('User', {
}, },
discordid: { discordid: {
type: DataTypes.CHAR(18), type: DataTypes.CHAR(20),
allowNull: true, allowNull: true,
}, },

View File

@ -7,11 +7,21 @@ import { banIP } from '../../data/sql/Ban';
import { getIPv6Subnet, getIPFromRequest } from '../../utils/ip'; import { getIPv6Subnet, getIPFromRequest } from '../../utils/ip';
async function banme(req, res) { async function banme(req, res) {
const { code } = req.query; const { code } = req.body;
const ip = getIPFromRequest(req);
// eslint-disable-next-line max-len
logger.info(`AUTOBAN ${code} - ${ip} of user ${req.user.id} with ua "${req.headers['user-agent']}"`);
let reason = 'AUTOBAN'; let reason = 'AUTOBAN';
if (code === '1') { let expires = 0;
if (code === 1) {
reason = 'Userscript Bot'; reason = 'Userscript Bot';
} else if (code === '2') { expires = Date.now() + 1000 * 3600 * 24 * 7;
/*
* ignore it for now to collect data manually
*
} else if (code === 2) {
const ua = req.headers['user-agent']; const ua = req.headers['user-agent'];
if (ua && (ua.includes('Android') || ua.includes('iPhone'))) { if (ua && (ua.includes('Android') || ua.includes('iPhone'))) {
res.json({ res.json({
@ -20,14 +30,18 @@ async function banme(req, res) {
return; return;
} }
reason = 'Captcha Solving Script'; reason = 'Captcha Solving Script';
expires = Date.now() + 1000 * 3600 * 24 * 3;
*/
} else {
res.json({
status: 'nope',
});
return;
} }
const ip = getIPFromRequest(req);
// eslint-disable-next-line max-len
logger.info(`AUTOBAN ${code}${ip} of user ${req.user.id} with ua "${req.headers['user-agent']}"`);
await banIP( await banIP(
getIPv6Subnet(ip), getIPv6Subnet(ip),
reason, reason,
0, expires,
1, 1,
); );
res.json({ res.json({

View File

@ -98,7 +98,7 @@ router.get('/chathistory', chatHistory);
router.get('/me', me); router.get('/me', me);
router.get('/banme', banme); router.post('/banme', banme);
router.use('/auth', auth); router.use('/auth', auth);

View File

@ -35,6 +35,17 @@ class MessageBroker extends SocketEvents {
super(); super();
this.isCluster = true; this.isCluster = true;
this.thisShard = SHARD_NAME; this.thisShard = SHARD_NAME;
/*
* currently running cross-shard requests,
* are tracked in order to only send them to receiving
* shard
* [{
* id: request id,
* shard: requesting shard name,
* ts: timestamp of request,
* },...]
*/
this.csReq = [];
/* /*
* all other shards * all other shards
*/ */
@ -95,22 +106,16 @@ class MessageBroker extends SocketEvents {
const key = message.slice(message.indexOf(':') + 1, comma); const key = message.slice(message.indexOf(':') + 1, comma);
console.log('CLUSTER: Broadcast', key); console.log('CLUSTER: Broadcast', key);
const val = JSON.parse(message.slice(comma + 1)); const val = JSON.parse(message.slice(comma + 1));
/*
if (key.startsWith('req:')) { if (key.startsWith('req:')) {
try { // cross-shard requests
const shard = message.slice(0, message.indexOf(':')); const shard = message.slice(0, message.indexOf(':'));
const chan = val.shift(); const id = val[0];
const ret = await super.req(key.slice(4), ...val); this.csReq.push({
this.publisher.publish( id,
`${LISTEN_PREFIX}:${shard}`, shard,
`res:${chan},${JSON.stringify([ret])}`, ts: Date.now(),
); });
} catch {
// nothing
}
return;
} }
*/
super.emit(key, ...val); super.emit(key, ...val);
return; return;
} }
@ -145,6 +150,7 @@ class MessageBroker extends SocketEvents {
try { try {
const comma = message.indexOf(','); const comma = message.indexOf(',');
const key = message.slice(0, comma); const key = message.slice(0, comma);
console.log(`CLUSTER shard listener got ${key}`);
const val = JSON.parse(message.slice(comma + 1)); const val = JSON.parse(message.slice(comma + 1));
super.emit(key, ...val); super.emit(key, ...val);
} catch (err) { } catch (err) {
@ -188,13 +194,14 @@ class MessageBroker extends SocketEvents {
const callback = (retn) => { const callback = (retn) => {
amountOtherShards -= 1; amountOtherShards -= 1;
ret = combineObjects(ret, retn); ret = combineObjects(ret, retn);
// eslint-disable-next-line
console.log(`CLUSTER got res:${type} from shard, ${amountOtherShards} still left`);
if (amountOtherShards <= 0) { if (amountOtherShards <= 0) {
console.log(`CLUSTER res:${type} finished`); console.log(`CLUSTER res:${chan}:${type} finished`);
this.off(chankey, callback); this.off(chankey, callback);
clearTimeout(id); clearTimeout(id);
resolve(ret); resolve(ret);
} else {
// eslint-disable-next-line
console.log(`CLUSTER got res:${chan}:${type} from shard, ${amountOtherShards} still left`);
} }
}; };
id = setTimeout(() => { id = setTimeout(() => {
@ -202,7 +209,7 @@ class MessageBroker extends SocketEvents {
if (ret) { if (ret) {
resolve(ret); resolve(ret);
} else { } else {
reject(new Error(`Timeout on req ${type}`)); reject(new Error(`CLUSTER Timeout on wait for res:${chan}:${type}`));
} }
}, 45000); }, 45000);
this.on(chankey, callback); this.on(chankey, callback);
@ -210,6 +217,22 @@ class MessageBroker extends SocketEvents {
}); });
} }
res(chan, ret) {
// only response to requesting shard
const csre = this.csReq.find((r) => r.id === chan);
// eslint-disable-next-line
console.log(`CLUSTER send res:${chan} to shard ${csre && csre.shard}`);
if (csre) {
this.publisher.publish(
`${LISTEN_PREFIX}:${csre.shard}`,
`res:${chan},${JSON.stringify([ret])}`,
);
this.csReq = this.csReq.filter((r) => r.id !== chan);
} else {
super.emit(`res:${chan}`, ret);
}
}
updateShardOnlineCounter(shard, cnt) { updateShardOnlineCounter(shard, cnt) {
const shardCounter = this.shardOnlineCounters.find( const shardCounter = this.shardOnlineCounters.find(
(c) => c[0] === shard, (c) => c[0] === shard,
@ -347,7 +370,7 @@ class MessageBroker extends SocketEvents {
checkHealth() { checkHealth() {
// remove disconnected shards // remove disconnected shards
const threshold = Date.now() - 30000; let threshold = Date.now() - 30000;
const { shards } = this; const { shards } = this;
Object.keys(shards).forEach((shard) => { Object.keys(shards).forEach((shard) => {
if (shards[shard] < threshold) { if (shards[shard] < threshold) {
@ -364,6 +387,9 @@ class MessageBroker extends SocketEvents {
}); });
// send keep alive to others // send keep alive to others
this.publisher.publish(BROADCAST_CHAN, this.thisShard); this.publisher.publish(BROADCAST_CHAN, this.thisShard);
// clean up dead shard requests
threshold -= 30000;
this.csReq = this.csReq.filter((r) => r.ts > threshold);
} }
} }

View File

@ -73,10 +73,14 @@ class SocketEvents extends EventEmitter {
}); });
} }
res(chan, ret) {
this.emit(`res:${chan}`, ret);
}
onReq(type, cb) { onReq(type, cb) {
this.on(`req:${type}`, async (chan, ...args) => { this.on(`req:${type}`, async (chan, ...args) => {
const ret = await cb(...args); const ret = await cb(...args);
this.emit(`res:${chan}`, ret); this.res(chan, ret);
}); });
} }

View File

@ -348,7 +348,8 @@ export function requestIID() {
} }
export function requestBanMe(code) { export function requestBanMe(code) {
return makeAPIGETRequest( return makeAPIPOSTRequest(
`/api/banme?code=${code}`, '/api/banme',
{ code },
); );
} }