shadowsocks-manager
Version:
A shadowsocks manager tool for multi user and traffic control.
267 lines (246 loc) • 7.84 kB
JavaScript
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;