新增部署到S3存储

This commit is contained in:
net909
2026-04-11 21:13:52 +08:00
parent bebd655bcc
commit 96e1c8a972
8 changed files with 599 additions and 279 deletions

View File

@@ -1342,7 +1342,7 @@ ctrl+x 保存退出<br/>',
'domain' => [
'name' => '绑定的域名',
'type' => 'input',
'placeholder' => '',
'placeholder' => '多个域名可用,隔开',
'show' => 'product!=\'esa\'&&product!=\'esa_saas\'&&product!=\'clb\'&&product!=\'alb\'&&product!=\'nlb\'&&product!=\'ga\'&&product!=\'upload\'',
'required' => true,
],
@@ -2160,7 +2160,7 @@ ctrl+x 保存退出<br/>',
'domain' => [
'name' => '绑定的域名',
'type' => 'input',
'placeholder' => '',
'placeholder' => '多个域名可使用,分隔',
'required' => true,
],
],
@@ -2657,6 +2657,73 @@ ctrl+x 保存退出<br/>',
],
],
],
's3storage' => [
'name' => 'S3存储',
'class' => 3,
'icon' => 'cloud.png',
'desc' => '支持将证书上传到S3兼容存储AWS S3、MinIO等',
'note' => '支持AWS S3、MinIO、阿里云OSSS3兼容模式等S3协议兼容的对象存储服务',
'tasknote' => '证书和私钥将以PEM格式上传到指定的存储桶路径',
'inputs' => [
'AccessKeyId' => [
'name' => 'AccessKeyId',
'type' => 'input',
'placeholder' => '',
'required' => true,
],
'SecretAccessKey' => [
'name' => 'SecretAccessKey',
'type' => 'input',
'placeholder' => '',
'required' => true,
],
'endpoint' => [
'name' => 'S3 Endpoint',
'type' => 'input',
'placeholder' => '如s3.amazonaws.com 或 minio.example.com:9000',
'note' => 'AWS S3填写s3.区域.amazonaws.com其他S3兼容服务填写对应地址',
'required' => true,
],
'region' => [
'name' => '区域',
'type' => 'input',
'placeholder' => '如us-east-1',
'value' => 'us-east-1',
'required' => true,
],
'proxy' => [
'name' => '使用代理服务器',
'type' => 'radio',
'options' => [
'0' => '否',
'1' => '是',
],
'value' => '0'
],
],
'taskinputs' => [
'bucket' => [
'name' => '存储桶名称',
'type' => 'input',
'placeholder' => '',
'required' => true,
],
'cert_path' => [
'name' => '证书保存路径',
'type' => 'input',
'placeholder' => 'ssl/cert.pem',
'note' => '在存储桶内的文件路径,如 ssl/domain.com/cert.pem',
'required' => true,
],
'key_path' => [
'name' => '私钥保存路径',
'type' => 'input',
'placeholder' => 'ssl/key.pem',
'note' => '在存储桶内的文件路径,如 ssl/domain.com/key.pem',
'required' => true,
],
],
],
'local' => [
'name' => '复制到本机',
'class' => 3,

View File

@@ -651,7 +651,7 @@ class DnsHelper
$dnstype = $account['type'];
$class = "\\app\\lib\\dns\\{$dnstype}";
if (class_exists($class)) {
$config = json_decode($account['config'], true);
$config = json_decode($account['config'] ?? '', true);
$config['domain'] = $domain;
$config['domainid'] = $domainid;
$model = new $class($config);
@@ -668,7 +668,7 @@ class DnsHelper
$dnstype = $account['type'];
$class = "\\app\\lib\\dns\\{$dnstype}";
if (class_exists($class)) {
$config = json_decode($account['config'], true);
$config = json_decode($account['config'] ?? '', true);
$config['domain'] = $account['name'];
$config['domainid'] = $account['thirdid'];
$model = new $class($config);

View File

@@ -136,36 +136,38 @@ class aliyun implements DeployInterface
private function deploy_cdn($cert_id, $cert_name, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('CDN绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('CDN绑定域名不能为空');
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'cdn.aliyuncs.com', '2018-05-10', $this->proxy);
$param = [
'Action' => 'SetCdnDomainSSLCertificate',
'DomainName' => $domain,
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('CDN域名 ' . $domain . ' 部署证书成功!');
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'SetCdnDomainSSLCertificate',
'DomainName' => $domain,
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('CDN域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_dcdn($cert_id, $cert_name, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('DCDN绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('DCDN绑定域名不能为空');
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'dcdn.aliyuncs.com', '2018-01-15', $this->proxy);
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'SetDcdnDomainSSLCertificate',
'DomainName' => $domain,
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('DCDN域名 ' . $domain . ' 部署证书成功!');
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('DCDN域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_esa_saas($cas_id, $config)
@@ -349,14 +351,16 @@ class aliyun implements DeployInterface
if (empty($config['oss_endpoint'])) throw new Exception('OSS Endpoint不能为空');
if (empty($config['oss_bucket'])) throw new Exception('OSS Bucket不能为空');
$client = new AliyunOSS($this->AccessKeyId, $this->AccessKeySecret, $config['oss_endpoint']);
$client->addBucketCnameCert($config['oss_bucket'], $config['domain'], $cert_id . '-cn-hangzhou');
$this->log('OSS域名 ' . $config['domain'] . ' 部署证书成功!');
foreach (explode(',', $config['domain']) as $domain) {
if (empty($domain)) continue;
$client->addBucketCnameCert($config['oss_bucket'], $domain, $cert_id . '-cn-hangzhou');
$this->log('OSS域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_waf($cert_id, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('WAF绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('WAF绑定域名不能为空');
if ($config['region'] == 'ap-southeast-1') {
$cert_id .= '-ap-southeast-1';
@@ -381,62 +385,63 @@ class aliyun implements DeployInterface
$instance_id = $data['InstanceId'];
$this->log('获取WAF实例ID成功 InstanceId=' . $instance_id);
$param = [
'Action' => 'DescribeDomainDetail',
'InstanceId' => $instance_id,
'Domain' => $domain,
'RegionId' => $config['region'],
];
try {
$data = $client->request($param, 'GET');
} catch (Exception $e) {
throw new Exception('查询CNAME接入详情失败' . $e->getMessage());
}
if (!isset($data['Listen'])) {
throw new Exception('没有找到' . $domain . '监听器');
}
if (isset($data['Listen']['CertId'])) {
$old_cert_id = $data['Listen']['CertId'];
if (!empty($old_cert_id) && $old_cert_id == $cert_id) {
$this->log('WAF域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
}
$data['Listen']['CertId'] = $cert_id;
if (empty($data['Listen']['HttpsPorts'])) {
$data['Listen']['HttpsPorts'] = [443];
$data['Listen']['TLSVersion'] = 'tlsv1.1';
$data['Listen']['EnableTLSv3'] = true;
$data['Listen']['CipherSuite'] = 1;
}
if (count($data['Redirect']['BackendPorts']) == 1 && $data['Redirect']['BackendPorts'][0]['Protocol'] == 'http') {
$data['Redirect']['BackendPorts'][] = [
'ListenPort' => 443,
'Protocol' => 'https',
'BackendPort' => $data['Redirect']['BackendPorts'][0]['BackendPort'],
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'DescribeDomainDetail',
'InstanceId' => $instance_id,
'Domain' => $domain,
'RegionId' => $config['region'],
];
$data['Redirect']['FocusHttpBackend'] = true;
}
$data['Redirect']['Backends'] = $data['Redirect']['AllBackends'];
$param = [
'Action' => 'ModifyDomain',
'InstanceId' => $instance_id,
'Domain' => $domain,
'Listen' => json_encode($data['Listen']),
'Redirect' => json_encode($data['Redirect']),
'RegionId' => $config['region'],
];
$data = $client->request($param);
try {
$data = $client->request($param, 'GET');
} catch (Exception $e) {
throw new Exception('查询CNAME接入详情失败' . $e->getMessage());
}
if (!isset($data['Listen'])) {
throw new Exception('没有找到' . $domain . '监听器');
}
$this->log('WAF域名 ' . $domain . ' 部署证书成功!');
if (isset($data['Listen']['CertId'])) {
$old_cert_id = $data['Listen']['CertId'];
if (!empty($old_cert_id) && $old_cert_id == $cert_id) {
$this->log('WAF域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
}
$data['Listen']['CertId'] = $cert_id;
if (empty($data['Listen']['HttpsPorts'])) {
$data['Listen']['HttpsPorts'] = [443];
$data['Listen']['TLSVersion'] = 'tlsv1.1';
$data['Listen']['EnableTLSv3'] = true;
$data['Listen']['CipherSuite'] = 1;
}
if (count($data['Redirect']['BackendPorts']) == 1 && $data['Redirect']['BackendPorts'][0]['Protocol'] == 'http') {
$data['Redirect']['BackendPorts'][] = [
'ListenPort' => 443,
'Protocol' => 'https',
'BackendPort' => $data['Redirect']['BackendPorts'][0]['BackendPort'],
];
$data['Redirect']['FocusHttpBackend'] = true;
}
$data['Redirect']['Backends'] = $data['Redirect']['AllBackends'];
$param = [
'Action' => 'ModifyDomain',
'InstanceId' => $instance_id,
'Domain' => $domain,
'Listen' => json_encode($data['Listen']),
'Redirect' => json_encode($data['Redirect']),
'RegionId' => $config['region'],
];
$data = $client->request($param);
$this->log('WAF域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_waf2($cert_id, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('WAF绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('WAF绑定域名不能为空');
$endpoint = 'wafopenapi.' . $config['region'] . '.aliyuncs.com';
@@ -455,23 +460,24 @@ class aliyun implements DeployInterface
$instance_id = $data['InstanceInfo']['InstanceId'];
$this->log('获取WAF实例ID成功 InstanceId=' . $instance_id);
$param = [
'Action' => 'CreateCertificateByCertificateId',
'InstanceId' => $instance_id,
'Domain' => $domain,
'CertificateId' => $cert_id,
];
$client->request($param);
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'CreateCertificateByCertificateId',
'InstanceId' => $instance_id,
'Domain' => $domain,
'CertificateId' => $cert_id,
];
$client->request($param);
$this->log('WAF域名 ' . $domain . ' 部署证书成功!');
$this->log('WAF域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_api($fullchain, $privatekey, $config)
{
$domain = $config['domain'];
$groupid = $config['api_groupid'];
if (empty($groupid)) throw new Exception('API分组ID不能为空');
if (empty($domain)) throw new Exception('API分组绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('API分组绑定域名不能为空');
$certInfo = openssl_x509_parse($fullchain, true);
if (!$certInfo) throw new Exception('证书解析失败');
@@ -481,76 +487,80 @@ class aliyun implements DeployInterface
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2016-07-14', $this->proxy);
$param = [
'Action' => 'SetDomainCertificate',
'GroupId' => $groupid,
'DomainName' => $domain,
'CertificateName' => $cert_name,
'CertificateBody' => $fullchain,
'CertificatePrivateKey' => $privatekey,
];
$client->request($param);
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'SetDomainCertificate',
'GroupId' => $groupid,
'DomainName' => $domain,
'CertificateName' => $cert_name,
'CertificateBody' => $fullchain,
'CertificatePrivateKey' => $privatekey,
];
$client->request($param);
$this->log('API网关域名 ' . $domain . ' 部署证书成功!');
$this->log('API网关域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_ddoscoo($cert_id, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('绑定域名不能为空');
$endpoint = 'ddoscoo.' . $config['region'] . '.aliyuncs.com';
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2020-01-01', $this->proxy);
$param = [
'Action' => 'AssociateWebCert',
'Domain' => $domain,
'CertId' => $cert_id,
];
$client->request($param);
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'AssociateWebCert',
'Domain' => $domain,
'CertId' => $cert_id,
];
$client->request($param);
$this->log('DDoS高防域名 ' . $domain . ' 部署证书成功!');
$this->log('DDoS高防域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_live($cert_id, $cert_name, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('视频直播绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('视频直播绑定域名不能为空');
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'live.aliyuncs.com', '2016-11-01', $this->proxy);
$param = [
'Action' => 'SetLiveDomainCertificate',
'DomainName' => $domain,
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('设置视频直播域名 ' . $domain . ' 证书成功!');
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'SetLiveDomainCertificate',
'DomainName' => $domain,
'CertName' => $cert_name,
'CertType' => 'cas',
'SSLProtocol' => 'on',
'CertId' => $cert_id,
];
$client->request($param);
$this->log('设置视频直播域名 ' . $domain . ' 证书成功!');
}
}
private function deploy_vod($fullchain, $privatekey, $config)
{
$domain = $config['domain'];
if (empty($domain)) throw new Exception('视频点播绑定域名不能为空');
if (empty($config['domain'])) throw new Exception('视频点播绑定域名不能为空');
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'vod.cn-shanghai.aliyuncs.com', '2017-03-21', $this->proxy);
$param = [
'Action' => 'SetVodDomainCertificate',
'DomainName' => $domain,
'SSLProtocol' => 'on',
'SSLPub' => $fullchain,
'SSLPri' => $privatekey,
];
$client->request($param);
$this->log('视频点播域名 ' . $domain . ' 部署证书成功!');
foreach (explode(',', $config['domain']) as $domain) {
$param = [
'Action' => 'SetVodDomainCertificate',
'DomainName' => $domain,
'SSLProtocol' => 'on',
'SSLPub' => $fullchain,
'SSLPri' => $privatekey,
];
$client->request($param);
$this->log('视频点播域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_fc($fullchain, $privatekey, $config)
{
$domain = $config['domain'];
$fc_cname = $config['fc_cname'];
if (empty($domain)) throw new Exception('函数计算域名不能为空');
if (empty($config['domain'])) throw new Exception('函数计算域名不能为空');
if (empty($fc_cname)) throw new Exception('域名CNAME地址不能为空');
$certInfo = openssl_x509_parse($fullchain, true);
@@ -559,41 +569,42 @@ class aliyun implements DeployInterface
$client = new AliyunNewClient($this->AccessKeyId, $this->AccessKeySecret, $fc_cname, '2023-03-30', $this->proxy);
try {
$data = $client->request('GET', 'GetCustomDomain', '/2023-03-30/custom-domains/' . $domain);
} catch (Exception $e) {
throw new Exception('获取绑定域名信息失败:' . $e->getMessage());
foreach (explode(',', $config['domain']) as $domain) {
try {
$data = $client->request('GET', 'GetCustomDomain', '/2023-03-30/custom-domains/' . $domain);
} catch (Exception $e) {
throw new Exception('获取绑定域名信息失败:' . $e->getMessage());
}
$this->log('获取函数计算绑定域名信息成功');
if (isset($data['certConfig']['certificate']) && $data['certConfig']['certificate'] == $fullchain) {
$this->log('函数计算域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$data['certConfig']['certName'] = $cert_name;
$data['certConfig']['certificate'] = $fullchain;
$data['certConfig']['privateKey'] = $privatekey;
$param = [
'authConfig' => $data['authConfig'],
'certConfig' => $data['certConfig'],
'protocol' => $data['protocol'],
'routeConfig' => $data['routeConfig'],
'tlsConfig' => $data['tlsConfig'],
'wafConfig' => $data['wafConfig'],
];
$client->request('PUT', 'UpdateCustomDomain', '/2023-03-30/custom-domains/' . $domain, $param);
$this->log('函数计算域名 ' . $domain . ' 部署证书成功!');
}
$this->log('获取函数计算绑定域名信息成功');
if (isset($data['certConfig']['certificate']) && $data['certConfig']['certificate'] == $fullchain) {
$this->log('函数计算域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$data['certConfig']['certName'] = $cert_name;
$data['certConfig']['certificate'] = $fullchain;
$data['certConfig']['privateKey'] = $privatekey;
$param = [
'authConfig' => $data['authConfig'],
'certConfig' => $data['certConfig'],
'protocol' => $data['protocol'],
'routeConfig' => $data['routeConfig'],
'tlsConfig' => $data['tlsConfig'],
'wafConfig' => $data['wafConfig'],
];
$client->request('PUT', 'UpdateCustomDomain', '/2023-03-30/custom-domains/' . $domain, $param);
$this->log('函数计算域名 ' . $domain . ' 部署证书成功!');
}
private function deploy_fc2($fullchain, $privatekey, $config)
{
$domain = $config['domain'];
$fc_cname = $config['fc_cname'];
if (empty($domain)) throw new Exception('函数计算域名不能为空');
if (empty($config['domain'])) throw new Exception('函数计算域名不能为空');
if (empty($fc_cname)) throw new Exception('域名CNAME地址不能为空');
$certInfo = openssl_x509_parse($fullchain, true);
@@ -602,33 +613,35 @@ class aliyun implements DeployInterface
$client = new AliyunNewClient($this->AccessKeyId, $this->AccessKeySecret, $fc_cname, '2021-04-06', $this->proxy);
try {
$data = $client->request('GET', 'GetCustomDomain', '/2021-04-06/custom-domains/' . $domain);
} catch (Exception $e) {
throw new Exception('获取绑定域名信息失败:' . $e->getMessage());
foreach (explode(',', $config['domain']) as $domain) {
try {
$data = $client->request('GET', 'GetCustomDomain', '/2021-04-06/custom-domains/' . $domain);
} catch (Exception $e) {
throw new Exception('获取绑定域名信息失败:' . $e->getMessage());
}
$this->log('获取函数计算绑定域名信息成功');
if (isset($data['certConfig']['certificate']) && $data['certConfig']['certificate'] == $fullchain) {
$this->log('函数计算域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$data['certConfig']['certName'] = $cert_name;
$data['certConfig']['certificate'] = $fullchain;
$data['certConfig']['privateKey'] = $privatekey;
$param = [
'protocol' => $data['protocol'],
'routeConfig' => $data['routeConfig'],
'certConfig' => $data['certConfig'],
'tlsConfig' => $data['tlsConfig'],
'wafConfig' => $data['wafConfig'],
];
$client->request('PUT', 'UpdateCustomDomain', '/2021-04-06/custom-domains/' . $domain, $param);
$this->log('函数计算域名 ' . $domain . ' 部署证书成功!');
}
$this->log('获取函数计算绑定域名信息成功');
if (isset($data['certConfig']['certificate']) && $data['certConfig']['certificate'] == $fullchain) {
$this->log('函数计算域名 ' . $domain . ' 证书已配置,无需重复操作');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$data['certConfig']['certName'] = $cert_name;
$data['certConfig']['certificate'] = $fullchain;
$data['certConfig']['privateKey'] = $privatekey;
$param = [
'protocol' => $data['protocol'],
'routeConfig' => $data['routeConfig'],
'certConfig' => $data['certConfig'],
'tlsConfig' => $data['tlsConfig'],
'wafConfig' => $data['wafConfig'],
];
$client->request('PUT', 'UpdateCustomDomain', '/2021-04-06/custom-domains/' . $domain, $param);
$this->log('函数计算域名 ' . $domain . ' 部署证书成功!');
}
private function deploy_clb($cert_id, $cert_name, $config)

View File

@@ -63,20 +63,23 @@ class ctyun implements DeployInterface
}
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
$param = [
'domain' => $config['domain'],
'https_status' => 'on',
'cert_name' => $config['cert_name'],
];
try {
$client->request('POST', '/v1/domain/update-domain', null, $param);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
foreach (explode(',', $config['domain']) as $domain) {
if (empty($domain)) continue;
$param = [
'domain' => $domain,
'https_status' => 'on',
'cert_name' => $config['cert_name'],
];
try {
$client->request('POST', '/v1/domain/update-domain', null, $param);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
}
}
}
$this->log('CDN域名 ' . $config['domain'] . ' 部署证书成功!');
$this->log('CDN域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_icdn($fullchain, $privatekey, $config)
@@ -98,20 +101,23 @@ class ctyun implements DeployInterface
}
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
$param = [
'domain' => $config['domain'],
'https_status' => 'on',
'cert_name' => $config['cert_name'],
];
try {
$client->request('POST', '/v1/domain/update-domain', null, $param);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
foreach (explode(',', $config['domain']) as $domain) {
if (empty($domain)) continue;
$param = [
'domain' => $domain,
'https_status' => 'on',
'cert_name' => $config['cert_name'],
];
try {
$client->request('POST', '/v1/domain/update-domain', null, $param);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
}
}
}
$this->log('CDN域名 ' . $config['domain'] . ' 部署证书成功!');
$this->log('CDN域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_accessone($fullchain, $privatekey, $config)
@@ -133,81 +139,87 @@ class ctyun implements DeployInterface
}
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
$param = [
'domain' => $config['domain'],
'product_code' => '020',
];
try {
$result = $client->request('POST', '/ctapi/v1/accessone/domain/config', null, $param);
} catch (Exception $e) {
throw new Exception('查询域名配置失败:' . $e->getMessage());
}
if ($result['https_status'] == 'on' && $result['cert_name'] == $config['cert_name']) {
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 证书已部署,无需重复操作!');
return;
}
$result['https_status'] = 'on';
$result['cert_name'] = $config['cert_name'];
$exclude_keys = ['status', 'area_scope', 'cname', 'insert_date', 'status_date', 'record_status', 'record_num', 'customer_name', 'outlink_replace_filter', 'website_ipv6_access_mark', 'websocket_speed', 'dynamic_config', 'dynamic_ability'];
foreach ($result as $key => $value) {
if (in_array($key, $exclude_keys) || is_array($value) && empty($value)) {
unset($result[$key]);
foreach (explode(',', $config['domain']) as $domain) {
if (empty($domain)) continue;
$param = [
'domain' => $domain,
'product_code' => '020',
];
try {
$result = $client->request('POST', '/ctapi/v1/accessone/domain/config', null, $param);
} catch (Exception $e) {
throw new Exception('查询域名配置失败:' . $e->getMessage());
}
}
if (isset($result['origin'])) {
foreach ($result['origin'] as &$origin) {
$origin['weight'] = strval($origin['weight']);
}
}
try {
$client->request('POST', '/ctapi/v1/scdn/domain/modify_config', null, $result);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
}
}
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 部署证书成功!');
if ($result['https_status'] == 'on' && $result['cert_name'] == $config['cert_name']) {
$this->log('边缘安全加速域名 ' . $domain . ' 证书已部署,无需重复操作!');
return;
}
$result['https_status'] = 'on';
$result['cert_name'] = $config['cert_name'];
$exclude_keys = ['status', 'area_scope', 'cname', 'insert_date', 'status_date', 'record_status', 'record_num', 'customer_name', 'outlink_replace_filter', 'website_ipv6_access_mark', 'websocket_speed', 'dynamic_config', 'dynamic_ability'];
foreach ($result as $key => $value) {
if (in_array($key, $exclude_keys) || is_array($value) && empty($value)) {
unset($result[$key]);
}
}
if (isset($result['origin'])) {
foreach ($result['origin'] as &$origin) {
$origin['weight'] = strval($origin['weight']);
}
}
try {
$client->request('POST', '/ctapi/v1/scdn/domain/modify_config', null, $result);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
}
}
$this->log('边缘安全加速域名 ' . $domain . ' 部署证书成功!');
}
}
private function deploy_cf($fullchain, $privatekey, $config)
{
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'cf-global.ctapi.ctyun.cn', $this->proxy);
try {
$data = $client->request('GET', '/openapi/v1/domains/customdomains/' . $config['domain'], null, null, ['regionId' => $config['region_id']]);
} catch (Exception $e) {
throw new Exception('获取自定义域名配置失败:' . $e->getMessage());
}
if (isset($data['certConfig']['certificate']) && trim($data['certConfig']['certificate']) == trim($fullchain)) {
$this->log('函数计算域名 ' . $config['domain'] . ' 证书已部署,无需重复操作!');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$param = [
'domainName' => $config['domain'],
'description' => $data['description'],
'protocol' => $data['protocol'],
'certConfig' => [
'certName' => 'cert' . substr($config['cert_name'], strpos($config['cert_name'], '-') + 1),
'certificate' => $fullchain,
'privateKey' => $privatekey,
],
'authConfig' => $data['authConfig'],
'routeConfig' => $data['routeConfig'],
];
try {
$client->request('PUT', '/openapi/v1/domains/customdomains/' . $config['domain'], null, $param, ['regionId' => $config['region_id']]);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
foreach (explode(',', $config['domain']) as $domain) {
if (empty($domain)) continue;
try {
$data = $client->request('GET', '/openapi/v1/domains/customdomains/' . $domain, null, null, ['regionId' => $config['region_id']]);
} catch (Exception $e) {
throw new Exception('获取自定义域名配置失败:' . $e->getMessage());
}
}
$this->log('函数计算域名 ' . $config['domain'] . ' 部署证书成功!');
if (isset($data['certConfig']['certificate']) && trim($data['certConfig']['certificate']) == trim($fullchain)) {
$this->log('函数计算域名 ' . $domain . ' 证书已部署,无需重复操作!');
return;
}
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
$param = [
'domainName' => $domain,
'description' => $data['description'],
'protocol' => $data['protocol'],
'certConfig' => [
'certName' => 'cert' . substr($config['cert_name'], strpos($config['cert_name'], '-') + 1),
'certificate' => $fullchain,
'privateKey' => $privatekey,
],
'authConfig' => $data['authConfig'],
'routeConfig' => $data['routeConfig'],
];
try {
$client->request('PUT', '/openapi/v1/domains/customdomains/' . $domain, null, $param, ['regionId' => $config['region_id']]);
} catch (Exception $e) {
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
throw new Exception($e->getMessage());
}
}
$this->log('函数计算域名 ' . $domain . ' 部署证书成功!');
}
}
public function setLogger($func)

View File

@@ -0,0 +1,221 @@
<?php
namespace app\lib\deploy;
use app\lib\DeployInterface;
use Exception;
class s3storage implements DeployInterface
{
private $logger;
private $AccessKeyId;
private $SecretAccessKey;
private $endpoint;
private $region;
private $proxy;
public function __construct($config)
{
$this->AccessKeyId = $config['AccessKeyId'];
$this->SecretAccessKey = $config['SecretAccessKey'];
$this->endpoint = rtrim($config['endpoint'], '/');
$this->region = !empty($config['region']) ? $config['region'] : 'us-east-1';
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
}
public function check()
{
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey) || empty($this->endpoint)) {
throw new Exception('必填参数不能为空');
}
$this->s3Request('GET', '/', '', null);
return true;
}
public function deploy($fullchain, $privatekey, $config, &$info)
{
$bucket = $config['bucket'];
if (empty($bucket)) throw new Exception('存储桶名称不能为空');
$certPath = trim($config['cert_path'], '/');
$keyPath = trim($config['key_path'], '/');
if (empty($certPath) || empty($keyPath)) throw new Exception('证书和私钥保存路径不能为空');
$this->putObject($bucket, $certPath, $fullchain);
$this->log("证书已上传到s3://{$bucket}/{$certPath}");
$this->putObject($bucket, $keyPath, $privatekey);
$this->log("私钥已上传到s3://{$bucket}/{$keyPath}");
}
private function putObject($bucket, $key, $content)
{
$path = '/' . $bucket . '/' . $key;
$this->s3Request('PUT', $path, $content, 'application/x-pem-file');
}
private function s3Request($method, $path, $body, $contentType)
{
$time = time();
$date = gmdate("Ymd\THis\Z", $time);
$shortDate = gmdate("Ymd", $time);
$host = preg_replace('#^https?://#', '', $this->endpoint);
$scheme = (strpos($this->endpoint, 'https://') === 0) ? 'https' : 'http';
if (strpos($this->endpoint, '://') === false) {
$scheme = 'https';
}
$payloadHash = hash('sha256', $body ?? '');
$headers = [
'Host' => $host,
'X-Amz-Date' => $date,
'X-Amz-Content-Sha256' => $payloadHash,
];
if ($contentType) {
$headers['Content-Type'] = $contentType;
}
$authorization = $this->generateSign($method, $path, [], $headers, $body ?? '', $date, $shortDate);
$headers['Authorization'] = $authorization;
$url = $scheme . '://' . $host . $path;
$headerArr = [];
foreach ($headers as $k => $v) {
$headerArr[] = $k . ': ' . $v;
}
$ch = curl_init($url);
if ($this->proxy) {
curl_set_proxy($ch);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($body !== null && $body !== '') {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$errno = curl_errno($ch);
if ($errno) {
$errmsg = curl_error($ch);
curl_close($ch);
throw new Exception('Curl error: ' . $errmsg);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 200 && $httpCode < 300) {
return $response;
}
$errmsg = 'HTTP Code: ' . $httpCode;
if ($response) {
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
$xml = @simplexml_load_string($response);
if ($xml && isset($xml->Message)) {
$errmsg = (string)$xml->Message;
} elseif ($xml && isset($xml->Error->Message)) {
$errmsg = (string)$xml->Error->Message;
}
}
throw new Exception($errmsg);
}
private function generateSign($method, $path, $query, $headers, $body, $date, $shortDate)
{
$algorithm = 'AWS4-HMAC-SHA256';
$canonicalUri = $this->getCanonicalURI($path);
$canonicalQueryString = $this->getCanonicalQueryString($query);
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
$hashedPayload = hash('sha256', $body);
$canonicalRequest = $method . "\n"
. $canonicalUri . "\n"
. $canonicalQueryString . "\n"
. $canonicalHeaders . "\n"
. $signedHeaders . "\n"
. $hashedPayload;
$credentialScope = $shortDate . '/' . $this->region . '/s3/aws4_request';
$stringToSign = $algorithm . "\n"
. $date . "\n"
. $credentialScope . "\n"
. hash('sha256', $canonicalRequest);
$kDate = hash_hmac('sha256', $shortDate, 'AWS4' . $this->SecretAccessKey, true);
$kRegion = hash_hmac('sha256', $this->region, $kDate, true);
$kService = hash_hmac('sha256', 's3', $kRegion, true);
$kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
$signature = hash_hmac('sha256', $stringToSign, $kSigning);
return $algorithm . ' Credential=' . $this->AccessKeyId . '/' . $credentialScope
. ', SignedHeaders=' . $signedHeaders
. ', Signature=' . $signature;
}
private function escape($str)
{
$search = ['+', '*', '%7E'];
$replace = ['%20', '%2A', '~'];
return str_replace($search, $replace, urlencode($str));
}
private function getCanonicalURI($path)
{
if (empty($path)) return '/';
$parts = explode('/', $path);
$parts = array_map(function ($item) {
return $this->escape($item);
}, $parts);
return implode('/', $parts);
}
private function getCanonicalQueryString($parameters)
{
if (empty($parameters)) return '';
ksort($parameters);
$pairs = [];
foreach ($parameters as $key => $value) {
$pairs[] = $this->escape($key) . '=' . $this->escape($value);
}
return implode('&', $pairs);
}
private function getCanonicalHeaders($oldHeaders)
{
$headers = [];
foreach ($oldHeaders as $key => $value) {
$headers[strtolower($key)] = trim($value);
}
ksort($headers);
$canonicalHeaders = '';
$signedHeaders = '';
foreach ($headers as $key => $value) {
$canonicalHeaders .= $key . ':' . $value . "\n";
$signedHeaders .= $key . ';';
}
$signedHeaders = substr($signedHeaders, 0, -1);
return [$canonicalHeaders, $signedHeaders];
}
public function setLogger($func)
{
$this->logger = $func;
}
private function log($txt)
{
if ($this->logger) {
call_user_func($this->logger, $txt);
}
}
}

View File

@@ -26,6 +26,7 @@ class OptimizeService
$url = 'https://www.wetest.vip/api/cf2dns/get_license?license='.$key;
}
$response = get_curl($url);
if (!$response) throw new Exception('接口请求失败');
$arr = json_decode($response, true);
if (isset($arr['code']) && $arr['code'] == 200 && isset($arr['count'])) {
return $arr['count'];
@@ -60,6 +61,7 @@ class OptimizeService
'type' => $ip_type,
];
$response = get_curl($url, json_encode($params), 0, 0, 0, 0, ['Content-Type' => 'application/json; charset=UTF-8']);
if (!$response) throw new Exception('接口请求失败');
$arr = json_decode($response, true);
if (isset($arr['code']) && $arr['code'] == 200) {
return $arr['info'];
@@ -98,7 +100,7 @@ class OptimizeService
$url = 'https://raw.githubusercontent.com/xingpingcn/enhanced-FaaS-in-China/refs/heads/main/Cf.json';
}
$response = get_curl($url);
if ($response === '') {
if (!$response) {
throw new Exception('获取优选IP数据失败网络请求失败请检查网络连接或代理地址');
}
$arr = json_decode($response, true);

View File

@@ -42,12 +42,14 @@ class DnsQueryUtils
$id = array_rand(self::$doh_servers);
$url = self::$doh_servers[$id].'?name='.urlencode($domain).'&type='.$dns_type[$type];
$data = get_curl($url);
if (!$data) return false;
$arr = json_decode($data, true);
if (!$arr) {
unset(self::$doh_servers[$id]);
$id = array_rand(self::$doh_servers);
$url = self::$doh_servers[$id].'?name='.urlencode($domain).'&type='.$dns_type[$type];
$data = get_curl($url);
if (!$data) return false;
$arr = json_decode($data, true);
if (!$arr) return false;
}

View File

@@ -223,6 +223,7 @@ class MsgNotice
$url = 'https://wxpusher.zjiecode.com/api/send/message';
$post = ['appToken' => $wechat_apptoken, 'content' => $content, 'summary' => $title, 'contentType' => 3, 'uids' => [$wechat_appuid]];
$result = get_curl($url, json_encode($post), 0, 0, 0, 0, ['Content-Type' => 'application/json; charset=UTF-8']);
if (!$result) return '请求失败';
$arr = json_decode($result, true);
if (isset($arr['success']) && $arr['success'] == true) {
return true;
@@ -246,6 +247,7 @@ class MsgNotice
$url = $tgbot_url.'/bot'.$tgbot_token.'/sendMessage';
$post = ['chat_id' => $tgbot_chatid, 'text' => $content, 'parse_mode' => 'HTML'];
$result = self::telegram_curl($url, http_build_query($post));
if (!$result) return '请求失败';
$arr = json_decode($result, true);
if (isset($arr['ok']) && $arr['ok'] == true) {
return true;
@@ -348,6 +350,7 @@ class MsgNotice
return '不支持的Webhook地址';
}
$result = get_curl($url, json_encode($post), 0, 0, 0, 0, ['Content-Type' => 'application/json; charset=UTF-8']);
if (!$result) return '请求失败';
$arr = json_decode($result, true);
if (isset($arr['errcode']) && $arr['errcode'] == 0 || isset($arr['code']) && $arr['code'] == 0) {
return true;