完成登录
This commit is contained in:
parent
0a75bacb9b
commit
3646554e73
14
app/Http/Controllers/Api/AbstractController.php
Normal file
14
app/Http/Controllers/Api/AbstractController.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\InvokeController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AbstractController
|
||||||
|
* @package App\Http\Controllers\Api
|
||||||
|
*/
|
||||||
|
class AbstractController extends InvokeController
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
436
app/Http/Controllers/Api/UsersController.php
Executable file
436
app/Http/Controllers/Api/UsersController.php
Executable file
@ -0,0 +1,436 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Module\Base;
|
||||||
|
use Cache;
|
||||||
|
use Captcha;
|
||||||
|
use Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiDefine users
|
||||||
|
*
|
||||||
|
* 会员
|
||||||
|
*/
|
||||||
|
class UsersController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/login 01. 登录、注册
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName login
|
||||||
|
*
|
||||||
|
* @apiParam {String} type 类型
|
||||||
|
* - login:登录(默认)
|
||||||
|
* - reg:注册
|
||||||
|
* @apiParam {String} email 邮箱
|
||||||
|
* @apiParam {String} userpass 密码
|
||||||
|
* @apiParam {String} [code] 登录验证码
|
||||||
|
* @apiParam {String} [key] 登陆验证码key
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
|
||||||
|
*/
|
||||||
|
public function login()
|
||||||
|
{
|
||||||
|
$type = trim(Request::input('type'));
|
||||||
|
$email = trim(Request::input('email'));
|
||||||
|
$userpass = trim(Request::input('userpass'));
|
||||||
|
if ($type == 'reg') {
|
||||||
|
$setting = Base::setting('system');
|
||||||
|
if ($setting['reg'] == 'close') {
|
||||||
|
return Base::retError('未开放注册!');
|
||||||
|
}
|
||||||
|
$user = User::reg($email, $userpass);
|
||||||
|
if (Base::isError($user)) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$user = User::IDE($user['data']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$needCode = !Base::isError(User::needCode($email));
|
||||||
|
if ($needCode) {
|
||||||
|
$code = trim(Request::input('code'));
|
||||||
|
$key = trim(Request::input('key'));
|
||||||
|
if (empty($code)) {
|
||||||
|
return Base::retError('请输入验证码!', ['code' => 'need']);
|
||||||
|
}
|
||||||
|
if (empty($key)) {
|
||||||
|
if (!Captcha::check($code)) {
|
||||||
|
return Base::retError('请输入正确的验证码!', ['code' => 'need']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!Captcha::check_api($code, $key)) {
|
||||||
|
return Base::retError('请输入正确的验证码!', ['code' => 'need']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$retError = function ($msg) use ($email) {
|
||||||
|
Cache::forever("code::" . $email, "need");
|
||||||
|
$needCode = !Base::isError(User::needCode($email));
|
||||||
|
$needData = [ 'code' => $needCode ? 'need' : 'no' ];
|
||||||
|
return Base::retError($msg, $needData);
|
||||||
|
};
|
||||||
|
$user = User::whereEmail($email)->first();
|
||||||
|
if (empty($user)) {
|
||||||
|
return $retError('账号或密码错误!');
|
||||||
|
}
|
||||||
|
if ($user->userpass != Base::md52($userpass, $user->encrypt)) {
|
||||||
|
return $retError('账号或密码错误!');
|
||||||
|
}
|
||||||
|
Cache::forget("code::" . $email);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$array = [
|
||||||
|
'loginnum' => $user['loginnum'] + 1,
|
||||||
|
'lastip' => Base::getIp(),
|
||||||
|
'lastdate' => time(),
|
||||||
|
'lineip' => Base::getIp(),
|
||||||
|
'linedate' => time(),
|
||||||
|
];
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
$user->$key = $value;
|
||||||
|
}
|
||||||
|
$user->save();
|
||||||
|
//
|
||||||
|
$user->token = User::token($user);
|
||||||
|
return Base::retSuccess($type == 'reg' ? "注册成功" : "登录成功", $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/login/needcode 02. 是否需要验证码
|
||||||
|
*
|
||||||
|
* @apiDescription 用于判断是否需要登录验证码
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName login__needcode
|
||||||
|
*
|
||||||
|
* @apiParam {String} email 用户名
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1需要、0不需要)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function login__needcode()
|
||||||
|
{
|
||||||
|
return User::needCode(trim(Request::input('email')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/login/codeimg 03. 验证码图片
|
||||||
|
*
|
||||||
|
* @apiDescription 用于判断是否需要登录验证码
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName login__codeimg
|
||||||
|
*
|
||||||
|
* @apiParam {String} email 用户名
|
||||||
|
*
|
||||||
|
* @apiSuccess {Image} data 返回数据(直接输出图片)
|
||||||
|
*/
|
||||||
|
public function login__codeimg()
|
||||||
|
{
|
||||||
|
return Captcha::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/info 04. 获取我的信息
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName info
|
||||||
|
*
|
||||||
|
* @apiParam {String} [callback] jsonp返回字段
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
* @apiSuccessExample {json} data:
|
||||||
|
{
|
||||||
|
"userid":1,
|
||||||
|
"agentid":0,
|
||||||
|
"identity":[
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
"token":"MUBhZG1pbkAwRWFGSFhAMTYwODY5MjM0MUBXcVJpQ1Q=",
|
||||||
|
"az":"G",
|
||||||
|
"username":"admin",
|
||||||
|
"nickname":"管理员",
|
||||||
|
"userimg":"http://127.0.0.1:6006/images/other/avatar.png",
|
||||||
|
"loginnum":10,
|
||||||
|
"changepass":0,
|
||||||
|
"lastip":"172.18.0.1",
|
||||||
|
"lastdate":1608692341,
|
||||||
|
"lineip":"172.18.0.1",
|
||||||
|
"linedate":1608704450,
|
||||||
|
"regip":"127.0.0.1",
|
||||||
|
"regdate":1600856611,
|
||||||
|
"setting":null,
|
||||||
|
"time":1608704450,
|
||||||
|
"setpass":1,
|
||||||
|
|
||||||
|
"service":{
|
||||||
|
"id":1,
|
||||||
|
"type":1,
|
||||||
|
"create_id":1,
|
||||||
|
"created_at":"2020-12-23 14:39:51",
|
||||||
|
"expire_at":"2021-01-23 14:39:51",
|
||||||
|
"type_name":"商业版",
|
||||||
|
"member":10,
|
||||||
|
"network":3
|
||||||
|
},
|
||||||
|
"num_member":3,
|
||||||
|
"num_network":2
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
public function info()
|
||||||
|
{
|
||||||
|
$callback = Request::input('callback');
|
||||||
|
//
|
||||||
|
$user = User::authE();
|
||||||
|
if (Base::isError($user)) {
|
||||||
|
if (strlen($callback) > 3) {
|
||||||
|
return $callback . '(' . json_encode($user) . ')';
|
||||||
|
}
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$user = User::IDE($user['data']);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (strlen($callback) > 3) {
|
||||||
|
return $callback . '(' . json_encode(Base::retSuccess('success', $user)) . ')';
|
||||||
|
}
|
||||||
|
return Base::retSuccess('success', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/editdata 05. 修改自己的资料
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName editdata
|
||||||
|
*
|
||||||
|
* @apiParam {Object} [userimg] 会员头像(地址)
|
||||||
|
* @apiParam {String} [nickname] 昵称
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
|
||||||
|
*/
|
||||||
|
public function editdata()
|
||||||
|
{
|
||||||
|
$user = User::authE();
|
||||||
|
if (Base::isError($user)) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$user = User::IDE($user['data']);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//头像
|
||||||
|
$userimg = Request::input('userimg');
|
||||||
|
if ($userimg) {
|
||||||
|
$userimg = is_array($userimg) ? $userimg[0]['path'] : $userimg;
|
||||||
|
$user->userimg = Base::unFillUrl($userimg);
|
||||||
|
}
|
||||||
|
//昵称
|
||||||
|
$nickname = trim(Request::input('nickname'));
|
||||||
|
if ($nickname) {
|
||||||
|
if (mb_strlen($nickname) < 2) {
|
||||||
|
return Base::retError('昵称不可以少于2个字!');
|
||||||
|
} elseif (mb_strlen($nickname) > 8) {
|
||||||
|
return Base::retError('昵称最多只能设置8个字!');
|
||||||
|
} else {
|
||||||
|
$user->nickname = $nickname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$user->save();
|
||||||
|
return Base::retSuccess('修改成功!', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/editpass 06. 修改自己的密码
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName editpass
|
||||||
|
*
|
||||||
|
* @apiParam {String} oldpass 旧密码
|
||||||
|
* @apiParam {String} newpass 新密码
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
|
||||||
|
*/
|
||||||
|
public function editpass()
|
||||||
|
{
|
||||||
|
$user = User::authE();
|
||||||
|
if (Base::isError($user)) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$user = User::IDE($user['data']);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$oldpass = trim(Request::input('oldpass'));
|
||||||
|
$newpass = trim(Request::input('newpass'));
|
||||||
|
if (strlen($newpass) < 6) {
|
||||||
|
return Base::retError('密码设置不能小于6位数!');
|
||||||
|
} elseif (strlen($newpass) > 32) {
|
||||||
|
return Base::retError('密码最多只能设置32位数!');
|
||||||
|
}
|
||||||
|
if ($oldpass == $newpass) {
|
||||||
|
return Base::retError('新旧密码一致!');
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (env("PASSWORD_ADMIN") == 'disabled') {
|
||||||
|
if ($user->userid == 1) {
|
||||||
|
return Base::retError('当前环境禁止修改密码!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (env("PASSWORD_OWNER") == 'disabled') {
|
||||||
|
return Base::retError('当前环境禁止修改密码!');
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$verify = User::whereUserid($user->userid)->whereUserpass(Base::md52($oldpass, User::token2encrypt()))->count();
|
||||||
|
if (empty($verify)) {
|
||||||
|
return Base::retError('请填写正确的旧密码!');
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$user->encrypt = Base::generatePassword(6);
|
||||||
|
$user->userpass = Base::md52($newpass, $user->encrypt);
|
||||||
|
$user->changepass = 0;
|
||||||
|
$user->save();
|
||||||
|
return Base::retSuccess('修改成功!', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/login/codejson 07. 验证码json
|
||||||
|
*
|
||||||
|
* @apiDescription 用于判断是否需要登录验证码
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName login__codejson
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function login__codejson()
|
||||||
|
{
|
||||||
|
$captcha = Captcha::create('default', true);
|
||||||
|
return Base::retSuccess('请求成功', $captcha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/searchinfo 08. 搜索会员列表
|
||||||
|
*
|
||||||
|
* @apiDescription 搜索会员列表
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName searchinfo
|
||||||
|
*
|
||||||
|
* @apiParam {Object} where 搜索条件
|
||||||
|
* - where.email
|
||||||
|
* - where.noemail
|
||||||
|
* - where.username
|
||||||
|
* - where.nousername
|
||||||
|
* - where.usernameequal
|
||||||
|
* - where.noidentity
|
||||||
|
* - where.identity
|
||||||
|
* @apiParam {Number} [take] 获取数量,10-100
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function searchinfo()
|
||||||
|
{
|
||||||
|
$keys = Request::input('where');
|
||||||
|
$whereArr = [];
|
||||||
|
$whereRaw = null;
|
||||||
|
if ($keys['email']) $whereArr[] = ['email', '=', $keys['email']];
|
||||||
|
if ($keys['usernameequal']) $whereArr[] = ['username', '=', $keys['usernameequal']];
|
||||||
|
if ($keys['identity']) $whereArr[] = ['identity', 'like', '%,' . $keys['identity'] . ',%'];
|
||||||
|
if ($keys['noidentity']) $whereArr[] = ['identity', 'not like', '%,' . $keys['noidentity'] . ',%'];
|
||||||
|
if ($keys['username']) {
|
||||||
|
$whereRaw.= $whereRaw ? ' AND ' : '';
|
||||||
|
$whereRaw.= "(`username` LIKE '%" . $keys['username'] . "%' OR `nickname` LIKE '%" . $keys['username'] . "%')";
|
||||||
|
}
|
||||||
|
if ($keys['nousername']) {
|
||||||
|
$nousername = [];
|
||||||
|
foreach (explode(",", $keys['nousername']) AS $name) {
|
||||||
|
$name = trim($name);
|
||||||
|
if ($name && !in_array($name, $nousername)) {
|
||||||
|
$nousername[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($nousername) {
|
||||||
|
$whereRaw.= $whereRaw ? ' AND ' : '';
|
||||||
|
$whereRaw.= "(`username` NOT IN ('" . implode("','", $nousername) . "'))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($keys['noemail']) {
|
||||||
|
$noemail = [];
|
||||||
|
foreach (explode(",", $keys['noemail']) AS $email) {
|
||||||
|
$email = trim($email);
|
||||||
|
if ($email && !in_array($email, $noemail)) {
|
||||||
|
$noemail[] = $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($noemail) {
|
||||||
|
$whereRaw.= $whereRaw ? ' AND ' : '';
|
||||||
|
$whereRaw.= "(`email` NOT IN ('" . implode("','", $noemail) . "'))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$list = User::select(['userid', 'email', 'username', 'nickname', 'userimg'])
|
||||||
|
->where($whereArr)
|
||||||
|
->whereRaw($whereRaw)
|
||||||
|
->orderBy('userid')
|
||||||
|
->take(Base::getPaginate(100, 10, 'take'))
|
||||||
|
->get();
|
||||||
|
return Base::retSuccess('success', $list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/users/basic 09. 获取指定会员基本信息
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup users
|
||||||
|
* @apiName basic
|
||||||
|
*
|
||||||
|
* @apiParam {String} email 会员用户名(多个格式:jsonArray,一次最多30个)
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function basic()
|
||||||
|
{
|
||||||
|
$email = trim(Request::input('email'));
|
||||||
|
$array = Base::json2array($email);
|
||||||
|
if (empty($array)) {
|
||||||
|
$array[] = $email;
|
||||||
|
}
|
||||||
|
if (count($array) > 50) {
|
||||||
|
return Base::retError(['一次最多只能获取%条数据!', 50]);
|
||||||
|
}
|
||||||
|
$retArray = [];
|
||||||
|
foreach ($array AS $name) {
|
||||||
|
$basic = User::email2basic($name);
|
||||||
|
if ($basic) {
|
||||||
|
$retArray[] = $basic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Base::retSuccess('success', $retArray);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ use Cache;
|
|||||||
* @property int $userid
|
* @property int $userid
|
||||||
* @property array $identity 身份
|
* @property array $identity 身份
|
||||||
* @property string|null $az A-Z
|
* @property string|null $az A-Z
|
||||||
|
* @property string|null $email 邮箱
|
||||||
* @property string|null $username 用户名
|
* @property string|null $username 用户名
|
||||||
* @property string $nickname 昵称
|
* @property string $nickname 昵称
|
||||||
* @property string|null $userimg 头像
|
* @property string|null $userimg 头像
|
||||||
@ -33,6 +34,7 @@ use Cache;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
|
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value)
|
||||||
@ -54,6 +56,8 @@ use Cache;
|
|||||||
*/
|
*/
|
||||||
class User extends AbstractModel
|
class User extends AbstractModel
|
||||||
{
|
{
|
||||||
|
protected $primaryKey = 'userid';
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'encrypt',
|
'encrypt',
|
||||||
'userpass',
|
'userpass',
|
||||||
@ -66,7 +70,13 @@ class User extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function getNicknameAttribute($value)
|
public function getNicknameAttribute($value)
|
||||||
{
|
{
|
||||||
return $value ?: $this->username;
|
if ($value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if ($this->username) {
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
return Base::getMiddle($this->email, null, "@");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,6 +100,49 @@ class User extends AbstractModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** ***************************************************************************************** */
|
||||||
|
/** ***************************************************************************************** */
|
||||||
|
/** ***************************************************************************************** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册会员
|
||||||
|
* @param $email
|
||||||
|
* @param $userpass
|
||||||
|
* @param array $other
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function reg($email, $userpass, $other = [])
|
||||||
|
{
|
||||||
|
//邮箱
|
||||||
|
if (!Base::isMail($email)) {
|
||||||
|
return Base::retError('请输入正确的邮箱地址!');
|
||||||
|
}
|
||||||
|
if (User::email2userid($email) > 0) {
|
||||||
|
return Base::retError('邮箱地址已存在!');
|
||||||
|
}
|
||||||
|
//密码
|
||||||
|
if (strlen($userpass) < 6) {
|
||||||
|
return Base::retError(['密码设置不能小于%位数!', 6]);
|
||||||
|
} elseif (strlen($userpass) > 32) {
|
||||||
|
return Base::retError(['密码最多只能设置%位数!', 32]);
|
||||||
|
}
|
||||||
|
//开始注册
|
||||||
|
$encrypt = Base::generatePassword(6);
|
||||||
|
$inArray = [
|
||||||
|
'encrypt' => $encrypt,
|
||||||
|
'email' => $email,
|
||||||
|
'userpass' => Base::md52($userpass, $encrypt),
|
||||||
|
'regip' => Base::getIp(),
|
||||||
|
'regdate' => time()
|
||||||
|
];
|
||||||
|
if ($other) {
|
||||||
|
$inArray = array_merge($inArray, $other);
|
||||||
|
}
|
||||||
|
$user = User::createInstance($inArray);
|
||||||
|
$user->save();
|
||||||
|
User::AZUpdate($user->userid);
|
||||||
|
return Base::retSuccess('success', $user);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* userid获取用户名
|
* userid获取用户名
|
||||||
@ -104,6 +157,19 @@ class User extends AbstractModel
|
|||||||
return self::whereUserid(intval($userid))->value('username');
|
return self::whereUserid(intval($userid))->value('username');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱获取userid
|
||||||
|
* @param $email
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function email2userid($email)
|
||||||
|
{
|
||||||
|
if (empty($email)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return intval(self::whereUsername($email)->value('userid'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名获取userid
|
* 用户名获取userid
|
||||||
* @param $username
|
* @param $username
|
||||||
@ -234,12 +300,12 @@ class User extends AbstractModel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成token
|
* 生成token
|
||||||
* @param $userinfo
|
* @param self $userinfo
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function token($userinfo)
|
public static function token($userinfo)
|
||||||
{
|
{
|
||||||
return base64_encode($userinfo['userid'] . '@' . $userinfo['username'] . '@' . $userinfo['encrypt'] . '@' . time() . '@' . Base::generatePassword(6));
|
return base64_encode($userinfo->userid . '@' . $userinfo->username . '@' . $userinfo->encrypt . '@' . time() . '@' . Base::generatePassword(6));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,47 +355,60 @@ class User extends AbstractModel
|
|||||||
/**
|
/**
|
||||||
* userid 获取 基本信息
|
* userid 获取 基本信息
|
||||||
* @param int $userid 会员ID
|
* @param int $userid 会员ID
|
||||||
* @return array
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function userid2basic(int $userid)
|
public static function userid2basic(int $userid)
|
||||||
{
|
{
|
||||||
global $_A;
|
global $_A;
|
||||||
if (empty($userid)) {
|
if (empty($userid)) {
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
if (isset($_A["__static_userid2basic_" . $userid])) {
|
if (isset($_A["__static_userid2basic_" . $userid])) {
|
||||||
return $_A["__static_userid2basic_" . $userid];
|
return $_A["__static_userid2basic_" . $userid];
|
||||||
}
|
}
|
||||||
$fields = ['userid', 'username', 'nickname', 'userimg'];
|
$fields = ['userid', 'email', 'username', 'nickname', 'userimg'];
|
||||||
$userInfo = self::whereUserid($userid)->select($fields)->first();
|
$userInfo = self::whereUserid($userid)->select($fields)->first();
|
||||||
if ($userInfo) {
|
|
||||||
$userInfo->userimg = self::userimg($userInfo->userimg);
|
|
||||||
}
|
|
||||||
return $_A["__static_userid2basic_" . $userid] = ($userInfo ?: []);
|
return $_A["__static_userid2basic_" . $userid] = ($userInfo ?: []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* username 获取 基本信息
|
* username 获取 基本信息
|
||||||
* @param string $username 用户名
|
* @param string $username 用户名
|
||||||
* @return array
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function username2basic(string $username)
|
public static function username2basic(string $username)
|
||||||
{
|
{
|
||||||
global $_A;
|
global $_A;
|
||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
if (isset($_A["__static_username2basic_" . $username])) {
|
if (isset($_A["__static_username2basic_" . $username])) {
|
||||||
return $_A["__static_username2basic_" . $username];
|
return $_A["__static_username2basic_" . $username];
|
||||||
}
|
}
|
||||||
$fields = ['userid', 'username', 'nickname', 'userimg'];
|
$fields = ['userid', 'email', 'username', 'nickname', 'userimg'];
|
||||||
$userInfo = self::whereUsername($username)->select($fields)->first();
|
$userInfo = self::whereUsername($username)->select($fields)->first();
|
||||||
if ($userInfo) {
|
|
||||||
$userInfo->userimg = self::userimg($userInfo->userimg);
|
|
||||||
}
|
|
||||||
return $_A["__static_username2basic_" . $username] = ($userInfo ?: []);
|
return $_A["__static_username2basic_" . $username] = ($userInfo ?: []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* email 获取 基本信息
|
||||||
|
* @param string $email 邮箱地址
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function email2basic(string $email)
|
||||||
|
{
|
||||||
|
global $_A;
|
||||||
|
if (empty($email)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isset($_A["__static_email2basic_" . $email])) {
|
||||||
|
return $_A["__static_email2basic_" . $email];
|
||||||
|
}
|
||||||
|
$fields = ['userid', 'email', 'username', 'nickname', 'userimg'];
|
||||||
|
$userInfo = self::whereEmail($email)->select($fields)->first();
|
||||||
|
return $_A["__static_email2basic_" . $email] = ($userInfo ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户头像,不存在时返回默认
|
* 用户头像,不存在时返回默认
|
||||||
* @param string $var 头像地址 或 会员用户名
|
* @param string $var 头像地址 或 会员用户名
|
||||||
@ -342,7 +421,9 @@ class User extends AbstractModel
|
|||||||
$var = "";
|
$var = "";
|
||||||
} else {
|
} else {
|
||||||
$userInfo = self::username2basic($var);
|
$userInfo = self::username2basic($var);
|
||||||
$var = $userInfo['userimg'];
|
if ($userInfo) {
|
||||||
|
$var = $userInfo->userimg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $var ? Base::fillUrl($var) : url('images/other/avatar.png');
|
return $var ? Base::fillUrl($var) : url('images/other/avatar.png');
|
||||||
@ -354,7 +435,7 @@ class User extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public static function AZUpdate($userid)
|
public static function AZUpdate($userid)
|
||||||
{
|
{
|
||||||
$row = self::whereUserid($userid)->select(['username', 'nickname'])->first();
|
$row = self::whereUserid($userid)->select(['email', 'username', 'nickname'])->first();
|
||||||
if ($row) {
|
if ($row) {
|
||||||
$row->az = Base::getFirstCharter($row->nickname);
|
$row->az = Base::getFirstCharter($row->nickname);
|
||||||
$row->save();
|
$row->save();
|
||||||
|
@ -2023,6 +2023,18 @@ class Base
|
|||||||
) * 6380';
|
) * 6380';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取每页数量
|
||||||
|
* @param $max
|
||||||
|
* @param $default
|
||||||
|
* @param string $inputName
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function getPaginate($max, $default, $inputName = 'pagesize')
|
||||||
|
{
|
||||||
|
return Min(Max(Base::nullShow(Request::input($inputName), $default), 1), $max);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* image64图片保存
|
* image64图片保存
|
||||||
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式] ]
|
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式] ]
|
||||||
|
48
config/captcha.php
Normal file
48
config/captcha.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'characters' => ['2', '3', '4', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'm', 'n', 'p', 'q', 'r', 't', 'u', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'X', 'Y', 'Z'],
|
||||||
|
'default' => [
|
||||||
|
'length' => 5,
|
||||||
|
'width' => 130,
|
||||||
|
'height' => 36,
|
||||||
|
'quality' => 90,
|
||||||
|
'math' => false,
|
||||||
|
],
|
||||||
|
'math' => [
|
||||||
|
'length' => 9,
|
||||||
|
'width' => 120,
|
||||||
|
'height' => 36,
|
||||||
|
'quality' => 90,
|
||||||
|
'math' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'flat' => [
|
||||||
|
'length' => 6,
|
||||||
|
'width' => 180,
|
||||||
|
'height' => 46,
|
||||||
|
'quality' => 90,
|
||||||
|
'lines' => 6,
|
||||||
|
'bgImage' => false,
|
||||||
|
'bgColor' => '#ecf2f4',
|
||||||
|
'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],
|
||||||
|
'contrast' => -5,
|
||||||
|
],
|
||||||
|
'mini' => [
|
||||||
|
'length' => 3,
|
||||||
|
'width' => 60,
|
||||||
|
'height' => 32,
|
||||||
|
],
|
||||||
|
'inverse' => [
|
||||||
|
'length' => 5,
|
||||||
|
'width' => 120,
|
||||||
|
'height' => 36,
|
||||||
|
'quality' => 90,
|
||||||
|
'sensitive' => true,
|
||||||
|
'angle' => 12,
|
||||||
|
'sharpen' => 10,
|
||||||
|
'blur' => 2,
|
||||||
|
'invert' => true,
|
||||||
|
'contrast' => -5,
|
||||||
|
]
|
||||||
|
];
|
@ -1,25 +1,200 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page-login">
|
<div class="page-login">
|
||||||
<PageTitle>{{$L('登录页面')}}</PageTitle>
|
<PageTitle>{{$L('登录页面')}}</PageTitle>
|
||||||
|
<div class="login-body">
|
||||||
|
<div class="login-logo"></div>
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-title">Welcome Dootask</div>
|
||||||
|
<div class="login-subtitle">Enter your credentials to access your account.</div>
|
||||||
|
<div class="login-input">
|
||||||
|
<Input v-model="email" prefix="ios-mail-outline" placeholder="Enter your email" size="large" @on-enter="onLogin" @on-blur="onBlur" />
|
||||||
|
<Input v-model="password" prefix="ios-lock-outline" placeholder="Enter your password" type="password" size="large" @on-enter="onLogin" />
|
||||||
|
<Input v-if="codeNeed" v-model="code" class="login-code" placeholder="Verification Code" size="large" @on-enter="onLogin">
|
||||||
|
<Icon type="ios-checkmark-circle-outline" class="login-icon" slot="prepend"></Icon>
|
||||||
|
<div slot="append" class="login-code-end" @click="reCode"><img :src="codeUrl"/></div>
|
||||||
|
</Input>
|
||||||
|
<Button type="primary" :loading="loadIng > 0" size="large" long @click="onLogin">Sign In</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="login-forgot">Forgot your password? <a href="#">Reset Password</a></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
:global {
|
||||||
|
.page-login {
|
||||||
|
.login-body {
|
||||||
|
.login-box {
|
||||||
|
.login-input {
|
||||||
|
.ivu-input {
|
||||||
|
border-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
.login-code {
|
||||||
|
.ivu-input-group-prepend,
|
||||||
|
.ivu-input-group-append {
|
||||||
|
background: transparent;
|
||||||
|
border-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
.ivu-input {
|
||||||
|
border-left-color: transparent;
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
.login-code-end {
|
||||||
|
margin: -6px -7px;
|
||||||
|
height: 38px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-login {
|
:global {
|
||||||
position: absolute;
|
.page-login {
|
||||||
top: 0;
|
position: absolute;
|
||||||
left: 0;
|
top: 0;
|
||||||
right: 0;
|
left: 0;
|
||||||
bottom: 0;
|
right: 0;
|
||||||
display: flex;
|
bottom: 0;
|
||||||
align-items: center;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #F3F6FE;
|
||||||
|
.login-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
.login-logo {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
background: url("../../statics/images/logo.svg") no-repeat center center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
.login-box {
|
||||||
|
margin-top: 32px;
|
||||||
|
width: 450px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 0 10px #e6ecfa;
|
||||||
|
.login-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
.login-subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
color: #AAAAAA;
|
||||||
|
}
|
||||||
|
.login-input {
|
||||||
|
margin: 40px;
|
||||||
|
> * {
|
||||||
|
margin-top: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-forgot {
|
||||||
|
color: #aaaaaa;
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
loadIng: 0,
|
||||||
|
|
||||||
|
codeNeed: false,
|
||||||
|
codeUrl: $A.apiUrl('users/login/codeimg'),
|
||||||
|
|
||||||
|
loginType: 'login',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
code: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reCode() {
|
||||||
|
this.codeUrl = $A.apiUrl('users/login/codeimg?_=' + Math.random())
|
||||||
|
},
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
|
if (this.loginType != 'login') {
|
||||||
|
this.codeNeed = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadIng++;
|
||||||
|
$A.ajaxc({
|
||||||
|
url: $A.apiUrl('users/login/needcode'),
|
||||||
|
data: {
|
||||||
|
email: this.email,
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.loadIng--;
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
this.reCode();
|
||||||
|
this.codeNeed = res.ret === 1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onLogin() {
|
||||||
|
if (!this.email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.password) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadIng++;
|
||||||
|
$A.ajaxc({
|
||||||
|
url: $A.apiUrl('users/login?type=' + this.loginType),
|
||||||
|
data: {
|
||||||
|
email: this.email,
|
||||||
|
password: this.password,
|
||||||
|
code: this.code,
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.loadIng--;
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
if (res.ret === 1) {
|
||||||
|
this.$store.commit('userInfo', res.data);
|
||||||
|
//
|
||||||
|
this.goNext();
|
||||||
|
} else {
|
||||||
|
$A.noticeError(res.msg);
|
||||||
|
if (res.data.code === 'need') {
|
||||||
|
this.reCode();
|
||||||
|
this.codeNeed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
goNext() {
|
||||||
|
let fromUrl = decodeURIComponent($A.getObject(this.$route.query, 'from'));
|
||||||
|
if (fromUrl) {
|
||||||
|
window.location.replace(fromUrl);
|
||||||
|
} else {
|
||||||
|
this.goForward({path: '/manage/dashboard'}, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
5
resources/assets/js/store/mutations.js
vendored
5
resources/assets/js/store/mutations.js
vendored
@ -2,5 +2,10 @@ export default {
|
|||||||
projectChatShowToggle(state) {
|
projectChatShowToggle(state) {
|
||||||
state.projectChatShow = !state.projectChatShow
|
state.projectChatShow = !state.projectChatShow
|
||||||
state.setStorage('projectChatShow', state.projectChatShow);
|
state.setStorage('projectChatShow', state.projectChatShow);
|
||||||
|
},
|
||||||
|
userInfo(state, info) {
|
||||||
|
state.userInfo = info
|
||||||
|
state.setStorage('userInfo', info);
|
||||||
|
state.setStorage('token', state._isJson(info) ? info.token : '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
resources/assets/js/store/state.js
vendored
20
resources/assets/js/store/state.js
vendored
@ -23,6 +23,24 @@ const state = {
|
|||||||
return typeof value === "boolean" ? value : def;
|
return typeof value === "boolean" ? value : def;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getStorageArray(key, def = {}) {
|
||||||
|
let value = this._storage(key);
|
||||||
|
return this._isArray(value) ? value : def;
|
||||||
|
},
|
||||||
|
|
||||||
|
getStorageJson(key, def = {}) {
|
||||||
|
let value = this._storage(key);
|
||||||
|
return this._isJson(value) ? value : def;
|
||||||
|
},
|
||||||
|
|
||||||
|
_isArray(obj) {
|
||||||
|
return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == '[object array]' && typeof obj.length == "number";
|
||||||
|
},
|
||||||
|
|
||||||
|
_isJson(obj) {
|
||||||
|
return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && typeof obj.length == "undefined";
|
||||||
|
},
|
||||||
|
|
||||||
_storage(key, value) {
|
_storage(key, value) {
|
||||||
let keyName = 'state';
|
let keyName = 'state';
|
||||||
if (typeof value === 'undefined') {
|
if (typeof value === 'undefined') {
|
||||||
@ -71,4 +89,6 @@ const state = {
|
|||||||
|
|
||||||
export default Object.assign(state, {
|
export default Object.assign(state, {
|
||||||
projectChatShow: state.getStorageBoolean('projectChatShow', true),
|
projectChatShow: state.getStorageBoolean('projectChatShow', true),
|
||||||
|
userInfo: state.getStorageJson('userInfo'),
|
||||||
|
token: state.getStorageString('token'),
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\Api\UsersController;
|
||||||
use App\Http\Controllers\IndexController;
|
use App\Http\Controllers\IndexController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@ -20,7 +21,9 @@ use Illuminate\Support\Facades\Route;
|
|||||||
* 接口
|
* 接口
|
||||||
*/
|
*/
|
||||||
Route::prefix('api')->middleware(['webapi'])->group(function () {
|
Route::prefix('api')->middleware(['webapi'])->group(function () {
|
||||||
|
// 会员
|
||||||
|
Route::any('users/{method}', UsersController::class);
|
||||||
|
Route::any('users/{method}/{action}', UsersController::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user