diff --git a/app/lib/DeployHelper.php b/app/lib/DeployHelper.php index 6176ef4..b06a550 100644 --- a/app/lib/DeployHelper.php +++ b/app/lib/DeployHelper.php @@ -1340,8 +1340,8 @@ class DeployHelper 'name' => '百度云', 'class' => 2, 'icon' => 'baidu.ico', - 'desc' => '支持部署到百度云CDN', - 'note' => '支持部署到百度云CDN', + 'desc' => '支持部署到百度云CDN、BLB', + 'note' => '支持部署到百度云CDN、BLB', 'inputs' => [ 'AccessKeyId' => [ 'name' => 'AccessKeyId', @@ -1366,10 +1366,54 @@ class DeployHelper ], ], 'taskinputs' => [ + 'product' => [ + 'name' => '要部署的产品', + 'type' => 'select', + 'options' => [ + ['value'=>'cdn', 'label'=>'CDN'], + ['value'=>'blb', 'label'=>'普通型BLB'], + ['value'=>'appblb', 'label'=>'应用型BLB'], + ], + 'value' => 'cdn', + 'required' => true, + ], 'domain' => [ 'name' => '绑定的域名', 'type' => 'input', + 'placeholder' => '多个域名可使用,分隔', + 'show' => 'product==\'cdn\'', + 'required' => true, + ], + 'region' => [ + 'name' => '所属地域', + 'type' => 'select', + 'options' => [ + ['value'=>'bj', 'label'=>'北京'], + ['value'=>'gz', 'label'=>'广州'], + ['value'=>'su', 'label'=>'苏州'], + ['value'=>'hkg', 'label'=>'香港'], + ['value'=>'fwh', 'label'=>'武汉'], + ['value'=>'bd', 'label'=>'保定'], + ['value'=>'fsh', 'label'=>'上海'], + ['value'=>'sin', 'label'=>'新加坡'], + ], + 'value' => 'bj', + 'show' => 'product==\'blb\'||product==\'appblb\'', + 'required' => true, + ], + 'blb_id' => [ + 'name' => '负载均衡实例ID', + 'type' => 'input', 'placeholder' => '', + 'show' => 'product==\'blb\'||product==\'appblb\'', + 'required' => true, + ], + 'blb_port' => [ + 'name' => 'HTTPS监听端口', + 'type' => 'input', + 'placeholder' => '', + 'value' => '443', + 'show' => 'product==\'blb\'||product==\'appblb\'', 'required' => true, ], ], @@ -1378,8 +1422,8 @@ class DeployHelper 'name' => '火山引擎', 'class' => 2, 'icon' => 'huoshan.ico', - 'desc' => '支持部署到火山引擎CDN', - 'note' => '支持部署到火山引擎CDN', + 'desc' => '支持部署到火山引擎CDN、CLB、TOS、直播、veImageX', + 'note' => '支持部署到火山引擎CDN、CLB、TOS、直播、veImageX', 'inputs' => [ 'AccessKeyId' => [ 'name' => 'AccessKeyId', diff --git a/app/lib/deploy/baidu.php b/app/lib/deploy/baidu.php index a1423c7..7476416 100644 --- a/app/lib/deploy/baidu.php +++ b/app/lib/deploy/baidu.php @@ -29,6 +29,23 @@ class baidu implements DeployInterface } public function deploy($fullchain, $privatekey, $config, &$info) + { + if (!isset($config['product']) || $config['product'] == 'cdn') { + $this->deploy_cdn($fullchain, $privatekey, $config, $info); + } else { + $cert_id = $this->get_cert_id($fullchain, $privatekey); + $info['cert_id'] = $cert_id; + if ($config['product'] == 'blb') { + $this->deploy_blb($cert_id, $config); + } elseif ($config['product'] == 'appblb') { + $this->deploy_appblb($cert_id, $config); + } else { + throw new Exception('不支持的产品类型'); + } + } + } + + public function deploy_cdn($fullchain, $privatekey, $config, &$info) { if (empty($config['domain'])) throw new Exception('绑定的域名不能为空'); $certInfo = openssl_x509_parse($fullchain, true); @@ -36,16 +53,6 @@ class baidu implements DeployInterface $config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t']; $client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy); - try { - $data = $client->request('GET', '/v2/' . $config['domain'] . '/certificates'); - if (isset($data['certName']) && $data['certName'] == $config['cert_name']) { - $this->log('CDN域名 ' . $config['domain'] . ' 证书已存在,无需重复部署'); - return; - } - } catch (Exception $e) { - $this->log($e->getMessage()); - } - $param = [ 'httpsEnable' => 'ON', 'certificate' => [ @@ -54,9 +61,89 @@ class baidu implements DeployInterface 'certPrivateData' => $privatekey, ], ]; - $data = $client->request('PUT', '/v2/' . $config['domain'] . '/certificates', null, $param); - $info['cert_id'] = $data['certId']; - $this->log('CDN域名 ' . $config['domain'] . ' 证书部署成功!'); + foreach (explode(',', $config['domain']) as $domain) { + if (empty($domain)) continue; + try { + $data = $client->request('GET', '/v2/' . $domain . '/certificates'); + if (isset($data['certName']) && $data['certName'] == $config['cert_name']) { + $this->log('CDN域名 ' . $domain . ' 证书已存在,无需重复部署'); + return; + } + } catch (Exception $e) { + $this->log($e->getMessage()); + } + + $data = $client->request('PUT', '/v2/' . $domain . '/certificates', null, $param); + $info['cert_id'] = $data['certId']; + $this->log('CDN域名 ' . $domain . ' 证书部署成功!'); + } + } + + public function deploy_blb($cert_id, $config) + { + if (empty($config['blb_id'])) throw new Exception('负载均衡实例ID不能为空'); + if (empty($config['blb_port'])) throw new Exception('HTTPS监听端口不能为空'); + $client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'blb.' . $config['region'] . '.baidubce.com', $this->proxy); + $query = [ + 'listenerPort' => $config['blb_port'], + ]; + $param = [ + 'certIds' => [$cert_id], + ]; + $client->request('PUT', '/v1/blb/' . $config['blb_id'] . '/HTTPSlistener', $query, $param); + $this->log('普通型BLB ' . $config['blb_id'] . ' 部署证书成功!'); + } + + public function deploy_appblb($cert_id, $config) + { + if (empty($config['blb_id'])) throw new Exception('负载均衡实例ID不能为空'); + if (empty($config['blb_port'])) throw new Exception('HTTPS监听端口不能为空'); + $client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'blb.' . $config['region'] . '.baidubce.com', $this->proxy); + $query = [ + 'listenerPort' => $config['blb_port'], + ]; + $param = [ + 'certIds' => [$cert_id], + ]; + $client->request('PUT', '/v1/appblb/' . $config['blb_id'] . '/HTTPSlistener', $query, $param); + $this->log('应用型BLB ' . $config['blb_id'] . ' 部署证书成功!'); + } + + private function get_cert_id($fullchain, $privatekey) + { + $certInfo = openssl_x509_parse($fullchain, true); + if (!$certInfo) throw new Exception('证书解析失败'); + $cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t']; + + $client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'certificate.baidubce.com', $this->proxy); + $query = [ + 'certName' => $cert_name, + ]; + try { + $data = $client->request('GET', '/v1/certificate', $query); + } catch (Exception $e) { + throw new Exception('查找证书失败:' . $e->getMessage()); + } + foreach ($data['certs'] as $row) { + if ($row['certName'] == $cert_name) { + $this->log('证书已存在 CertId=' . $row['certId']); + return $row['certId']; + } + } + + $param = [ + 'certName' => $cert_name, + 'certServerData' => $fullchain, + 'certPrivateData' => $privatekey, + ]; + try { + $data = $client->request('POST', '/v1/certificate', null, $param); + } catch (Exception $e) { + throw new Exception('上传证书失败:' . $e->getMessage()); + } + $cert_id = $data['certId']; + $this->log('上传证书成功 CertId=' . $cert_id); + return $cert_id; } public function setLogger($func) diff --git a/app/lib/deploy/doge.php b/app/lib/deploy/doge.php index 06e2d76..0646519 100644 --- a/app/lib/deploy/doge.php +++ b/app/lib/deploy/doge.php @@ -38,6 +38,7 @@ class doge implements DeployInterface $cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name); foreach (explode(',', $domains) as $domain) { + if (empty($domain)) continue; $param = [ 'id' => $cert_id, 'domain' => $domain, diff --git a/app/lib/deploy/huawei.php b/app/lib/deploy/huawei.php index b562177..3a38523 100644 --- a/app/lib/deploy/huawei.php +++ b/app/lib/deploy/huawei.php @@ -59,6 +59,7 @@ class huawei implements DeployInterface ], ]; foreach (explode(',', $config['domain']) as $domain) { + if (empty($domain)) continue; $client->request('PUT', '/v1.1/cdn/configuration/domains/' . $domain . '/configs', null, $param); $this->log('CDN域名 ' . $domain . ' 部署证书成功!'); } diff --git a/app/lib/deploy/huoshan.php b/app/lib/deploy/huoshan.php index 107e64a..dd8df10 100644 --- a/app/lib/deploy/huoshan.php +++ b/app/lib/deploy/huoshan.php @@ -89,6 +89,7 @@ class huoshan implements DeployInterface if (empty($config['domain'])) throw new Exception('绑定的域名不能为空'); $client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $config['bucket_domain'], 'tos', '2021-04-01', 'cn-beijing', $this->proxy); foreach (explode(',', $config['domain']) as $domain) { + if (empty($domain)) continue; $param = [ 'CustomDomainRule' => [ 'Domain' => $domain, @@ -122,6 +123,7 @@ class huoshan implements DeployInterface $this->log('上传证书成功 ChainID=' . $result['ChainID']); foreach (explode(',', $config['domain']) as $domain) { + if (empty($domain)) continue; $param = [ 'ChainID' => $result['ChainID'], 'Domain' => $domain, @@ -138,6 +140,7 @@ class huoshan implements DeployInterface if (empty($config['domain'])) throw new Exception('绑定的域名不能为空'); $client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'imagex.volcengineapi.com', 'imagex', '2018-08-01', 'cn-north-1', $this->proxy); foreach (explode(',', $config['domain']) as $domain) { + if (empty($domain)) continue; $param = [ [ 'domain' => $domain, diff --git a/app/lib/deploy/qiniu.php b/app/lib/deploy/qiniu.php index 4b29b62..79af719 100644 --- a/app/lib/deploy/qiniu.php +++ b/app/lib/deploy/qiniu.php @@ -39,6 +39,7 @@ class qiniu implements DeployInterface $cert_id = $this->get_cert_id($fullchain, $privatekey, $certInfo['subject']['CN'], $cert_name); foreach (explode(',', $domains) as $domain) { + if (empty($domain)) continue; if ($config['product'] == 'cdn') { $this->deploy_cdn($domain, $cert_id); } elseif ($config['product'] == 'oss') { diff --git a/app/lib/deploy/unicloud.php b/app/lib/deploy/unicloud.php index cd663e1..afb93d2 100644 --- a/app/lib/deploy/unicloud.php +++ b/app/lib/deploy/unicloud.php @@ -35,6 +35,7 @@ class unicloud implements DeployInterface $url = 'https://unicloud-api.dcloud.net.cn/unicloud/api/host/create-domain-with-cert'; foreach (explode(',', $config['domains']) as $domain) { + if (empty($domain)) continue; $params = [ 'appid' => '', 'provider' => $config['provider'], diff --git a/app/service/CertDeployService.php b/app/service/CertDeployService.php index 3140641..047b841 100644 --- a/app/service/CertDeployService.php +++ b/app/service/CertDeployService.php @@ -29,7 +29,7 @@ class CertDeployService $this->client = DeployHelper::getModel($this->aid); if (!$this->client) throw new Exception('该自动部署任务类型不存在', 102); - $this->info = $task['info'] ? json_decode($task['info'], true) : null; + $this->info = $task['info'] ? json_decode($task['info'], true) : []; } public function process($isManual = false) diff --git a/composer.lock b/composer.lock index 41c91bb..747855a 100644 --- a/composer.lock +++ b/composer.lock @@ -120,22 +120,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -226,7 +226,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -242,20 +242,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -263,7 +263,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -309,7 +309,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -325,20 +325,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -354,7 +354,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -425,7 +425,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -441,7 +441,7 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "phpmailer/phpmailer", @@ -1527,16 +1527,16 @@ }, { "name": "topthink/think-orm", - "version": "v4.0.49", + "version": "v4.0.50", "source": { "type": "git", "url": "https://github.com/top-think/think-orm.git", - "reference": "b04d227fefc7dbfd611f4b5e66975eee4791bba4" + "reference": "ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/top-think/think-orm/zipball/b04d227fefc7dbfd611f4b5e66975eee4791bba4", - "reference": "b04d227fefc7dbfd611f4b5e66975eee4791bba4", + "url": "https://api.github.com/repos/top-think/think-orm/zipball/ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc", + "reference": "ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc", "shasum": "" }, "require": { @@ -1581,9 +1581,9 @@ ], "support": { "issues": "https://github.com/top-think/think-orm/issues", - "source": "https://github.com/top-think/think-orm/tree/v4.0.49" + "source": "https://github.com/top-think/think-orm/tree/v4.0.50" }, - "time": "2025-08-10T14:08:31+00:00" + "time": "2025-08-26T05:32:22+00:00" }, { "name": "topthink/think-template",