UNPKG

shadowsocks-manager

Version:

A shadowsocks manager tool for multi user and traffic control.

267 lines (246 loc) 7.84 kB
const knex = appRequire('init/knex').knex; const crypto = require('crypto'); const macAccount = appRequire('plugins/macAccount/index'); const checkPasswordLimit = { number: 5, time: 30 * 1000, }; const checkPasswordFail = {}; const checkExist = async (obj) => { const user = await knex('user').select().where(obj); if(user.length === 0) { return; } else { return Promise.reject('user exists'); } }; const md5 = function(text) { return crypto.createHash('md5').update(text).digest('hex'); }; const createPassword = function(password, username) { return md5(password + username); }; const addUser = async (options) => { try { const insert = {}; if(options.username) { await checkExist({username: options.username}); Object.assign(insert, {username: options.username}); } if(options.email) { await checkExist({email: options.email}); Object.assign(insert, {email: options.email}); } if(options.telegram) { await checkExist({telegram: options.telegram}); Object.assign(insert, {telegram: options.telegram}); } Object.assign(insert, { type: options.type, createTime: Date.now() }); if(options.username && options.password) { Object.assign(insert, { password: createPassword(options.password, options.username) }); } if(options.group) { Object.assign(insert, { group: options.group }); } if(options.telegramId) { Object.assign(insert, { telegram: options.telegramId }); } const user = await knex('user').insert(insert); return user; } catch(err) { console.log(err); return Promise.reject(err); } }; const checkPassword = async (username, password) => { try { const user = await knex('user').select(['id', 'type', 'username', 'password']).where({ username, }); if(user.length === 0) { return Promise.reject('user not exists'); } for(const cpf in checkPasswordFail) { if(Date.now() - checkPasswordFail[cpf].time >= checkPasswordLimit.time) { delete checkPasswordFail[cpf]; } }; if(checkPasswordFail[username] && checkPasswordFail[username].number > checkPasswordLimit.number && Date.now() - checkPasswordFail[username].time < checkPasswordLimit.time ) { return Promise.reject('password retry out of limit'); } if(createPassword(password, username) === user[0].password) { await knex('user').update({ lastLogin: Date.now(), }).where({ username, }); return user[0]; } else { if(!checkPasswordFail[username] || Date.now() - checkPasswordFail[username].time >= checkPasswordLimit.time) { checkPasswordFail[username] = { number: 1, time: Date.now() }; } else if(checkPasswordFail[username].number <= checkPasswordLimit.number) { checkPasswordFail[username].number += 1; checkPasswordFail[username].time = Date.now(); } return Promise.reject('invalid password'); } } catch(err) { return Promise.reject(err); } }; const editUser = async (userInfo, edit) => { try { const username = (await knex('user').select().where(userInfo))[0].username; if(!username) { throw new Error('user not found'); } if(edit.password) { edit.password = createPassword(edit.password, username); } const user = await knex('user').update(edit).where(userInfo); return; } catch(err) { return Promise.reject(err); } }; const getUsers = async () => { const users = await knex('user').select().where({ type: 'normal', }); return users; }; const getRecentSignUpUsers = async (number, group) => { const where = { type: 'normal' }; if(group >= 0) { where.group = group; } const users = await knex('user').select().where(where).orderBy('createTime', 'desc').limit(number); return users; }; const getRecentLoginUsers = async (number, group) => { const where = { type: 'normal' }; if(group >= 0) { where.group = group; } const users = await knex('user').select().where(where).orderBy('lastLogin', 'desc').limit(number); return users; }; const getOneUser = async (id) => { const user = await knex('user').select().where({ type: 'normal', id, }); if(!user.length) { return Promise.reject('User not found'); } return user[0]; }; const getOneAdmin = async (id) => { const user = await knex('user').select().where({ type: 'admin', id, }).where('id', '>', 1); if(!user.length) { return Promise.reject('User not found'); } return user[0]; }; const getUserAndPaging = async (opt = {}) => { const search = opt.search || ''; const filter = opt.filter || 'all'; const sort = opt.sort || 'id_asc'; const page = opt.page || 1; const pageSize = opt.pageSize || 20; const type = opt.type || ['normal']; const group = opt.hasOwnProperty('group') ? opt.group : -1; let count = knex('user').select() .where('id', '>', 1) .whereIn('type', type); let users = knex('user').select([ 'user.id as id', 'user.username as username', 'user.email as email', 'user.telegram as telegram', 'user.password as password', 'user.type as type', 'user.createTime as createTime', 'user.lastLogin as lastLogin', 'user.resetPasswordId as resetPasswordId', 'user.resetPasswordTime as resetPasswordTime', 'account_plugin.port as port', ]).leftJoin('account_plugin', 'user.id', 'account_plugin.userId') .where('user.id', '>', 1) .whereIn('user.type', type).groupBy('user.id'); if(group >= 0) { count = count.where({ 'user.group': group }); users = users.where({ 'user.group': group }); } if(search) { count = count.where('username', 'like', `%${ search }%`); users = users.where('username', 'like', `%${ search }%`); } count = await count.count('id as count').then(success => success[0].count); users = await users.orderBy(sort.split('_')[0], sort.split('_')[1]).limit(pageSize).offset((page - 1) * pageSize); const maxPage = Math.ceil(count / pageSize); return { total: count, page, maxPage, pageSize, users, }; }; const deleteUser = async userId => { if(!userId) { return Promise.reject('invalid userId'); } const existAccount = await knex('account_plugin').select().where({ userId, }); if(existAccount.length) { return Promise.reject('delete user fail'); } const macAccounts = await macAccount.getAccountByUserId(userId); if(macAccounts.length) { macAccounts.forEach(f => { macAccount.deleteAccount(f.id); }); } const deleteCount = await knex('user').delete().where({ id: userId, }).where('id', '>', 1); if(deleteCount >= 1) { return; } else { return Promise.reject('delete user fail'); } }; const changePassword = async (userId, oldPassword, newPassword) => { const userInfo = await knex('user').where({ id: userId, }).then(user => { if(!user.length) { return Promise.reject('user not found'); } return user[0]; }); await checkPassword(userInfo.username, oldPassword); await editUser({ id: userId, }, { password: newPassword, }); }; exports.add = addUser; exports.edit = editUser; exports.checkPassword = checkPassword; exports.get = getUsers; exports.getRecentSignUp = getRecentSignUpUsers; exports.getRecentLogin = getRecentLoginUsers; exports.getOne = getOneUser; exports.getOneAdmin = getOneAdmin; exports.getUserAndPaging = getUserAndPaging; exports.delete = deleteUser; exports.changePassword = changePassword;