diff --git a/app/admin/controller/system/Plugin.php b/app/admin/controller/system/Plugin.php index 54218e5..c7f029d 100644 --- a/app/admin/controller/system/Plugin.php +++ b/app/admin/controller/system/Plugin.php @@ -12,9 +12,12 @@ declare (strict_types=1); // +---------------------------------------------------------------------- namespace app\admin\controller\system; + use app\admin\service\AuthService; +use app\common\exception\OperateException; use GuzzleHttp\Exception\TransferException; use process\Monitor; +use Psr\SimpleCache\InvalidArgumentException; use support\Response; use system\File; use system\Http; @@ -23,8 +26,8 @@ use app\AdminController; use think\db\exception\DataNotFoundException; use think\db\exception\DbException; use think\db\exception\ModelNotFoundException; +use think\facade\Db; use Throwable; -use app\common\library\DataBase; use app\common\model\system\AdminRules; /** @@ -40,11 +43,6 @@ class Plugin extends AdminController */ protected mixed $limit = 500; - /** - * 错误信息 - * @var mixed - */ - static mixed $ServerBody = ''; public function __construct() { @@ -69,7 +67,7 @@ class Plugin extends AdminController /** * 安装插件 * @return Response|void - * @throws \Exception|\Psr\SimpleCache\InvalidArgumentException + * @throws InvalidArgumentException */ public function install() { @@ -83,7 +81,13 @@ class Plugin extends AdminController try { - $pluginZip = self::downLoad($name, ['name' => $name, 'token' => input('token')]); + $pluginZip = self::downLoad($name, [ + 'name' => $name, + 'token' => input('token'), + 'version' => input('version'), + 'app' => config('app.version'), + ]); + ZipArchives::unzip($pluginZip, plugin_path(), '', true); $listFiles = File::mutexCompare(File::getCopyDirs($name), root_path(), $pluginPath, true); if (!empty($listFiles)) { @@ -95,29 +99,31 @@ class Plugin extends AdminController self::pluginMenu($name); self::executeSql($name); self::enabled($name); - } catch (\Throwable $th) { + } catch (OperateException $e) { recursive_delete($pluginPath); - return $this->error($th->getMessage(), null, self::$ServerBody, $th->getCode()); + return $this->error($e->getMessage(), '/', $e->getData(), $e->getCode()); + } catch (\Throwable $th) { + recursive_delete($pluginPath); + return $this->error($th->getMessage(), '/', [], $th->getCode()); } - return $this->success('插件安装成功', null, get_plugin_config($name, true)); + return $this->success('插件安装成功', '/', get_plugin_config($name, true)); } } /** * 卸载插件 * @return Response|void - * @throws \Exception|\Psr\SimpleCache\InvalidArgumentException + * @throws InvalidArgumentException */ public function uninstall() { - if (request()->isAjax()) { $name = input('name'); $config = get_plugin_config($name, true); if (empty($config) || $config['status']) { - return $this->error('插件不存在或未禁用'); + return $this->error('请先禁用插件后再卸载'); } try { @@ -142,9 +148,8 @@ class Plugin extends AdminController /** * 插件升级 - * @return mixed|void - * @throws \Psr\SimpleCache\InvalidArgumentException - * @throws \Exception + * @return Response|void + * @throws InvalidArgumentException */ public function upgrade() { @@ -166,7 +171,13 @@ class Plugin extends AdminController } $pluginPath = plugin_path($name); - $pluginZip = self::downLoad($name, ['name' => $name, 'token' => $token, 'version' => $version]); + $pluginZip = self::downLoad($name, [ + 'name' => $name, + 'token' => $token, + 'version' => $version, + 'app' => config('app.version') + ]); + $formIndex = ZipArchives::unzip($pluginZip, plugin_path(), 'config.json'); $upgradeInfo = json_decode($formIndex, true); @@ -190,8 +201,10 @@ class Plugin extends AdminController self::pluginMenu($name); self::executeSql($name); self::enabled($name); - } catch (\Throwable $th) { - return $this->error($th->getMessage(), null, self::$ServerBody, $th->getCode()); + } catch (OperateException $e) { + return $this->error($e->getMessage(), '/', $e->getData(), $e->getCode()); + } catch (\Throwable $th) { + return $this->error($th->getMessage(), '/', [], $th->getCode()); } return $this->success('插件更新成功', null, $data); @@ -202,7 +215,8 @@ class Plugin extends AdminController * 启用插件 * @param string $name * @return bool - * @throws \Exception|\Psr\SimpleCache\InvalidArgumentException + * @throws InvalidArgumentException + * @throws \Exception */ public static function enabled(string $name): bool { @@ -232,7 +246,7 @@ class Plugin extends AdminController * 禁用插件 * @param string $name * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException + * @throws InvalidArgumentException * @throws \Exception */ public static function disabled(string $name): bool @@ -276,7 +290,7 @@ class Plugin extends AdminController /** * 修改插件配置 * @return Response - * @throws \Psr\SimpleCache\InvalidArgumentException + * @throws InvalidArgumentException */ public function config(): Response { @@ -344,26 +358,20 @@ class Plugin extends AdminController */ public static function downLoad(string $name, array $extends): string { - try { + $query = get_plugin_query(); + $response = Http::get($query, $extends); + $body = json_decode($response, true); - $query = get_plugin_query(); - $response = Http::get($query, $extends); - $body = json_decode($response, true); - $url = ''; - if (isset($body['data']['url'])) { - $url = $body['data']['url']; - } - if (!empty($url) && stristr($url, 'download')) { - $content = file_get_contents($url); - $filePath = plugin_path() . $name . '.zip'; - write_file($filePath, $content); - } else { - self::$ServerBody = $body['data']; - throw new \Exception($body['msg'], $body['code']); - } + if (isset($body['data']['url'])) { + $url = $body['data']['url']; + } - } catch (TransferException $th) { - throw new \Exception(__("安装包下载失败"), -111); + if (isset($url) && stristr($url, 'download')) { + $content = file_get_contents($url); + $filePath = plugin_path() . $name . '.zip'; + write_file($filePath, $content); + } else { + throw new OperateException($body['msg'], $body['code'], $body['data'] ?? []); } return $filePath; @@ -373,12 +381,23 @@ class Plugin extends AdminController * 执行SQL脚本文件 * @param string $name * @param string $type + * @throws \Exception */ public static function executeSql(string $name, string $type = 'install') { $pluginPath = plugin_path($name); $sqlFile = $pluginPath . $type . '.sql'; - DataBase::importSql($sqlFile); + $sql = file_get_contents($sqlFile); + $queries = explode(';', $sql); + $queries = str_replace("__PREFIX__", get_env('DATABASE_PREFIX'), $queries); + foreach ($queries as $query) { + $query = trim($query); + if (empty($query)) { + continue; + } + // 执行SQL语句 + Db::execute($query); + } } /** diff --git a/public/static/system/css/plugin.css b/public/static/system/css/plugin.css index 5be663b..093578d 100644 --- a/public/static/system/css/plugin.css +++ b/public/static/system/css/plugin.css @@ -18,14 +18,14 @@ body,.layui-fluid { text-indent: 1em; } -.pluginCenter .fa-user { +.pluginCenter .fa-user, .fa-sign-out { position: absolute; right: 15px; font-size: 18px; top: 6px; } -.pluginCenter .fa-user:hover { +.pluginCenter .fa-user:hover, .fa-sign-out:hover { color: #FF5722; } @@ -43,13 +43,13 @@ body,.layui-fluid { padding: 10px; } -.layui-dropdown.upgrade-version { +.layui-dropdown.upgrade-version,.layui-dropdown.install-version { min-width: 80px!important; } -.upgrade-version .layui-menu li { +.upgrade-version .layui-menu li, .layui-dropdown.install-version li { width: 80px; - line-height: 15px; + line-height: 20px; text-align: center; font-size: 12px; } @@ -180,4 +180,9 @@ button[lay-filter="formSearch"] { padding-top:2px; padding-left: 3px; display: inline-block; +} + +.layui-table-cell .layui-icon-down { + font-size: 10px; + top: 0!important; } \ No newline at end of file diff --git a/public/static/system/module/plugin.js b/public/static/system/module/plugin.js index ccfa3d6..44ccf88 100644 --- a/public/static/system/module/plugin.js +++ b/public/static/system/module/plugin.js @@ -1,22 +1,20 @@ /* 插件管理模块 */ -layui.define(['i18n'], function (exports) { +layui.define(['admin','show'], function (exports) { - var $ = layui.jquery; - var table = layui.table; - var i18n = layui.i18n; - var form = layui.form; - var notice = layui.notice; + let $ = layui.jquery; + let table = layui.table; + let form = layui.form; + let admin = layui.admin; + let show = layui.show; - i18n.render(layui.admin.getStorage('language') || 'zh-CN'); - var area = [$(window).width() > 800 ? '660px' : '85%', $(window).height() > 800 ? '680px' : '85%']; - - var plugin = { - apiUrl: _global_.api, - baseUrl: _global_.app, + let plugin = { + apiUrl: app_Config.api, + baseUrl: app_Config.app, + data:{}, // 插件数据 request: function (name, version, url) { let index = layer.load(); - let token = layui.admin.getStorage('api_cross_token') || null; + let token = admin.getStorage('api_cross_token') || null; if (token == null || token === 'undefined') { layer.close(index); plugin.login(); @@ -28,49 +26,19 @@ layui.define(['i18n'], function (exports) { version: version, token: token, }, function (res) { - - let data; layer.closeAll(); if (res.code === 200) { - - layer.msg(res.msg); - let index = layui.sessionData('api_install_index').index, - elems = $('tr[data-index="' + index + '"]'); - if (url.indexOf('install') !== -1) { - let c = '', h = ''; - $(elems).find('[data-field="status"]').children('div').html(h); - data = res.data || []; - if (data.config) { - c += '' + i18n.prop('配置') + ''; - c += '
'; - } - c += '' + i18n.prop('卸载') + ' '; - $(elems).find('td:last').children('div').html(c); - window.plugins[name] = res.data; - } else { - elems.find('.layui-upgrade-elem').remove(); - elems.find('.layui-form-switch').addClass('bubble'); - elems.find('.layui-form-switch').trigger('click', ['stopPropagation']); - } - - form.render(); + show.msg(res.msg); + window.plugins[name] = res.data; + table.reloadData('lay-tableList'); top.layui.admin.reloadLayout(); - } else { - notice.error({ - message: res.msg, - }) + show.error(res.msg); // 登录超时 if (res.code === -101) { plugin.login(); return false; } - // 付费插件 if (res.code === -102) { plugin.pay(res.data); @@ -80,21 +48,22 @@ layui.define(['i18n'], function (exports) { }, 'json'); } - ,upgrade: function (data, token, url) { + , upgrade: function (data, token, url) { let html = '温馨提示'; - var confirm = layer.confirm(html, { - title: i18n.prop('更新提示'), + let confirm = layer.confirm(html, { + title: '更新提示', }, function () { layer.close(confirm); - plugin.request(data.name, data.v, plugin.getUrl('Plugin', 'upgrade')); - }, function () {}); + plugin.request(data.name, data.version, plugin.getUrl('Plugin', 'upgrade')); + }, function () { + }); } , login: function () { layer.open({ @@ -106,16 +75,13 @@ layui.define(['i18n'], function (exports) { success: function (index, layero) { form.on('submit(login)', function (data) { $.post(plugin.apiUrl + '/user/login', - data.field, function (res) { if (res.code === 200) { - layui.admin.setStorage('api_cross_token', res.data.token); + admin.setStorage('api_cross_token', res.data.token); layer.closeAll(); plugin.againClick(); } else { - notice.error({ - message: res.msg, - }) + show.error(res.msg); } }, 'json') @@ -126,20 +92,19 @@ layui.define(['i18n'], function (exports) { } , clearLogin: function () { layer.msg('清除登录信息成功'); - layui.admin.setStorage('api_cross_token', null); + admin.setStorage('api_cross_token', null); } , pay: function (data) { layer.open({ type: 2, - title: i18n.prop('立即支付'), - area: ['500px','550px'], + title: '立即支付', + area: ['500px', '550px'], offset: "30px", resize: false, shade: 0.8, shadeClose: true, content: data.pay_url, success: function (index, layero) { - // 父类消息监听 window.onmessage = function (res) { let data = res.data; if (res.data !== null && data.code === 200) { @@ -151,20 +116,14 @@ layui.define(['i18n'], function (exports) { }); } , againClick: function () { - try { - - var index = layui.sessionData('api_install_index').index, - install = $('tr[data-index="' + index + '"]').children().find('.install'); - if (install.length <= 0) { - install = $('[layui-value="' + index + '"]'); - } - if (install && index != null) { - $(install).trigger("click"); - } - - } catch (error) { - console.log(error); + if (plugin.data == null || plugin.data === 'undefined') { + return false; } + plugin.request(plugin.data.name, plugin.data.version, plugin.getUrl('Plugin', 'install')); + } + , install(name, version) { + plugin.data = {name: name, version: version}; + plugin.request(name, version, plugin.getUrl('Plugin', 'install')); } , uninstall: function (name, tables) { let appURL = plugin.baseUrl; @@ -172,28 +131,13 @@ layui.define(['i18n'], function (exports) { name: name, tables: tables, }, function (res) { - if (res.code === 200) { - layer.msg(res.msg); - let index = layui.sessionData('api_install_index').index, - elems = $('tr[data-index="' + index + '"]'); - $(elems).find('[data-field="status"]').children('div').html('--'); - let html = '' + i18n.prop('安装') + ''; - let plugin = table.cache['lay-tableList'][index]; - if (typeof plugin.demourl != 'undefined') { - html += ''; - html += '' + i18n.prop('演示') + ''; - } + show.msg(res.msg); delete window.plugins[name]; - $(elems).find('td:last').children('div').html(html); + table.reloadData('lay-tableList'); } else { - notice.error({ - message: res.msg, - }) + show.error(res.msg); } - layer.close(window.unIndex); }, 'json'); } @@ -206,12 +150,12 @@ layui.define(['i18n'], function (exports) { return false; } - var index = $(that).parents('tr').attr('data-index'); + let index = $(that).parents('tr').attr('data-index'); index = table.cache['lay-tableList'][index]; return index; } , getHtml: function () { - var html = ' '; - layui.sessionData('api_install_index', { - key: 'index', - value: plugin.getTableData(this)['LAY_INDEX'], - }); + layer.open({ type: 1, - title: i18n.prop('卸载插件'), + title: '卸载插件', shadeClose: true, area: ['380px', '300px'], content: html, @@ -378,7 +313,7 @@ layui.define(['i18n'], function (exports) { * @param that */ $('#pluginInstall').click(function (res) { - table.reload('lay-tableList', { + table.reloadData('lay-tableList', { url: plugin.baseUrl + '/system/Plugin/index', }); }) @@ -388,7 +323,7 @@ layui.define(['i18n'], function (exports) { * @param that */ $('#pluginCache').click(function (res) { - var confirm = layer.confirm('确定要更新缓存吗?', { + let confirm = layer.confirm('确定要更新缓存吗?', { title: '更新提示' }, function () { $.get(plugin.baseUrl + plugin.getUrl('Admin', 'clear?type=plugin'), {}, function (res) { @@ -396,9 +331,7 @@ layui.define(['i18n'], function (exports) { layer.msg(res.msg); layer.close(confirm); } else { - notice.error({ - message: res.msg, - }) + show.error(res.msg); } }) });
'; - html += '确认升级 《' + data.pluginName + '》 '+data.v+' 版本吗?
'; + html += '确认升级 《' + data.pluginName + '》 ' + data.version + ' 版本吗?
'; html += '1、请务必做好服务器代码和数据库备份
'; html += '2、升级后如出现冗余数据,请根据需要移除即可
'; html += '3、请勿跨版本升级,如必要请参考插件使用文档
'; html += '4、已部署完成的插件,请确保服务器Web权限可读写
'; html += '5、生产环境下更新维护插件,请勿在流量高峰期操作
'; html += '