UNPKG

shadowsocks-manager

Version:

A shadowsocks manager tool for multi user and traffic control.

807 lines (773 loc) 26.4 kB
const knex = appRequire('init/knex').knex; const serverManager = appRequire('plugins/flowSaver/server'); const manager = appRequire('services/manager'); const config = appRequire('services/config').all(); const macAccount = appRequire('plugins/macAccount/index'); const orderPlugin = appRequire('plugins/webgui_order'); const accountFlow = appRequire('plugins/account/accountFlow'); const addAccount = async (type, options) => { if(!options.hasOwnProperty('active')) { options.active = 1; } if(type === 6 || type === 7) { type = 3; } if(type === 1) { const [ accountId ] = await knex('account_plugin').insert({ type, orderId: 0, userId: options.user, port: options.port, password: options.password, status: 0, server: options.server ? options.server : null, autoRemove: 0, }); await accountFlow.add(accountId); return; } else if (type >= 2 && type <= 5) { const [ accountId ] = await knex('account_plugin').insert({ type, orderId: options.orderId || 0, userId: options.user, port: options.port, password: options.password, data: JSON.stringify({ create: options.time || Date.now(), flow: options.flow || 1 * 1000 * 1000 * 1000, limit: options.limit || 1, }), status: 0, server: options.server ? options.server : null, autoRemove: options.autoRemove || 0, autoRemoveDelay: options.autoRemoveDelay || 0, multiServerFlow: options.multiServerFlow || 0, active: options.active, }); await accountFlow.add(accountId); return; } }; const changePort = async (id, port) => { const result = await knex('account_plugin').update({ port }).where({ id }); await accountFlow.edit(id); }; const getAccount = async (options = {}) => { const where = {}; if(options.id) { where['account_plugin.id'] = options.id; } if(options.userId) { where['user.id'] = options.userId; } if(options.port) { where['account_plugin.port'] = options.port; } if(options.group >= 0) { where['user.group'] = options.group; } const account = await knex('account_plugin').select([ 'account_plugin.id', 'account_plugin.type', 'account_plugin.orderId', 'account_plugin.userId', 'account_plugin.server', 'account_plugin.port', 'account_plugin.password', 'account_plugin.data', 'account_plugin.status', 'account_plugin.autoRemove', 'account_plugin.autoRemoveDelay', 'account_plugin.multiServerFlow', 'account_plugin.active', 'user.id as userId', 'user.email as user', ]) .leftJoin('user', 'user.id', 'account_plugin.userId') .where(where); return account; }; const delAccount = async id => { const accountInfo = await knex('account_plugin').where({ id }).then(s => s[0]); if(!accountInfo) { return Promise.reject('Account id[' + id + '] not found'); } const result = await knex('account_plugin').delete().where({ id }); const servers = await knex('server').where({}); servers.forEach(server => { manager.send({ command: 'del', port: accountInfo.port + server.shift, }, { host: server.host, port: server.port, password: server.password, }); }); await accountFlow.del(id); return result; }; const editAccount = async (id, options) => { const account = await knex('account_plugin').where({ id }).then(success => { if(success.length) { return success[0]; } return Promise.reject('account not found'); }); const update = {}; if(options.hasOwnProperty('active')) { update.active = options.active; } update.type = options.type; update.orderId = options.orderId; update.userId = options.userId; update.autoRemove = options.autoRemove; update.autoRemoveDelay = options.autoRemoveDelay; update.multiServerFlow = options.multiServerFlow; if(options.hasOwnProperty('server')) { update.server = options.server ? JSON.stringify(options.server) : null; } if(options.type === 1) { update.data = null; } else if(options.type >= 2 && options.type <= 5) { update.data = JSON.stringify({ create: options.time || Date.now(), flow: options.flow || 1 * 1000 * 1000 * 1000, limit: options.limit || 1, }); } if(options.port) { update.port = +options.port; if(+options.port !== account.port) { const servers = await knex('server').where({}); servers.forEach(server => { manager.send({ command: 'del', port: account.port + server.shift, }, { host: server.host, port: server.port, password: server.password, }); }); } } await knex('account_plugin').update(update).where({ id }); await await accountFlow.edit(id); return; }; const editAccountTime = async (id, timeString, check) => { const time = +timeString; let accountInfo = await knex('account_plugin').where({ id }).then(s => s[0]); if(accountInfo.type < 2 || accountInfo.type > 5) { return; } accountInfo.data = JSON.parse(accountInfo.data); const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; accountInfo.data.create += time; while(time > 0 && accountInfo.data.create >= Date.now()) { accountInfo.data.limit += 1; accountInfo.data.create -= timePeriod[accountInfo.type]; } await knex('account_plugin').update({ data: JSON.stringify(accountInfo.data) }).where({ id }); await accountFlow.edit(id); }; const editAccountTimeForRef = async (id, timeString, check) => { const time = +timeString; let accountInfo = await knex('account_plugin').where({ id }).then(s => s[0]); if(accountInfo.type < 2 || accountInfo.type > 5) { return; } accountInfo.data = JSON.parse(accountInfo.data); const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; if(accountInfo.data.create + timePeriod[accountInfo.type] * accountInfo.data.limit <= Date.now()) { accountInfo.data.limit = 1; accountInfo.data.create = Date.now() + time - timePeriod[accountInfo.type]; } else { accountInfo.data.create += time; } while(time > 0 && accountInfo.data.create >= Date.now()) { accountInfo.data.limit += 1; accountInfo.data.create -= timePeriod[accountInfo.type]; } await knex('account_plugin').update({ data: JSON.stringify(accountInfo.data) }).where({ id }); await accountFlow.edit(id); }; const changePassword = async (id, password) => { const account = await knex('account_plugin').select().where({ id }).then(success => { if(success.length) { return success[0]; } return Promise.reject('account not found'); }); await knex('account_plugin').update({ password, }).where({ id }); await accountFlow.pwd(id, password); return; }; const addAccountLimit = async (id, number = 1) => { const account = await knex('account_plugin').select().where({ id }).then(success => { if(success.length) { return success[0]; } return Promise.reject('account not found'); }); if(account.type < 2 || account.type > 5) { return; } const accountData = JSON.parse(account.data); const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; if(accountData.create + accountData.limit * timePeriod[account.type] <= Date.now()) { accountData.create = Date.now(); accountData.limit = number; } else { accountData.limit += number; } await knex('account_plugin').update({ data: JSON.stringify(accountData), }).where({ id }); return; }; const addAccountLimitToMonth = async (userId, accountId, number = 1) => { if(!accountId) { const port = await knex('account_plugin').select() .orderBy('port', 'DESC').limit(1) .then(success => { if(success.length) { return success[0].port + 1; } else { return 50000; } }); await addAccount(3, { user: userId, port, password: Math.random().toString().substr(2,10), time: Date.now(), limit: number, flow: 200 * 1000 * 1000 * 1000, autoRemove: 0, }); return; } const account = await knex('account_plugin').select().where({ id: accountId }).then(success => { if(success.length) { return success[0]; } return Promise.reject('account not found'); }); if(account.type < 2 || account.type > 5) { return; } const accountData = JSON.parse(account.data); accountData.flow = 200 * 1000 * 1000 * 1000; if(account.type === 3) { if(accountData.create + accountData.limit * 30 * 86400 * 1000 <= Date.now()) { accountData.create = Date.now(); accountData.limit = number; } else { accountData.limit += number; } } else { const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; let expireTime = accountData.create + accountData.limit * timePeriod[account.type]; if(expireTime <= Date.now()) { expireTime = 30 * 86400 * 1000 * number + Date.now(); } else { expireTime += 30 * 86400 * 1000 * number; } accountData.create = expireTime; accountData.limit = 0; while(accountData.create >= Date.now()) { accountData.limit += 1; accountData.create -= 30 * 86400 * 1000; } } await knex('account_plugin').update({ type: 3, data: JSON.stringify(accountData), autoRemove: 0, }).where({ id: accountId }); return; }; const setAccountLimit = async (userId, accountId, orderId) => { const orderInfo = await orderPlugin.getOneOrder(orderId); if(orderInfo.baseId) { await knex('webgui_flow_pack').insert({ accountId, flow: orderInfo.flow, createTime: Date.now(), }); await accountFlow.edit(accountId); return; } const limit = orderInfo.cycle; const orderType = orderInfo.type; let account; if(accountId) { account = await knex('account_plugin').select().where({ id: accountId }).then(success => { if(success.length) { return success[0]; } return null; }); } if(!accountId || !account) { const getNewPort = () => { let orderPorts = []; if(orderInfo.portRange !== '0') { try { orderInfo.portRange.split(',').filter(f => f.trim()).forEach(f => { if(f.indexOf('-')) { const start = f.split('-').filter(f => f.trim())[0]; const end = f.split('-').filter(f => f.trim())[1]; if(start >= end) { return; } for(let p = start; p <= end; p++) { orderPorts.indexOf(p) >= 0 || orderPorts.push(p); } } else { orderPorts.indexOf(+f) >= 0 || orderPorts.push(+f); } }); } catch(err) { console.log(err); } } return knex('webguiSetting').select().where({ key: 'account', }).then(success => { if(!success.length) { return Promise.reject('settings not found'); } success[0].value = JSON.parse(success[0].value); return success[0].value.port; }).then(port => { if(port.random) { const getRandomPort = () => { if(orderPorts.length) { return orderPorts[Math.floor(Math.random() * orderPorts.length)]; } else { return Math.floor(Math.random() * (port.end - port.start + 1) + port.start); } }; let retry = 0; let myPort = getRandomPort(); const checkIfPortExists = port => { let myPort = port; return knex('account_plugin').select() .where({ port }).then(success => { if(success.length && retry <= 30) { retry++; myPort = getRandomPort(); return checkIfPortExists(myPort); } else if (success.length && retry > 30) { return Promise.reject('Can not get a random port'); } else { return myPort; } }); }; return checkIfPortExists(myPort); } else { let query; if(orderPorts.length) { query = knex('account_plugin').select() .whereIn('port', orderPorts) .orderBy('port', 'ASC'); } else { query = knex('account_plugin').select() .whereBetween('port', [port.start, port.end]) .orderBy('port', 'ASC'); } return query.then(success => { const portArray = success.map(m => m.port); let myPort; if(orderPorts.length) { for(const p of orderPorts) { if(portArray.indexOf(+p) < 0) { myPort = p; break; } } } else { for(let p = port.start; p <= port.end; p++) { if(portArray.indexOf(+p) < 0) { myPort = p; break; } } } if(myPort) { return myPort; } else { return Promise.reject('no port'); } }); } }); }; const port = await getNewPort(); await addAccount(orderType, { orderId, user: userId, port, password: Math.random().toString().substr(2,10), time: Date.now(), limit, flow: orderInfo.flow, server: orderInfo.server, autoRemove: orderInfo.autoRemove ? 1 : 0, autoRemoveDelay: orderInfo.autoRemoveDelay, multiServerFlow: orderInfo.multiServerFlow ? 1 : 0, active: 0, }); return; } const compareType = (current, order) => { if(current === order) { return false; } else if(current === 3) { return true; } else if(current === 2 && order !== 3) { return true; } else if(current === 4 && order === 5) { return true; } else { return false; } }; const onlyIncreaseTime = compareType(account.type, orderType); if(onlyIncreaseTime) { const accountData = JSON.parse(account.data); const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; let expireTime = accountData.create + accountData.limit * timePeriod[account.type]; if(expireTime <= Date.now()) { expireTime = timePeriod[orderType] * limit + Date.now(); } else { expireTime += timePeriod[orderType] * limit; } let countTime = timePeriod[account.type]; accountData.create = expireTime - countTime; accountData.limit = 1; while(accountData.create >= Date.now()) { accountData.limit += 1; accountData.create -= countTime; } await knex('account_plugin').update({ data: JSON.stringify(accountData), }).where({ id: accountId }); await accountFlow.edit(accountId); return; } const accountData = JSON.parse(account.data); accountData.flow = orderInfo.flow; const timePeriod = { '2': 7 * 86400 * 1000, '3': 30 * 86400 * 1000, '4': 1 * 86400 * 1000, '5': 3600 * 1000, }; let expireTime = accountData.create + accountData.limit * timePeriod[account.type]; if(expireTime <= Date.now()) { expireTime = timePeriod[orderType] * limit + Date.now(); } else { expireTime += timePeriod[orderType] * limit; } let countTime = timePeriod[orderType]; accountData.create = expireTime - countTime; accountData.limit = 1; while(accountData.create >= Date.now()) { accountData.limit += 1; accountData.create -= countTime; } // let port = await getAccount({ id: accountId }).then(success => success[0].port); await knex('account_plugin').update({ type: orderType, orderId, data: JSON.stringify(accountData), server: orderInfo.server, autoRemove: orderInfo.autoRemove ? 1 : 0, multiServerFlow: orderInfo.multiServerFlow ? 1 : 0, }).where({ id: accountId }); await accountFlow.edit(accountId); return; }; const addAccountTime = async (userId, accountId, accountType, accountPeriod = 1) => { // type: 2 周 ,3 月, 4 天, 5 小时 const getTimeByType = type => { const time = { '2': 7 * 24 * 60 * 60 * 1000, '3': 30 * 24 * 60 * 60 * 1000, '4': 24 * 60 * 60 * 1000, '5': 60 * 60 * 1000, }; return time[type]; }; const paymentInfo = await knex('webguiSetting').select().where({ key: 'payment', }).then(success => { if(!success.length) { return Promise.reject('settings not found'); } success[0].value = JSON.parse(success[0].value); return success[0].value; }); const getPaymentInfo = type => { const pay = { '2': 'week', '3': 'month', '4': 'day', '5': 'hour', }; return paymentInfo[pay[type]]; }; const checkIfAccountExists = async (accountId) => { if(!accountId) { return null; } const account = await knex('account_plugin').where({ id: accountId }); if(!account.length) { return null; } const accountInfo = account[0]; accountInfo.data = JSON.parse(account[0].data); return accountInfo; }; const accountInfo = await checkIfAccountExists(accountId); if(!accountInfo) { const getNewPort = async () => { const port = await knex('webguiSetting').select().where({ key: 'account', }).then(success => { if(!success.length) { return Promise.reject('settings not found'); } success[0].value = JSON.parse(success[0].value); return success[0].value.port; }); if(port.random) { const getRandomPort = () => Math.floor(Math.random() * (port.end - port.start + 1) + port.start); let retry = 0; let myPort = getRandomPort(); const checkIfPortExists = port => { let myPort = port; return knex('account_plugin').select() .where({ port }).then(success => { if(success.length && retry <= 30) { retry++; myPort = getRandomPort(); return checkIfPortExists(myPort); } else if (success.length && retry > 30) { return Promise.reject('Can not get a random port'); } else { return myPort; } }); }; return checkIfPortExists(myPort); } else { return knex('account_plugin').select() .whereBetween('port', [port.start, port.end]) .orderBy('port', 'DESC').limit(1).then(success => { if(success.length) { return success[0].port + 1; } return port.start; }); } }; const port = await getNewPort(); await knex('account_plugin').insert({ type: accountType, userId, server: getPaymentInfo(accountType).server ? JSON.stringify(getPaymentInfo(accountType).server) : null, port, password: Math.random().toString().substr(2,10), data: JSON.stringify({ create: Date.now(), flow: getPaymentInfo(accountType).flow * 1000 * 1000, limit: accountPeriod, }), autoRemove: getPaymentInfo(accountType).autoRemove, multiServerFlow: getPaymentInfo(accountType).multiServerFlow, }); return; } let onlyIncreaseTime = false; if(accountInfo.type === 3 && accountType !== 3) { onlyIncreaseTime = true; } if(accountInfo.type === 2 && (accountType === 4 || accountType === 5)) { onlyIncreaseTime = true; } if(accountInfo.type === 4 && accountType === 5) { onlyIncreaseTime = true; } const isAccountOutOfDate = accountInfo => { const expire = accountInfo.data.create + accountInfo.data.limit * getTimeByType(accountInfo.type); return expire <= Date.now(); }; if(onlyIncreaseTime) { let expireTime; if(isAccountOutOfDate(accountInfo)) { expireTime = Date.now() + getTimeByType(accountType) * accountPeriod; } else { expireTime = accountInfo.data.create + getTimeByType(accountInfo.type) * accountInfo.data.limit + getTimeByType(accountType) * accountPeriod; } let createTime = expireTime - getTimeByType(accountInfo.type); let limit = 1; while(createTime >= Date.now()) { limit += 1; createTime -= getTimeByType(accountInfo.type); } await knex('account_plugin').update({ data: JSON.stringify({ create: createTime, flow: accountInfo.data.flow, limit, }), }).where({ id: accountId }); return; } let expireTime; if(isAccountOutOfDate(accountInfo)) { expireTime = Date.now() + getTimeByType(accountType) * accountPeriod; } else { expireTime = accountInfo.data.create + getTimeByType(accountInfo.type) * accountInfo.data.limit + getTimeByType(accountType) * accountPeriod; } let createTime = expireTime - getTimeByType(accountType); let limit = 1; while(createTime >= Date.now()) { limit += 1; createTime -= getTimeByType(accountType); } await knex('account_plugin').update({ type: accountType, server: getPaymentInfo(accountType).server ? JSON.stringify(getPaymentInfo(accountType).server) : null, data: JSON.stringify({ create: createTime, flow: getPaymentInfo(accountType).flow * 1000 * 1000, limit, }), autoRemove: getPaymentInfo(accountType).autoRemove, multiServerFlow: getPaymentInfo(accountType).multiServerFlow, }).where({ id: accountId }); return; }; const banAccount = async options => { const serverId = options.serverId; const accountId = options.accountId; const time = options.time; await knex('account_flow').update({ status: 'ban', nextCheckTime: Date.now(), autobanTime: Date.now() + time, }).where({ serverId, accountId, }); }; const getBanAccount = async options => { const serverId = options.serverId; const accountId = options.accountId; const accountInfo = await knex('account_flow').select([ 'autobanTime as banTime' ]).where({ serverId, accountId, status: 'ban' }); if(!accountInfo.length) { return { banTime: 0 }; } return accountInfo[0]; }; const loginLog = {}; const scanLoginLog = ip => { for(let i in loginLog) { if(Date.now() - loginLog[i].time >= 10 * 60 * 1000) { delete loginLog[i]; } } if(!loginLog[ip]) { return false; } else if (loginLog[ip].mac.length <= 10) { return false; } else { return true; } }; const loginFail = (mac, ip) => { if(!loginLog[ip]) { loginLog[ip] = { mac: [ mac ], time: Date.now() }; } else { if(loginLog[ip].mac.indexOf(mac) < 0) { loginLog[ip].mac.push(mac); loginLog[ip].time = Date.now(); } } }; const getAccountForSubscribe = async (token, ip) => { if(scanLoginLog(ip)) { return Promise.reject('ip is in black list'); } const account = await knex('account_plugin').where({ subscribe: token }).then(s => s[0]); if(!account) { loginFail(token, ip); return Promise.reject('can not find account'); } if(account.data) { account.data = JSON.parse(account.data); } else { account.data = {}; } if(account.server) { account.server = JSON.parse(account.server); } const servers = await serverManager.list({ status: false }); const validServers = servers.filter(server => { if(!account.server) { return true; } return account.server.indexOf(server.id) >= 0; }); return { server: validServers, account }; }; const editMultiAccounts = async (orderId, update) => { const accounts = await knex('account_plugin').where({ orderId }); const updateData = {}; for(const account of accounts) { if(update.hasOwnProperty('flow')) { const accountData = JSON.parse(account.data); accountData.flow = update.flow; updateData.data = JSON.stringify(accountData); } if(update.hasOwnProperty('server')) { updateData.server = update.server ? JSON.stringify(update.server): null; } if(update.hasOwnProperty('autoRemove')) { updateData.autoRemove = update.autoRemove; } if(Object.keys(updateData).length === 0) { break; } await knex('account_plugin').update(updateData).where({ id: account.id }); await accountFlow.edit(account.id); } }; const activeAccount = async accountId => { const accountInfo = await getAccount({ id: accountId }).then(s => s[0]); await knex('account_plugin').update({ active: 1 }).where({ id: accountInfo.id }); await accountFlow.edit(accountInfo.id); if(accountInfo.type > 1) { const accountData = JSON.parse(accountInfo.data); accountData.create = Date.now(); await knex('account_plugin').update({ data: JSON.stringify(accountData) }).where({ id: accountInfo.id }); } }; exports.addAccount = addAccount; exports.getAccount = getAccount; exports.delAccount = delAccount; exports.editAccount = editAccount; exports.editAccountTime = editAccountTime; exports.editAccountTimeForRef = editAccountTimeForRef; exports.changePassword = changePassword; exports.changePort = changePort; exports.addAccountLimit = addAccountLimit; exports.addAccountLimitToMonth = addAccountLimitToMonth; exports.setAccountLimit = setAccountLimit; exports.addAccountTime = addAccountTime; exports.banAccount = banAccount; exports.getBanAccount = getBanAccount; exports.getAccountForSubscribe = getAccountForSubscribe; exports.editMultiAccounts = editMultiAccounts; exports.activeAccount = activeAccount;