初始化提交

This commit is contained in:
maticarmy
2025-02-10 10:39:00 +08:00
commit 59cd2c19d1
491 changed files with 54545 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemAuth;
use think\admin\model\SystemNode;
use think\admin\Plugin;
use think\admin\service\AdminService;
/**
* 系统权限管理
* @class Auth
* @package app\admin\controller
*/
class Auth extends Controller
{
/**
* 系统权限管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
SystemAuth::mQuery()->layTable(function () {
$this->title = '系统权限管理';
}, static function (QueryHelper $query) {
$query->like('title,desc')->equal('status,utype')->dateBetween('create_at');
});
}
/**
* 修改权限状态
* @auth true
*/
public function state()
{
SystemAuth::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除系统权限
* @auth true
*/
public function remove()
{
SystemAuth::mDelete();
}
/**
* 添加系统权限
* @auth true
*/
public function add()
{
SystemAuth::mForm('form');
}
/**
* 编辑系统权限
* @auth true
*/
public function edit()
{
SystemAuth::mForm('form');
}
/**
* 表单后置数据处理
* @param array $data
*/
protected function _form_filter(array $data)
{
if ($this->request->isGet()) {
$this->title = empty($data['title']) ? "添加访问授权" : "编辑【{$data['title']}】授权";
} elseif ($this->request->post('action') === 'json') {
if ($this->app->isDebug()) AdminService::clear();
$ztree = AdminService::getTree(empty($data['id']) ? [] : SystemNode::mk()->where(['auth' => $data['id']])->column('node'));
usort($ztree, static function ($a, $b) {
if (explode('-', $a['node'])[0] !== explode('-', $b['node'])[0]) {
if (stripos($a['node'], 'plugin-') === 0) return 1;
}
return $a['node'] === $b['node'] ? 0 : ($a['node'] > $b['node'] ? 1 : -1);
});
[$ps, $cs] = [Plugin::get(), (array)$this->app->config->get('app.app_names', [])];
foreach ($ztree as &$n) $n['title'] = lang($cs[$n['node']] ?? (($ps[$n['node']] ?? [])['name'] ?? $n['title']));
$this->success('获取权限节点成功!', $ztree);
} elseif (empty($data['nodes'])) {
$this->error('未配置功能节点!');
}
}
/**
* 节点更新处理
* @param boolean $state
* @param array $post
* @return void
*/
protected function _form_result(bool $state, array $post)
{
if ($state && $this->request->post('action') === 'save') {
[$map, $data] = [['auth' => $post['id']], []];
foreach ($post['nodes'] ?? [] as $node) $data[] = $map + ['node' => $node];
SystemNode::mk()->where($map)->delete();
count($data) > 0 && SystemNode::mk()->insertAll($data);
sysoplog('系统权限管理', "配置系统权限[{$map['auth']}]授权成功");
$this->success('权限修改成功!', 'javascript:history.back()');
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemBase;
/**
* 数据字典管理
* @class Base
* @package app\admin\controller
*/
class Base extends Controller
{
/**
* 数据字典管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
SystemBase::mQuery()->layTable(function () {
$this->title = '数据字典管理';
$this->types = SystemBase::types();
$this->type = $this->get['type'] ?? ($this->types[0] ?? '-');
}, static function (QueryHelper $query) {
$query->where(['deleted' => 0])->equal('type');
$query->like('code,name,status')->dateBetween('create_at');
});
}
/**
* 添加数据字典
* @auth true
*/
public function add()
{
SystemBase::mForm('form');
}
/**
* 编辑数据字典
* @auth true
*/
public function edit()
{
SystemBase::mForm('form');
}
/**
* 表单数据处理
* @param array $data
* @throws \think\db\exception\DbException
*/
protected function _form_filter(array &$data)
{
if ($this->request->isGet()) {
$this->types = SystemBase::types();
$this->types[] = '--- ' . lang('新增类型') . ' ---';
$this->type = $this->get['type'] ?? ($this->types[0] ?? '-');
} else {
$map = [];
$map[] = ['deleted', '=', 0];
$map[] = ['code', '=', $data['code']];
$map[] = ['type', '=', $data['type']];
$map[] = ['id', '<>', $data['id'] ?? 0];
if (SystemBase::mk()->where($map)->count() > 0) {
$this->error("数据编码已经存在!");
}
}
}
/**
* 修改数据状态
* @auth true
*/
public function state()
{
SystemBase::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除数据记录
* @auth true
*/
public function remove()
{
SystemBase::mDelete();
}
}

View File

@@ -0,0 +1,146 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\Plugin;
use think\admin\service\AdminService;
use think\admin\service\ModuleService;
use think\admin\service\RuntimeService;
use think\admin\service\SystemService;
use think\admin\Storage;
use think\admin\storage\AliossStorage;
use think\admin\storage\QiniuStorage;
use think\admin\storage\TxcosStorage;
/**
* 系统参数配置
* @class Config
* @package app\admin\controller
*/
class Config extends Controller
{
const themes = [
'default' => '默认色0',
'white' => '简约白0',
'red-1' => '玫瑰红1',
'blue-1' => '深空蓝1',
'green-1' => '小草绿1',
'black-1' => '经典黑1',
'red-2' => '玫瑰红2',
'blue-2' => '深空蓝2',
'green-2' => '小草绿2',
'black-2' => '经典黑2',
];
/**
* 系统参数配置
* @auth true
* @menu true
*/
public function index()
{
$this->title = '系统参数配置';
$this->files = Storage::types();
$this->plugins = Plugin::get(null, true);
$this->issuper = AdminService::isSuper();
$this->systemid = ModuleService::getRunVar('uni');
$this->framework = ModuleService::getLibrarys('topthink/framework');
$this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library');
if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) {
$url = url('admin/index/pass', ['id' => AdminService::getUserId()]);
$this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即<a data-modal='%s'>修改密码</a>", [$url]);
}
uasort($this->plugins, static function ($a, $b) {
if ($a['space'] === $b['space']) return 0;
return $a['space'] > $b['space'] ? 1 : -1;
});
$this->fetch();
}
/**
* 修改系统参数
* @auth true
* @throws \think\admin\Exception
*/
public function system()
{
if ($this->request->isGet()) {
$this->title = '修改系统参数';
$this->themes = static::themes;
$this->fetch();
} else {
$post = $this->request->post();
// 修改网站后台入口路径
if (!empty($post['xpath'])) {
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $post['xpath'])) {
$this->error('后台入口格式错误!');
}
if ($post['xpath'] !== 'admin') {
if (is_dir(syspath("app/{$post['xpath']}")) || !empty(Plugin::get($post['xpath']))) {
$this->error(lang('已存在 %s 应用!', [$post['xpath']]));
}
}
RuntimeService::set(null, [$post['xpath'] => 'admin']);
}
// 修改网站 ICON 图标,替换 public/favicon.ico
if (preg_match('#^https?://#', $post['site_icon'] ?? '')) try {
SystemService::setFavicon($post['site_icon'] ?? '');
} catch (\Exception $exception) {
trace_file($exception);
}
// 数据数据到系统配置表
foreach ($post as $k => $v) sysconf($k, $v);
sysoplog('系统配置管理', "修改系统参数成功");
$this->success('数据保存成功!', admuri('admin/config/index'));
}
}
/**
* 修改文件存储
* @auth true
* @throws \think\admin\Exception
*/
public function storage()
{
$this->_applyFormToken();
if ($this->request->isGet()) {
$this->type = input('type', 'local');
if ($this->type === 'alioss') {
$this->points = AliossStorage::region();
} elseif ($this->type === 'qiniu') {
$this->points = QiniuStorage::region();
} elseif ($this->type === 'txcos') {
$this->points = TxcosStorage::region();
}
$this->fetch("storage-{$this->type}");
} else {
$post = $this->request->post();
if (!empty($post['storage']['allow_exts'])) {
$deny = ['sh', 'asp', 'bat', 'cmd', 'exe', 'php'];
$exts = array_unique(str2arr(strtolower($post['storage']['allow_exts'])));
if (count(array_intersect($deny, $exts)) > 0) $this->error('禁止上传可执行的文件!');
$post['storage']['allow_exts'] = join(',', $exts);
}
foreach ($post as $name => $value) sysconf($name, $value);
sysoplog('系统配置管理', "修改系统存储参数");
$this->success('修改文件存储成功!');
}
}
}

View File

@@ -0,0 +1,118 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemFile;
use think\admin\service\AdminService;
use think\admin\Storage;
/**
* 系统文件管理
* @class File
* @package app\admin\controller
*/
class File extends Controller
{
/**
* 存储类型
* @var array
*/
protected $types;
/**
* 控制器初始化
* @return void
*/
protected function initialize()
{
$this->types = Storage::types();
}
/**
* 系统文件管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
SystemFile::mQuery()->layTable(function () {
$this->title = '系统文件管理';
$this->xexts = SystemFile::mk()->distinct()->column('xext');
}, static function (QueryHelper $query) {
$query->like('name,hash,xext')->equal('type')->dateBetween('create_at');
$query->where(['issafe' => 0, 'status' => 2, 'uuid' => AdminService::getUserId()]);
});
}
/**
* 数据列表处理
* @param array $data
* @return void
*/
protected function _page_filter(array &$data)
{
foreach ($data as &$vo) {
$vo['ctype'] = $this->types[$vo['type']] ?? $vo['type'];
}
}
/**
* 编辑系统文件
* @auth true
* @return void
*/
public function edit()
{
SystemFile::mForm('form');
}
/**
* 删除系统文件
* @auth true
* @return void
*/
public function remove()
{
if (!AdminService::isSuper()) {
$where = ['uuid' => AdminService::getUserId()];
}
SystemFile::mDelete('', $where ?? []);
}
/**
* 清理重复文件
* @auth true
* @return void
* @throws \think\db\exception\DbException
*/
public function distinct()
{
$map = ['uuid' => AdminService::getUserId()];
$db1 = SystemFile::mk()->fieldRaw('max(id) id')->where($map)->group('type,xkey');
$db2 = $this->app->db->table($db1->buildSql())->alias('dt')->field('id');
SystemFile::mk()->whereRaw("id not in {$db2->buildSql()}")->delete();
SystemFile::mk()->where($map)->where(['status' => 1])->delete();
$this->success('清理重复文件成功!');
}
}

View File

@@ -0,0 +1,157 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\model\SystemUser;
use think\admin\service\AdminService;
use think\admin\service\MenuService;
/**
* 后台界面入口
* @class Index
* @package app\admin\controller
*/
class Index extends Controller
{
/**
* 显示后台首页
* @throws \think\admin\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
/*! 根据运行模式刷新权限 */
AdminService::apply($this->app->isDebug());
/*! 读取当前用户权限菜单树 */
$this->menus = MenuService::getTree();
/*! 判断当前用户的登录状态 */
$this->login = AdminService::isLogin();
/*! 菜单为空且未登录跳转到登录页 */
if (empty($this->menus) && empty($this->login)) {
$this->redirect(sysuri('admin/login/index'));
} else {
$this->title = '系统管理后台';
$this->super = AdminService::isSuper();
$this->theme = AdminService::getUserTheme();
$this->fetch();
}
}
/**
* 后台主题切换
* @login true
* @return void
* @throws \think\admin\Exception
*/
public function theme()
{
if ($this->request->isGet()) {
$this->theme = AdminService::getUserTheme();
$this->themes = Config::themes;
$this->fetch();
} else {
$data = $this->_vali(['site_theme.require' => '主题名称不能为空!']);
if (AdminService::setUserTheme($data['site_theme'])) {
$this->success('主题配置保存成功!');
} else {
$this->error('主题配置保存失败!');
}
}
}
/**
* 修改用户资料
* @login true
* @param mixed $id 用户ID
*/
public function info($id = 0)
{
$this->_applyFormToken();
if (AdminService::getUserId() === intval($id)) {
SystemUser::mForm('user/form', 'id', [], ['id' => $id]);
} else {
$this->error('只能修改自己的资料!');
}
}
/**
* 资料修改表单处理
* @param array $data
*/
protected function _info_form_filter(array &$data)
{
if ($this->request->isPost()) {
unset($data['username'], $data['authorize']);
}
}
/**
* 资料修改结果处理
* @param bool $status
*/
protected function _info_form_result(bool $status)
{
if ($status) {
$this->success('用户资料修改成功!', 'javascript:location.reload()');
}
}
/**
* 修改当前用户密码
* @login true
* @param mixed $id
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function pass($id = 0)
{
$this->_applyFormToken();
if (AdminService::getUserId() !== intval($id)) {
$this->error('禁止修改他人密码!');
}
if ($this->app->request->isGet()) {
$this->verify = true;
SystemUser::mForm('user/pass', 'id', [], ['id' => $id]);
} else {
$data = $this->_vali([
'password.require' => '登录密码不能为空!',
'repassword.require' => '重复密码不能为空!',
'oldpassword.require' => '旧的密码不能为空!',
'password.confirm:repassword' => '两次输入的密码不一致!',
]);
$user = SystemUser::mk()->find($id);
if (empty($user)) $this->error('用户不存在!');
if (md5($data['oldpassword']) !== $user['password']) {
$this->error('旧密码验证失败,请重新输入!');
}
if ($user->save(['password' => md5($data['password'])])) {
sysoplog('系统用户管理', "修改用户[{$user['id']}]密码成功");
// 修改密码同步事件处理
$this->app->event->trigger('PluginAdminChangePassword', [
'uuid' => intval($user['id']), 'pass' => $data['password']
]);
$this->success('密码修改成功,下次请使用新密码登录!', '');
} else {
$this->error('密码修改失败,请稍候再试!');
}
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\extend\CodeExtend;
use think\admin\model\SystemUser;
use think\admin\service\AdminService;
use think\admin\service\CaptchaService;
use think\admin\service\RuntimeService;
use think\admin\service\SystemService;
/**
* 用户登录管理
* @class Login
* @package app\admin\controller
*/
class Login extends Controller
{
/**
* 后台登录入口
* @return void
* @throws \think\admin\Exception
*/
public function index()
{
if ($this->app->request->isGet()) {
if (AdminService::isLogin()) {
$this->redirect(sysuri('admin/index/index'));
} else {
// 加载登录模板
$this->title = '系统登录';
// 登录验证令牌
$this->captchaType = 'LoginCaptcha';
$this->captchaToken = CodeExtend::uuid();
// 当前运行模式
$this->runtimeMode = RuntimeService::check();
// 后台背景处理
$images = str2arr(sysconf('login_image|raw') ?: '', '|');
if (empty($images)) $images = [
SystemService::uri('/static/theme/img/login/bg1.jpg'),
SystemService::uri('/static/theme/img/login/bg2.jpg'),
];
$this->loginStyle = sprintf('style="background-image:url(%s)" data-bg-transition="%s"', $images[0], join(',', $images));
// 更新后台主域名,用于部分无法获取域名的场景调用
if ($this->request->domain() !== sysconf('base.site_host|raw')) {
sysconf('base.site_host', $this->request->domain());
}
$this->fetch();
}
} else {
$data = $this->_vali([
'username.require' => '登录账号不能为空!',
'username.min:4' => '账号不能少于4位字符!',
'password.require' => '登录密码不能为空!',
'password.min:4' => '密码不能少于4位字符!',
'verify.require' => '图形验证码不能为空!',
'uniqid.require' => '图形验证标识不能为空!',
]);
if (!CaptchaService::instance()->check($data['verify'], $data['uniqid'])) {
$this->error('图形验证码验证失败,请重新输入!');
}
/*! 用户信息验证 */
$map = ['username' => $data['username'], 'is_deleted' => 0];
$user = SystemUser::mk()->where($map)->findOrEmpty();
if ($user->isEmpty()) {
$this->app->session->set('LoginInputSessionError', true);
$this->error('登录账号或密码错误,请重新输入!');
}
if (empty($user['status'])) {
$this->app->session->set('LoginInputSessionError', true);
$this->error('账号已经被禁用,请联系管理员!');
}
if (md5("{$user['password']}{$data['uniqid']}") !== $data['password']) {
$this->app->session->set('LoginInputSessionError', true);
$this->error('登录账号或密码错误,请重新输入!');
}
$user->hidden(['sort', 'status', 'password', 'is_deleted']);
$this->app->session->set('user', $user->toArray());
$this->app->session->delete('LoginInputSessionError');
// 更新登录次数
$user->where(['id' => $user->getAttr('id')])->inc('login_num')->update([
'login_at' => date('Y-m-d H:i:s'), 'login_ip' => $this->app->request->ip(),
]);
// 刷新用户权限
AdminService::apply(true);
sysoplog('系统用户登录', '登录系统后台成功');
$this->success('登录成功', sysuri('admin/index/index'));
}
}
/**
* 生成验证码
* @return void
*/
public function captcha()
{
$input = $this->_vali([
'type.require' => '类型不能为空!',
'token.require' => '标识不能为空!',
]);
$image = CaptchaService::instance()->initialize();
$captcha = ['image' => $image->getData(), 'uniqid' => $image->getUniqid()];
// 未发生异常时,直接返回验证码内容
if (!$this->app->session->get('LoginInputSessionError')) {
$captcha['code'] = $image->getCode();
}
$this->success('生成验证码成功', $captcha);
}
/**
* 退出登录
* @return void
*/
public function out()
{
$this->app->session->destroy();
$this->success('退出登录成功!', sysuri('admin/login/index'));
}
}

View File

@@ -0,0 +1,147 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\extend\DataExtend;
use think\admin\model\SystemMenu;
use think\admin\service\AdminService;
use think\admin\service\MenuService;
use think\admin\service\NodeService;
/**
* 系统菜单管理
* @class Menu
* @package app\admin\controller
*/
class Menu extends Controller
{
/**
* 系统菜单管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '系统菜单管理';
$this->type = $this->get['type'] ?? 'index';
SystemMenu::mQuery()->layTable();
}
/**
* 列表数据处理
* @param array $data
*/
protected function _index_page_filter(array &$data)
{
$data = DataExtend::arr2tree($data);
// 回收站过滤有效菜单
if ($this->type === 'recycle') foreach ($data as $k1 => &$p1) {
if (!empty($p1['sub'])) foreach ($p1['sub'] as $k2 => &$p2) {
if (!empty($p2['sub'])) foreach ($p2['sub'] as $k3 => $p3) {
if ($p3['status'] > 0) unset($p2['sub'][$k3]);
}
if (empty($p2['sub']) && ($p2['url'] === '#' or $p2['status'] > 0)) unset($p1['sub'][$k2]);
}
if (empty($p1['sub']) && ($p1['url'] === '#' or $p1['status'] > 0)) unset($data[$k1]);
}
// 菜单数据树数据变平化
$data = DataExtend::arr2table($data);
foreach ($data as &$vo) {
if ($vo['url'] !== '#' && !preg_match('/^(https?:)?(\/\/|\\\\)/i', $vo['url'])) {
$vo['url'] = trim(url($vo['url']) . ($vo['params'] ? "?{$vo['params']}" : ''), '\\/');
}
}
}
/**
* 添加系统菜单
* @auth true
*/
public function add()
{
$this->_applyFormToken();
SystemMenu::mForm('form');
}
/**
* 编辑系统菜单
* @auth true
*/
public function edit()
{
$this->_applyFormToken();
SystemMenu::mForm('form');
}
/**
* 表单数据处理
* @param array $vo
*/
protected function _form_filter(array &$vo)
{
if ($this->request->isGet()) {
$debug = $this->app->isDebug();
/* 清理权限节点 */
$debug && AdminService::clear();
/* 读取系统功能节点 */
$this->auths = [];
$this->nodes = MenuService::getList($debug);
foreach (NodeService::getMethods($debug) as $node => $item) {
if ($item['isauth'] && substr_count($node, '/') >= 2) {
$this->auths[] = ['node' => $node, 'title' => $item['title']];
}
}
/* 选择自己上级菜单 */
$vo['pid'] = $vo['pid'] ?? input('pid', '0');
/* 列出可选上级菜单 */
$menus = SystemMenu::mk()->order('sort desc,id asc')->column('id,pid,icon,url,node,title,params', 'id');
$this->menus = DataExtend::arr2table(array_merge($menus, [['id' => '0', 'pid' => '-1', 'url' => '#', 'title' => '顶部菜单']]));
if (isset($vo['id'])) foreach ($this->menus as $menu) if ($menu['id'] === $vo['id']) $vo = $menu;
foreach ($this->menus as $key => $menu) if ($menu['spt'] >= 3 || $menu['url'] !== '#') unset($this->menus[$key]);
if (isset($vo['spt']) && isset($vo['spc']) && in_array($vo['spt'], [1, 2]) && $vo['spc'] > 0) {
foreach ($this->menus as $key => $menu) if ($vo['spt'] <= $menu['spt']) unset($this->menus[$key]);
}
}
}
/**
* 修改菜单状态
* @auth true
*/
public function state()
{
SystemMenu::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除系统菜单
* @auth true
*/
public function remove()
{
SystemMenu::mDelete();
}
}

View File

@@ -0,0 +1,95 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use Ip2Region;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemOplog;
use think\exception\HttpResponseException;
/**
* 系统日志管理
* @class Oplog
* @package app\admin\controller
*/
class Oplog extends Controller
{
/**
* 系统日志管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
SystemOplog::mQuery()->layTable(function () {
$this->title = '系统日志管理';
$columns = SystemOplog::mk()->column('action,username', 'id');
$this->users = array_unique(array_column($columns, 'username'));
$this->actions = array_unique(array_column($columns, 'action'));
}, static function (QueryHelper $query) {
$query->dateBetween('create_at')->equal('username,action')->like('content,geoip,node');
});
}
/**
* 列表数据处理
* @param array $data
* @throws \Exception
*/
protected function _index_page_filter(array &$data)
{
$region = new Ip2Region();
foreach ($data as &$vo) try {
$vo['geoisp'] = $region->simple($vo['geoip']);
} catch (\Exception $exception) {
$vo['geoip'] = $exception->getMessage();
}
}
/**
* 清理系统日志
* @auth true
*/
public function clear()
{
try {
SystemOplog::mQuery()->empty();
sysoplog('系统运维管理', '成功清理所有日志');
$this->success('日志清理成功!');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error(lang("日志清理失败,%s", [$exception->getMessage()]));
}
}
/**
* 删除系统日志
* @auth true
*/
public function remove()
{
SystemOplog::mDelete();
}
}

View File

@@ -0,0 +1,117 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemQueue;
use think\admin\service\AdminService;
use think\admin\service\ProcessService;
use think\admin\service\QueueService;
use think\exception\HttpResponseException;
/**
* 系统任务管理
* @class Queue
* @package app\admin\controller
*/
class Queue extends Controller
{
/**
* 系统任务管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
SystemQueue::mQuery()->layTable(function () {
$this->title = '系统任务管理';
$this->iswin = ProcessService::iswin();
if ($this->super = AdminService::isSuper()) {
$this->command = ProcessService::think('xadmin:queue start');
if (!$this->iswin && !empty($_SERVER['USER'])) {
$this->command = "sudo -u {$_SERVER['USER']} {$this->command}";
}
}
}, static function (QueryHelper $query) {
$query->equal('status')->like('code|title#title,command');
$query->timeBetween('enter_time,exec_time')->dateBetween('create_at');
});
}
/**
* 分页数据回调处理
* @param array $data
* @param array $result
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function _index_page_filter(array $data, array &$result)
{
$result['extra'] = ['dos' => 0, 'pre' => 0, 'oks' => 0, 'ers' => 0];
SystemQueue::mk()->field('status,count(1) count')->group('status')->select()->map(static function ($item) use (&$result) {
if (intval($item['status']) === 1) $result['extra']['pre'] = $item['count'];
if (intval($item['status']) === 2) $result['extra']['dos'] = $item['count'];
if (intval($item['status']) === 3) $result['extra']['oks'] = $item['count'];
if (intval($item['status']) === 4) $result['extra']['ers'] = $item['count'];
});
}
/**
* 重启系统任务
* @auth true
*/
public function redo()
{
try {
$data = $this->_vali(['code.require' => '任务编号不能为空!']);
$queue = QueueService::instance()->initialize($data['code'])->reset();
$queue->progress(1, '>>> 任务重置成功 <<<', '0.00');
$this->success('任务重置成功!', $queue->code);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 清理运行数据
* @auth true
*/
public function clean()
{
$this->_queue('定时清理系统运行数据', "xadmin:queue clean", 0, [], 0, 3600);
}
/**
* 删除系统任务
* @auth true
*/
public function remove()
{
SystemQueue::mDelete();
}
}

View File

@@ -0,0 +1,182 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
namespace app\admin\controller;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemAuth;
use think\admin\model\SystemBase;
use think\admin\model\SystemUser;
use think\admin\service\AdminService;
/**
* 系统用户管理
* @class User
* @package app\admin\controller
*/
class User extends Controller
{
/**
* 系统用户管理
* @auth true
* @menu true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->type = $this->get['type'] ?? 'index';
SystemUser::mQuery()->layTable(function () {
$this->title = '系统用户管理';
$this->bases = SystemBase::items('身份权限');
}, function (QueryHelper $query) {
// 加载对应数据列表
$query->where(['is_deleted' => 0, 'status' => intval($this->type === 'index')]);
// 关联用户身份资料
/** @var \think\model\Relation|\think\db\Query $query */
$query->with(['userinfo' => static function ($query) {
$query->field('code,name,content');
}]);
// 数据列表搜索过滤
$query->equal('status,usertype')->dateBetween('login_at,create_at');
$query->like('username|nickname#username,contact_phone#phone,contact_mail#mail');
});
}
/**
* 添加系统用户
* @auth true
*/
public function add()
{
SystemUser::mForm('form');
}
/**
* 编辑系统用户
* @auth true
*/
public function edit()
{
SystemUser::mForm('form');
}
/**
* 修改用户密码
* @auth true
*/
public function pass()
{
$this->_applyFormToken();
if ($this->request->isGet()) {
$this->verify = false;
SystemUser::mForm('pass');
} else {
$data = $this->_vali([
'id.require' => '用户ID不能为空',
'password.require' => '登录密码不能为空!',
'repassword.require' => '重复密码不能为空!',
'repassword.confirm:password' => '两次输入的密码不一致!',
]);
$user = SystemUser::mk()->findOrEmpty($data['id']);
if ($user->isExists() && $user->save(['password' => md5($data['password'])])) {
// 修改密码同步事件处理
$this->app->event->trigger('PluginAdminChangePassword', [
'uuid' => $data['id'], 'pass' => $data['password']
]);
sysoplog('系统用户管理', "修改用户[{$data['id']}]密码成功");
$this->success('密码修改成功,请使用新密码登录!', '');
} else {
$this->error('密码修改失败,请稍候再试!');
}
}
}
/**
* 表单数据处理
* @param array $data
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function _form_filter(array &$data)
{
if ($this->request->isPost()) {
// 检查资料是否完整
empty($data['username']) && $this->error('登录账号不能为空!');
if ($data['username'] !== AdminService::getSuperName()) {
empty($data['authorize']) && $this->error('未配置权限!');
}
// 处理上传的权限格式
$data['authorize'] = arr2str($data['authorize'] ?? []);
if (empty($data['id'])) {
// 检查账号是否重复
$map = ['username' => $data['username'], 'is_deleted' => 0];
if (SystemUser::mk()->where($map)->count() > 0) {
$this->error("账号已经存在,请使用其它账号!");
}
// 新添加的用户密码与账号相同
$data['password'] = md5($data['username']);
} else {
unset($data['username']);
}
} else {
// 权限绑定处理
$data['authorize'] = str2arr($data['authorize'] ?? '');
$this->auths = SystemAuth::items();
$this->bases = SystemBase::items('身份权限');
$this->super = AdminService::getSuperName();
}
}
/**
* 修改用户状态
* @auth true
*/
public function state()
{
$this->_checkInput();
SystemUser::mSave($this->_vali([
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]));
}
/**
* 删除系统用户
* @auth true
*/
public function remove()
{
$this->_checkInput();
SystemUser::mDelete();
}
/**
* 检查输入变量
*/
private function _checkInput()
{
if (in_array('10000', str2arr(input('id', '')))) {
$this->error('系统超级账号禁止删除!');
}
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\api;
use think\admin\Controller;
/**
* 邮箱配置接口
*/
class Mail extends Controller
{
/**
* 邮箱配置列表
*/
private $mailConfigs = [
[
'name' => '默认配置1',
'config' => [
'DOMAIN' => '586vip.cn',
'TEMP_MAIL' => 'ademyyk',
'TEMP_MAIL_EXT' => '@mailto.plus',
'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
// 'BROWSER_PROXY' => 'http://127.0.0.1:2080',
// 'BROWSER_HEADLESS' => 'True',
'MAIL_SERVER' => 'https://tempmail.plus'
]
],
[
'name' => '备用配置1',
'config' => [
'DOMAIN' => 'nosqli.com',
'TEMP_MAIL' => 'ademyyk',
'TEMP_MAIL_EXT' => '@mailto.plus',
'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
'MAIL_SERVER' => 'https://tempmail.plus'
]
]
// ,
// [
// 'name' => 'IMAP配置',
// 'config' => [
// 'DOMAIN' => 'wuen.site',
// 'TEMP_MAIL' => null,
// 'IMAP_SERVER' => 'imap.163.com',
// 'IMAP_PORT' => 993,
// 'IMAP_USER' => 'maticarmy@163.com',
// 'IMAP_PASS' => 'LQer6rsSWan6vtuz',
// 'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36',
// 'MAIL_SERVER' => 'https://tempmail.plus'
// ]
// ]
];
/**
* 获取所有配置
*/
public function getAll()
{
return json([
'code' => 0,
'msg' => '获取成功',
'data' => $this->mailConfigs
]);
}
/**
* 获取随机配置
*/
public function getRandom()
{
// 随机获取一个配置
$config = $this->mailConfigs[array_rand($this->mailConfigs)];
return json([
'code' => 0,
'msg' => '获取成功',
'data' => [
'name' => $config['name'],
'env' => $config['config']
]
]);
}
/**
* 获取指定配置
*/
public function getConfig()
{
$name = input('name', '');
// 如果未指定名称,返回随机配置
if (empty($name)) {
return $this->getRandom();
}
// 查找指定配置
foreach ($this->mailConfigs as $config) {
if ($config['name'] === $name) {
return json([
'code' => 0,
'msg' => '获取成功',
'data' => [
'name' => $config['name'],
'env' => $config['config']
]
]);
}
}
// 未找到指定配置,返回随机配置
return $this->getRandom();
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\api;
use app\manager\model\Member as MemberModel;
use think\admin\Controller;
/**
* 会员接口管理
*/
class Member extends Controller
{
/**
* 验证会员状态
*/
public function check()
{
// 接收参数
$keyword = trim(input('keyword', ''));
if (empty($keyword)) {
return json([
'code' => 1,
'msg' => '请输入查询关键字'
]);
}
// 查询会员信息 (email = xxx OR order_id = xxx)
$member = MemberModel::whereOr('email', '=', $keyword)
->whereOr('order_id', '=', $keyword)
->find();
if (empty($member)) {
return json([
'code' => 1,
'msg' => '会员不存在'
]);
}
// 检查会员状态
if ($member['status'] != 1) {
return json([
'code' => 1,
'msg' => '会员已被禁用'
]);
}
// 检查有效期
if (strtotime($member['expire_time']) < time()) {
return json([
'code' => 1,
'msg' => '会员已过期'
]);
}
// 检查使用次数
if ($member['usage_limit'] > 0 && $member['used_count'] >= $member['usage_limit']) {
return json([
'code' => 1,
'msg' => '使用次数已达上限'
]);
}
// 更新使用次数和最后登录信息
$member->used_count = $member->used_count + 1;
$member->last_login_time = date('Y-m-d H:i:s');
$member->last_login_ip = $this->request->ip();
$member->save();
// 返回成功
return json([
'code' => 0,
'msg' => '验证通过',
'data' => [
'email' => $member['email'],
'order_id' => $member['order_id'],
'expire_time' => $member['expire_time'],
'usage_limit' => $member['usage_limit'],
'used_count' => $member['used_count'],
'last_login_time' => $member['last_login_time']
]
]);
}
}

View File

@@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\service\AdminService;
use think\Response;
/**
* 扩展插件管理
* @class Plugs
* @package app\admin\controller\api
*/
class Plugs extends Controller
{
/**
* 图标选择器
* @login true
*/
public function icon()
{
$this->title = '图标选择器';
// 读取 layui 字体图标
if (empty($this->layuiIcons = $this->app->cache->get('LayuiIcons', []))) {
$style = file_get_contents(syspath('public/static/plugs/layui/css/layui.css'));
if (preg_match_all('#\.(layui-icon-[\w-]+):#', $style, $matches)) {
if (count($this->layuiIcons = $matches[1]) > 0) {
$this->app->cache->set('LayuiIcons', $this->layuiIcons, 60);
}
}
}
// 读取自定义字体图标
if (empty($this->thinkIcons = $this->app->cache->get('ThinkAdminSelfIcons', []))) {
$style = file_get_contents(syspath('public/static/theme/css/iconfont.css'));
if (preg_match_all('#\.(iconfont-[\w-]+):#', $style, $matches)) {
if (count($this->thinkIcons = $matches[1]) > 0) {
$this->app->cache->set('ThinkAdminSelfIcons', $this->thinkIcons, 60);
}
}
}
$this->field = $this->app->request->get('field', 'icon');
$this->fetch(dirname(__DIR__, 2) . '/view/api/icon.html');
}
/**
* 前端脚本变量
* @return \think\Response
* @throws \think\admin\Exception
*/
public function script(): Response
{
$token = $this->request->get('uptoken', '');
$domain = boolval(AdminService::withUploadUnid($token));
return response(join("\r\n", [
sprintf("window.taDebug = %s;", $this->app->isDebug() ? 'true' : 'false'),
sprintf("window.taAdmin = '%s';", sysuri('admin/index/index', [], false, $domain)),
sprintf("window.taEditor = '%s';", sysconf('base.editor|raw') ?: 'ckeditor4'),
]))->contentType('application/javascript');
}
/**
* 优化数据库
* @login true
*/
public function optimize()
{
if (AdminService::isSuper()) {
sysoplog('系统运维管理', '创建数据库优化任务');
$this->_queue('优化数据库所有数据表', 'xadmin:database optimize');
} else {
$this->error('请使用超管账号操作!');
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\api;
use think\admin\Controller;
/**
* 程序更新接口
*/
class Program extends Controller
{
/**
* 程序目录配置
*/
private $programPath = 'program/';
/**
* 获取程序信息
*/
public function info()
{
$path = public_path() . $this->programPath;
if (!is_dir($path)) {
return json([
'code' => 1,
'msg' => '程序目录不存在'
]);
}
// 获取目录中最新的文件
$files = glob($path . '*');
if (empty($files)) {
return json([
'code' => 1,
'msg' => '暂无可用程序'
]);
}
// 获取最新文件
$latest = array_reduce($files, function($carry, $file) {
return (!$carry || filemtime($file) > filemtime($carry)) ? $file : $carry;
});
$name = basename($latest);
$size = filesize($latest);
$md5 = md5_file($latest);
return json([
'code' => 0,
'msg' => '获取成功',
'data' => [
'name' => $name,
'size' => $size,
'md5' => $md5,
'url' => request()->domain() . '/' . $this->programPath . $name,
'time' => date('Y-m-d H:i:s', filemtime($latest))
]
]);
}
/**
* 下载程序
*/
public function down()
{
$path = public_path() . $this->programPath;
if (!is_dir($path)) {
return json([
'code' => 1,
'msg' => '程序目录不存在'
]);
}
// 获取目录中最新的文件
$files = glob($path . '*');
if (empty($files)) {
return json([
'code' => 1,
'msg' => '暂无可用程序'
]);
}
// 获取最新文件
$latest = array_reduce($files, function($carry, $file) {
return (!$carry || filemtime($file) > filemtime($carry)) ? $file : $carry;
});
return download($latest, basename($latest));
}
}

View File

@@ -0,0 +1,118 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use Psr\Log\NullLogger;
use think\admin\Controller;
use think\admin\model\SystemQueue;
use think\admin\service\AdminService;
use think\exception\HttpResponseException;
/**
* 任务监听服务管理
* @class Queue
* @package app\admin\controller\api
*/
class Queue extends Controller
{
/**
* 停止监听服务
* @login true
*/
public function stop()
{
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['stop'])->fetch();
if (stripos($message, 'sent end signal to process')) {
sysoplog('系统运维管理', '尝试停止任务监听服务');
$this->success('停止任务监听服务成功!');
} elseif (stripos($message, 'processes to stop')) {
$this->success('没有找到需要停止的服务!');
} else {
$this->error(nl2br($message));
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 启动监听服务
* @login true
*/
public function start()
{
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['start'])->fetch();
if (stripos($message, 'daemons started successfully for pid')) {
sysoplog('系统运维管理', '尝试启动任务监听服务');
$this->success('任务监听服务启动成功!');
} elseif (stripos($message, 'daemons already exist for pid')) {
$this->success('任务监听服务已经启动!');
} else {
$this->error(nl2br($message));
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 检查监听服务
* @login true
*/
public function status()
{
if (AdminService::isSuper()) try {
$message = $this->app->console->call('xadmin:queue', ['status'])->fetch();
if (preg_match('/process.*?\d+.*?running/', $message)) {
echo "<span class='color-green pointer' data-tips-text='{$message}'>{$this->app->lang->get('已启动')}</span>";
} else {
echo "<span class='color-red pointer' data-tips-text='{$message}'>{$this->app->lang->get('未启动')}</span>";
}
} catch (\Error|\Exception $exception) {
echo "<span class='color-red pointer' data-tips-text='{$exception->getMessage()}'>{$this->app->lang->get('异 常')}</span>";
} else {
$message = lang('只有超级管理员才能操作!');
echo "<span class='color-red pointer' data-tips-text='{$message}'>{$this->app->lang->get('无权限')}</span>";
}
}
/**
* 查询任务进度
* @login true
*/
public function progress()
{
$input = $this->_vali(['code.require' => '任务编号不能为空!']);
$this->app->db->setLog(new NullLogger()); /* 关闭数据库请求日志 */
$message = SystemQueue::mk()->where($input)->value('message', '');
$this->success('获取任务进度成功d', json_decode($message, true));
}
}

View File

@@ -0,0 +1,138 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\model\SystemConfig;
use think\admin\service\AdminService;
use think\admin\service\RuntimeService;
use think\exception\HttpResponseException;
/**
* 系统运行管理
* @class System
* @package app\admin\controller\api
*/
class System extends Controller
{
/**
* 网站压缩发布
* @login true
*/
public function push()
{
if (AdminService::isSuper()) try {
RuntimeService::push() && sysoplog('系统运维管理', '刷新发布运行缓存');
$this->success('网站缓存加速成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 清理运行缓存
* @login true
*/
public function clear()
{
if (AdminService::isSuper()) try {
RuntimeService::clear() && sysoplog('系统运维管理', '清理网站日志缓存');
$this->success('清空日志缓存成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 当前运行模式
* @login true
*/
public function debug()
{
if (AdminService::isSuper()) if (input('state')) {
RuntimeService::set('product');
sysoplog('系统运维管理', '开发模式切换为生产模式');
$this->success('已切换为生产模式!', 'javascript:location.reload()');
} else {
RuntimeService::set('debug');
sysoplog('系统运维管理', '生产模式切换为开发模式');
$this->success('已切换为开发模式!', 'javascript:location.reload()');
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 修改富文本编辑器
* @return void
* @throws \think\admin\Exception
*/
public function editor()
{
if (AdminService::isSuper()) {
$editor = input('editor', 'auto');
sysconf('base.editor', $editor);
sysoplog('系统运维管理', "切换编辑器为{$editor}");
$this->success('已切换后台编辑器!', 'javascript:location.reload()');
} else {
$this->error('请使用超管账号操作!');
}
}
/**
* 清理系统配置
* @login true
*/
public function config()
{
if (AdminService::isSuper()) try {
[$tmpdata, $newdata] = [[], []];
foreach (SystemConfig::mk()->order('type,name asc')->cursor() as $item) {
$tmpdata[$item['type']][$item['name']] = $item['value'];
}
foreach ($tmpdata as $type => $items) foreach ($items as $name => $value) {
$newdata[] = ['type' => $type, 'name' => $name, 'value' => $value];
}
$this->app->db->transaction(static function () use ($newdata) {
SystemConfig::mQuery()->empty()->insertAll($newdata);
});
$this->app->cache->delete('SystemConfig');
sysoplog('系统运维管理', '清理系统配置参数');
$this->success('清理系统配置成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
} else {
$this->error('请使用超管账号操作!');
}
}
}

View File

@@ -0,0 +1,336 @@
<?php
// +----------------------------------------------------------------------
// | Admin Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-admin
// | github 代码仓库https://github.com/zoujingli/think-plugs-admin
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemFile;
use think\admin\service\AdminService;
use think\admin\Storage;
use think\admin\storage\AliossStorage;
use think\admin\storage\AlistStorage;
use think\admin\storage\LocalStorage;
use think\admin\storage\QiniuStorage;
use think\admin\storage\TxcosStorage;
use think\admin\storage\UpyunStorage;
use think\exception\HttpResponseException;
use think\file\UploadedFile;
use think\Response;
/**
* 文件上传接口
* @class Upload
* @package app\admin\controller\api
*/
class Upload extends Controller
{
/**
* 文件上传脚本
* @return Response
* @throws \think\admin\Exception
*/
public function index(): Response
{
$data = ['exts' => []];
[$uuid, $unid, $exts] = $this->initUnid(false);
$allows = str2arr(sysconf('storage.allow_exts|raw'));
if (empty($uuid) && $unid > 0) $allows = array_intersect($exts, $allows);
foreach ($allows as $ext) $data['exts'][$ext] = Storage::mime($ext);
$data['exts'] = json_encode($data['exts'], JSON_UNESCAPED_UNICODE);
$data['nameType'] = sysconf('storage.name_type|raw') ?: 'xmd5';
return view(dirname(__DIR__, 2) . '/view/api/upload.js', $data)->contentType('application/x-javascript');
}
/**
* 文件选择器
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function image()
{
[$uuid, $unid] = $this->initUnid();
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) use ($unid, $uuid) {
if ($unid && $uuid) $query->where(function ($query) use ($uuid, $unid) {
/** @var \think\db\Query $query */
$query->whereOr([['uuid', '=', $uuid], ['unid', '=', $unid]]);
}); else {
$query->where($unid ? ['unid' => $unid] : ['uuid' => $uuid]);
}
$query->where(['status' => 2, 'issafe' => 0])->in('xext#type');
$query->like('name,hash')->dateBetween('create_at')->order('id desc');
});
}
/**
* 文件上传检查
*/
public function state()
{
try {
[$uuid, $unid] = $this->initUnid();
[$name, $safe] = [input('name'), $this->getSafe()];
$data = ['uptype' => $this->getType(), 'safe' => intval($safe), 'key' => input('key')];
$file = SystemFile::mk()->data($this->_vali([
'xkey.value' => $data['key'],
'type.value' => $this->getType(),
'uuid.value' => $uuid,
'unid.value' => $unid,
'name.require' => '名称不能为空!',
'hash.require' => '哈希不能为空!',
'xext.require' => '后缀不能为空!',
'size.require' => '大小不能为空!',
'mime.default' => '',
'status.value' => 1,
]));
$mime = $file->getAttr('mime');
if (empty($mime)) $file->setAttr('mime', Storage::mime($file->getAttr('xext')));
$info = Storage::instance($data['uptype'])->info($data['key'], $safe, $name);
if (isset($info['url']) && isset($info['key'])) {
$file->save(['xurl' => $info['url'], 'isfast' => 1, 'issafe' => $data['safe']]);
$extr = ['id' => $file->id ?? 0, 'url' => $info['url'], 'key' => $info['key']];
$this->success('文件已经上传', array_merge($data, $extr), 200);
} elseif ('local' === $data['uptype']) {
$local = LocalStorage::instance();
$data['url'] = $local->url($data['key'], $safe, $name);
$data['server'] = $local->upload();
} elseif ('qiniu' === $data['uptype']) {
$qiniu = QiniuStorage::instance();
$data['url'] = $qiniu->url($data['key'], $safe, $name);
$data['token'] = $qiniu->token($data['key'], 3600, $name);
$data['server'] = $qiniu->upload();
} elseif ('alioss' === $data['uptype']) {
$alioss = AliossStorage::instance();
$token = $alioss->token($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['signature'] = $token['signature'];
$data['OSSAccessKeyId'] = $token['keyid'];
$data['server'] = $alioss->upload();
} elseif ('txcos' === $data['uptype']) {
$txcos = TxcosStorage::instance();
$token = $txcos->token($data['key'], 3600, $name);
$data['url'] = $token['siteurl'];
$data['q-ak'] = $token['q-ak'];
$data['policy'] = $token['policy'];
$data['q-key-time'] = $token['q-key-time'];
$data['q-signature'] = $token['q-signature'];
$data['q-sign-algorithm'] = $token['q-sign-algorithm'];
$data['server'] = $txcos->upload();
} elseif ('upyun' === $data['uptype']) {
$upyun = UpyunStorage::instance();
$token = $upyun->token($data['key'], 3600, $name, input('hash', ''));
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['server'] = $upyun->upload();
$data['authorization'] = $token['authorization'];
} elseif ('alist' === $data['uptype']) {
$alist = AlistStorage::instance();
$data['url'] = $alist->url($data['key']);
$data['server'] = $alist->upload();
$data['filepath'] = $alist->real($data['key']);
$data['authorization'] = $alist->token();
} else {
$this->error('未知的存储引擎!');
}
$file->save(['xurl' => $data['url'], 'isfast' => 0, 'issafe' => $data['safe']]);
$this->success('获取上传授权参数', array_merge($data, ['id' => $file->id ?? 0]), 404);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
}
/**
* 更新文件状态
* @return void
*/
public function done()
{
[$uuid, $unid] = $this->initUnid();
$data = $this->_vali([
'id.require' => '编号不能为空!',
'hash.require' => '哈希不能为空!',
'uuid.value' => $uuid,
'unid.value' => $unid,
]);
$file = SystemFile::mk()->where($data)->findOrEmpty();
if ($file->isEmpty()) $this->error('文件不存在!');
if ($file->save(['status' => 2])) {
$this->success('更新成功!');
} else {
$this->error('更新失败!');
}
}
/**
* 文件上传入口
* @throws \think\admin\Exception
*/
public function file()
{
[$uuid, $unid, $unexts] = $this->initUnid();
// 开始处理文件上传
$file = $this->getFile();
$extension = strtolower($file->getOriginalExtension());
$saveFileName = input('key') ?: Storage::name($file->getPathname(), $extension, '', 'md5_file');
// 检查文件名称是否合法
if (strpos($saveFileName, '..') !== false) {
$this->error('文件路径不能出现跳级操作!');
}
// 检查文件后缀是否被恶意修改
if (strtolower(pathinfo(parse_url($saveFileName, PHP_URL_PATH), PATHINFO_EXTENSION)) !== $extension) {
$this->error('文件后缀异常,请重新上传文件!');
}
// 屏蔽禁止上传指定后缀的文件
if (!in_array($extension, str2arr(sysconf('storage.allow_exts|raw')))) {
$this->error('文件类型受限,请在后台配置规则!');
}
// 前端用户上传后缀检查处理
if (empty($uuid) && $unid > 0 && !in_array($extension, $unexts)) {
$this->error('文件类型受限,请上传允许的文件类型!');
}
if (in_array($extension, ['sh', 'asp', 'bat', 'cmd', 'exe', 'php'])) {
$this->error('文件安全保护,禁止上传可执行文件!');
}
try {
$safeMode = $this->getSafe();
if (($type = $this->getType()) === 'local') {
$local = LocalStorage::instance();
$distName = $local->path($saveFileName, $safeMode);
if (PHP_SAPI === 'cli') {
is_dir(dirname($distName)) || mkdir(dirname($distName), 0777, true);
rename($file->getPathname(), $distName);
} else {
$file->move(dirname($distName), basename($distName));
}
$info = $local->info($saveFileName, $safeMode, $file->getOriginalName());
if (in_array($extension, ['jpg', 'gif', 'png', 'bmp', 'jpeg', 'wbmp'])) {
if ($this->imgNotSafe($distName) && $local->del($saveFileName)) {
$this->error('图片未通过安全检查!');
}
[$width, $height] = getimagesize($distName);
if (($width < 1 || $height < 1) && $local->del($saveFileName)) {
$this->error('读取图片的尺寸失败!');
}
}
} else {
$bina = file_get_contents($file->getPathname());
$info = Storage::instance($type)->set($saveFileName, $bina, $safeMode, $file->getOriginalName());
}
if (isset($info['url'])) {
$this->success('文件上传成功!', ['url' => $safeMode ? $saveFileName : $info['url']]);
} else {
$this->error('文件处理失败,请稍候再试!');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error($exception->getMessage());
}
}
/**
* 获取上传类型
* @return boolean
*/
private function getSafe(): bool
{
return boolval(input('safe', '0'));
}
/**
* 获取上传方式
* @return string
* @throws \think\admin\Exception
*/
private function getType(): string
{
$type = strtolower(input('uptype', ''));
if (in_array($type, array_keys(Storage::types()))) {
return $type;
} else {
return strtolower(sysconf('storage.type|raw'));
}
}
/**
* 获取文件对象
* @return UploadedFile|void
*/
private function getFile(): UploadedFile
{
try {
$file = $this->request->file('file');
if ($file instanceof UploadedFile) {
return $file;
} else {
$this->error('读取临时文件失败!');
}
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
trace_file($exception);
$this->error(lang($exception->getMessage()));
}
}
/**
* 初始化用户状态
* @param boolean $check
* @return array
*/
private function initUnid(bool $check = true): array
{
$uuid = AdminService::getUserId();
[$unid, $exts] = AdminService::withUploadUnid();
if ($check && empty($uuid) && empty($unid)) {
$this->error('未登录,禁止使用文件上传!');
} else {
return [$uuid, $unid, $exts];
}
}
/**
* 检查图片是否安全
* @param string $filename
* @return boolean
*/
private function imgNotSafe(string $filename): bool
{
$source = fopen($filename, 'rb');
if (($size = filesize($filename)) > 512) {
$hexs = bin2hex(fread($source, 512));
fseek($source, $size - 512);
$hexs .= bin2hex(fread($source, 512));
} else {
$hexs = bin2hex(fread($source, $size));
}
if (is_resource($source)) fclose($source);
$bins = hex2bin($hexs);
/* 匹配十六进制中的 <% ( ) %> 或 <? ( ) ?> 或 <script | /script> */
foreach (['<?php ', '<% ', '<script '] as $key) if (stripos($bins, $key) !== false) return true;
$result = preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexs);
return $result === false || $result > 0;
}
}