From 642d3339fcc0143d8dd97714bb99c3c0500474c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Fri, 2 Dec 2022 23:55:45 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=B9=E6=80=A7=EF=BC=88=E7=BD=91=E7=AB=99?= =?UTF-8?q?=EF=BC=89=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=A4=87=E4=BB=BD=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=8F=8A=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 46 ++ README.md | 2 + .../Controllers/Api/PluginsController.php | 5 +- .../Controllers/Api/SettingsController.php | 21 +- app/Http/Controllers/Api/UsersController.php | 12 +- .../Controllers/Api/WebsitesController.php | 412 +++++++++++++++++- resources/views/cron.blade.php | 8 +- resources/views/home.blade.php | 3 - resources/views/plugin.blade.php | 56 ++- resources/views/safe.blade.php | 13 +- resources/views/setting.blade.php | 16 +- resources/views/website/add.blade.php | 2 +- resources/views/website/backup.blade.php | 159 +++++++ .../views/website/default_settings.blade.php | 11 +- resources/views/website/edit.blade.php | 59 ++- resources/views/website/list.blade.php | 58 ++- routes/api.php | 9 + routes/web.php | 2 + 18 files changed, 802 insertions(+), 92 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 resources/views/website/backup.blade.php diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e03b50d6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# 更新日志 + +所有重要的更改都将在此文件中记录。 + + +## [20221203] - 常规更新 + +- 修复禁用Ping不生效的问题 +- 修复网站设置PHP版本未赋值的问题 +- 新增网站目录备份功能 +- 新增面板多用户登录功能 +- 实装网站运行状态设置功能 +- 实装网站配置文件重置功能 +- 前端多处优化 + +## [20221202] - 常规更新 + +- 修复网站添加数据库不能为空的问题 +- 校验网站目录必须以/开头 +- 修复网站名可以编辑的问题 +- 新增面板API开关 +- 新增计划任务模块 +- 安装脚本优化,提高在不同地区下的安装成功率 + +## [20221201] - 首版发布 + +- 修复了二测时发现的一些问题 +- 首发于Hostloc,感谢各位mjj大佬的支持。 + +## [20221130] - 内部二测 + +- 内部二测 +- 修复了一测发现的问题 +- 新增了一些插件,基本功能已经完善 + +## [20221121] - 内部一测 + +- 内部一测 + +## 2022-10 - 2022-11 + +- 重构版本开始开发 + +## 2022-07 - 2022-08 + +- 项目启动、早期开发 diff --git a/README.md b/README.md index e430682d..b1085aec 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ | RockyLinux | 8 | | AlmaLinux | 9 | | AlmaLinux | 8 | +| 其他RHEL发行版 | 8 | +| 其他RHEL发行版 | 9 | 广告: [`WeAvatar` —统一头像服务](https://weavatar.com) diff --git a/app/Http/Controllers/Api/PluginsController.php b/app/Http/Controllers/Api/PluginsController.php index de5cea89..4d8340ac 100644 --- a/app/Http/Controllers/Api/PluginsController.php +++ b/app/Http/Controllers/Api/PluginsController.php @@ -286,7 +286,10 @@ class PluginsController extends Controller return response()->json($data); } - Plugin::query()->where('slug', $slug)->update(['show' => $show]); + Plugin::query()->where('slug', $slug)->updateOrInsert( + ['slug' => $slug], + ['show' => $show] + ); $res['code'] = 0; $res['msg'] = 'success'; $res['data'] = '设置成功'; diff --git a/app/Http/Controllers/Api/SettingsController.php b/app/Http/Controllers/Api/SettingsController.php index 474dc986..fd10b6d8 100644 --- a/app/Http/Controllers/Api/SettingsController.php +++ b/app/Http/Controllers/Api/SettingsController.php @@ -34,7 +34,13 @@ class SettingsController extends Controller // 从nginx配置文件中获取面板端口 $nginxConf = file_get_contents('/www/server/nginx/conf/nginx.conf'); preg_match('/listen\s+(\d+)/', $nginxConf, $matches); + if (!isset($matches[1])) { + $res['code'] = 1; + $res['msg'] = '获取面板端口失败,请检查nginx主配置文件'; + return response()->json($res); + } + // API $api = 0; $apiToken = ''; if (isset($settingArr['api']) && $settingArr['api'] == 1) { @@ -42,11 +48,12 @@ class SettingsController extends Controller $apiToken = $settingArr['api_token'] ?? ''; } - if (!isset($matches[1])) { - $res['code'] = 1; - $res['msg'] = '获取面板端口失败,请检查nginx主配置文件'; - return response()->json($res); + // 多设备登录 + $multiLogin = 0; + if (isset($settingArr['multi_login']) && $settingArr['multi_login'] == 1) { + $multiLogin = 1; } + $data = [ 'name' => $settingArr['name'], 'username' => $request->user()->username, @@ -54,6 +61,7 @@ class SettingsController extends Controller 'port' => $matches[1], 'api' => $api, 'api_token' => $apiToken, + 'multi_login' => $multiLogin, ]; $res['code'] = 0; $res['msg'] = 'success'; @@ -72,10 +80,11 @@ class SettingsController extends Controller $settings = $request->all(); // 将数据入库 foreach ($settings as $key => $value) { - if ($key == 'access_token' || $key == 'username' || $key == 'password' || $key == 'api_token' || $key == 'api') { + if ($key == 'access_token' || $key == 'username' || $key == 'password' || $key == 'api_token' || $key == 'api' || $key == 'port') { continue; } - Setting::query()->where('name', $key)->update(['value' => $value]); + // 创建或更新 + Setting::query()->updateOrCreate(['name' => $key], ['value' => $value]); } // 单独处理用户名和密码 if ($request->input('username') != $request->user()->username) { diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 0742021b..170551fc 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -3,9 +3,11 @@ * 耗子Linux面板 - 用户控制器 * @author 耗子 */ + namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Models\Setting; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; @@ -34,15 +36,21 @@ class UsersController extends Controller 'errors' => $e->errors() ], 422); } - if (auth()->attempt(['username' => $credentials['username'], 'password' => $credentials['password']], $credentials['remember'])) { + if (auth()->attempt(['username' => $credentials['username'], 'password' => $credentials['password']], + $credentials['remember'])) { $user = auth()->user(); - $user->tokens()->delete(); + // 多设备登录 + $multiLogin = Setting::query()->where('name', 'multi_login')->value('value'); + if ($multiLogin != 1) { + $user->tokens()->delete(); + } $token = $user->createToken('token')->plainTextToken; return response()->json(['code' => 0, 'msg' => '登录成功', 'data' => ['access_token' => $token]]); } else { return response()->json(['code' => 1, 'msg' => '登录失败,用户名或密码错误']); } } + public function getInfo(Request $request): JsonResponse { $user = $request->user(); diff --git a/app/Http/Controllers/Api/WebsitesController.php b/app/Http/Controllers/Api/WebsitesController.php index 3bfaf749..8d05dd55 100644 --- a/app/Http/Controllers/Api/WebsitesController.php +++ b/app/Http/Controllers/Api/WebsitesController.php @@ -30,6 +30,12 @@ class WebsitesController extends Controller 'data' => [] ]); } + foreach ($websiteList as $website) { + // 如果PHP是0,将其设置为字符串的00 + if ($website->php == '0') { + $website->php = '00'; + } + } return response()->json([ 'code' => 0, 'msg' => '获取成功', @@ -335,10 +341,10 @@ EOF; $website['domain'] .= PHP_EOL.$v; } } - // 从nginx配置中root标记位提取全部根目录 + // 从nginx配置中root标记位提取运行目录 $root_raw = $this->cut('# root标记位开始', '# root标记位结束', $nginx_config); preg_match_all('/root\s+(.+);/', $root_raw, $matches2); - $website['path'] = $matches2[1][0]; + $website['root'] = $matches2[1][0]; // 从nginx配置中index标记位提取全部默认文件 $index_raw = $this->cut('# index标记位开始', '# index标记位结束', $nginx_config); preg_match_all('/index\s+(.+);/', $index_raw, $matches3); @@ -396,6 +402,11 @@ EOF; // 读取访问日志 $website['log'] = shell_exec('tail -n 100 /www/wwwlogs/'.$name.'.log'); + // 如果PHP是0,将其设置为字符串的00 + if ($website['php'] == '0') { + $website['php'] = '00'; + } + $res['code'] = 0; $res['msg'] = 'success'; $res['data'] = $website; @@ -413,6 +424,20 @@ EOF; $name = $request->input('name'); $config = $request->input('config'); + $website = Website::query()->where('name', $name)->first(); + if (!$website) { + return response()->json([ + 'code' => 1, + 'msg' => '网站不存在', + ], 200); + } + if ($website->status != 1) { + return response()->json([ + 'code' => 1, + 'msg' => '网站已停用,请先启用', + ], 200); + } + $res['code'] = 0; $res['msg'] = 'success'; @@ -467,12 +492,12 @@ EOF; $configRaw = str_replace($port_config_old, PHP_EOL.$port.PHP_EOL.' ', $configRaw); } - // 网站目录 + // 运行目录 $pathConfig = $this->cut('# root标记位开始', '# root标记位结束', $configRaw); preg_match_all('/root\s+(.+);/', $pathConfig, $matches1); $pathConfigOld = $matches1[1][0]; if (!empty(trim($pathConfigOld)) && $pathConfigOld != PHP_EOL) { - $pathConfigNew = str_replace($pathConfigOld, $config['path'], $pathConfig); + $pathConfigNew = str_replace($pathConfigOld, $config['root'], $pathConfig); $configRaw = str_replace($pathConfig, $pathConfigNew, $configRaw); } @@ -497,7 +522,7 @@ EOF; if (is_dir($config['path'])) { file_put_contents($config['path'].'/.user.ini', $open_basedir); // 为.user.ini文件添加i权限 - shell_exec('chattr +i '.$config['path'].'/.user.ini'); + // shell_exec('chattr +i '.$config['path'].'/.user.ini'); } } else { // 移除.user.ini文件的i权限 @@ -600,8 +625,10 @@ EOL; } // 将数据入库 - Website::query()->where('name', $name)->update(['php' => $config['php']]); - Website::query()->where('name', $name)->update(['ssl' => $config['ssl']]); + $website->php = $config['php']; + $website->ssl = $config['ssl']; + $website->path = $config['path']; + $website->save(); file_put_contents('/www/server/vhost/'.$name.'.conf', $configRaw); file_put_contents('/www/server/vhost/rewrite/'.$name.'.conf', $config['rewrite']); shell_exec('systemctl reload nginx'); @@ -637,7 +664,376 @@ EOL; return response()->json($res); } - // 裁剪字符串 + /** + * 获取备份列表 + */ + public function getBackupList(): JsonResponse + { + $backupPath = '/www/backup/website'; + // 判断备份目录是否存在 + if (!is_dir($backupPath)) { + mkdir($backupPath, 0644, true); + } + $backupFiles = scandir($backupPath); + $backupFiles = array_diff($backupFiles, ['.', '..']); + $backupFiles = array_values($backupFiles); + $backupFiles = array_map(function ($backupFile) { + return [ + 'backup' => $backupFile, + 'size' => formatBytes(filesize('/www/backup/website/'.$backupFile)), + ]; + }, $backupFiles); + $res['code'] = 0; + $res['msg'] = 'success'; + $res['data'] = $backupFiles; + return response()->json($res); + } + + /** + * 创建备份 + */ + public function createBackup(Request $request): JsonResponse + { + // 消毒数据 + try { + $credentials = $this->validate($request, [ + 'name' => 'required|max:255', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $backupPath = '/www/backup/website'; + // 判断备份目录是否存在 + if (!is_dir($backupPath)) { + mkdir($backupPath, 0644, true); + } + + // 从数据库中获取网站目录 + $sitePath = Website::query()->where('name', $credentials['name'])->value('path'); + $backupFile = $backupPath.'/'.$credentials['name'].'_'.date('YmdHis').'.zip'; + shell_exec('zip -r '.$backupFile.' '.$sitePath.' 2>&1'); + + $res['code'] = 0; + $res['msg'] = 'success'; + return response()->json($res); + } + + /** + * 上传备份 + */ + public function uploadBackup(Request $request): JsonResponse + { + // 消毒数据 + try { + $credentials = $this->validate($request, [ + 'file' => 'required|file', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $file = $request->file('file'); + $backupPath = '/www/backup/website'; + + // 判断备份目录是否存在 + if (!is_dir($backupPath)) { + mkdir($backupPath, 0644, true); + } + $backupFile = $backupPath.'/'.$file->getClientOriginalName(); + $file->move($backupPath, $file->getClientOriginalName()); + + // 返回文件名 + $res['code'] = 0; + $res['msg'] = 'success'; + $res['data'] = $file->getClientOriginalName(); + return response()->json($res); + } + + /** + * 恢复备份 + */ + public function restoreBackup(Request $request): JsonResponse + { + // 消毒数据 + try { + $credentials = $this->validate($request, [ + 'name' => 'required|max:255', + 'backup' => 'required|max:255', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $backupPath = '/www/backup/website'; + // 判断备份目录是否存在 + if (!is_dir($backupPath)) { + mkdir($backupPath, 0644, true); + } + $backupFile = $backupPath.'/'.$credentials['backup']; + // 判断备份文件是否存在 + if (!is_file($backupFile)) { + return response()->json([ + 'code' => 1, + 'msg' => '备份文件不存在', + ], 200); + } + + shell_exec('rm -rf /www/wwwroot/'.$credentials['name'].'/*'); + shell_exec('unzip -o '.$backupFile.' -d /www/wwwroot/'.$credentials['name'].' 2>&1'); + // 设置权限 + shell_exec('chown -R www:www /www/wwwroot/'.$credentials['name']); + shell_exec('chmod -R 755 /www/wwwroot/'.$credentials['name']); + + $res['code'] = 0; + $res['msg'] = 'success'; + return response()->json($res); + } + + /** + * 删除备份 + */ + public function deleteBackup(Request $request): JsonResponse + { + // 消毒数据 + try { + $credentials = $this->validate($request, [ + 'backup' => 'required|max:255', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $backupPath = '/www/backup/website'; + // 判断备份目录是否存在 + if (!is_dir($backupPath)) { + mkdir($backupPath, 0644, true); + } + $backupFile = $backupPath.'/'.$credentials['backup']; + // 判断备份文件是否存在 + if (!is_file($backupFile)) { + return response()->json([ + 'code' => 1, + 'msg' => '备份文件不存在', + ], 200); + } + + unlink($backupFile); + $res['code'] = 0; + $res['msg'] = 'success'; + return response()->json($res); + } + + /** + * 重置网站配置文件 + */ + public function resetSiteConfig(Request $request): JsonResponse + { + try { + $credentials = $this->validate($request, [ + 'name' => 'required|max:255', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $website = Website::query()->where('name', $credentials['name'])->first(); + if (!$website) { + return response()->json([ + 'code' => 1, + 'msg' => '网站不存在', + ], 200); + } + + // 如果PHP是0,将其设置为字符串的00 + if ($website['php'] == '0') { + $website['php'] = '00'; + } + + // 更新网站状态为运行 + $website->status = 1; + // 更新网站ssl状态为关闭 + $website->ssl = 0; + $website->save(); + + $nginxConfig = << /www/server/vhost/rewrite/'.$website['name'].'.conf'); + // 重载nginx + shell_exec('systemctl reload nginx'); + + // 返回 + $res['code'] = 0; + $res['msg'] = 'success'; + return response()->json($res); + } + + /** + * 设置网站运行状态 + */ + public function setSiteStatus(Request $request): JsonResponse + { + try { + $credentials = $this->validate($request, [ + 'name' => 'required|max:255', + 'status' => 'required|in:0,1', + ]); + } catch (ValidationException $e) { + return response()->json([ + 'code' => 1, + 'msg' => '参数错误:'.$e->getMessage(), + 'errors' => $e->errors() + ], 200); + } + + $website = Website::query()->where('name', $credentials['name'])->first(); + if (!$website) { + return response()->json([ + 'code' => 1, + 'msg' => '网站不存在', + ], 200); + } + + $nginxConfig = file_get_contents('/www/server/vhost/'.$website['name'].'.conf'); + + // 运行目录 + $pathConfig = $this->cut('# root标记位开始', '# root标记位结束', $nginxConfig); + preg_match_all('/root\s+(.+);/', $pathConfig, $matches1); + $pathConfigOld = $matches1[1][0]; + if (!empty(trim($pathConfigOld)) && $pathConfigOld != PHP_EOL) { + if ($credentials['status'] == 0) { + $pathConfigNew = str_replace($pathConfigOld, '/www/server/nginx/html', $pathConfig); + // 将旧配置追加到新配置中 + $pathConfigNew .= '# '.$pathConfigOld.PHP_EOL.' '; + } else { + // 匹配旧配置 + preg_match_all('/# (.+)/', $pathConfig, $matches2); + // 还原旧配置 + $pathConfigNew = str_replace($pathConfigOld, $matches2[1][0], $pathConfig); + // 删除旧配置 + $pathConfigNew = str_replace(PHP_EOL.' # '.$matches2[1][0], '', $pathConfigNew); + } + $nginxConfig = str_replace($pathConfig, $pathConfigNew, $nginxConfig); + } + + // 默认文件 + $indexConfig = $this->cut('# index标记位开始', '# index标记位结束', $nginxConfig); + preg_match_all('/index\s+(.+);/', $indexConfig, $matches2); + $indexConfigOld = $matches2[1][0]; + if (!empty(trim($indexConfigOld)) && $indexConfigOld != PHP_EOL) { + if ($credentials['status'] == 0) { + $indexConfigNew = str_replace($indexConfigOld, 'stop.html', $indexConfig); + // 将旧配置追加到新配置中 + $indexConfigNew .= '# '.$indexConfigOld.PHP_EOL.' '; + } else { + // 匹配旧配置 + preg_match_all('/# (.+)/', $indexConfig, $matches2); + // 还原旧配置 + $indexConfigNew = str_replace($indexConfigOld, $matches2[1][0], $indexConfig); + // 删除旧配置 + $indexConfigNew = str_replace(PHP_EOL.' # '.$matches2[1][0], '', $indexConfigNew); + } + $nginxConfig = str_replace($indexConfig, $indexConfigNew, $nginxConfig); + } + + // 写入配置文件 + file_put_contents('/www/server/vhost/'.$website['name'].'.conf', $nginxConfig); + + $website->status = $credentials['status']; + $website->save(); + + // 重载nginx + shell_exec('systemctl reload nginx'); + + // 返回 + $res['code'] = 0; + $res['msg'] = 'success'; + return response()->json($res); + } + + /** + * 裁剪字符串 + * @param $begin + * @param $end + * @param $str + * @return string + */ private function cut($begin, $end, $str): string { $b = mb_strpos($str, $begin) + mb_strlen($begin); diff --git a/resources/views/cron.blade.php b/resources/views/cron.blade.php index dd47067f..cfcbc864 100644 --- a/resources/views/cron.blade.php +++ b/resources/views/cron.blade.php @@ -88,14 +88,14 @@ , url: '/api/panel/cron/getList' , cols: [[ {field: 'id', hide: true, title: 'ID', sort: true} - , {field: 'name', width: '15%', title: '任务名'} - , {field: 'type', width: '10%', title: '任务类型'} + , {field: 'name', width: 150, title: '任务名'} + , {field: 'type', width: 150, title: '任务类型'} , {field: 'status', title: '启用', width: 100, templet: '#cron-table-status', unresize: true} - , {field: 'time', width: '25%', title: '任务周期(cron表达式)'} + , {field: 'time', width: 200, title: '任务周期(cron表达式)'} , {field: 'updated_at', title: '上次运行时间'} , { field: 'edit', - width: '15%', + width: 160, title: '操作', templet: '#cron-table-edit', fixed: 'right', diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 38628a53..c455023d 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -272,8 +272,5 @@ Date: 2022-11-30 }); }); }); - /** - * TODO: 为主页安排一个应用展示 - */ diff --git a/resources/views/plugin.blade.php b/resources/views/plugin.blade.php index d9841d51..3c139ebe 100644 --- a/resources/views/plugin.blade.php +++ b/resources/views/plugin.blade.php @@ -2,42 +2,29 @@
- +
+ 按钮点击一次即可,请勿重复点击以免重复操作,任务中心在右上角! +
+ + + + + diff --git a/resources/views/website/default_settings.blade.php b/resources/views/website/default_settings.blade.php index 0d86cedb..bbb1492d 100644 --- a/resources/views/website/default_settings.blade.php +++ b/resources/views/website/default_settings.blade.php @@ -1,9 +1,10 @@ - - @@ -77,13 +79,13 @@ Date: 2022-11-28 , toolbar: '#website-list-bar' , title: '网站列表' , cols: [[ - {field: 'name', title: '网站名', width: 200, fixed: 'left', unresize: true, sort: true} + {field: 'name', title: '网站名', width: 150, unresize: true, sort: true} , {field: 'run', title: '运行', width: 100, templet: '#website-run', unresize: true} , {field: 'path', title: '目录', width: 250} , {field: 'php', title: 'PHP', width: 60} , {field: 'ssl', title: 'SSL', width: 110, templet: '#website-ssl'} , {field: 'note', title: '备注', edit: 'textarea'} - , {fixed: 'right', title: '操作', toolbar: '#website-setting', width: 150} + , {fixed: 'right', title: '操作', toolbar: '#website-control', width: 160} ]] /** * TODO: 分页 @@ -96,7 +98,7 @@ Date: 2022-11-28 if (obj.event === 'website_add') { admin.popup({ title: '添加网站' - , area: ['70%', '60%'] + , area: ['80%', '80%'] , id: 'LAY-popup-website-add' , success: function (layer, index) { view(this.id).render('website/add', { @@ -110,11 +112,10 @@ Date: 2022-11-28 } else if (obj.event === 'website_default_settings') { admin.popup({ title: '全局设置' - , area: ['70%', '60%'] + , area: ['80%', '80%'] , id: 'LAY-popup-website-add' , success: function (layer, index) { - view(this.id).render('website/default_settings', { - }).done(function () { + view(this.id).render('website/default_settings', {}).done(function () { form.render(null, 'LAY-popup-website-default-settings'); }); } @@ -166,7 +167,7 @@ Date: 2022-11-28 // 打开编辑网站页面 admin.popup({ title: '编辑网站 - ' + data.name - , area: ['70%', '80%'] + , area: ['80%', '80%'] , id: 'LAY-popup-website-edit' , success: function (layero, index) { view(this.id).render('website/edit', { @@ -184,12 +185,26 @@ Date: 2022-11-28 console.log('耗子Linux面板:ajax请求出错,错误' + error); } }); + } else if (obj.event === 'backup') { + // 打开备份页面 + admin.popup({ + title: '备份管理 - ' + data.name + , area: ['70%', '80%'] + , id: 'LAY-popup-website-backup' + , success: function (layero, index) { + view(this.id).render('website/backup', { + data: data + }).done(function () { + form.render(null, 'LAY-popup-website-backup'); + }); + } + }); } }); // 网站备注编辑 table.on('edit(website-list)', function (obj) { - var value = obj.value // 得到修改后的值 + let value = obj.value // 得到修改后的值 , data = obj.data; // 得到行数据 admin.req({ url: "/api/panel/website/updateSiteNote" @@ -216,10 +231,27 @@ Date: 2022-11-28 form.on('switch(website-run-checkbox)', function (obj) { let $ = layui.$; let website_name = $(this).data('website-name'); - let run = obj.elem.checked ? 1 : 0; + let status = obj.elem.checked ? 1 : 0; - //console.log(website_name); //当前行数据 - layer.msg('待开发功能!', {icon: 2}); + admin.req({ + url: "/api/panel/website/setSiteStatus" + , method: 'post' + , data: { + name: website_name, + status: status + } + , success: function (result) { + if (result.code !== 0) { + console.log('耗子Linux面板:网站运行状态设置失败,接口返回' + result); + layer.msg('网站运行状态设置失败,请刷新重试!') + return false; + } + layer.alert('网站 ' + website_name + ' 运行状态设置成功!'); + } + , error: function (xhr, status, error) { + console.log('耗子Linux面板:ajax请求出错,错误' + error); + } + }); }); }); diff --git a/routes/api.php b/routes/api.php index 53f40d88..a2f2fbac 100644 --- a/routes/api.php +++ b/routes/api.php @@ -75,6 +75,15 @@ Route::prefix('panel')->group(function () { Route::post('saveSiteSettings', [WebsitesController::class, 'saveSiteSettings']); Route::post('clearSiteLog', [WebsitesController::class, 'clearSiteLog']); Route::post('updateSiteNote', [WebsitesController::class, 'updateSiteNote']); + Route::post('setSiteStatus', [WebsitesController::class, 'setSiteStatus']); + // 获取备份列表 + Route::get('getBackupList', [WebsitesController::class, 'getBackupList']); + Route::post('createBackup', [WebsitesController::class, 'createBackup']); + Route::post('uploadBackup', [WebsitesController::class, 'uploadBackup']); + Route::post('restoreBackup', [WebsitesController::class, 'restoreBackup']); + Route::post('deleteBackup', [WebsitesController::class, 'deleteBackup']); + // 重置网站配置 + Route::post('resetSiteConfig', [WebsitesController::class, 'resetSiteConfig']); }); // 监控 Route::middleware('auth:sanctum')->prefix('monitor')->group(function () { diff --git a/routes/web.php b/routes/web.php index 7fc8b227..f4cdd225 100644 --- a/routes/web.php +++ b/routes/web.php @@ -36,6 +36,8 @@ Route::prefix('panel/views')->group(function () { Route::view('add', 'website.add'); // 编辑 Route::view('edit', 'website.edit'); + // 备份 + Route::view('backup', 'website.backup'); }); // 监控 Route::view('monitor', 'monitor');