初始化提交
This commit is contained in:
114
app/admin/controller/api/Mail.php
Normal file
114
app/admin/controller/api/Mail.php
Normal 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();
|
||||
}
|
||||
}
|
||||
85
app/admin/controller/api/Member.php
Normal file
85
app/admin/controller/api/Member.php
Normal 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']
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
app/admin/controller/api/Plugs.php
Normal file
91
app/admin/controller/api/Plugs.php
Normal 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('请使用超管账号操作!');
|
||||
}
|
||||
}
|
||||
}
|
||||
91
app/admin/controller/api/Program.php
Normal file
91
app/admin/controller/api/Program.php
Normal 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));
|
||||
}
|
||||
}
|
||||
118
app/admin/controller/api/Queue.php
Normal file
118
app/admin/controller/api/Queue.php
Normal 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));
|
||||
}
|
||||
}
|
||||
138
app/admin/controller/api/System.php
Normal file
138
app/admin/controller/api/System.php
Normal 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('请使用超管账号操作!');
|
||||
}
|
||||
}
|
||||
}
|
||||
336
app/admin/controller/api/Upload.php
Normal file
336
app/admin/controller/api/Upload.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user