UNPKG

shadowsocks-manager

Version:

A shadowsocks manager tool for multi user and traffic control.

320 lines (270 loc) 8.5 kB
"use strict"; const log4js = require('log4js'); const moment = require('moment'); const logger = log4js.getLogger('autoban'); const knex = appRequire('init/knex').knex; const config = appRequire('services/config').all(); const banConfig = config.plugins.webgui_autoban.data; const queue = []; const convertTimeString = str => { if (!str.match) { return str; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[sS]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[sS]$/)[1] * 1000; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[mM]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[mM]$/)[1] * 60 * 1000; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[hH]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[hH]$/)[1] * 60 * 60 * 1000; } }; const convertFlowString = str => { if (!str.match) { return str; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[kK]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[kK]$/)[1] * 1000; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[mM]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[mM]$/)[1] * 1000 * 1000; } if (str.match(/^(\d{1,}.{0,1}\d{0,})[hH]$/)) { return +str.match(/^(\d{1,}.{0,1}\d{0,})[gG]$/)[1] * 1000 * 1000 * 1000; } }; banConfig.forEach(f => { const accountIds = []; const serverIds = []; f.accountId.split(',').forEach(ports => { if (ports.indexOf('-') < 0) { accountIds.push(+ports); } else { const start = +ports.split('-')[0]; const end = +ports.split('-')[1]; for (let i = start; i <= end; i++) { accountIds.push(i); } } }); f.serverId.split(',').forEach(ids => { if (ids.indexOf('-') < 0) { serverIds.push(+ids); } else { const start = +ids.split('-')[0]; const end = +ids.split('-')[1]; for (let i = start; i <= end; i++) { serverIds.push(i); } } }); const time = convertTimeString(f.time); const flow = convertFlowString(f.flow); const banTime = convertTimeString(f.banTime); serverIds.forEach(serverId => { accountIds.forEach(accountId => { queue.push({ serverId, accountId, time, flow, banTime }); }); }); }); const ban = async (serverId, accountId, time) => { logger.info('ban [' + serverId + '][' + accountId + ']'); await knex('account_flow').update({ status: 'ban', nextCheckTime: Date.now(), autobanTime: Date.now() + time }).where({ serverId, accountId }); }; const splitTime = async (start, end) => { const time = { day: [], hour: [], fiveMin: [], origin: [] }; const now = Date.now(); const getMinute = moment(now).get('minute'); const splitEnd = { day: moment(now).hour(0).minute(0).second(0).millisecond(0).toDate().getTime(), hour: moment(now).minute(0).second(0).millisecond(0).toDate().getTime(), fiveMin: moment(now).minute(getMinute - getMinute % 5).second(0).millisecond(0).toDate().getTime() }; const isDay = time => { const hour = moment(time).get('hour'); const minute = moment(time).get('minute'); const second = moment(time).get('second'); const millisecond = moment(time).get('millisecond'); if (hour || minute || second || millisecond) { return false; } return true; }; const isHour = time => { const minute = moment(time).get('minute'); const second = moment(time).get('second'); const millisecond = moment(time).get('millisecond'); if (minute || second || millisecond) { return false; } return true; }; const is5min = time => { const minute = moment(time).get('minute'); const second = moment(time).get('second'); const millisecond = moment(time).get('millisecond'); if (minute % 5 || second || millisecond) { return false; } return true; }; const next = (time, type) => { if (type === 'day') { return moment(time).add(1, 'days').hour(0).minute(0).second(0).millisecond(0).toDate().getTime(); } if (type === 'hour') { return moment(time).add(1, 'hours').minute(0).second(0).millisecond(0).toDate().getTime(); } if (type === '5min') { const getMinute = moment(time).get('minute'); return moment(time).minute(getMinute - getMinute % 5).add(5, 'minutes').second(0).millisecond(0).toDate().getTime(); } }; let timeStart = start; let timeEnd = end; let last; while (timeStart < timeEnd) { if (isDay(timeStart) && next(timeStart, 'day') <= splitEnd.day && next(timeStart, 'day') <= end) { if (last === 'day' && time.day.length) { const length = time.day.length; time.day[length - 1] = [time.day[length - 1][0], next(timeStart, 'day')]; } else { time.day.push([timeStart, next(timeStart, 'day')]); } timeStart = next(timeStart, 'day'); last = 'day'; } else if (isHour(timeStart) && next(timeStart, 'hour') <= splitEnd.hour && next(timeStart, 'hour') <= end) { if (last === 'hour' && time.hour.length) { const length = time.hour.length; time.hour[length - 1] = [time.hour[length - 1][0], next(timeStart, 'hour')]; } else { time.hour.push([timeStart, next(timeStart, 'hour')]); } timeStart = next(timeStart, 'hour'); last = 'hour'; } else if (is5min(timeStart) && next(timeStart, '5min') <= splitEnd.fiveMin && next(timeStart, '5min') <= end) { if (last === '5min' && time.fiveMin.length) { const length = time.fiveMin.length; time.fiveMin[length - 1] = [time.fiveMin[length - 1][0], next(timeStart, '5min')]; } else { time.fiveMin.push([timeStart, next(timeStart, '5min')]); } timeStart = next(timeStart, '5min'); last = '5min'; } else if (next(timeStart, '5min') <= end && timeStart === start) { time.origin.push([timeStart, next(timeStart, '5min')]); timeStart = next(timeStart, '5min'); last = '5min'; } else { time.origin.push([timeStart, timeEnd]); timeStart = timeEnd; last = 'origin'; } } return time; }; const getFlowFromSplitTime = async (serverId, accountId, start, end) => { const time = await splitTime(start, end); const sum = []; const getFlow = (tableName, startTime, endTime) => { return knex(tableName).sum('flow as sumFlow').groupBy('id').select(['id']).where({ id: serverId, accountId }).whereBetween('time', [startTime, endTime - 1]).then(success => { if (success[0]) { return success[0].sumFlow; } return 0; }); }; time.day.forEach(f => { sum.push(getFlow('saveFlowDay', f[0], f[1])); }); time.hour.forEach(f => { sum.push(getFlow('saveFlowHour', f[0], f[1])); }); time.fiveMin.forEach(f => { sum.push(getFlow('saveFlow5min', f[0], f[1])); }); time.origin.forEach(f => { sum.push(getFlow('saveFlow', f[0], f[1])); }); const result = (await Promise.all(sum)).reduce((a, b) => a + b); return result; }; const check = async opt => { const start = Date.now(); const { serverId, accountId, time, flow, banTime } = opt; const accountFlowData = await knex('account_flow').where({ serverId, accountId }).then(s => s[0]); if (!accountFlowData) { return 'not exists'; } let checkTime; if (accountFlowData && accountFlowData.autobanTime >= Date.now() - time) { checkTime = accountFlowData.autobanTime; } else { checkTime = Date.now() - time; } // const lastConnect = await knex('saveFlow5min') // .select(['time']) // .where({ id: serverId, accountId }) // .orderBy('time', 'desc') // .limit(1).then(success => { // if(success[0]) { // return success[0].time; // } // return 0; // }); // if(!lastConnect || lastConnect <= checkTime) { logger.info('no need to check'); return; } const myFlow = await getFlowFromSplitTime(serverId, accountId, checkTime, Date.now()); if (myFlow >= flow) { await ban(serverId, accountId, banTime); } }; let position = 0; const promise = () => { const speed = config.plugins.webgui_autoban.speed || 1000; return check(queue[position]).then(success => { position += 1; if (queue.length <= position) { position = 0; } if (success === 'not exists') { return promise(); } else { return new Promise((resolve, reject) => { setTimeout(() => { return promise(resolve, reject); }, speed); }); } }); }; promise();