UNPKG

shadowsocks-manager

Version:

A shadowsocks manager tool for multi user and traffic control.

957 lines (822 loc) 24.9 kB
"use strict"; 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 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, '6': 3 * 30 * 86400 * 1000, '7': 12 * 30 * 86400 * 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]; if (orderType === 6) { countTime = timePeriod[3]; } if (orderType === 7) { countTime = timePeriod[3]; } 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 >= 6 ? 3 : 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 = {}; } const servers = await serverManager.list({ status: false }); const validServers = servers.filter(server => { if (!account.data.server) { return true; } return account.data.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;