项目初始化
This commit is contained in:
38
app/api/common.php
Normal file
38
app/api/common.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use ba\Filesystem;
|
||||
use app\admin\library\module\Server;
|
||||
|
||||
if (!function_exists('get_account_verification_type')) {
|
||||
|
||||
/**
|
||||
* 获取可用的账户验证方式
|
||||
* 用于:用户找回密码|用户注册
|
||||
* @return string[] email=电子邮件,mobile=手机短信验证
|
||||
* @throws Throwable
|
||||
*/
|
||||
function get_account_verification_type(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
// 电子邮件,检查后台系统邮件配置是否全部填写
|
||||
$sysMailConfig = get_sys_config('', 'mail');
|
||||
$configured = true;
|
||||
foreach ($sysMailConfig as $item) {
|
||||
if (!$item) {
|
||||
$configured = false;
|
||||
}
|
||||
}
|
||||
if ($configured) {
|
||||
$types[] = 'email';
|
||||
}
|
||||
|
||||
// 手机号,检查是否安装短信模块
|
||||
$sms = Server::getIni(Filesystem::fsFit(root_path() . 'modules/sms/'));
|
||||
if ($sms && $sms['state'] == 1) {
|
||||
$types[] = 'mobile';
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
259
app/api/controller/Account.php
Normal file
259
app/api/controller/Account.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use ba\Date;
|
||||
use Throwable;
|
||||
use ba\Captcha;
|
||||
use ba\Random;
|
||||
use app\common\model\User;
|
||||
use think\facade\Validate;
|
||||
use app\common\facade\Token;
|
||||
use app\common\model\UserScoreLog;
|
||||
use app\common\model\UserMoneyLog;
|
||||
use app\common\controller\Frontend;
|
||||
use app\api\validate\Account as AccountValidate;
|
||||
|
||||
class Account extends Frontend
|
||||
{
|
||||
protected array $noNeedLogin = ['retrievePassword'];
|
||||
|
||||
protected array $noNeedPermission = ['verification', 'changeBind'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
public function overview(): void
|
||||
{
|
||||
$sevenDays = Date::unixTime('day', -6);
|
||||
$score = $money = $days = [];
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$days[$i] = date("Y-m-d", $sevenDays + ($i * 86400));
|
||||
$tempToday0 = strtotime($days[$i]);
|
||||
$tempToday24 = strtotime('+1 day', $tempToday0) - 1;
|
||||
$score[$i] = UserScoreLog::where('user_id', $this->auth->id)
|
||||
->where('create_time', 'BETWEEN', $tempToday0 . ',' . $tempToday24)
|
||||
->sum('score');
|
||||
|
||||
$userMoneyTemp = UserMoneyLog::where('user_id', $this->auth->id)
|
||||
->where('create_time', 'BETWEEN', $tempToday0 . ',' . $tempToday24)
|
||||
->sum('money');
|
||||
$money[$i] = bcdiv($userMoneyTemp, 100, 2);
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'days' => $days,
|
||||
'score' => $score,
|
||||
'money' => $money,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员资料
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function profile(): void
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$model = $this->auth->getUser();
|
||||
$data = $this->request->only(['avatar', 'username', 'nickname', 'gender', 'birthday', 'motto']);
|
||||
|
||||
$data['id'] = $this->auth->id;
|
||||
if (!isset($data['birthday'])) {
|
||||
$data['birthday'] = null;
|
||||
}
|
||||
|
||||
try {
|
||||
$validate = new AccountValidate();
|
||||
$validate->scene('edit')->check($data);
|
||||
} catch (Throwable $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$model->startTrans();
|
||||
try {
|
||||
$model->save($data);
|
||||
$model->commit();
|
||||
} catch (Throwable $e) {
|
||||
$model->rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->success(__('Data updated successfully~'));
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'accountVerificationType' => get_account_verification_type()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过手机号或邮箱验证账户
|
||||
* 此处检查的验证码是通过 api/Ems或api/Sms发送的
|
||||
* 验证成功后,向前端返回一个 email-pass Token或着 mobile-pass Token
|
||||
* 在 changBind 方法中,通过 pass Token来确定用户已经通过了账户验证(用户未绑定邮箱/手机时通过账户密码验证)
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function verification(): void
|
||||
{
|
||||
$captcha = new Captcha();
|
||||
$params = $this->request->only(['type', 'captcha']);
|
||||
if ($captcha->check($params['captcha'], ($params['type'] == 'email' ? $this->auth->email : $this->auth->mobile) . "user_{$params['type']}_verify")) {
|
||||
$uuid = Random::uuid();
|
||||
Token::set($uuid, $params['type'] . '-pass', $this->auth->id, 600);
|
||||
$this->success('', [
|
||||
'type' => $params['type'],
|
||||
'accountVerificationToken' => $uuid,
|
||||
]);
|
||||
}
|
||||
$this->error(__('Please enter the correct verification code'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改绑定信息(手机号、邮箱)
|
||||
* 通过 pass Token来确定用户已经通过了账户验证,也就是以上的 verification 方法,同时用户未绑定邮箱/手机时通过账户密码验证
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function changeBind(): void
|
||||
{
|
||||
$captcha = new Captcha();
|
||||
$params = $this->request->only(['type', 'captcha', 'email', 'mobile', 'accountVerificationToken', 'password']);
|
||||
$user = $this->auth->getUser();
|
||||
|
||||
if ($user[$params['type']]) {
|
||||
if (!Token::check($params['accountVerificationToken'], $params['type'] . '-pass', $user->id)) {
|
||||
$this->error(__('You need to verify your account before modifying the binding information'));
|
||||
}
|
||||
} elseif (!isset($params['password']) || !verify_password($params['password'], $user->password, ['salt' => $user->salt])) {
|
||||
$this->error(__('Password error'));
|
||||
}
|
||||
|
||||
// 检查验证码
|
||||
if ($captcha->check($params['captcha'], $params[$params['type']] . "user_change_{$params['type']}")) {
|
||||
if ($params['type'] == 'email') {
|
||||
$validate = Validate::rule(['email' => 'require|email|unique:user'])->message([
|
||||
'email.require' => 'email format error',
|
||||
'email.email' => 'email format error',
|
||||
'email.unique' => 'email is occupied',
|
||||
]);
|
||||
if (!$validate->check(['email' => $params['email']])) {
|
||||
$this->error(__($validate->getError()));
|
||||
}
|
||||
$user->email = $params['email'];
|
||||
} elseif ($params['type'] == 'mobile') {
|
||||
$validate = Validate::rule(['mobile' => 'require|mobile|unique:user'])->message([
|
||||
'mobile.require' => 'mobile format error',
|
||||
'mobile.mobile' => 'mobile format error',
|
||||
'mobile.unique' => 'mobile is occupied',
|
||||
]);
|
||||
if (!$validate->check(['mobile' => $params['mobile']])) {
|
||||
$this->error(__($validate->getError()));
|
||||
}
|
||||
$user->mobile = $params['mobile'];
|
||||
}
|
||||
Token::delete($params['accountVerificationToken']);
|
||||
$user->save();
|
||||
$this->success();
|
||||
}
|
||||
$this->error(__('Please enter the correct verification code'));
|
||||
}
|
||||
|
||||
public function changePassword(): void
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$model = $this->auth->getUser();
|
||||
$params = $this->request->only(['oldPassword', 'newPassword']);
|
||||
|
||||
if (!verify_password($params['oldPassword'], $model->password, ['salt' => $model->salt])) {
|
||||
$this->error(__('Old password error'));
|
||||
}
|
||||
|
||||
$model->startTrans();
|
||||
try {
|
||||
$validate = new AccountValidate();
|
||||
$validate->scene('changePassword')->check(['password' => $params['newPassword']]);
|
||||
$model->resetPassword($this->auth->id, $params['newPassword']);
|
||||
$model->commit();
|
||||
} catch (Throwable $e) {
|
||||
$model->rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->auth->logout();
|
||||
$this->success(__('Password has been changed, please login again~'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分日志
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function integral(): void
|
||||
{
|
||||
$limit = $this->request->request('limit');
|
||||
$integralModel = new UserScoreLog();
|
||||
$res = $integralModel->where('user_id', $this->auth->id)
|
||||
->order('create_time desc')
|
||||
->paginate($limit);
|
||||
|
||||
$this->success('', [
|
||||
'list' => $res->items(),
|
||||
'total' => $res->total(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额日志
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function balance(): void
|
||||
{
|
||||
$limit = $this->request->request('limit');
|
||||
$moneyModel = new UserMoneyLog();
|
||||
$res = $moneyModel->where('user_id', $this->auth->id)
|
||||
->order('create_time desc')
|
||||
->paginate($limit);
|
||||
|
||||
$this->success('', [
|
||||
'list' => $res->items(),
|
||||
'total' => $res->total(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找回密码
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function retrievePassword(): void
|
||||
{
|
||||
$params = $this->request->only(['type', 'account', 'captcha', 'password']);
|
||||
try {
|
||||
$validate = new AccountValidate();
|
||||
$validate->scene('retrievePassword')->check($params);
|
||||
} catch (Throwable $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
if ($params['type'] == 'email') {
|
||||
$user = User::where('email', $params['account'])->find();
|
||||
} else {
|
||||
$user = User::where('mobile', $params['account'])->find();
|
||||
}
|
||||
if (!$user) {
|
||||
$this->error(__('Account does not exist~'));
|
||||
}
|
||||
|
||||
$captchaObj = new Captcha();
|
||||
if (!$captchaObj->check($params['captcha'], $params['account'] . 'user_retrieve_pwd')) {
|
||||
$this->error(__('Please enter the correct verification code'));
|
||||
}
|
||||
|
||||
if ($user->resetPassword($user->id, $params['password'])) {
|
||||
$this->success(__('Password has been changed~'));
|
||||
} else {
|
||||
$this->error(__('Failed to modify password, please try again later~'));
|
||||
}
|
||||
}
|
||||
}
|
||||
59
app/api/controller/Ajax.php
Normal file
59
app/api/controller/Ajax.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use Throwable;
|
||||
use think\Response;
|
||||
use app\common\library\Upload;
|
||||
use app\common\controller\Frontend;
|
||||
|
||||
class Ajax extends Frontend
|
||||
{
|
||||
protected array $noNeedLogin = ['area', 'buildSuffixSvg'];
|
||||
|
||||
protected array $noNeedPermission = ['upload'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
public function upload(): void
|
||||
{
|
||||
$file = $this->request->file('file');
|
||||
$driver = $this->request->param('driver', 'local');
|
||||
$topic = $this->request->param('topic', 'default');
|
||||
try {
|
||||
$upload = new Upload();
|
||||
$attachment = $upload
|
||||
->setFile($file)
|
||||
->setDriver($driver)
|
||||
->setTopic($topic)
|
||||
->upload(null, 0, $this->auth->id);
|
||||
unset($attachment['create_time'], $attachment['quote']);
|
||||
} catch (Throwable $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
$this->success(__('File uploaded successfully'), [
|
||||
'file' => $attachment ?? []
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 省份地区数据
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function area(): void
|
||||
{
|
||||
$this->success('', get_area());
|
||||
}
|
||||
|
||||
public function buildSuffixSvg(): Response
|
||||
{
|
||||
$suffix = $this->request->param('suffix', 'file');
|
||||
$background = $this->request->param('background');
|
||||
$content = build_suffix_svg((string)$suffix, (string)$background);
|
||||
return response($content, 200, ['Content-Length' => strlen($content)])->contentType('image/svg+xml');
|
||||
}
|
||||
}
|
||||
92
app/api/controller/Common.php
Normal file
92
app/api/controller/Common.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use ba\Random;
|
||||
use Throwable;
|
||||
use ba\Captcha;
|
||||
use think\Response;
|
||||
use ba\ClickCaptcha;
|
||||
use think\facade\Config;
|
||||
use app\common\facade\Token;
|
||||
use app\common\controller\Api;
|
||||
use app\admin\library\Auth as AdminAuth;
|
||||
use app\common\library\Auth as UserAuth;
|
||||
|
||||
class Common extends Api
|
||||
{
|
||||
/**
|
||||
* 图形验证码
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function captcha(): Response
|
||||
{
|
||||
$captchaId = $this->request->request('id');
|
||||
$config = array(
|
||||
'codeSet' => '123456789', // 验证码字符集合
|
||||
'fontSize' => 22, // 验证码字体大小(px)
|
||||
'useCurve' => false, // 是否画混淆曲线
|
||||
'useNoise' => true, // 是否添加杂点
|
||||
'length' => 4, // 验证码位数
|
||||
'bg' => array(255, 255, 255), // 背景颜色
|
||||
);
|
||||
|
||||
$captcha = new Captcha($config);
|
||||
return $captcha->entry($captchaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点选验证码
|
||||
*/
|
||||
public function clickCaptcha(): void
|
||||
{
|
||||
$id = $this->request->request('id/s');
|
||||
$captcha = new ClickCaptcha();
|
||||
$this->success('', $captcha->creat($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 点选验证码检查
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function checkClickCaptcha(): void
|
||||
{
|
||||
$id = $this->request->post('id/s');
|
||||
$info = $this->request->post('info/s');
|
||||
$unset = $this->request->post('unset/b', false);
|
||||
$captcha = new ClickCaptcha();
|
||||
if ($captcha->check($id, $info, $unset)) $this->success();
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新 token
|
||||
* 无需主动删除原 token,由 token 驱动自行实现过期 token 清理,可避免并发场景下无法获取到过期 token 数据
|
||||
*/
|
||||
public function refreshToken(): void
|
||||
{
|
||||
$refreshToken = $this->request->post('refreshToken');
|
||||
$refreshToken = Token::get($refreshToken);
|
||||
|
||||
if (!$refreshToken || $refreshToken['expire_time'] < time()) {
|
||||
$this->error(__('Login expired, please login again.'));
|
||||
}
|
||||
|
||||
$newToken = Random::uuid();
|
||||
|
||||
// 管理员token刷新
|
||||
if ($refreshToken['type'] == AdminAuth::TOKEN_TYPE . '-refresh') {
|
||||
Token::set($newToken, AdminAuth::TOKEN_TYPE, $refreshToken['user_id'], (int)Config::get('buildadmin.admin_token_keep_time'));
|
||||
}
|
||||
|
||||
// 会员token刷新
|
||||
if ($refreshToken['type'] == UserAuth::TOKEN_TYPE . '-refresh') {
|
||||
Token::set($newToken, UserAuth::TOKEN_TYPE, $refreshToken['user_id'], (int)Config::get('buildadmin.user_token_keep_time'));
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'type' => $refreshToken['type'],
|
||||
'token' => $newToken
|
||||
]);
|
||||
}
|
||||
}
|
||||
108
app/api/controller/Ems.php
Normal file
108
app/api/controller/Ems.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use Throwable;
|
||||
use ba\Captcha;
|
||||
use ba\ClickCaptcha;
|
||||
use think\facade\Validate;
|
||||
use app\common\model\User;
|
||||
use app\common\library\Email;
|
||||
use app\common\controller\Frontend;
|
||||
use PHPMailer\PHPMailer\Exception as PHPMailerException;
|
||||
|
||||
class Ems extends Frontend
|
||||
{
|
||||
protected array $noNeedLogin = ['send'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
* event 事件:user_register=用户注册,user_change_email=用户修改邮箱,user_retrieve_pwd=用户找回密码,user_email_verify=验证账户
|
||||
* 不同的事件,会自动做各种必要检查,其中 验证账户 要求用户输入当前密码才能发送验证码邮件
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function send(): void
|
||||
{
|
||||
$params = $this->request->post(['email', 'event', 'captchaId', 'captchaInfo']);
|
||||
$mail = new Email();
|
||||
if (!$mail->configured) {
|
||||
$this->error(__('Mail sending service unavailable'));
|
||||
}
|
||||
|
||||
$validate = Validate::rule([
|
||||
'email' => 'require|email',
|
||||
'event' => 'require',
|
||||
'captchaId' => 'require',
|
||||
'captchaInfo' => 'require'
|
||||
])->message([
|
||||
'email' => 'email format error',
|
||||
'event' => 'Parameter error',
|
||||
'captchaId' => 'Captcha error',
|
||||
'captchaInfo' => 'Captcha error'
|
||||
]);
|
||||
if (!$validate->check($params)) {
|
||||
$this->error(__($validate->getError()));
|
||||
}
|
||||
|
||||
// 检查验证码
|
||||
$captchaObj = new Captcha();
|
||||
$clickCaptcha = new ClickCaptcha();
|
||||
if (!$clickCaptcha->check($params['captchaId'], $params['captchaInfo'])) {
|
||||
$this->error(__('Captcha error'));
|
||||
}
|
||||
|
||||
// 检查频繁发送
|
||||
$captcha = $captchaObj->getCaptchaData($params['email'] . $params['event']);
|
||||
if ($captcha && time() - $captcha['create_time'] < 60) {
|
||||
$this->error(__('Frequent email sending'));
|
||||
}
|
||||
|
||||
// 检查邮箱
|
||||
$userInfo = User::where('email', $params['email'])->find();
|
||||
if ($params['event'] == 'user_register' && $userInfo) {
|
||||
$this->error(__('Email has been registered, please log in directly'));
|
||||
} elseif ($params['event'] == 'user_change_email' && $userInfo) {
|
||||
$this->error(__('The email has been occupied'));
|
||||
} elseif (in_array($params['event'], ['user_retrieve_pwd', 'user_email_verify']) && !$userInfo) {
|
||||
$this->error(__('Email not registered'));
|
||||
}
|
||||
|
||||
// 通过邮箱验证账户
|
||||
if ($params['event'] == 'user_email_verify') {
|
||||
if (!$this->auth->isLogin()) {
|
||||
$this->error(__('Please login first'));
|
||||
}
|
||||
if ($this->auth->email != $params['email']) {
|
||||
$this->error(__('Please use the account registration email to send the verification code'));
|
||||
}
|
||||
// 验证账户密码
|
||||
$password = $this->request->post('password');
|
||||
if (!verify_password($password, $this->auth->password, ['salt' => $this->auth->salt])) {
|
||||
$this->error(__('Password error'));
|
||||
}
|
||||
}
|
||||
|
||||
// 生成一个验证码
|
||||
$code = $captchaObj->create($params['email'] . $params['event']);
|
||||
$subject = __($params['event']) . '-' . get_sys_config('site_name');
|
||||
$body = __('Your verification code is: %s', [$code]);
|
||||
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->addAddress($params['email']);
|
||||
$mail->isHTML();
|
||||
$mail->setSubject($subject);
|
||||
$mail->Body = $body;
|
||||
$mail->send();
|
||||
} catch (PHPMailerException) {
|
||||
$this->error($mail->ErrorInfo);
|
||||
}
|
||||
|
||||
$this->success(__('Mail sent successfully~'));
|
||||
}
|
||||
}
|
||||
84
app/api/controller/Index.php
Normal file
84
app/api/controller/Index.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use ba\Tree;
|
||||
use Throwable;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\common\controller\Frontend;
|
||||
use app\common\library\token\TokenExpirationException;
|
||||
|
||||
class Index extends Frontend
|
||||
{
|
||||
protected array $noNeedLogin = ['index'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台和会员中心的初始化请求
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): void
|
||||
{
|
||||
$menus = [];
|
||||
if ($this->auth->isLogin()) {
|
||||
$rules = [];
|
||||
$userMenus = $this->auth->getMenus();
|
||||
|
||||
// 首页加载的规则,验权,但过滤掉会员中心菜单
|
||||
foreach ($userMenus as $item) {
|
||||
if ($item['type'] == 'menu_dir') {
|
||||
$menus[] = $item;
|
||||
} elseif ($item['type'] != 'menu') {
|
||||
$rules[] = $item;
|
||||
}
|
||||
}
|
||||
$rules = array_values($rules);
|
||||
} else {
|
||||
// 若是从前台会员中心内发出的请求,要求必须登录,否则会员中心异常
|
||||
$requiredLogin = $this->request->get('requiredLogin/b', false);
|
||||
if ($requiredLogin) {
|
||||
|
||||
// 触发可能的 token 过期异常
|
||||
try {
|
||||
$token = get_auth_token(['ba', 'user', 'token']);
|
||||
$this->auth->init($token);
|
||||
} catch (TokenExpirationException) {
|
||||
$this->error(__('Token expiration'), [], 409);
|
||||
}
|
||||
|
||||
$this->error(__('Please login first'), [
|
||||
'type' => $this->auth::NEED_LOGIN
|
||||
], $this->auth::LOGIN_RESPONSE_CODE);
|
||||
}
|
||||
|
||||
$rules = Db::name('user_rule')
|
||||
->where('status', 1)
|
||||
->where('no_login_valid', 1)
|
||||
->where('type', 'in', ['route', 'nav', 'button'])
|
||||
->order('weigh', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
$rules = Tree::instance()->assembleChild($rules);
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'site' => [
|
||||
'siteName' => get_sys_config('site_name'),
|
||||
'version' => get_sys_config('version'),
|
||||
'cdnUrl' => full_url(),
|
||||
'upload' => keys_to_camel_case(get_upload_config(), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
|
||||
'recordNumber' => get_sys_config('record_number'),
|
||||
'cdnUrlParams' => Config::get('buildadmin.cdn_url_params'),
|
||||
],
|
||||
'openMemberCenter' => Config::get('buildadmin.open_member_center'),
|
||||
'userInfo' => $this->auth->getUserInfo(),
|
||||
'rules' => $rules,
|
||||
'menus' => $menus,
|
||||
]);
|
||||
}
|
||||
}
|
||||
671
app/api/controller/Install.php
Normal file
671
app/api/controller/Install.php
Normal file
@@ -0,0 +1,671 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use Throwable;
|
||||
use ba\Random;
|
||||
use ba\Version;
|
||||
use think\App;
|
||||
use ba\Terminal;
|
||||
use ba\Filesystem;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\common\controller\Api;
|
||||
use think\db\exception\PDOException;
|
||||
use app\admin\model\Admin as AdminModel;
|
||||
use app\admin\model\User as UserModel;
|
||||
|
||||
/**
|
||||
* 安装控制器
|
||||
*/
|
||||
class Install extends Api
|
||||
{
|
||||
public const X64 = 'x64';
|
||||
|
||||
public const X86 = 'x86';
|
||||
|
||||
protected bool $useSystemSettings = false;
|
||||
|
||||
/**
|
||||
* 环境检查状态
|
||||
*/
|
||||
static string $ok = 'ok';
|
||||
static string $fail = 'fail';
|
||||
static string $warn = 'warn';
|
||||
|
||||
/**
|
||||
* 安装锁文件名称
|
||||
*/
|
||||
static string $lockFileName = 'install.lock';
|
||||
|
||||
/**
|
||||
* 配置文件
|
||||
*/
|
||||
static string $dbConfigFileName = 'database.php';
|
||||
static string $buildConfigFileName = 'buildadmin.php';
|
||||
|
||||
/**
|
||||
* 自动构建的前端文件的 outDir 相对于根目录
|
||||
*/
|
||||
static string $distDir = 'web' . DIRECTORY_SEPARATOR . 'dist';
|
||||
|
||||
/**
|
||||
* 需要的依赖版本
|
||||
*/
|
||||
static array $needDependentVersion = [
|
||||
'php' => '8.2.0',
|
||||
'npm' => '9.8.1',
|
||||
'cnpm' => '7.1.0',
|
||||
'node' => '20.14.0',
|
||||
'yarn' => '1.2.0',
|
||||
'pnpm' => '6.32.13',
|
||||
];
|
||||
|
||||
/**
|
||||
* 安装完成标记
|
||||
* 配置完成则建立lock文件
|
||||
* 执行命令成功执行再写入标记到lock文件
|
||||
* 实现命令执行失败,重载页面可重新执行
|
||||
*/
|
||||
static string $InstallationCompletionMark = 'install-end';
|
||||
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/**
|
||||
* 命令执行窗口
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function terminal(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
(new Terminal())->exec(false);
|
||||
}
|
||||
|
||||
public function changePackageManager(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newPackageManager = request()->post('manager', Config::get('terminal.npm_package_manager'));
|
||||
if (Terminal::changeTerminalConfig()) {
|
||||
$this->success('', [
|
||||
'manager' => $newPackageManager
|
||||
]);
|
||||
} else {
|
||||
$this->error(__('Failed to switch package manager. Please modify the configuration file manually:%s', ['根目录/config/buildadmin.php']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 环境基础检查
|
||||
*/
|
||||
public function envBaseCheck(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
$this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]), []);
|
||||
}
|
||||
if (env('database.type')) {
|
||||
$this->error(__('The .env file with database configuration was detected. Please clean up and try again!'));
|
||||
}
|
||||
|
||||
// php版本-start
|
||||
$phpVersion = phpversion();
|
||||
$phpBit = PHP_INT_SIZE == 8 ? self::X64 : self::X86;
|
||||
$phpVersionCompare = Version::compare(self::$needDependentVersion['php'], $phpVersion);
|
||||
if (!$phpVersionCompare) {
|
||||
$phpVersionLink = [
|
||||
[
|
||||
// 需要PHP版本
|
||||
'name' => __('need') . ' >= ' . self::$needDependentVersion['php'],
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
// 如何解决
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/preparePHP.html'
|
||||
]
|
||||
];
|
||||
} elseif ($phpBit != self::X64) {
|
||||
$phpVersionLink = [
|
||||
[
|
||||
// 需要 64 位 PHP
|
||||
'name' => __('need') . ' x64 PHP',
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
// 如何解决
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/preparePHP.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
// php版本-end
|
||||
|
||||
// 配置文件-start
|
||||
$dbConfigFile = config_path() . self::$dbConfigFileName;
|
||||
$configIsWritable = Filesystem::pathIsWritable(config_path()) && Filesystem::pathIsWritable($dbConfigFile);
|
||||
if (!$configIsWritable) {
|
||||
$configIsWritableLink = [
|
||||
[
|
||||
// 查看原因
|
||||
'name' => __('View reason'),
|
||||
'title' => __('Click to view the reason'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/dirNoPermission.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
// 配置文件-end
|
||||
|
||||
// public-start
|
||||
$publicIsWritable = Filesystem::pathIsWritable(public_path());
|
||||
if (!$publicIsWritable) {
|
||||
$publicIsWritableLink = [
|
||||
[
|
||||
'name' => __('View reason'),
|
||||
'title' => __('Click to view the reason'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/dirNoPermission.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
// public-end
|
||||
|
||||
// PDO-start
|
||||
$phpPdo = extension_loaded("PDO") && extension_loaded('pdo_mysql');
|
||||
if (!$phpPdo) {
|
||||
$phpPdoLink = [
|
||||
[
|
||||
'name' => __('PDO extensions need to be installed'),
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/missingExtension.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
// PDO-end
|
||||
|
||||
// GD2和freeType-start
|
||||
$phpGd2 = extension_loaded('gd') && function_exists('imagettftext');
|
||||
if (!$phpGd2) {
|
||||
$phpGd2Link = [
|
||||
[
|
||||
'name' => __('The gd extension and freeType library need to be installed'),
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/gdFail.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
// GD2和freeType-end
|
||||
|
||||
// proc_open
|
||||
$phpProc = function_exists('proc_open') && function_exists('proc_close') && function_exists('proc_get_status');
|
||||
if (!$phpProc) {
|
||||
$phpProcLink = [
|
||||
[
|
||||
'name' => __('View reason'),
|
||||
'title' => __('proc_open or proc_close functions in PHP Ini is disabled'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/disablement.html'
|
||||
],
|
||||
[
|
||||
'name' => __('How to modify'),
|
||||
'title' => __('Click to view how to modify'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/disablement.html'
|
||||
],
|
||||
[
|
||||
'name' => __('Security assurance?'),
|
||||
'title' => __('Using the installation service correctly will not cause any potential security problems. Click to view the details'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/senior.html'
|
||||
],
|
||||
];
|
||||
}
|
||||
// proc_open-end
|
||||
|
||||
$this->success('', [
|
||||
'php_version' => [
|
||||
'describe' => $phpVersion . " ($phpBit)",
|
||||
'state' => $phpVersionCompare && $phpBit == self::X64 ? self::$ok : self::$fail,
|
||||
'link' => $phpVersionLink ?? [],
|
||||
],
|
||||
'config_is_writable' => [
|
||||
'describe' => self::writableStateDescribe($configIsWritable),
|
||||
'state' => $configIsWritable ? self::$ok : self::$fail,
|
||||
'link' => $configIsWritableLink ?? []
|
||||
],
|
||||
'public_is_writable' => [
|
||||
'describe' => self::writableStateDescribe($publicIsWritable),
|
||||
'state' => $publicIsWritable ? self::$ok : self::$fail,
|
||||
'link' => $publicIsWritableLink ?? []
|
||||
],
|
||||
'php_pdo' => [
|
||||
'describe' => $phpPdo ? __('already installed') : __('Not installed'),
|
||||
'state' => $phpPdo ? self::$ok : self::$fail,
|
||||
'link' => $phpPdoLink ?? []
|
||||
],
|
||||
'php_gd2' => [
|
||||
'describe' => $phpGd2 ? __('already installed') : __('Not installed'),
|
||||
'state' => $phpGd2 ? self::$ok : self::$fail,
|
||||
'link' => $phpGd2Link ?? []
|
||||
],
|
||||
'php_proc' => [
|
||||
'describe' => $phpProc ? __('Allow execution') : __('disabled'),
|
||||
'state' => $phpProc ? self::$ok : self::$warn,
|
||||
'link' => $phpProcLink ?? []
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* npm环境检查
|
||||
*/
|
||||
public function envNpmCheck(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
$this->error('', [], 2);
|
||||
}
|
||||
|
||||
$packageManager = request()->post('manager', 'none');
|
||||
|
||||
// npm
|
||||
$npmVersion = Version::getVersion('npm');
|
||||
$npmVersionCompare = Version::compare(self::$needDependentVersion['npm'], $npmVersion);
|
||||
if (!$npmVersionCompare || !$npmVersion) {
|
||||
$npmVersionLink = [
|
||||
[
|
||||
// 需要版本
|
||||
'name' => __('need') . ' >= ' . self::$needDependentVersion['npm'],
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
// 如何解决
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/prepareNpm.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 包管理器
|
||||
if (in_array($packageManager, ['npm', 'cnpm', 'pnpm', 'yarn'])) {
|
||||
$pmVersion = Version::getVersion($packageManager);
|
||||
$pmVersionCompare = Version::compare(self::$needDependentVersion[$packageManager], $pmVersion);
|
||||
|
||||
if (!$pmVersion) {
|
||||
// 安装
|
||||
$pmVersionLink[] = [
|
||||
// 需要版本
|
||||
'name' => __('need') . ' >= ' . self::$needDependentVersion[$packageManager],
|
||||
'type' => 'text'
|
||||
];
|
||||
if ($npmVersionCompare) {
|
||||
$pmVersionLink[] = [
|
||||
// 点击安装
|
||||
'name' => __('Click Install %s', [$packageManager]),
|
||||
'title' => '',
|
||||
'type' => 'install-package-manager'
|
||||
];
|
||||
} else {
|
||||
$pmVersionLink[] = [
|
||||
// 请先安装npm
|
||||
'name' => __('Please install NPM first'),
|
||||
'type' => 'text'
|
||||
];
|
||||
}
|
||||
} elseif (!$pmVersionCompare) {
|
||||
// 版本不足
|
||||
$pmVersionLink[] = [
|
||||
// 需要版本
|
||||
'name' => __('need') . ' >= ' . self::$needDependentVersion[$packageManager],
|
||||
'type' => 'text'
|
||||
];
|
||||
$pmVersionLink[] = [
|
||||
// 请升级
|
||||
'name' => __('Please upgrade %s version', [$packageManager]),
|
||||
'type' => 'text'
|
||||
];
|
||||
}
|
||||
} elseif ($packageManager == 'ni') {
|
||||
$pmVersion = __('nothing');
|
||||
$pmVersionCompare = true;
|
||||
} else {
|
||||
$pmVersion = __('nothing');
|
||||
$pmVersionCompare = false;
|
||||
}
|
||||
|
||||
// nodejs
|
||||
$nodejsVersion = Version::getVersion('node');
|
||||
$nodejsVersionCompare = Version::compare(self::$needDependentVersion['node'], $nodejsVersion);
|
||||
if (!$nodejsVersionCompare || !$nodejsVersion) {
|
||||
$nodejsVersionLink = [
|
||||
[
|
||||
// 需要版本
|
||||
'name' => __('need') . ' >= ' . self::$needDependentVersion['node'],
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
// 如何解决
|
||||
'name' => __('How to solve?'),
|
||||
'title' => __('Click to see how to solve it'),
|
||||
'type' => 'faq',
|
||||
'url' => 'https://doc.buildadmin.com/guide/install/prepareNodeJs.html'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'npm_version' => [
|
||||
'describe' => $npmVersion ?: __('Acquisition failed'),
|
||||
'state' => $npmVersionCompare ? self::$ok : self::$warn,
|
||||
'link' => $npmVersionLink ?? [],
|
||||
],
|
||||
'nodejs_version' => [
|
||||
'describe' => $nodejsVersion ?: __('Acquisition failed'),
|
||||
'state' => $nodejsVersionCompare ? self::$ok : self::$warn,
|
||||
'link' => $nodejsVersionLink ?? []
|
||||
],
|
||||
'npm_package_manager' => [
|
||||
'describe' => $pmVersion ?: __('Acquisition failed'),
|
||||
'state' => $pmVersionCompare ? self::$ok : self::$warn,
|
||||
'link' => $pmVersionLink ?? [],
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据库连接
|
||||
*/
|
||||
public function testDatabase(): void
|
||||
{
|
||||
$database = [
|
||||
'hostname' => $this->request->post('hostname'),
|
||||
'username' => $this->request->post('username'),
|
||||
'password' => $this->request->post('password'),
|
||||
'hostport' => $this->request->post('hostport'),
|
||||
'database' => '',
|
||||
];
|
||||
|
||||
$conn = $this->connectDb($database);
|
||||
if ($conn['code'] == 0) {
|
||||
$this->error($conn['msg']);
|
||||
} else {
|
||||
$this->success('', [
|
||||
'databases' => $conn['databases']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统基础配置
|
||||
* post请求=开始安装
|
||||
*/
|
||||
public function baseConfig(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
$this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$envOk = $this->commandExecutionCheck();
|
||||
$rootPath = str_replace('\\', '/', root_path());
|
||||
if ($this->request->isGet()) {
|
||||
$this->success('', [
|
||||
'rootPath' => $rootPath,
|
||||
'executionWebCommand' => $envOk
|
||||
]);
|
||||
}
|
||||
|
||||
$connectData = $databaseParam = $this->request->only(['hostname', 'username', 'password', 'hostport', 'database', 'prefix']);
|
||||
|
||||
// 数据库配置测试
|
||||
$connectData['database'] = '';
|
||||
$connect = $this->connectDb($connectData, true);
|
||||
if ($connect['code'] == 0) {
|
||||
$this->error($connect['msg']);
|
||||
}
|
||||
|
||||
// 建立数据库
|
||||
if (!in_array($databaseParam['database'], $connect['databases'])) {
|
||||
$sql = "CREATE DATABASE IF NOT EXISTS `{$databaseParam['database']}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
|
||||
$connect['pdo']->exec($sql);
|
||||
}
|
||||
|
||||
// 写入数据库配置文件
|
||||
$dbConfigFile = config_path() . self::$dbConfigFileName;
|
||||
$dbConfigContent = @file_get_contents($dbConfigFile);
|
||||
$callback = function ($matches) use ($databaseParam) {
|
||||
$value = $databaseParam[$matches[1]] ?? '';
|
||||
return "'$matches[1]'$matches[2]=>$matches[3]env('database.$matches[1]', '$value'),";
|
||||
};
|
||||
$dbConfigText = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)env\('database\.(.*)',\s+'(.*)'\),/", $callback, $dbConfigContent);
|
||||
$result = @file_put_contents($dbConfigFile, $dbConfigText);
|
||||
if (!$result) {
|
||||
$this->error(__('File has no write permission:%s', ['config/' . self::$dbConfigFileName]));
|
||||
}
|
||||
|
||||
// 写入.env-example文件
|
||||
$envFile = root_path() . '.env-example';
|
||||
$envFileContent = @file_get_contents($envFile);
|
||||
if ($envFileContent) {
|
||||
$databasePos = stripos($envFileContent, '[DATABASE]');
|
||||
if ($databasePos !== false) {
|
||||
// 清理已有数据库配置
|
||||
$envFileContent = substr($envFileContent, 0, $databasePos);
|
||||
}
|
||||
$envFileContent .= "\n" . '[DATABASE]' . "\n";
|
||||
$envFileContent .= 'TYPE = mysql' . "\n";
|
||||
$envFileContent .= 'HOSTNAME = ' . $databaseParam['hostname'] . "\n";
|
||||
$envFileContent .= 'DATABASE = ' . $databaseParam['database'] . "\n";
|
||||
$envFileContent .= 'USERNAME = ' . $databaseParam['username'] . "\n";
|
||||
$envFileContent .= 'PASSWORD = ' . $databaseParam['password'] . "\n";
|
||||
$envFileContent .= 'HOSTPORT = ' . $databaseParam['hostport'] . "\n";
|
||||
$envFileContent .= 'PREFIX = ' . $databaseParam['prefix'] . "\n";
|
||||
$envFileContent .= 'CHARSET = utf8mb4' . "\n";
|
||||
$envFileContent .= 'DEBUG = true' . "\n";
|
||||
$result = @file_put_contents($envFile, $envFileContent);
|
||||
if (!$result) {
|
||||
$this->error(__('File has no write permission:%s', ['/' . $envFile]));
|
||||
}
|
||||
}
|
||||
|
||||
// 设置新的Token随机密钥key
|
||||
$oldTokenKey = Config::get('buildadmin.token.key');
|
||||
$newTokenKey = Random::build('alnum', 32);
|
||||
$buildConfigFile = config_path() . self::$buildConfigFileName;
|
||||
$buildConfigContent = @file_get_contents($buildConfigFile);
|
||||
$buildConfigContent = preg_replace("/'key'(\s+)=>(\s+)'$oldTokenKey'/", "'key'\$1=>\$2'$newTokenKey'", $buildConfigContent);
|
||||
$result = @file_put_contents($buildConfigFile, $buildConfigContent);
|
||||
if (!$result) {
|
||||
$this->error(__('File has no write permission:%s', ['config/' . self::$buildConfigFileName]));
|
||||
}
|
||||
|
||||
// 建立安装锁文件
|
||||
$result = @file_put_contents(public_path() . self::$lockFileName, date('Y-m-d H:i:s'));
|
||||
if (!$result) {
|
||||
$this->error(__('File has no write permission:%s', ['public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'rootPath' => $rootPath,
|
||||
'executionWebCommand' => $envOk
|
||||
]);
|
||||
}
|
||||
|
||||
protected function isInstallComplete(): bool
|
||||
{
|
||||
if (is_file(public_path() . self::$lockFileName)) {
|
||||
$contents = @file_get_contents(public_path() . self::$lockFileName);
|
||||
if ($contents == self::$InstallationCompletionMark) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记命令执行完毕
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function commandExecComplete(): void
|
||||
{
|
||||
if ($this->isInstallComplete()) {
|
||||
$this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$param = $this->request->only(['type', 'adminname', 'adminpassword', 'sitename']);
|
||||
if ($param['type'] == 'web') {
|
||||
$result = @file_put_contents(public_path() . self::$lockFileName, self::$InstallationCompletionMark);
|
||||
if (!$result) {
|
||||
$this->error(__('File has no write permission:%s', ['public/' . self::$lockFileName]));
|
||||
}
|
||||
} else {
|
||||
// 管理员配置入库
|
||||
$adminModel = new AdminModel();
|
||||
$defaultAdmin = $adminModel->where('username', 'admin')->find();
|
||||
$defaultAdmin->username = $param['adminname'];
|
||||
$defaultAdmin->nickname = ucfirst($param['adminname']);
|
||||
$defaultAdmin->save();
|
||||
|
||||
if (isset($param['adminpassword']) && $param['adminpassword']) {
|
||||
$adminModel->resetPassword($defaultAdmin->id, $param['adminpassword']);
|
||||
}
|
||||
|
||||
// 默认用户密码修改
|
||||
$user = new UserModel();
|
||||
$user->resetPassword(1, Random::build());
|
||||
|
||||
// 修改站点名称
|
||||
\app\admin\model\Config::where('name', 'site_name')->update([
|
||||
'value' => $param['sitename']
|
||||
]);
|
||||
}
|
||||
$this->success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取命令执行检查的结果
|
||||
* @return bool 是否拥有执行命令的条件
|
||||
*/
|
||||
private function commandExecutionCheck(): bool
|
||||
{
|
||||
$pm = Config::get('terminal.npm_package_manager');
|
||||
if ($pm == 'none') {
|
||||
return false;
|
||||
}
|
||||
$check['phpPopen'] = function_exists('proc_open') && function_exists('proc_close');
|
||||
$check['npmVersionCompare'] = Version::compare(self::$needDependentVersion['npm'], Version::getVersion('npm'));
|
||||
$check['pmVersionCompare'] = Version::compare(self::$needDependentVersion[$pm], Version::getVersion($pm));
|
||||
$check['nodejsVersionCompare'] = Version::compare(self::$needDependentVersion['node'], Version::getVersion('node'));
|
||||
|
||||
$envOk = true;
|
||||
foreach ($check as $value) {
|
||||
if (!$value) {
|
||||
$envOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $envOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装指引
|
||||
*/
|
||||
public function manualInstall(): void
|
||||
{
|
||||
$this->success('', [
|
||||
'webPath' => str_replace('\\', '/', root_path() . 'web')
|
||||
]);
|
||||
}
|
||||
|
||||
public function mvDist(): void
|
||||
{
|
||||
if (!is_file(root_path() . self::$distDir . DIRECTORY_SEPARATOR . 'index.html')) {
|
||||
$this->error(__('No built front-end file found, please rebuild manually!'));
|
||||
}
|
||||
|
||||
if (Terminal::mvDist()) {
|
||||
$this->success();
|
||||
} else {
|
||||
$this->error(__('Failed to move the front-end file, please move it manually!'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 目录是否可写
|
||||
* @param $writable
|
||||
* @return string
|
||||
*/
|
||||
private static function writableStateDescribe($writable): string
|
||||
{
|
||||
return $writable ? __('Writable') : __('No write permission');
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库连接-获取数据表列表
|
||||
* @param array $database
|
||||
* @param bool $returnPdo
|
||||
* @return array
|
||||
*/
|
||||
private function connectDb(array $database, bool $returnPdo = false): array
|
||||
{
|
||||
try {
|
||||
$dbConfig = Config::get('database');
|
||||
$dbConfig['connections']['mysql'] = array_merge($dbConfig['connections']['mysql'], $database);
|
||||
Config::set(['connections' => $dbConfig['connections']], 'database');
|
||||
|
||||
$connect = Db::connect('mysql');
|
||||
$connect->execute("SELECT 1");
|
||||
} catch (PDOException $e) {
|
||||
$errorMsg = $e->getMessage();
|
||||
return [
|
||||
'code' => 0,
|
||||
'msg' => __('Database connection failed:%s', [mb_convert_encoding($errorMsg ?: 'unknown', 'UTF-8', 'UTF-8,GBK,GB2312,BIG5')])
|
||||
];
|
||||
}
|
||||
|
||||
$databases = [];
|
||||
// 不需要的数据表
|
||||
$databasesExclude = ['information_schema', 'mysql', 'performance_schema', 'sys'];
|
||||
$res = $connect->query("SHOW DATABASES");
|
||||
foreach ($res as $row) {
|
||||
if (!in_array($row['Database'], $databasesExclude)) {
|
||||
$databases[] = $row['Database'];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 1,
|
||||
'msg' => '',
|
||||
'databases' => $databases,
|
||||
'pdo' => $returnPdo ? $connect->getPdo() : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
100
app/api/controller/User.php
Normal file
100
app/api/controller/User.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use Throwable;
|
||||
use ba\Captcha;
|
||||
use ba\ClickCaptcha;
|
||||
use think\facade\Config;
|
||||
use app\common\facade\Token;
|
||||
use app\common\controller\Frontend;
|
||||
use app\api\validate\User as UserValidate;
|
||||
|
||||
class User extends Frontend
|
||||
{
|
||||
protected array $noNeedLogin = ['checkIn', 'logout'];
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员签入(登录和注册)
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function checkIn(): void
|
||||
{
|
||||
$openMemberCenter = Config::get('buildadmin.open_member_center');
|
||||
if (!$openMemberCenter) {
|
||||
$this->error(__('Member center disabled'));
|
||||
}
|
||||
|
||||
// 检查登录态
|
||||
if ($this->auth->isLogin()) {
|
||||
$this->success(__('You have already logged in. There is no need to log in again~'), [
|
||||
'type' => $this->auth::LOGGED_IN
|
||||
], $this->auth::LOGIN_RESPONSE_CODE);
|
||||
}
|
||||
|
||||
$userLoginCaptchaSwitch = Config::get('buildadmin.user_login_captcha');
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$params = $this->request->post(['tab', 'email', 'mobile', 'username', 'password', 'keep', 'captcha', 'captchaId', 'captchaInfo', 'registerType']);
|
||||
|
||||
// 提前检查 tab ,然后将以 tab 值作为数据验证场景
|
||||
if (!in_array($params['tab'] ?? '', ['login', 'register'])) {
|
||||
$this->error(__('Unknown operation'));
|
||||
}
|
||||
|
||||
$validate = new UserValidate();
|
||||
try {
|
||||
$validate->scene($params['tab'])->check($params);
|
||||
} catch (Throwable $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
if ($params['tab'] == 'login') {
|
||||
if ($userLoginCaptchaSwitch) {
|
||||
$captchaObj = new ClickCaptcha();
|
||||
if (!$captchaObj->check($params['captchaId'], $params['captchaInfo'])) {
|
||||
$this->error(__('Captcha error'));
|
||||
}
|
||||
}
|
||||
$res = $this->auth->login($params['username'], $params['password'], !empty($params['keep']));
|
||||
} elseif ($params['tab'] == 'register') {
|
||||
$captchaObj = new Captcha();
|
||||
if (!$captchaObj->check($params['captcha'], $params[$params['registerType']] . 'user_register')) {
|
||||
$this->error(__('Please enter the correct verification code'));
|
||||
}
|
||||
$res = $this->auth->register($params['username'], $params['password'], $params['mobile'], $params['email']);
|
||||
}
|
||||
|
||||
if (isset($res) && $res === true) {
|
||||
$this->success(__('Login succeeded!'), [
|
||||
'userInfo' => $this->auth->getUserInfo(),
|
||||
'routePath' => '/user'
|
||||
]);
|
||||
} else {
|
||||
$msg = $this->auth->getError();
|
||||
$msg = $msg ?: __('Check in failed, please try again or contact the website administrator~');
|
||||
$this->error($msg);
|
||||
}
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'userLoginCaptchaSwitch' => $userLoginCaptchaSwitch,
|
||||
'accountVerificationType' => get_account_verification_type()
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(): void
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$refreshToken = $this->request->post('refreshToken', '');
|
||||
if ($refreshToken) Token::delete((string)$refreshToken);
|
||||
$this->auth->logout();
|
||||
$this->success();
|
||||
}
|
||||
}
|
||||
}
|
||||
15
app/api/lang/en.php
Normal file
15
app/api/lang/en.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
return [
|
||||
'Login expired, please login again.' => 'Login expired, please login again.',
|
||||
'Account not exist' => 'Account does not exist',
|
||||
'Account disabled' => 'Account is disabled',
|
||||
'Token login failed' => 'Token login failed',
|
||||
'Please try again after 1 day' => 'The number of failed login attempts has exceeded the limit, please try again after 24 hours.',
|
||||
'Password is incorrect' => 'Incorrect password',
|
||||
'You are not logged in' => 'You are not logged in.',
|
||||
'Unknown operation' => 'Unknown operation',
|
||||
'No action available, please contact the administrator~' => 'There is no action available, please contact the administrator~',
|
||||
'Please login first' => 'Please login first!',
|
||||
'You have no permission' => 'No permission to operate!',
|
||||
'Captcha error' => 'Captcha error!',
|
||||
];
|
||||
16
app/api/lang/en/account.php
Normal file
16
app/api/lang/en/account.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
return [
|
||||
'nickname' => 'Nickname',
|
||||
'birthday' => 'Birthday',
|
||||
'captcha' => 'Captcha',
|
||||
'Old password error' => 'Old password error',
|
||||
'Data updated successfully~' => 'Data updated successfully',
|
||||
'Please input correct password' => 'Please enter the correct password',
|
||||
'nicknameChsDash' => 'Usernames can only be Chinese characters, letters, numbers, underscores_ and dashes-.',
|
||||
'Password has been changed~' => 'Password has been changed~',
|
||||
'Password has been changed, please login again~' => 'Password has been changed, please login again~',
|
||||
'Account does not exist~' => 'Account does not exist',
|
||||
'Failed to modify password, please try again later~' => 'Failed to modify password, please try again later~',
|
||||
'Please enter the correct verification code' => 'Please enter the correct Captcha',
|
||||
'%s has been registered' => '%s has been registered, please login directly.',
|
||||
];
|
||||
16
app/api/lang/en/ems.php
Normal file
16
app/api/lang/en/ems.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
return [
|
||||
'email format error' => 'email format error',
|
||||
'user_register' => 'Member registration verification',
|
||||
'user_retrieve_pwd' => 'Retrieve password verification',
|
||||
'user_change_email' => 'Modify mailbox validation',
|
||||
'user_email_verify' => 'Member Email Verification',
|
||||
'Your verification code is: %s' => 'Your Captcha is: %s,valid for 10 minutes~',
|
||||
'Mail sent successfully~' => 'Mail sent successfully',
|
||||
'Account does not exist~' => 'Account does not exist',
|
||||
'Mail sending service unavailable' => 'The mail sending service is not working, please contact the webmaster to configure it.',
|
||||
'Frequent email sending' => 'Frequent email sending',
|
||||
'Email has been registered, please log in directly' => 'Email has been registered, please log in directly~',
|
||||
'The email has been occupied' => 'The email has been occupied',
|
||||
'Email not registered' => 'Email not registered',
|
||||
];
|
||||
44
app/api/lang/en/install.php
Normal file
44
app/api/lang/en/install.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
return [
|
||||
'Install the controller' => 'Install the controller',
|
||||
'need' => 'Need',
|
||||
'Click to see how to solve it' => 'Click to see how to solve.',
|
||||
'Please check the config directory permissions' => 'Please check the Config directory permissions',
|
||||
'Please check the public directory permissions' => 'Please check the Public directory permissions',
|
||||
'open' => 'Open',
|
||||
'close' => 'Close',
|
||||
'The installation can continue, and some operations need to be completed manually' => 'You can continue to install, and some operations need to be completed manually ',
|
||||
'Allow execution' => 'Allow execution',
|
||||
'disabled' => 'Disabled',
|
||||
'Allow operation' => 'Allow operation',
|
||||
'Acquisition failed' => 'Access failed',
|
||||
'Click Install %s' => 'Click Install %s',
|
||||
'Writable' => 'Writable',
|
||||
'No write permission' => 'No write permissions',
|
||||
'already installed' => 'Installed',
|
||||
'Not installed' => 'Not installed',
|
||||
'File has no write permission:%s' => 'File has no write permission:%s',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %s file first' => 'The system has been installed, if you need to reinstall, please delete the %s file first.',
|
||||
'Database connection failed:%s' => 'Database connection failure:%s',
|
||||
'Failed to install SQL execution:%s' => 'Installation SQL execution failed:%s',
|
||||
'unknown' => 'Unknown',
|
||||
'Database does not exist' => 'Database does not exist!',
|
||||
'No built front-end file found, please rebuild manually!' => 'No built front-end file found, please rebuild manually.',
|
||||
'Failed to move the front-end file, please move it manually!' => 'Failed to move the front-end file, please move manually!',
|
||||
'How to solve?' => 'How to solve?',
|
||||
'View reason' => 'View reasons',
|
||||
'Click to view the reason' => 'Click to see the reason',
|
||||
'PDO extensions need to be installed' => 'pdo_mysql extensions need to be installed.',
|
||||
'proc_open or proc_close functions in PHP Ini is disabled' => 'proc_open and proc_close functions in PHP.Ini is disabled.',
|
||||
'How to modify' => 'How to modify?',
|
||||
'Click to view how to modify' => 'Click to see how to modify.',
|
||||
'Security assurance?' => 'Security assurance?',
|
||||
'Using the installation service correctly will not cause any potential security problems. Click to view the details' => 'The correct use of the installation service will not cause any potential security issues. Click to view the details.',
|
||||
'Please install NPM first' => 'Please install NPM first.',
|
||||
'Installation error:%s' => 'Installation error:%s',
|
||||
'Failed to switch package manager. Please modify the configuration file manually:%s' => 'Package manager switch failed, please modify the configuration file manually:%s.',
|
||||
'Please upgrade %s version' => 'Please upgrade the %s version',
|
||||
'nothing' => 'Nothing',
|
||||
'The gd extension and freeType library need to be installed' => 'The gd2 extension and freeType library need to be installed',
|
||||
'The .env file with database configuration was detected. Please clean up and try again!' => 'The .env file with database configuration was detected. Please clean up and try again!',
|
||||
];
|
||||
13
app/api/lang/en/user.php
Normal file
13
app/api/lang/en/user.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
return [
|
||||
'captcha' => 'Captcha',
|
||||
'captchaId' => 'Captcha ID',
|
||||
'Please input correct username' => 'Please enter the correct username.',
|
||||
'Please input correct password' => 'Please enter the correct password.',
|
||||
'Registration parameter error' => 'Wrong registration parameter',
|
||||
'Login succeeded!' => 'Login succeeded!',
|
||||
'Please enter the correct verification code' => 'Please enter the correct Captcha.',
|
||||
'You have already logged in. There is no need to log in again~' => 'You have already logged in, no need to log in again.',
|
||||
'Check in failed, please try again or contact the website administrator~' => 'Check in failed,please try again or contact the webmaster.',
|
||||
'Member center disabled' => 'The member centre has been disabled, please contact the webmaster to turn it on.',
|
||||
];
|
||||
47
app/api/lang/zh-cn.php
Normal file
47
app/api/lang/zh-cn.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
return [
|
||||
// 时间格式化-s
|
||||
'%d second%s ago' => '%d秒前',
|
||||
'%d minute%s ago' => '%d分钟前',
|
||||
'%d hour%s ago' => '%d小时前',
|
||||
'%d day%s ago' => '%d天前',
|
||||
'%d week%s ago' => '%d周前',
|
||||
'%d month%s ago' => '%d月前',
|
||||
'%d year%s ago' => '%d年前',
|
||||
'%d second%s after' => '%d秒后',
|
||||
'%d minute%s after' => '%d分钟后',
|
||||
'%d hour%s after' => '%d小时后',
|
||||
'%d day%s after' => '%d天后',
|
||||
'%d week%s after' => '%d周后',
|
||||
'%d month%s after' => '%d月后',
|
||||
'%d year%s after' => '%d年后',
|
||||
// 时间格式化-e
|
||||
// 文件上传-s
|
||||
'File uploaded successfully' => '文件上传成功!',
|
||||
'No files were uploaded' => '没有文件被上传',
|
||||
'The uploaded file format is not allowed' => '上传的文件格式未被允许',
|
||||
'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
|
||||
'Topic format error' => '上传存储子目录格式错误!',
|
||||
'Driver %s not supported' => '不支持的驱动:%s',
|
||||
// 文件上传-e
|
||||
'Username' => '用户名',
|
||||
'Email' => '邮箱',
|
||||
'Mobile' => '手机号',
|
||||
'Password' => '密码',
|
||||
'Login expired, please login again.' => '登录过期,请重新登录。',
|
||||
'Account not exist' => '帐户不存在',
|
||||
'Account disabled' => '帐户已禁用',
|
||||
'Token login failed' => '令牌登录失败',
|
||||
'Please try again after 1 day' => '登录失败次数超限,请在1天后再试',
|
||||
'Password is incorrect' => '密码不正确',
|
||||
'You are not logged in' => '你没有登录',
|
||||
'Unknown operation' => '未知操作',
|
||||
'No action available, please contact the administrator~' => '没有可用操作,请联系管理员~',
|
||||
'Please login first' => '请先登录!',
|
||||
'You have no permission' => '没有权限操作!',
|
||||
'Parameter error' => '参数错误!',
|
||||
'Token expiration' => '登录态过期,请重新登录!',
|
||||
'Captcha error' => '验证码错误!',
|
||||
];
|
||||
22
app/api/lang/zh-cn/account.php
Normal file
22
app/api/lang/zh-cn/account.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
return [
|
||||
'nickname' => '昵称',
|
||||
'birthday' => '生日',
|
||||
'captcha' => '验证码',
|
||||
'Old password error' => '旧密码错误',
|
||||
'Data updated successfully~' => '资料更新成功~',
|
||||
'Please input correct password' => '请输入正确的密码',
|
||||
'nicknameChsDash' => '用户名只能是汉字、字母、数字和下划线_及破折号-',
|
||||
'Password has been changed~' => '密码已修改~',
|
||||
'Password has been changed, please login again~' => '密码已修改,请重新登录~',
|
||||
'Account does not exist~' => '账户不存在~',
|
||||
'Failed to modify password, please try again later~' => '修改密码失败,请稍后重试~',
|
||||
'Please enter the correct verification code' => '请输入正确的验证码!',
|
||||
'%s has been registered' => '%s已被注册,请直接登录~',
|
||||
'email format error' => '电子邮箱格式错误!',
|
||||
'mobile format error' => '手机号格式错误!',
|
||||
'You need to verify your account before modifying the binding information' => '您需要先通过账户验证才能修改绑定信息!',
|
||||
'Password error' => '密码错误!',
|
||||
'email is occupied' => '电子邮箱地址已被占用!',
|
||||
'mobile is occupied' => '手机号已被占用!',
|
||||
];
|
||||
18
app/api/lang/zh-cn/ems.php
Normal file
18
app/api/lang/zh-cn/ems.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return [
|
||||
'email format error' => '电子邮箱格式错误',
|
||||
'user_register' => '会员注册验证',
|
||||
'user_change_email' => '修改邮箱验证',
|
||||
'user_retrieve_pwd' => '找回密码验证',
|
||||
'user_email_verify' => '会员身份验证',
|
||||
'Your verification code is: %s' => '您的验证码是:%s,十分钟内有效~',
|
||||
'Mail sent successfully~' => '邮件发送成功~',
|
||||
'Account does not exist~' => '账户不存在~',
|
||||
'Mail sending service unavailable' => '邮件发送服务不可用,请联系网站管理员进行配置~',
|
||||
'Frequent email sending' => '频繁发送电子邮件',
|
||||
'Email has been registered, please log in directly' => '电子邮箱已注册,请直接登录~',
|
||||
'The email has been occupied' => '电子邮箱已被占用!',
|
||||
'Email not registered' => '电子邮箱未注册',
|
||||
'Please use the account registration email to send the verification code' => '请使用账户注册邮箱发送验证码!',
|
||||
'Password error' => '密码错误!',
|
||||
];
|
||||
44
app/api/lang/zh-cn/install.php
Normal file
44
app/api/lang/zh-cn/install.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
return [
|
||||
'Install the controller' => '安装控制器',
|
||||
'need' => '需要',
|
||||
'Click to see how to solve it' => '点击查看如何解决',
|
||||
'Please check the config directory permissions' => '请检查 config 目录权限',
|
||||
'Please check the public directory permissions' => '请检查 public 目录权限',
|
||||
'open' => '开启',
|
||||
'close' => '关闭',
|
||||
'The installation can continue, and some operations need to be completed manually' => '可以继续安装,部分操作需手动完成',
|
||||
'Allow execution' => '允许执行',
|
||||
'disabled' => '已禁用',
|
||||
'Allow operation' => '允许操作',
|
||||
'Acquisition failed' => '获取失败',
|
||||
'Click Install %s' => '点击安装%s',
|
||||
'Writable' => '可写',
|
||||
'No write permission' => '无写权限',
|
||||
'already installed' => '已安装',
|
||||
'Not installed' => '未安装',
|
||||
'File has no write permission:%s' => '文件无写入权限:%s',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %s file first' => '系统已完成安装。如果需要重新安装,请先删除 %s 文件',
|
||||
'Database connection failed:%s' => '数据库连接失败:%s',
|
||||
'Failed to install SQL execution:%s' => '安装SQL执行失败:%s',
|
||||
'unknown' => '未知',
|
||||
'Database does not exist' => '数据库不存在!',
|
||||
'No built front-end file found, please rebuild manually!' => '没有找到构建好的前端文件,请手动重新构建!',
|
||||
'Failed to move the front-end file, please move it manually!' => '移动前端文件失败,请手动移动!',
|
||||
'How to solve?' => '如何解决?',
|
||||
'View reason' => '查看原因',
|
||||
'Click to view the reason' => '点击查看原因',
|
||||
'PDO extensions need to be installed' => '需要安装 pdo_mysql 扩展',
|
||||
'proc_open or proc_close functions in PHP Ini is disabled' => 'proc_open和proc_close函数在php.ini中被禁用掉了',
|
||||
'How to modify' => '如何修改',
|
||||
'Click to view how to modify' => '点击查看如何修改',
|
||||
'Security assurance?' => '安全保证?',
|
||||
'Using the installation service correctly will not cause any potential security problems. Click to view the details' => '安装服务使用正确不会造成任何潜在安全问题,点击查看详情',
|
||||
'Please install NPM first' => '请先安装npm',
|
||||
'Installation error:%s' => '安装出错:%s',
|
||||
'Failed to switch package manager. Please modify the configuration file manually:%s' => '包管理器切换失败,请手动修改配置文件:%s',
|
||||
'Please upgrade %s version' => '请升级%s版本',
|
||||
'nothing' => '无',
|
||||
'The gd extension and freeType library need to be installed' => '需要gd2扩展和freeType库',
|
||||
'The .env file with database configuration was detected. Please clean up and try again!' => '检测到带有数据库配置的 .env 文件。请清理后再试一次!',
|
||||
];
|
||||
14
app/api/lang/zh-cn/user.php
Normal file
14
app/api/lang/zh-cn/user.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
return [
|
||||
'captcha' => '验证码',
|
||||
'captchaId' => '验证码标识',
|
||||
'Register type' => '注册类型',
|
||||
'Please input correct username' => '请输入正确的用户名',
|
||||
'Please input correct password' => '请输入正确的密码',
|
||||
'Registration parameter error' => '注册参数错误',
|
||||
'Login succeeded!' => '登录成功',
|
||||
'Please enter the correct verification code' => '请输入正确的验证码',
|
||||
'You have already logged in. There is no need to log in again~' => '您已经登录过了,无需重复登录~',
|
||||
'Check in failed, please try again or contact the website administrator~' => '签入失败,请重试或联系网站管理员~',
|
||||
'Member center disabled' => '会员中心已禁用,请联系网站管理员开启。',
|
||||
];
|
||||
5
app/api/middleware.php
Normal file
5
app/api/middleware.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
return [
|
||||
\app\common\middleware\AllowCrossDomain::class,
|
||||
\think\middleware\LoadLangPack::class,
|
||||
];
|
||||
47
app/api/validate/Account.php
Normal file
47
app/api/validate/Account.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
class Account extends Validate
|
||||
{
|
||||
protected $failException = true;
|
||||
|
||||
protected $rule = [
|
||||
'username' => 'require|regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$|unique:user',
|
||||
'nickname' => 'require|chsDash',
|
||||
'birthday' => 'date',
|
||||
'email' => 'require|email|unique:user',
|
||||
'mobile' => 'require|mobile|unique:user',
|
||||
'password' => 'require|regex:^(?!.*[&<>"\'\n\r]).{6,32}$',
|
||||
'account' => 'require',
|
||||
'captcha' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 验证场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'edit' => ['username', 'nickname', 'birthday'],
|
||||
'changePassword' => ['password'],
|
||||
'retrievePassword' => ['account', 'captcha', 'password'],
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->field = [
|
||||
'username' => __('Username'),
|
||||
'email' => __('Email'),
|
||||
'mobile' => __('Mobile'),
|
||||
'password' => __('Password'),
|
||||
'nickname' => __('nickname'),
|
||||
'birthday' => __('birthday'),
|
||||
];
|
||||
$this->message = array_merge($this->message, [
|
||||
'nickname.chsDash' => __('nicknameChsDash'),
|
||||
'password.regex' => __('Please input correct password')
|
||||
]);
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
67
app/api/validate/User.php
Normal file
67
app/api/validate/User.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\validate;
|
||||
|
||||
use think\Validate;
|
||||
use think\facade\Config;
|
||||
|
||||
class User extends Validate
|
||||
{
|
||||
protected $failException = true;
|
||||
|
||||
protected $rule = [
|
||||
'username' => 'require|regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$|unique:user',
|
||||
'password' => 'require|regex:^(?!.*[&<>"\'\n\r]).{6,32}$',
|
||||
'registerType' => 'require|in:email,mobile',
|
||||
'email' => 'email|unique:user|requireIf:registerType,email',
|
||||
'mobile' => 'mobile|unique:user|requireIf:registerType,mobile',
|
||||
// 注册邮箱或手机验证码
|
||||
'captcha' => 'require',
|
||||
// 登录点选验证码
|
||||
'captchaId' => 'require',
|
||||
'captchaInfo' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 验证场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'register' => ['username', 'password', 'registerType', 'email', 'mobile', 'captcha'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 登录验证场景
|
||||
*/
|
||||
public function sceneLogin(): User
|
||||
{
|
||||
$fields = ['username', 'password'];
|
||||
|
||||
// 根据系统配置的登录验证码开关调整验证场景的字段
|
||||
$userLoginCaptchaSwitch = Config::get('buildadmin.user_login_captcha');
|
||||
if ($userLoginCaptchaSwitch) {
|
||||
$fields[] = 'captchaId';
|
||||
$fields[] = 'captchaInfo';
|
||||
}
|
||||
|
||||
return $this->only($fields)->remove('username', ['regex', 'unique']);
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->field = [
|
||||
'username' => __('Username'),
|
||||
'email' => __('Email'),
|
||||
'mobile' => __('Mobile'),
|
||||
'password' => __('Password'),
|
||||
'captcha' => __('captcha'),
|
||||
'captchaId' => __('captchaId'),
|
||||
'captchaInfo' => __('captcha'),
|
||||
'registerType' => __('Register type'),
|
||||
];
|
||||
$this->message = array_merge($this->message, [
|
||||
'username.regex' => __('Please input correct username'),
|
||||
'password.regex' => __('Please input correct password')
|
||||
]);
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user