2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-07 07:03:12 +08:00

特性(网站):新增备份功能及问题修复

This commit is contained in:
耗子
2022-12-02 23:55:45 +08:00
parent b4b43cb4c4
commit 642d3339fc
18 changed files with 802 additions and 92 deletions

View File

@@ -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'] = '设置成功';

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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 = <<<EOF
# 配置文件中的标记位请勿随意修改,改错将导致面板无法识别!
# 有自定义配置需求的,请将自定义的配置写在各标记位下方。
server
{
# port标记位开始
listen 80;
# port标记位结束
# server_name标记位开始
server_name localhost;
# server_name标记位结束
# index标记位开始
index index.php index.html;
# index标记位结束
# root标记位开始
root $website[path];
# root标记位结束
# ssl标记位开始
# ssl标记位结束
# php标记位开始
include enable-php-$website[php].conf;
# php标记位结束
# waf标记位开始
waf on;
waf_rule_path /www/server/nginx/ngx_waf/assets/rules/;
waf_mode DYNAMIC;
waf_cc_deny rate=1000r/m duration=60m;
waf_cache capacity=50;
# waf标记位结束
# 错误页配置,可自行设置
#error_page 404 /404.html;
#error_page 502 /502.html;
# 伪静态规则引入,修改后将导致面板设置的伪静态规则失效
include /www/server/vhost/rewrite/$website[name].conf;
# 面板默认禁止访问部分敏感目录,可自行修改
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn)
{
return 404;
}
# 面板默认不记录静态资源的访问日志并开启1小时浏览器缓存可自行修改
location ~ .*\.(js|css)$
{
expires 1h;
error_log /dev/null;
access_log /dev/null;
}
access_log /www/wwwlogs/$website[name].log;
error_log /www/wwwlogs/$website[name].log;
}
EOF;
file_put_contents('/www/server/vhost/'.$website['name'].'.conf', $nginxConfig);
// 重置伪静态规则
shell_exec('echo "" > /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);