diff --git a/app/controller/Schedule.php b/app/controller/Schedule.php index 9b83cd4..dace308 100644 --- a/app/controller/Schedule.php +++ b/app/controller/Schedule.php @@ -67,7 +67,7 @@ class Schedule extends BaseController 'switchdate' => input('post.switchdate', null, 'trim'), 'switchtime' => input('post.switchtime', null, 'trim'), 'value' => input('post.value', null, 'trim'), - 'line' => input('post.value', null, 'trim'), + 'line' => input('post.line', null, 'trim'), 'remark' => input('post.remark', null, 'trim'), 'recordinfo' => input('post.recordinfo', null, 'trim'), 'addtime' => time(), @@ -96,7 +96,7 @@ class Schedule extends BaseController 'switchdate' => input('post.switchdate', null, 'trim'), 'switchtime' => input('post.switchtime', null, 'trim'), 'value' => input('post.value', null, 'trim'), - 'line' => input('post.value', null, 'trim'), + 'line' => input('post.line', null, 'trim'), 'remark' => input('post.remark', null, 'trim'), 'recordinfo' => input('post.recordinfo', null, 'trim'), ]; diff --git a/app/lib/DeployHelper.php b/app/lib/DeployHelper.php index a6fff72..3291fa3 100644 --- a/app/lib/DeployHelper.php +++ b/app/lib/DeployHelper.php @@ -11,7 +11,7 @@ class DeployHelper 'name' => '宝塔面板', 'class' => 1, 'icon' => 'bt.png', - 'desc' => '支持部署到宝塔面板搭建的站点、Docker、邮局与面板本身', + 'desc' => '支持部署到宝塔面板&aaPanel搭建的站点、Docker、邮局与面板本身', 'note' => null, 'inputs' => [ 'url' => [ @@ -27,6 +27,15 @@ class DeployHelper 'placeholder' => '宝塔面板设置->面板设置->API接口', 'required' => true, ], + 'version' => [ + 'name' => '面板版本', + 'type' => 'radio', + 'options' => [ + '0' => 'Linux面板+Win经典版', + '1' => 'Win极速版', + ], + 'value' => '0' + ], 'proxy' => [ 'name' => '使用代理服务器', 'type' => 'radio', @@ -54,10 +63,20 @@ class DeployHelper 'name' => '网站名称列表', 'type' => 'textarea', 'placeholder' => '填写要部署证书的网站名称,每行一个', - 'note' => 'PHP项目和反代项目填写创建时绑定的第一个域名,Java/Node/Go等其他项目填写项目名称,邮局填写域名', + 'note' => 'PHP项目和反代项目填写创建时绑定的第一个域名,Java/Node/Go等其他项目填写项目名称,邮局和IIS站点填写绑定的域名', 'show' => 'type==0||type==2||type==3', 'required' => true, ], + 'is_iis' => [ + 'name' => '是否IIS站点', + 'type' => 'radio', + 'options' => [ + '0' => '否', + '1' => '是', + ], + 'show' => 'type==0', + 'value' => '0' + ], ], ], 'kangle' => [ @@ -360,17 +379,36 @@ class DeployHelper 'note' => '填写示例:http://demo.xxxx.cn', 'required' => true, ], + 'auth' => [ + 'name' => '认证方式', + 'type' => 'radio', + 'options' => [ + '0' => '账号密码(旧版)', + '1' => 'API访问令牌', + ], + 'value' => '0', + 'required' => true, + ], + 'api_key' => [ + 'name' => 'API访问令牌', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + 'show' => 'auth==1', + ], 'email' => [ 'name' => '邮箱地址', 'type' => 'input', 'placeholder' => '', 'required' => true, + 'show' => 'auth==0', ], 'password' => [ 'name' => '密码', 'type' => 'input', 'placeholder' => '', 'required' => true, + 'show' => 'auth==0', ], 'proxy' => [ 'name' => '使用代理服务器', diff --git a/app/lib/deploy/btpanel.php b/app/lib/deploy/btpanel.php index f7d2bb7..06a5218 100644 --- a/app/lib/deploy/btpanel.php +++ b/app/lib/deploy/btpanel.php @@ -3,6 +3,7 @@ namespace app\lib\deploy; use app\lib\DeployInterface; +use app\lib\CertHelper; use Exception; class btpanel implements DeployInterface @@ -10,12 +11,14 @@ class btpanel implements DeployInterface private $logger; private $url; private $key; + private $version; private $proxy; public function __construct($config) { $this->url = rtrim($config['url'], '/'); $this->key = $config['key']; + $this->version = isset($config['version']) ? intval($config['version']) : 0; $this->proxy = $config['proxy'] == 1; } @@ -23,13 +26,24 @@ class btpanel implements DeployInterface { if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥'); - $path = '/config?action=get_config'; - $response = $this->request($path, []); - $result = json_decode($response, true); - if (isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))) { - return true; + if ($this->version == 1) { + $path = '/config/get_config'; + $response = $this->request($path, []); + $result = json_decode($response, true); + if (isset($result['panel']['status']) && $result['panel']['status']) { + return true; + } else { + throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接'); + } } else { - throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接'); + $path = '/config?action=get_config'; + $response = $this->request($path, []); + $result = json_decode($response, true); + if (isset($result['status']) && ($result['status'] == 1 || isset($result['sites_path']))) { + return true; + } else { + throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接'); + } } } @@ -40,6 +54,40 @@ class btpanel implements DeployInterface $this->log("面板证书部署成功"); return; } + + $isIIS = $config['type'] == '0' && $this->version == 1 && isset($config['is_iis']) && $config['is_iis'] == '1'; + if ($isIIS) { + $response = $this->request('/panel/get_config', []); + $result = json_decode($response, true); + if (isset($result['paths']['soft'])) { + if ($result['config']['webserver'] != 'iis') { + throw new Exception('当前安装的Web服务器不是IIS'); + } + $panel_path = $result['paths']['soft']; + } else { + throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接'); + } + + $pfx_dir = $panel_path . '/temp/ssl/' . getMillisecond(); + $pfx_path = $pfx_dir . '/cert.pfx'; + $pfx_password = '123456'; + $pfx = CertHelper::getPfx($fullchain, $privatekey, $pfx_password); + $data = [ + ['name' => 'path', 'contents' => $pfx_dir], + ['name' => 'filename', 'contents' => 'cert.pfx'], + ['name' => 'size', 'contents' => strlen($pfx)], + ['name' => 'start', 'contents' => '0'], + ['name' => 'blob', 'filename' => 'cert.pfx', 'contents' => $pfx], + ['name' => 'force', 'contents' => 'true'], + ]; + $response = $this->request('/files/upload', $data, true); + $result = json_decode($response, true); + if (isset($result['status']) && $result['status']) { + } else { + throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接'); + } + } + $sites = explode("\n", $config['sites']); $success = 0; $errmsg = null; @@ -64,6 +112,15 @@ class btpanel implements DeployInterface $errmsg = $e->getMessage(); $this->log("邮局域名 {$siteName} 证书部署失败:" . $errmsg); } + } elseif ($isIIS) { + try { + $this->deployIISSite($siteName, $pfx_path, $pfx_password); + $this->log("域名 {$siteName} 证书部署成功"); + $success++; + } catch (Exception $e) { + $errmsg = $e->getMessage(); + $this->log("域名 {$siteName} 证书部署失败:" . $errmsg); + } } else { try { $this->deploySite($siteName, $fullchain, $privatekey); @@ -82,30 +139,113 @@ class btpanel implements DeployInterface private function deployPanel($fullchain, $privatekey) { - $path = '/config?action=SavePanelSSL'; - $data = [ - 'privateKey' => $privatekey, - 'certPem' => $fullchain, - ]; - $response = $this->request($path, $data); - $result = json_decode($response, true); - if (isset($result['status']) && $result['status']) { - return true; - } elseif (isset($result['msg'])) { - throw new Exception($result['msg']); + if ($this->version == 1) { + $path = '/config/set_panel_ssl'; + $data = [ + 'ssl_key' => $privatekey, + 'ssl_pem' => $fullchain, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['status']) && $result['status']) { + return true; + } elseif (isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } } else { - throw new Exception($response ? $response : '返回数据解析失败'); + $path = '/config?action=SavePanelSSL'; + $data = [ + 'privateKey' => $privatekey, + 'certPem' => $fullchain, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['status']) && $result['status']) { + return true; + } elseif (isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } } } private function deploySite($siteName, $fullchain, $privatekey) { - $path = '/site?action=SetSSL'; + if ($this->version == 1) { + $path = '/datalist/get_data_list'; + $data = [ + 'table' => 'sites', + 'search_type' => 'PHP', + 'search' => $siteName, + 'p' => 1, + 'limit' => 10, + 'type' => -1, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['data'])) { + if (empty($result['data'])) throw new Exception("网站 {$siteName} 不存在"); + $siteId = null; + foreach ($result['data'] as $item) { + if ($item['name'] == $siteName) { + $siteId = $item['id']; + break; + } + } + if (is_null($siteId)) throw new Exception("网站 {$siteName} 不存在"); + $path = '/site/set_site_ssl'; + $data = [ + 'siteid' => $siteId, + 'status' => 'true', + 'sslType' => '', + 'cert' => $fullchain, + 'key' => $privatekey, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['status']) && $result['status']) { + return true; + } elseif (isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } + return true; + } elseif (isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } + } else { + $path = '/site?action=SetSSL'; + $data = [ + 'type' => '0', + 'siteName' => $siteName, + 'key' => $privatekey, + 'csr' => $fullchain, + ]; + $response = $this->request($path, $data); + $result = json_decode($response, true); + if (isset($result['status']) && $result['status']) { + return true; + } elseif (isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception($response ? $response : '返回数据解析失败'); + } + } + } + + private function deployIISSite($domain, $pfx_path, $password = '123456') + { + $path = '/site/set_site_domain_ssl'; $data = [ - 'type' => '0', - 'siteName' => $siteName, - 'key' => $privatekey, - 'csr' => $fullchain, + 'domain' => $domain, + 'path' => $pfx_path, + 'password' => $password, ]; $response = $this->request($path, $data); $result = json_decode($response, true); @@ -169,17 +309,27 @@ class btpanel implements DeployInterface } } - private function request($path, $params) + private function request($path, $params, $file = false) { $url = $this->url . $path; $now_time = time(); - $post_data = [ - 'request_token' => md5($now_time . md5($this->key)), - 'request_time' => $now_time - ]; - $post_data = array_merge($post_data, $params); - $response = http_request($url, $post_data, null, null, null, $this->proxy); + $headers = []; + if ($file) { + $post_data = [ + ['name' => 'request_token', 'contents' => md5($now_time . md5($this->key))], + ['name' => 'request_time', 'contents' => $now_time], + ]; + $post_data = array_merge($post_data, $params); + $headers['Content-Type'] = 'multipart/form-data'; + } else { + $post_data = [ + 'request_token' => md5($now_time . md5($this->key)), + 'request_time' => $now_time + ]; + $post_data = array_merge($post_data, $params); + } + $response = http_request($url, $post_data, null, null, $headers, $this->proxy); return $response['body']; } } diff --git a/app/lib/deploy/lecdn.php b/app/lib/deploy/lecdn.php index 6e55940..6ec628a 100644 --- a/app/lib/deploy/lecdn.php +++ b/app/lib/deploy/lecdn.php @@ -11,6 +11,8 @@ class lecdn implements DeployInterface private $url; private $email; private $password; + private $auth; + private $apiKey; private $proxy; private $accessToken; @@ -19,13 +21,22 @@ class lecdn implements DeployInterface $this->url = rtrim($config['url'], '/'); $this->email = $config['email']; $this->password = $config['password']; + $this->auth = isset($config['auth']) ? intval($config['auth']) : 0; + if ($this->auth == 1) { + $this->apiKey = $config['api_key']; + } $this->proxy = $config['proxy'] == 1; } public function check() { - if (empty($this->url) || empty($this->email) || empty($this->password)) throw new Exception('账号和密码不能为空'); - $this->login(); + if ($this->auth == 1) { + if (empty($this->url) || empty($this->apiKey)) throw new Exception('API访问令牌不能为空'); + $this->request('/prod-api/system/info'); + } else { + if (empty($this->url) || empty($this->email) || empty($this->password)) throw new Exception('账号和密码不能为空'); + $this->login(); + } } public function deploy($fullchain, $privatekey, $config, &$info) @@ -33,7 +44,9 @@ class lecdn implements DeployInterface $id = $config['id']; if (empty($id)) throw new Exception('证书ID不能为空'); - $this->login(); + if ($this->auth == 0) { + $this->login(); + } try { $data = $this->request('/prod-api/certificate/' . $id); @@ -77,6 +90,8 @@ class lecdn implements DeployInterface $body = null; if ($this->accessToken) { $headers['Authorization'] = 'Bearer ' . $this->accessToken; + } elseif ($this->auth == 1 && $this->apiKey) { + $headers['Authorization'] = $this->apiKey; } if ($params) { $headers['Content-Type'] = 'application/json;charset=UTF-8';