From 2b51a2d0153ab0f174deceaa579b584cf15bb8ae Mon Sep 17 00:00:00 2001 From: net909 Date: Wed, 3 Sep 2025 19:48:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8D=97=E5=A2=99WAF?= =?UTF-8?q?=E3=80=81=E5=B0=8F=E7=9A=AE=E9=9D=A2=E6=9D=BF=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/DeployHelper.php | 94 ++++++++++++++++++++++++++++++ app/lib/deploy/uusec.php | 103 ++++++++++++++++++++++++++++++++ app/lib/deploy/xp.php | 113 ++++++++++++++++++++++++++++++++++++ public/static/images/xp.png | Bin 0 -> 6219 bytes 4 files changed, 310 insertions(+) create mode 100644 app/lib/deploy/uusec.php create mode 100644 app/lib/deploy/xp.php create mode 100644 public/static/images/xp.png diff --git a/app/lib/DeployHelper.php b/app/lib/DeployHelper.php index b06a550..2e5c804 100644 --- a/app/lib/DeployHelper.php +++ b/app/lib/DeployHelper.php @@ -489,6 +489,59 @@ class DeployHelper ], 'taskinputs' => [], ], + 'uusec' => [ + 'name' => '南墙WAF', + 'class' => 1, + 'icon' => 'waf.png', + 'desc' => '', + 'note' => null, + 'inputs' => [ + 'url' => [ + 'name' => '控制台地址', + 'type' => 'input', + 'placeholder' => '南墙WAF控制台地址', + 'note' => '填写规则如:http://192.168.1.100:4443 ,不要带其他后缀', + 'required' => true, + ], + 'username' => [ + 'name' => '用户名', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + ], + 'password' => [ + 'name' => '密码', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + ], + 'proxy' => [ + 'name' => '使用代理服务器', + 'type' => 'radio', + 'options' => [ + '0' => '否', + '1' => '是', + ], + 'value' => '0' + ], + ], + 'taskinputs' => [ + 'id' => [ + 'name' => '证书ID', + 'type' => 'input', + 'placeholder' => '', + 'note' => '在证书管理查看证书的ID,注意域名是否与证书匹配', + 'required' => true, + ], + 'name' => [ + 'name' => '证书名称', + 'type' => 'input', + 'placeholder' => '', + 'note' => '在证书管理查看证书的名称', + 'required' => true, + ], + ], + ], 'opanel' => [ 'name' => '1Panel', 'class' => 1, @@ -649,6 +702,47 @@ class DeployHelper ], ], ], + 'xp' => [ + 'name' => '小皮面板', + 'class' => 1, + 'icon' => 'xp.png', + 'desc' => '', + 'note' => null, + 'tasknote' => '', + 'inputs' => [ + 'url' => [ + 'name' => '面板地址', + 'type' => 'input', + 'placeholder' => '小皮面板地址', + 'note' => '填写规则如:http://192.168.1.100:8888 ,不要带其他后缀', + 'required' => true, + ], + 'apikey' => [ + 'name' => '接口密钥', + 'type' => 'input', + 'placeholder' => '设置->OpenAPI接口', + 'required' => true, + ], + 'proxy' => [ + 'name' => '使用代理服务器', + 'type' => 'radio', + 'options' => [ + '0' => '否', + '1' => '是', + ], + 'value' => '0' + ], + ], + 'taskinputs' => [ + 'sites' => [ + 'name' => '网站名称列表', + 'type' => 'textarea', + 'placeholder' => '填写要部署证书的网站名称,每行一个', + 'note' => '网站名称,即为网站创建时绑定的第一个域名', + 'required' => true, + ], + ], + ], 'synology' => [ 'name' => '群晖面板', 'class' => 1, diff --git a/app/lib/deploy/uusec.php b/app/lib/deploy/uusec.php new file mode 100644 index 0000000..f578643 --- /dev/null +++ b/app/lib/deploy/uusec.php @@ -0,0 +1,103 @@ +url = rtrim($config['url'], '/'); + $this->username = $config['username']; + $this->password = $config['password']; + $this->proxy = $config['proxy'] == 1; + } + + public function check() + { + if (empty($this->url) || empty($this->password) || empty($this->password)) throw new Exception('用户名和密码不能为空'); + $this->login(); + } + + public function deploy($fullchain, $privatekey, $config, &$info) + { + $id = $config['id']; + if (empty($id)) throw new Exception('证书ID不能为空'); + + $this->login(); + + $params = [ + 'id' => intval($id), + 'type' => 1, + 'name' => $config['name'], + 'crt' => $fullchain, + 'key' => $privatekey, + ]; + $result = $this->request('/api/v1/certs', $params, 'PUT'); + if (is_string($result) && $result == 'OK') { + $this->log('证书ID:' . $id . '更新成功!'); + } else { + throw new Exception('证书ID:' . $id . '更新失败,' . (isset($result['err']) ? $result['err'] : '未知错误')); + } + } + + private function login() + { + $path = '/api/v1/users/login'; + $params = [ + 'usr' => $this->username, + 'pwd' => $this->password, + 'otp' => '', + ]; + $result = $this->request($path, $params); + if (isset($result['token'])) { + $this->accessToken = $result['token']; + } else { + throw new Exception('登录失败,' . (isset($result['err']) ? $result['err'] : '未知错误')); + } + } + + private function request($path, $params = null, $method = null) + { + $url = $this->url . $path; + $headers = []; + $body = null; + if ($this->accessToken) { + $headers['Authorization'] = 'Bearer ' . $this->accessToken; + } + if ($params) { + $headers['Content-Type'] = 'application/json;charset=UTF-8'; + $body = json_encode($params); + } + $response = http_request($url, $body, null, null, $headers, $this->proxy, $method); + $result = json_decode($response['body'], true); + if ($response['code'] == 200) { + return $result; + } elseif (isset($result['message'])) { + throw new Exception($result['message']); + } else { + throw new Exception('请求失败,HTTP状态码:' . $response['code']); + } + } + + public function setLogger($func) + { + $this->logger = $func; + } + + private function log($txt) + { + if ($this->logger) { + call_user_func($this->logger, $txt); + } + } +} diff --git a/app/lib/deploy/xp.php b/app/lib/deploy/xp.php new file mode 100644 index 0000000..6d27201 --- /dev/null +++ b/app/lib/deploy/xp.php @@ -0,0 +1,113 @@ +url = rtrim($config['url'], '/'); + $this->apikey = $config['apikey']; + $this->proxy = $config['proxy'] == 1; + } + + public function check() + { + if (empty($this->url) || empty($this->apikey)) throw new Exception('请填写面板地址和接口密钥'); + + $path = '/openApi/siteList'; + $response = $this->request($path); + $result = json_decode($response, true); + if (isset($result['code']) && $result['code'] == 1000) { + return true; + } else { + throw new Exception(isset($result['message']) ? $result['message'] : '面板地址无法连接'); + } + } + + public function deploy($fullchain, $privatekey, $config, &$info) + { + $path = '/openApi/siteList'; + $response = $this->request($path); + $result = json_decode($response, true); + if (isset($result['code']) && $result['code'] == 1000) { + + $sites = explode("\n", $config['sites']); + $sites = array_map('trim', $sites); + $success = 0; + $errmsg = null; + + foreach ($result['data'] as $item) { + if (!in_array($item['name'], $sites)) { + continue; + } + try { + $this->deploySite($item['id'], $fullchain, $privatekey); + $this->log("网站 {$item['name']} 证书部署成功"); + $success++; + } catch (Exception $e) { + $errmsg = $e->getMessage(); + $this->log("网站 {$item['name']} 证书部署失败:" . $errmsg); + } + } + if ($success == 0) { + throw new Exception($errmsg ? $errmsg : '要部署的网站不存在'); + } + + } elseif (isset($result['message'])) { + throw new Exception($result['message']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } + } + + private function deploySite($id, $fullchain, $privatekey) + { + $path = '/openApi/setSSL'; + $data = [ + 'id' => $id, + 'key' => $privatekey, + 'pem' => $fullchain, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['code']) && $result['code'] == 1000) { + return true; + } elseif (isset($result['message'])) { + throw new Exception($result['message']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } + } + + public function setLogger($func) + { + $this->logger = $func; + } + + private function log($txt) + { + if ($this->logger) { + call_user_func($this->logger, $txt); + } + } + + private function request($path, $params = null) + { + $url = $this->url . $path; + + $headers = [ + 'XP-API-KEY' => $this->apikey, + ]; + $response = http_request($url, $params ? json_encode($params) : null, null, null, $headers, $this->proxy); + return $response['body']; + } +} diff --git a/public/static/images/xp.png b/public/static/images/xp.png new file mode 100644 index 0000000000000000000000000000000000000000..68947850200c32848a7c4f0527dfbec643097d00 GIT binary patch literal 6219 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 Lpe|o0YZ38(j{EIi literal 0 HcmV?d00001