mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-02-21 07:17:22 +08:00
支持火山引擎更多产品部署
This commit is contained in:
parent
2c81b36249
commit
d1eb6267a2
@ -667,7 +667,7 @@ class DeployHelper
|
||||
'name' => '实例ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'lighthouse\'',
|
||||
'show' => 'product==\'lighthouse\'||product==\'ddos\'',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
@ -816,10 +816,18 @@ class DeployHelper
|
||||
'options' => [
|
||||
['value'=>'cdn', 'label'=>'CDN'],
|
||||
['value'=>'oss', 'label'=>'OSS'],
|
||||
['value'=>'pili', 'label'=>'视频直播'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
],
|
||||
'pili_hub' => [
|
||||
'name' => '直播空间名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'pili\'',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
@ -931,10 +939,39 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'product' => [
|
||||
'name' => '要部署的产品',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
['value'=>'cdn', 'label'=>'内容分发网络CDN'],
|
||||
['value'=>'dcdn', 'label'=>'全站加速DCDN'],
|
||||
['value'=>'clb', 'label'=>'负载均衡CLB'],
|
||||
['value'=>'tos', 'label'=>'对象存储TOS'],
|
||||
['value'=>'live', 'label'=>'视频直播'],
|
||||
['value'=>'imagex', 'label'=>'veImageX'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
],
|
||||
'bucket_domain' => [
|
||||
'name' => 'Bucket域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'tos\'',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product!=\'clb\'',
|
||||
'required' => true,
|
||||
],
|
||||
'listener_id' => [
|
||||
'name' => '监听器ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'clb\'',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
|
||||
@ -227,7 +227,7 @@ class ACMECert extends ACMEv2
|
||||
|
||||
public function authOrder($order)
|
||||
{
|
||||
if ($order['status'] != 'ready' && empty($order['challenges'])) {
|
||||
if ($order['status'] != 'pending' && $order['status'] != 'ready' && empty($order['challenges'])) {
|
||||
throw new Exception('No challenges available');
|
||||
}
|
||||
|
||||
|
||||
@ -61,6 +61,39 @@ class Qiniu
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
public function pili_request($method, $path, $query = null, $params = null)
|
||||
{
|
||||
$this->ApiUrl = 'https://pili.qiniuapi.com';
|
||||
$url = $this->ApiUrl . $path;
|
||||
$query_str = null;
|
||||
$body = null;
|
||||
if (!empty($query)) {
|
||||
$query = array_filter($query, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
$query_str = http_build_query($query);
|
||||
$url .= '?' . $query_str;
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
$body = json_encode($params);
|
||||
}
|
||||
|
||||
$sign_str = $method . ' ' . $path . ($query_str ? '?' . $query_str : '') . "\nHost: pili.qiniuapi.com" . ($body ? "\nContent-Type: application/json" : '') . "\n\n" . $body;
|
||||
$hmac = hash_hmac('sha1', $sign_str, $this->SecretKey, true);
|
||||
$sign = $this->AccessKey . ':' . $this->base64_urlSafeEncode($hmac);
|
||||
|
||||
$header = [
|
||||
'Authorization: Qiniu ' . $sign,
|
||||
];
|
||||
if ($body) {
|
||||
$header[] = 'Content-Type: application/json';
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function base64_urlSafeEncode($data)
|
||||
{
|
||||
$find = array('+', '/');
|
||||
@ -95,7 +128,7 @@ class Qiniu
|
||||
|
||||
if ($httpCode == 200) {
|
||||
$arr = json_decode($response, true);
|
||||
if($arr) return $arr;
|
||||
if ($arr) return $arr;
|
||||
return true;
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
|
||||
@ -38,7 +38,9 @@ class Volcengine
|
||||
public function request($method, $action, $params = [], $querys = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) { return $a !== null;});
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$query = [
|
||||
@ -78,9 +80,51 @@ class Volcengine
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function tos_request($method, $params = [], $query = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if ($method != 'GET') {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Tos-Date' => gmdate("Ymd\THis\Z", $time),
|
||||
'X-Tos-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
$path = '/';
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
|
||||
$headers['Authorization'] = $authorization;
|
||||
|
||||
$url = 'https://' . $this->endpoint . $path . '?' . http_build_query($query);
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function generateSign($method, $path, $query, $headers, $body, $time)
|
||||
{
|
||||
$algorithm = "HMAC-SHA256";
|
||||
$algorithm = $this->service == 'tos' ? "TOS4-HMAC-SHA256" : "HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
@ -177,21 +221,27 @@ class Volcengine
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($httpCode == 200) {
|
||||
if (isset($arr['Result'])) {
|
||||
return $arr['Result'];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (isset($arr['ResponseMetadata']['Error']['MessageCN'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['MessageCN']);
|
||||
} elseif (isset($arr['ResponseMetadata']['Error']['Message'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['Message']);
|
||||
} elseif (isset($arr['Result'])) {
|
||||
return $arr['Result'];
|
||||
} elseif (isset($arr['Message'])) {
|
||||
throw new Exception($arr['Message']);
|
||||
} elseif (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
return true;
|
||||
throw new Exception('返回数据解析失败(http_code=' . $httpCode . ')');
|
||||
}
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,18 +23,31 @@ class huoshan implements DeployInterface
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey)) throw new Exception('必填参数不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client->request('POST', 'ListCertInfo', ['Source' => 'volc_cert_center']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('获取证书ID失败');
|
||||
$info['cert_id'] = $cert_id;
|
||||
$this->deploy_cdn($cert_id, $config);
|
||||
if ($config['product'] == 'live') {
|
||||
$this->deploy_live($fullchain, $privatekey, $config);
|
||||
} else {
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('获取证书ID失败');
|
||||
$info['cert_id'] = $cert_id;
|
||||
if (!isset($config['product']) || $config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($cert_id, $config);
|
||||
} elseif ($config['product'] == 'dcdn') {
|
||||
$this->deploy_dcdn($cert_id, $config);
|
||||
} elseif ($config['product'] == 'tos') {
|
||||
$this->deploy_tos($cert_id, $config);
|
||||
} elseif ($config['product'] == 'imagex') {
|
||||
$this->deploy_imagex($cert_id, $config);
|
||||
} elseif ($config['product'] == 'clb') {
|
||||
$this->deploy_clb($cert_id, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cdn($cert_id, $config)
|
||||
@ -51,37 +64,137 @@ class huoshan implements DeployInterface
|
||||
if ($row['Status'] == 'success') {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书成功!');
|
||||
} else {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书失败:' . isset($row['ErrorMsg']) ? $row['ErrorMsg'] : '');
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书失败:' . (isset($row['ErrorMsg']) ? $row['ErrorMsg'] : ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_dcdn($cert_id, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'dcdn', '2021-04-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertId' => $cert_id,
|
||||
'DomainNames' => explode(',', $config['domain']),
|
||||
];
|
||||
$client->request('POST', 'CreateCertBind', $param);
|
||||
$this->log('DCDN域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_tos($cert_id, $config)
|
||||
{
|
||||
if (empty($config['bucket_domain'])) throw new Exception('Bucket域名不能为空');
|
||||
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) {
|
||||
$param = [
|
||||
'CustomDomainRule' => [
|
||||
'Domain' => $domain,
|
||||
'CertId' => $cert_id,
|
||||
]
|
||||
];
|
||||
$query = ['customdomain' => ''];
|
||||
$client->tos_request('PUT', $param, $query);
|
||||
$this->log('对象存储域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_live($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'live.volcengineapi.com', 'live', '2023-01-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
'Rsa' => [
|
||||
'Pubkey' => $fullchain,
|
||||
'Prikey' => $privatekey,
|
||||
],
|
||||
'UseWay' => 'https',
|
||||
];
|
||||
$result = $client->request('POST', 'CreateCert', $param);
|
||||
$this->log('上传证书成功 ChainID=' . $result['ChainID']);
|
||||
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
$param = [
|
||||
'ChainID' => $result['ChainID'],
|
||||
'Domain' => $domain,
|
||||
'HTTPS' => true,
|
||||
'HTTP2' => true,
|
||||
];
|
||||
$client->request('POST', 'BindCert', $param);
|
||||
$this->log('视频直播域名 ' . $domain . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_imagex($cert_id, $config)
|
||||
{
|
||||
if (empty($config['bucket_domain'])) throw new Exception('Bucket域名不能为空');
|
||||
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) {
|
||||
$param = [
|
||||
[
|
||||
'domain' => $domain,
|
||||
'cert_id' => $cert_id,
|
||||
]
|
||||
];
|
||||
$result = $client->request('POST', 'UpdateImageBatchDomainCert', $param);
|
||||
if (isset($result['SuccessDomains']) && count($result['SuccessDomains']) > 0) {
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书成功!');
|
||||
} elseif (isset($result['FailedDomains']) && count($result['FailedDomains']) > 0) {
|
||||
$errmsg = $result['FailedDomains'][0]['ErrMsg'];
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书失败:' . $errmsg);
|
||||
} else {
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_clb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['listener_id'])) throw new Exception('监听器ID不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'clb', '2020-04-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'ListenerId' => $config['listener_id'],
|
||||
'CertificateSource' => 'cert_center',
|
||||
'CertCenterCertificateId' => $cert_id,
|
||||
];
|
||||
$client->request('GET', 'ModifyListenerAttributes', $param);
|
||||
$this->log('CLB监听器 ' . $config['listener_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 Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'Source' => 'volc_cert_center',
|
||||
'Certificate' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
'Desc' => $cert_name,
|
||||
'Tag' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
'CertificateInfo' => [
|
||||
'CertificateChain' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', 'AddCertificate', $param);
|
||||
$data = $client->request('POST', 'ImportCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '证书已存在,ID为') !== false) {
|
||||
$cert_id = trim(getSubstr($e->getMessage(), '证书已存在,ID为', '。'));
|
||||
$this->log('证书已存在 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 CertId=' . $data['CertId']);
|
||||
return $data['CertId'];
|
||||
if (!empty($data['InstanceId'])) {
|
||||
$cert_id = $data['InstanceId'];
|
||||
} else {
|
||||
$cert_id = $data['RepeatId'];
|
||||
}
|
||||
$this->log('上传证书成功 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
|
||||
@ -42,6 +42,8 @@ class qiniu implements DeployInterface
|
||||
$this->deploy_cdn($domain, $cert_id);
|
||||
} elseif ($config['product'] == 'oss') {
|
||||
$this->deploy_oss($domain, $cert_id);
|
||||
} elseif ($config['product'] == 'pili') {
|
||||
$this->deploy_pili($config['pili_hub'], $domain, $cert_name);
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
}
|
||||
@ -87,6 +89,15 @@ class qiniu implements DeployInterface
|
||||
$this->log('OSS域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function deploy_pili($hub, $domain, $cert_name)
|
||||
{
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
];
|
||||
$this->client->pili_request('POST', '/v2/hubs/'.$hub.'/domains/'.$domain.'/cert', null, $param);
|
||||
$this->log('视频直播域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey, $common_name, $cert_name)
|
||||
{
|
||||
$cert_id = null;
|
||||
|
||||
@ -37,7 +37,7 @@ class tencent implements DeployInterface
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['cos_bucket'])) throw new Exception('存储桶名称不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['regionid'] . '#' . $config['cos_bucket'] . '#' . $config['domain'];
|
||||
$instance_id = $config['regionid'] . '|' . $config['cos_bucket'] . '|' . $config['domain'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'tke') {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
@ -52,6 +52,10 @@ class tencent implements DeployInterface
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['regionid'] . '|' . $config['lighthouse_id'] . '|' . $config['domain'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'ddos') {
|
||||
if (empty($config['lighthouse_id'])) throw new Exception('实例ID不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['lighthouse_id'] . '|' . $config['domain'] . '|443';
|
||||
} elseif ($config['product'] == 'clb') {
|
||||
return $this->deploy_clb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'scf') {
|
||||
|
||||
@ -79,6 +79,9 @@ class CheckUtils
|
||||
$target = gethostbyname($target);
|
||||
if (!$target) return ['status' => false, 'errmsg' => 'DNS resolve failed', 'usetime' => 0];
|
||||
}
|
||||
if (filter_var($target, FILTER_VALIDATE_IP) && strpos($target, ':') !== false) {
|
||||
$target = '['.$target.']';
|
||||
}
|
||||
$starttime = getMillisecond();
|
||||
$fp = @fsockopen($target, $port, $errCode, $errStr, $timeout);
|
||||
if ($fp) {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<style>
|
||||
.copy-btn{color:#52c41a;cursor:pointer;margin-right: 5px;}
|
||||
.copy-btn:hover{color:#85ef79;}
|
||||
.btn-refresh{margin-left:5px;font-size:10px;background-color:#6896cf}
|
||||
tbody tr>td:nth-child(3){word-break:break-all;max-width:180px;}
|
||||
tbody tr>td:nth-child(4){word-break:break-all;max-width:260px;}
|
||||
</style>
|
||||
@ -120,7 +121,7 @@ $(document).ready(function(){
|
||||
} else {
|
||||
html += '<span class="label label-warning">未验证</span>';
|
||||
}
|
||||
html += ' <a href="javascript:checkItem('+row.id+')" title="立即验证" class="btn btn-primary btn-xs"><i class="fa fa-refresh"></i></a>';
|
||||
html += '<a href="javascript:checkItem('+row.id+')" title="立即验证" class="btn btn-primary btn-xs btn-refresh"><i class="fa fa-refresh"></i></a>';
|
||||
return html;
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,7 +31,7 @@ return [
|
||||
'show_error_msg' => true,
|
||||
'exception_tmpl' => \think\facade\App::getAppPath() . 'view/exception.tpl',
|
||||
|
||||
'version' => '1026',
|
||||
'version' => '1027',
|
||||
|
||||
'dbversion' => '1023'
|
||||
];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user