Commit c6a45512 authored by ml's avatar ml

资产管理后台-账户管理、登陆、谷歌等(未测试)

parent cb257484
......@@ -82,5 +82,5 @@ export const ErrorCode = {
PAIR_IS_NOT_ACTIVE:'30078',//交易对已是未激活状态
PAIR_IS_HIDE:'30079',//交易对已是隐藏状态
PAIR_IS_NOT_HIDE:'30080',//交易对已是未隐藏状态
PWD_FORMAT_ERR:'30081',//密码格式错误
PWD_FORMAT_ERR:'30083',//密码格式错误
}
import * as abkUserService from "../../service/abkservice/abkUserInfo.service";
import { AbkUserInfoVO, AbkUserInfoPageVO } from "../../service/abkservice/abkUserInfo.service";
import * as abkUserService from "../../service/v2/abkUserInfo.service";
import { AbkUserInfoVO, AbkUserInfoPageVO, resetAbkTotp } from "../../service/v2/abkUserInfo.service";
let { logger, Res3Utils, optionalUtils: Optional, apiAssertUtils: ApiAssert } = require('@madex/ex-js-public');
import { ErrorCode } from "../../../../constant/errorCode";
import { getCurrentUser, getCurrentUserId, } from "../../../utils/aclUserUtils";
import { getCurrentUser, getCurrentUserId, } from "../../../../utils/aclUserUtils";
import { isSuperUser } from "../../../../utils/abkUserUtils";
let isIp = require('is-ip');
/**
* 获取当前登陆的用户信息
* @param req
* @param infoVO
*/
export const getInfo = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.getInfo";
let cmd = req.path;
try {
let currentUserId = await getCurrentUserId(req.cookies.session_id);
let res = await userOptService.getInfo(currentUserId, req.cookies.session_id);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 获取用户信息根据uid
* @param req
* @param infoVO
*/
export const getInfoByUserId = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.getInfoByUserId";
let cmd = req.path;
try {
if (!aclUserInfoVO.user_id) {
throw ErrorCode.PARAM_MISS
}
let res = await userOptService.getInfoByUserId(aclUserInfoVO.user_id);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 获取用户信息详情(这里主要包含密码、google等敏感信息)根据uid
* @param req
* @param infoVO
*/
export const getInfoDetailByUserId = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.getInfoDetailByUserId";
let cmd = req.path;
try {
if (!aclUserInfoVO.user_id) {
throw ErrorCode.PARAM_MISS
}
let res = await userOptService.getInfoDetailByUserId(aclUserInfoVO.user_id);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 登陆
* @param req
* @param infoVO
*/
export const login = async (req: any, abkUserInfoVO: AbkUserInfoVO, res: any) => {
let func_name = "userOptCtrl.login";
let func_name = "abkUserInfo.control.login";
let cmd = req.path;
let result: any;
try {
......@@ -116,14 +55,14 @@ export const login = async (req: any, abkUserInfoVO: AbkUserInfoVO, res: any) =>
* @param aclUserInfoVO
* @param res
*/
export const logout = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.logout";
export const logout = async (req: any, abkUserInfoVO: AbkUserInfoVO) => {
let func_name = "abkUserInfo.control.logout";
let cmd = req.path;
try {
if (req.cookies.session_id) {
let currentUser = await getCurrentUser(req.cookies.session_id);
if (currentUser) {
await userOptService.deleteAllSessionByUserId(currentUser.userId);
await abkUserService.deleteAllAbkSessionByUserId(currentUser.userId);
}
}
return Res3Utils.result('ok');
......@@ -134,74 +73,124 @@ export const logout = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
}
};
/**
* 登录-二次验证
* 获取当前登陆的用户信息
* @param req
* @param aclUserInfoVO
* @param res
* @param infoVO
*/
export const loginConfirm = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.loginConfirm";
export const getInfo = async (req: any, abkUserInfoVO: AbkUserInfoVO) => {
let func_name = "abkUserInfo.control.getInfo";
let cmd = req.path;
try {
ApiAssert.notNull(ErrorCode.PARAM_MISS, aclUserInfoVO.totpCode);
let currentUserId = await getCurrentUserId(req.cookies.session_id);
let res = await userOptService.loginConfirm(req.cookies.session_id, currentUserId, aclUserInfoVO.totpCode);
let res = await abkUserService.getInfo(currentUserId, req.cookies.session_id);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 绑定谷歌-生成新的密钥
* 获取用户信息详情(这里主要包含密码、google等敏感信息)根据uid
* @param req
* @param aclUserInfoVO
* @param res
* @param infoVO
*/
export const bindTotpAsk = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.bindTotpAsk";
export const getInfoDetailByUserId = async (req: any, abkUserInfoVO: AbkUserInfoVO) => {
let func_name = "abkUserInfo.control.getInfoDetailByUserId";
let cmd = req.path;
try {
if (!abkUserInfoVO.user_id) {
throw ErrorCode.PARAM_MISS
}
if (!abkUserInfoVO.totp_code) {
throw ErrorCode.PARAM_MISS
}
let currentUserId = await getCurrentUserId(req.cookies.session_id);
let res = await userOptService.bindTotpAsk(req.cookies.session_id, currentUserId);
await isSuperUser(currentUserId);
let res = await abkUserService.getInfoDetailByUserId(abkUserInfoVO.user_id, abkUserInfoVO.totp_code, currentUserId);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 重置谷歌-生成一个新的密钥返回,保存时重新绑定谷歌
* @param req
* @param authConfigVO
*/
export const resetTotp = async (req: any, abkUserInfoVO: AbkUserInfoVO) => {
let func_name = "abkUserInfo.control.resetTotp";
let cmd = req.path;
try {
let currentUserId = await getCurrentUserId(req.cookies.session_id);
await isSuperUser(currentUserId)
let res = await abkUserService.resetAbkTotp(abkUserInfoVO.user_id);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 绑定谷歌-验证新密钥
* 获取用户列表
* @param req
* @param aclUserInfoVO
* @param res
* @param infoVO
*/
export const bindTotpConfirm = async (req: any, aclUserInfoVO: AclUserInfoVO) => {
let func_name = "userOptCtrl.bindTotpConfirm";
export const userList = async (req: any, abkUserInfoPageVO: AbkUserInfoPageVO) => {
let func_name = "abkUserInfo.control.userList";
let cmd = req.path;
try {
abkUserInfoPageVO.page = Optional.opt(abkUserInfoPageVO, 'page', 1);
abkUserInfoPageVO.size = Optional.opt(abkUserInfoPageVO, 'size', 20);
let currentUserId = await getCurrentUserId(req.cookies.session_id);
ApiAssert.notNull(ErrorCode.PARAM_MISS, aclUserInfoVO.totpCode);
let res = await userOptService.bindTotpConfirm(req.cookies.session_id, currentUserId, aclUserInfoVO.totpCode);
await isSuperUser(currentUserId)
let res = await abkUserService.userList(abkUserInfoPageVO, currentUserId);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
/**
* 修改用户状态
* @param req
* @param infoVO
*/
export const updateStatus = async (req: any, abkUserInfoPageVO: AbkUserInfoPageVO) => {
let func_name = "abkUserInfo.control.updateStatus";
let cmd = req.path;
try {
if (!abkUserInfoPageVO.user_id) {
throw ErrorCode.PARAM_MISS
}
if (!abkUserInfoPageVO.user_status) {
throw ErrorCode.PARAM_MISS
}
let currentUserId = await getCurrentUserId(req.cookies.session_id);
await isSuperUser(currentUserId)
let res = await abkUserService.updateStatus(abkUserInfoPageVO.user_id, abkUserInfoPageVO.user_status, currentUserId);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
function checkPwd(pwd: string) {
let reg = /^(?=.[0-9])(?=.[A-Z])(?=.[a-z])(?=.[!@#%^&*?]).{8,12}$/;
if (!reg.test(pwd)) {
......@@ -210,3 +199,31 @@ function checkPwd(pwd: string) {
}
/**
* 新增用户信息
* @param req
* @param infoVO
*/
export const addAbkUser = async (req: any, abkUserInfoVO: AbkUserInfoVO) => {
let func_name = "abkUserInfo.control.getInfo";
let cmd = req.path;
try {
let currentUserId = await getCurrentUserId(req.cookies.session_id);
await isSuperUser(currentUserId);
addCheck(abkUserInfoVO);
let res = await abkUserService.addAbkUser(abkUserInfoVO);
return Res3Utils.result(res);
}
catch (e) {
logger.error(`${func_name} error:${e}`);
return Res3Utils.getErrorResult(e);
}
};
function addCheck(abkUserInfoVO: AbkUserInfoVO) {
if (!abkUserInfoVO.account || !abkUserInfoVO.pwd || !abkUserInfoVO.totp_encrypt) {
throw ErrorCode.PARAM_MISS;
}
checkPwd(abkUserInfoVO.pwd);
}
import { madAdminOrmDB, abkUserInfo, aclUserInfo } from "@madex/ex-ts-dao";
import { getOneAclUserByAccount, getOneAclUserByUid } from "../../../../utils/aclUserUtils";
import { madAdminOrmDB, abkUserInfo } from "@madex/ex-ts-dao";
import { getOneAbkUserByAccount, getOneAbkUserByUid, checkAbkTotp } from "../../../../utils/abkUserUtils";
import { ErrorCode } from "../../../../constant/errorCode";
import { AbkUserInfoConst } from "../../../../constant/abkUserConstant";
import { CryptUtils } from "../../../../utils/crypt-utils";
import { RedisVal } from "../../../../constant/redis-val";
import * as aclRoleAuthService from "../aclRoleAuth.service";
import Config from "../../../../../config";
import { addOptLog } from "../userOptLog.service";
import * as aclUserService from "../aclUser.service";
const Otplib = require('otplib');
let { logger, apiAssertUtils: ApiAssert, BigNumberUtils } = require('@madex/ex-js-public');
let { authCommon: AuthCommon, redisUtilsCommon: RedisClient, } = require('@madex/ex-js-common');
......@@ -55,17 +54,17 @@ export async function login(account: any, pwd: any, totp_code: any, ip: string)
ApiAssert.isFalse(ErrorCode.ACCOUNT_STOP, userInfo.user_status === AbkUserInfoConst.USER_STATUS.STOP);
ApiAssert.isFalse(ErrorCode.USER_IS_DEL, userInfo.user_status === AbkUserInfoConst.USER_STATUS.DEL);
//校验谷歌
await checkAbkTotp(userInfo.user_id, totp_code);
await _checkPwd(userInfo, pwd);
let sessionId = CryptUtils.sessionId(userInfo.user_id);
RedisClient.rpush(RedisVal.sessionListKey(userInfo.user_id), sessionId);
RedisClient.rpush(RedisVal.abkSessionListKey(userInfo.user_id), sessionId);
let cookies = {
account: userInfo.account,
userId: userInfo.user_id,
//是否需要进行二次验证。0:不需要或者已通过验证;1:需要;
needConfirm: 0,
is_abk_user: 1,
allow_ips: userInfo.allow_ips,//ip白名单
totp_encrypt: ''
......@@ -75,7 +74,6 @@ export async function login(account: any, pwd: any, totp_code: any, ip: string)
//如果绑定了谷歌则必须校验, 增加属性 needConfirm = 1
let totp_encrypt = userInfo.totp_encrypt;
if (totp_encrypt) {
cookies.needConfirm = 1;
cookies.totp_encrypt = totp_encrypt;
hasTotp = 1;
}
......@@ -83,8 +81,10 @@ export async function login(account: any, pwd: any, totp_code: any, ip: string)
await RedisClient.writeSync(sessionId, cookies, Config.LOGIN_EXPIRED);
await _unlockPwd(userInfo.user_id);
//第一次登陆状态改为在用
if (!userInfo.user_status) {
updateAbkUserStatus(userInfo.user_id, 1);
}
return {
result: "success",
sessionId: sessionId,
......@@ -94,40 +94,6 @@ export async function login(account: any, pwd: any, totp_code: any, ip: string)
};
}
/**
* 根据account 查询 资产管理后台用户
* @param account
*/
export async function getOneAbkUserByAccount(account: string) {
let dbOne = await abkUserInfo.prototype.findOne({
where: {
account
},
raw: true
});
if (!dbOne) {
throw ErrorCode.USER_NOT_EXIST;
}
return dbOne;
}
/**
* 根据user_id 查询 资产管理后台用户
* @param account
*/
export async function getOneAbkUserByUserId(user_id: number) {
let dbOne = await abkUserInfo.prototype.findOne({
where: {
user_id
},
raw: true
});
if (!dbOne) {
throw ErrorCode.USER_NOT_EXIST;
}
return dbOne;
}
/**
* 校验密码
......@@ -178,42 +144,117 @@ export async function updateAbkUserStatus(user_id: number, user_status: number)
}
}
export async function loginConfirm(sessionId: any, userId: any, totpCode: any) {
export async function deleteAllAbkSessionByUserId(userId: number) {
//获取该账户使用过的所有sessionId
let sessionListKey = RedisVal.abkSessionListKey(userId)
RedisClient.lrange(sessionListKey, 0, -1, async (err, reply) => {
//删除所有sessionId
if (!err && reply && reply.length >= 1) {
await RedisClient.delSync(...reply)
}
//删除sessionList
await RedisClient.delSync(sessionListKey)
});
}
//判断是否在登录锁定中
let cookies = await RedisClient.getSync(sessionId)
ApiAssert.isTrue(ErrorCode.PARAM_MISS, Number(cookies.needConfirm) === 1);
let totp_encrypt = cookies.totp_encrypt;
//谷歌密钥验证
ApiAssert.isTrue(ErrorCode.UNBOUND_TOTP, totp_encrypt !== '');
await AuthCommon.totpCheckSync(totpCode, totp_encrypt)
export const getInfo = async (currentUserId: number | any, sessionId: string) => {
let dbUserInfo = await getOneAbkUserByUid(currentUserId);
//判断是否已经使用过
let latestVerifiedKey = "bastard.totp.used.user." + userId
let latestUsed = RedisClient.getSync(latestVerifiedKey)
ApiAssert.isFalse(ErrorCode.TOTP_CODE_USED, totpCode === latestUsed)
await RedisClient.writeSync(latestVerifiedKey, totpCode, 60 * 60)
ApiAssert.isNotEmpty(ErrorCode.USER_NOT_EXIST, dbUserInfo);
ApiAssert.isFalse(ErrorCode.ACCOUNT_STOP, dbUserInfo.user_status === AbkUserInfoConst.USER_STATUS.STOP);
ApiAssert.isFalse(ErrorCode.USER_IS_DEL, dbUserInfo.user_status === AbkUserInfoConst.USER_STATUS.DEL);
//解除缓存中的锁定标识
cookies.needConfirm = 0
await RedisClient.writeSync(sessionId, cookies, Config.LOGIN_EXPIRED)
return "success";
}
let data = {
userId: dbUserInfo.user_id,
account: dbUserInfo.account,
remark: dbUserInfo.remark,
allow_ips: dbUserInfo.allow_ips,
user_status: dbUserInfo.user_status,
user_type: dbUserInfo.user_type,
sessionId: sessionId,
hasTotp: dbUserInfo && dbUserInfo.totp_encrypt ? 1 : 0,
}
return data
};
export const getInfoDetailByUserId = async (user_id: any, totp_code: any, currentUserId: any) => {
let dbUserInfo = await getOneAbkUserByUid(user_id);
await checkAbkTotp(currentUserId, totp_code);
ApiAssert.isNotEmpty(ErrorCode.USER_NOT_EXIST, dbUserInfo);
let data = {
userId: dbUserInfo.user_id,
account: dbUserInfo.account,
remark: dbUserInfo.remark,
allow_ips: dbUserInfo.allow_ips,
user_status: dbUserInfo.user_status,
user_type: dbUserInfo.user_type,
hasTotp: dbUserInfo && dbUserInfo.totp_encrypt ? 1 : 0,
pwd: dbUserInfo.pwd,
pwd_salt: dbUserInfo.pwd_salt,
totp_encrypt: dbUserInfo ? dbUserInfo.totp_encrypt : "",
}
return data
};
export async function resetAbkTotp(userId: number | undefined) {
if (userId) {
let userInfo = await getOneAbkUserByUid(Number(userId));
ApiAssert.isNotEmpty(ErrorCode.USER_NOT_EXIST, userInfo);
}
//生成新的密钥
let totpEncrypt = Otplib.authenticator.generateSecret();
let email = userId ? userId : 0 + '-' + totpEncrypt.slice(0, 3)
let uri = 'otpauth://totp/' + email + '?secret=' + totpEncrypt + '&issuer=team888';
return { uri: uri, totpEncrypt: totpEncrypt };
}
export const checkAbkTotp = async function (user_id: number, totp_code: string) {
export async function userList(abkUserInfoPageVO: AbkUserInfoPageVO, currentUserId: any) {
let page = Number(abkUserInfoPageVO.page);
let size = Number(abkUserInfoPageVO.size);
//获取谷歌密钥并验证
let dbUserInfo = await getOneAbkUserByUserId(user_id);
ApiAssert.isTrue(ErrorCode.UNBOUND_TOTP, dbUserInfo && dbUserInfo.totp_encrypt !== '');
await AuthCommon.totpCheckSync(totp_code, dbUserInfo.totp_encrypt)
let res = await abkUserInfo.prototype.findAndCount({
where: {},
limit: size,
offset: (page - 1) * size,
order: [["user_id", "asc"]],
raw: true
});
for (let item of res.rows) {
delete item.pwd;
delete item.pwd_salt;
delete item.totp_encrypt;
item.del_time = item.user_status == AbkUserInfoConst.USER_STATUS.DEL ? item.updatedAt : ""
}
return res;
}
//判断是否已经使用过
let latestVerifiedKey = "abk.totp.used.user." + user_id
let latestUsed = RedisClient.getSync(latestVerifiedKey)
ApiAssert.isFalse(ErrorCode.TOTP_CODE_USED, totp_code === latestUsed)
await RedisClient.writeSync(latestVerifiedKey, totp_code, 60 * 60)
export async function updateStatus(user_id: number, user_status: number, currentUserId: any) {
await getOneAbkUserByUid(user_id)
await updateAbkUserStatus(user_id, user_status);
return 'success';
}
export async function addAbkUser(abkUserInfoVO: AbkUserInfoVO) {
let insertData = {
account: abkUserInfoVO.account,
pwd: abkUserInfoVO.pwd,
user_status: 0,
user_type: 0,
remark: abkUserInfoVO.remark ? abkUserInfoVO.remark : "",
totp_encrypt: abkUserInfoVO.totp_encrypt,
allow_ips: abkUserInfoVO.allow_ips ? abkUserInfoVO.allow_ips : "",
}
await abkUserInfo.prototype.create(insertData);
return 'success';
}
......@@ -10,7 +10,7 @@ const {
const router = Express.Router();
import * as abkUserCtrl from "../../mvc/control/abkctrl/abkUserInfo.control";
import * as abkUserCtrl from "../../mvc/control/v2/abkUserInfo.control";
import * as userAuthConfigCtrl from "../../mvc/control/userAuthConfig.control";
import Config from "../../../../config";
......@@ -18,6 +18,12 @@ import Config from "../../../../config";
const postFunc = {
'abkUser/login': abkUserCtrl.login,
'abkUser/logout': abkUserCtrl.logout,
'abkUser/getInfo': abkUserCtrl.getInfo,
'abkUser/getInfoDetailByUserId': abkUserCtrl.getInfoDetailByUserId,
'abkUser/reset/totp': abkUserCtrl.resetTotp,
'abkUser/list': abkUserCtrl.userList,
'abkUser/updateStatus': abkUserCtrl.updateStatus,
'abkUser/addUser': abkUserCtrl.addAbkUser,
/*
'user/login/confirm': userOptCtrl.loginConfirm,
'user/bind/totp/ask': userOptCtrl.bindTotpAsk,
......
import * as ReqUtils from "./req-utils";
import { ErrorCode } from "../constant/errorCode";
import { abkUserInfo, aclUserDepartmentPosition, madAdminOrmDB } from "@madex/ex-ts-dao";
let { logger } = require("@madex/ex-js-public");
let { apiAssertUtils: ApiAssert, BigNumberUtils } = require('@madex/ex-js-public');
let { authCommon: AuthCommon, redisUtilsCommon: RedisClient, } = require('@madex/ex-js-common');
/**
* 通过 user_id 查询用户
* @param user_id
*/
export const getOneAbkUserByUid = async function (user_id: number) {
if (!user_id) {
logger.error('abkUserUtils.getOneAbkUserByUid.error:' + 'user_id is null');
throw ErrorCode.PARAM_MISS
}
let dbInfo = await abkUserInfo.prototype.findOne({
where: {
user_id: user_id,
},
raw: true
});
if (!dbInfo) {
logger.error('abkUserUtils.getOneAbkUserByUid.error:' + 'dbInfo is null');
throw ErrorCode.USER_NOT_EXIST
}
return dbInfo;
}
/**
* 通过 account 查询用户
* @param account
*/
export const getOneAbkUserByAccount = async function (account: string) {
if (!account) {
logger.error('abkUserUtils.getOneAbkUserByAccount.error:' + 'account is null');
throw ErrorCode.PARAM_MISS
}
let dbInfo = await abkUserInfo.prototype.findOne({
where: {
account: account,
},
raw: true
});
if (!dbInfo) {
logger.error('abkUserUtils.getOneAbkUserByAccount.error:' + 'dbInfo is null');
throw ErrorCode.USER_NOT_EXIST
}
return dbInfo;
}
export const checkAbkTotp = async function (user_id: number, totp_code: string) {
//获取谷歌密钥并验证
let dbUserInfo = await getOneAbkUserByUid(user_id);
ApiAssert.isTrue(ErrorCode.UNBOUND_TOTP, dbUserInfo && dbUserInfo.totp_encrypt !== '');
await AuthCommon.totpCheckSync(totp_code, dbUserInfo.totp_encrypt)
//判断是否已经使用过
let latestVerifiedKey = "abk.totp.used.user." + user_id
let latestUsed = RedisClient.getSync(latestVerifiedKey)
ApiAssert.isFalse(ErrorCode.TOTP_CODE_USED, totp_code === latestUsed)
await RedisClient.writeSync(latestVerifiedKey, totp_code, 60 * 60)
return 'success';
}
export const isSuperUser = async function (user_id: any) {
let dbUser = await getOneAbkUserByUid(user_id);
if (!dbUser.user_type) {
throw ErrorCode.NO_PERMISSION;
}
}
......@@ -73,7 +73,7 @@ export const checkAbkCookie = async (cookies: any, req: any) => {
let cookieData = await RedisUtils.getSync(sessionId);
ApiAssert.isNotEmpty(ErrorCode.NOT_LOGIN, cookieData);
ApiAssert.isNotEmpty(ErrorCode.NO_PERMISSION, cookieData.is_abk_user);
ApiAssert.isTrue(ErrorCode.NEED_INPUT_GOOGLE_CODE, Number(cookieData.needConfirm) === 0)
//ApiAssert.isTrue(ErrorCode.NEED_INPUT_GOOGLE_CODE, Number(cookieData.needConfirm) === 0)
if (cookieData.allow_ips) {
let ips = cookieData.allow_ips.split(",");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment