diff --git a/app/common.php b/app/common.php
index eec1643..f334612 100644
--- a/app/common.php
+++ b/app/common.php
@@ -7,6 +7,7 @@ function get_curl($url, $post = 0, $referer = 0, $cookie = 0, $header = 0, $ua =
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$httpheader[] = "Accept: */*";
diff --git a/app/controller/System.php b/app/controller/System.php
index 9411a14..f5d126e 100644
--- a/app/controller/System.php
+++ b/app/controller/System.php
@@ -76,6 +76,20 @@ class System extends BaseController
}
}
+ public function webhooktest()
+ {
+ if (!checkPermission(2)) return $this->alert('error', '无权限');
+ $webhook_url = config_get('webhook_url');
+ if (empty($webhook_url)) return json(['code' => -1, 'msg' => '请先保存设置']);
+ $content = "这是一封测试消息!\n来自:" . $this->request->root(true);
+ $result = \app\utils\MsgNotice::send_webhook('消息发送测试', $content);
+ if ($result === true) {
+ return json(['code' => 0, 'msg' => '消息发送成功!']);
+ } else {
+ return json(['code' => -1, 'msg' => '消息发送失败!' . $result]);
+ }
+ }
+
public function proxytest()
{
if (!checkPermission(2)) return $this->alert('error', '无权限');
diff --git a/app/lib/DeployHelper.php b/app/lib/DeployHelper.php
index 72e4cd2..b81c697 100644
--- a/app/lib/DeployHelper.php
+++ b/app/lib/DeployHelper.php
@@ -342,6 +342,63 @@ class DeployHelper
],
'taskinputs' => [],
],
+ 'mwpanel' => [
+ 'name' => 'mdserver-web',
+ 'class' => 1,
+ 'icon' => 'mwpanel.ico',
+ 'note' => null,
+ 'tasknote' => '',
+ 'inputs' => [
+ 'url' => [
+ 'name' => '面板地址',
+ 'type' => 'input',
+ 'placeholder' => 'MW面板地址',
+ 'note' => '填写规则如:http://192.168.1.100:8888 ,不要带其他后缀',
+ 'required' => true,
+ ],
+ 'appid' => [
+ 'name' => '应用ID',
+ 'type' => 'input',
+ 'placeholder' => 'MW面板设置->API接口',
+ 'required' => true,
+ ],
+ 'appsecret' => [
+ 'name' => '应用密钥',
+ 'type' => 'input',
+ 'placeholder' => '面板设置->API接口',
+ 'required' => true,
+ ],
+ 'proxy' => [
+ 'name' => '使用代理服务器',
+ 'type' => 'radio',
+ 'options' => [
+ '0' => '否',
+ '1' => '是',
+ ],
+ 'value' => '0'
+ ],
+ ],
+ 'taskinputs' => [
+ 'type' => [
+ 'name' => '部署类型',
+ 'type' => 'radio',
+ 'options' => [
+ '0' => 'MW面板站点的证书',
+ '1' => 'MW面板本身的证书',
+ ],
+ 'value' => '0',
+ 'required' => true,
+ ],
+ 'sites' => [
+ 'name' => '网站名称列表',
+ 'type' => 'textarea',
+ 'placeholder' => '填写要部署证书的网站名称,每行一个',
+ 'note' => '网站名称,即为网站创建时绑定的第一个域名',
+ 'show' => 'type==0',
+ 'required' => true,
+ ],
+ ],
+ ],
'aliyun' => [
'name' => '阿里云',
'class' => 2,
diff --git a/app/lib/deploy/lecdn.php b/app/lib/deploy/lecdn.php
index 2e64c99..83b545b 100644
--- a/app/lib/deploy/lecdn.php
+++ b/app/lib/deploy/lecdn.php
@@ -62,8 +62,8 @@ class lecdn implements DeployInterface
'password' => $this->password,
];
$result = $this->request($path, $params);
- if (isset($result['access_token'])) {
- $this->accessToken = $result['access_token'];
+ if (isset($result['token'])) {
+ $this->accessToken = $result['token'];
} else {
throw new Exception('登录成功,获取access_token失败');
}
@@ -83,7 +83,7 @@ class lecdn implements DeployInterface
}
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method);
$result = json_decode($response['body'], true);
- if (isset($result['code']) && $result['code'] == 0) {
+ if (isset($result['code']) && $result['code'] == 200) {
return isset($result['data']) ? $result['data'] : null;
} elseif (isset($result['message'])) {
throw new Exception($result['message']);
diff --git a/app/lib/deploy/mwpanel.php b/app/lib/deploy/mwpanel.php
new file mode 100644
index 0000000..f3fdc6a
--- /dev/null
+++ b/app/lib/deploy/mwpanel.php
@@ -0,0 +1,127 @@
+url = rtrim($config['url'], '/');
+ $this->appid = $config['appid'];
+ $this->appsecret = $config['appsecret'];
+ $this->proxy = $config['proxy'] == 1;
+ }
+
+ public function check()
+ {
+ if (empty($this->url) || empty($this->appid) || empty($this->appsecret)) throw new Exception('请填写面板地址和接口密钥');
+
+ $path = '/task/count';
+ $response = $this->request($path);
+ $result = json_decode($response, true);
+ if (isset($result['status']) && $result['status'] == true) {
+ return true;
+ } else {
+ throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
+ }
+ }
+
+ public function deploy($fullchain, $privatekey, $config, &$info)
+ {
+ if ($config['type'] == '1') {
+ $this->deployPanel($fullchain, $privatekey);
+ $this->log("面板证书部署成功");
+ return;
+ }
+ $sites = explode("\n", $config['sites']);
+ $success = 0;
+ $errmsg = null;
+ foreach ($sites as $site) {
+ $siteName = trim($site);
+ if (empty($siteName)) continue;
+ try {
+ $this->deploySite($siteName, $fullchain, $privatekey);
+ $this->log("网站 {$siteName} 证书部署成功");
+ $success++;
+ } catch (Exception $e) {
+ $errmsg = $e->getMessage();
+ $this->log("网站 {$siteName} 证书部署失败:" . $errmsg);
+ }
+ }
+ if ($success == 0) {
+ throw new Exception($errmsg ? $errmsg : '要部署的网站不存在');
+ }
+ }
+
+ private function deployPanel($fullchain, $privatekey)
+ {
+ $path = '/setting/save_panel_ssl';
+ $data = [
+ 'privateKey' => $privatekey,
+ 'certPem' => $fullchain,
+ 'choose' => 'local',
+ ];
+ $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/set_ssl';
+ $data = [
+ 'type' => '1',
+ '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 : '返回数据解析失败');
+ }
+ }
+
+ 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 = [
+ 'app-id: '.$this->appid,
+ 'app-secret: '.$this->appsecret,
+ ];
+ $response = curl_client($url, $params ? http_build_query($params) : null, null, null, $headers, $this->proxy);
+ return $response['body'];
+ }
+}
diff --git a/app/service/CertTaskService.php b/app/service/CertTaskService.php
index c953ce8..64f38ce 100644
--- a/app/service/CertTaskService.php
+++ b/app/service/CertTaskService.php
@@ -31,7 +31,7 @@ class CertTaskService
$retcode = $service->process();
if ($retcode == 3) {
echo 'ID:'.$row['id'].' 证书已签发成功!'.PHP_EOL;
- if($row['issend'] == 0) MsgNotice::cert_send($row['id'], true);
+ if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], true);
} elseif ($retcode == 1) {
echo 'ID:'.$row['id'].' 添加DNS记录成功!'.PHP_EOL;
}
@@ -41,7 +41,7 @@ class CertTaskService
if ($e->getCode() == 102) {
break;
} elseif ($e->getCode() == 103) {
- if($row['issend'] == 0) MsgNotice::cert_send($row['id'], false);
+ if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], false);
} else {
$failcount++;
}
@@ -74,14 +74,14 @@ class CertTaskService
$service = new CertDeployService($row['id']);
$service->process();
echo 'ID:'.$row['id'].' 部署任务执行成功!'.PHP_EOL;
- if($row['issend'] == 0) MsgNotice::deploy_send($row['id'], true);
+ if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], true);
$count++;
} catch (Exception $e) {
echo 'ID:'.$row['id'].' '.$e->getMessage().PHP_EOL;
if ($e->getCode() == 102) {
break;
} elseif ($e->getCode() == 103) {
- if($row['issend'] == 0) MsgNotice::deploy_send($row['id'], false);
+ if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], false);
} else {
$count++;
}
diff --git a/app/utils/MsgNotice.php b/app/utils/MsgNotice.php
index 5fb8e6c..9c1fd66 100644
--- a/app/utils/MsgNotice.php
+++ b/app/utils/MsgNotice.php
@@ -13,7 +13,7 @@ class MsgNotice
{
if ($action == 1) {
$mail_title = 'DNS容灾切换-发生告警通知';
- $mail_content = '尊敬的系统管理员,您好:
您的域名 '.$task['domain'].' 的 '.$task['main_value'].' 记录发生了异常';
+ $mail_content = '尊敬的用户,您好:
您的域名 '.$task['domain'].' 的 '.$task['main_value'].' 记录发生了异常';
if ($task['type'] == 2) {
$mail_content .= ',已自动切换为备用解析记录 '.$task['backup_value'].' ';
} elseif ($task['type'] == 1) {
@@ -22,11 +22,11 @@ class MsgNotice
$mail_content .= ',请及时处理';
}
if (!empty($result['errmsg'])) {
- $mail_content .= '。
异常信息:'.$result['errmsg'];
+ $mail_content .= '。
异常信息:'.$result['errmsg'].'';
}
} else {
$mail_title = 'DNS容灾切换-恢复正常通知';
- $mail_content = '尊敬的系统管理员,您好:
您的域名 '.$task['domain'].' 的 '.$task['main_value'].' 记录已恢复正常';
+ $mail_content = '尊敬的用户,您好:
您的域名 '.$task['domain'].' 的 '.$task['main_value'].' 记录已恢复正常';
if ($task['type'] == 2) {
$mail_content .= ',已自动切换回当前解析记录';
} elseif ($task['type'] == 1) {
@@ -41,7 +41,7 @@ class MsgNotice
if (!empty($task['remark'])) {
$mail_content .= '
备注:'.$task['remark'];
}
- $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s');
+ $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s').'';
if (config_get('notice_mail') == 1) {
$mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name');
@@ -49,16 +49,20 @@ class MsgNotice
}
if (config_get('notice_wxtpl') == 1) {
$content = str_replace(['
', '', ''], ["\n\n", '**', '**'], $mail_content);
- self::send_wechat_tplmsg($mail_title, $content);
+ self::send_wechat_tplmsg($mail_title, strip_tags($content));
}
if (config_get('notice_tgbot') == 1) {
$content = str_replace('
', "\n", $mail_content);
- $content = "".$mail_title."\n".$content;
+ $content = "".$mail_title."\n".strip_tags($content);
self::send_telegram_bot($content);
}
+ if (config_get('notice_webhook') == 1) {
+ $content = str_replace(['
', '', ''], ["\n", '**', '**'], $mail_content);
+ self::send_webhook($mail_title, $content);
+ }
}
- public static function cert_send($id, $result)
+ public static function cert_order_send($id, $result)
{
$row = Db::name('cert_order')->field('id,aid,issuetime,expiretime,issuer,status,error')->where('id', $id)->find();
if (!$row) return;
@@ -71,7 +75,7 @@ class MsgNotice
} else {
$mail_title = $domainList[0] . '域名SSL证书签发成功通知';
}
- $mail_content = '尊敬的用户,您好:您的SSL证书已签发成功!
证书账户:'.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')
证书域名:'.implode('、', $domainList).'
签发时间:'.$row['issuetime'].'
到期时间:'.$row['expiretime'].'
颁发机构:'.$row['issuer'];
+ $mail_content = '尊敬的用户,您好:您的SSL证书已签发成功!
证书账户: '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')
证书域名: '.implode('、', $domainList).'
签发时间: '.$row['issuetime'].'
到期时间: '.$row['expiretime'].'
颁发机构: '.$row['issuer'];
} else {
$status_arr = [0 => '失败', -1 => '购买证书失败', -2 => '创建订单失败', -3 => '添加DNS失败', -4 => '验证DNS失败', -5 => '验证订单失败', -6 => '订单验证未通过', -7 => '签发证书失败'];
if(count($domainList) > 1){
@@ -79,27 +83,15 @@ class MsgNotice
}else{
$mail_title = $domainList[0].'域名SSL证书'.$status_arr[$row['status']].'通知';
}
- $mail_content = '尊敬的用户,您好:您的SSL证书'.$status_arr[$row['status']].'!
证书账户:'.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')
证书域名:'.implode('、', $domainList).'
失败时间:'.date('Y-m-d H:i:s').'
失败原因:'.$row['error'];
+ $mail_content = '尊敬的用户,您好:您的SSL证书'.$status_arr[$row['status']].'!
证书账户: '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')
证书域名: '.implode('、', $domainList).'
失败时间: '.date('Y-m-d H:i:s').'
失败原因: '.$row['error'].'';
}
- $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s');
+ $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s').'';
- if (config_get('cert_notice_mail') == 1 || config_get('cert_notice_mail') == 2 && !$result) {
- $mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name');
- self::send_mail($mail_name, $mail_title, $mail_content);
- }
- if (config_get('cert_notice_wxtpl') == 1 || config_get('cert_notice_wxtpl') == 2 && !$result) {
- $content = str_replace(['
', '', ''], ["\n\n", '**', '**'], $mail_content);
- self::send_wechat_tplmsg($mail_title, $content);
- }
- if (config_get('cert_notice_tgbot') == 1 || config_get('cert_notice_tgbot') == 2 && !$result) {
- $content = str_replace('
', "\n", $mail_content);
- $content = "" . $mail_title . "\n" . $content;
- self::send_telegram_bot($content);
- }
+ self::cert_send($mail_title, $mail_content, $result);
Db::name('cert_order')->where('id', $id)->update(['issend' => 1]);
}
- public static function deploy_send($id, $result)
+ public static function cert_deploy_send($id, $result)
{
$row = Db::name('cert_deploy')->field('id,aid,oid,remark,status,error')->where('id', $id)->find();
if (!$row) return;
@@ -108,28 +100,37 @@ class MsgNotice
$typename = DeployHelper::$deploy_config[$account['type']]['name'];
$mail_title = $typename;
if(!empty($row['remark'])) $mail_title .= '('.$row['remark'].')';
- $mail_title .= '证书部署'.($result?'成功':'失败').'通知';
+ $mail_title .= 'SSL证书部署'.($result?'成功':'失败').'通知';
if ($result) {
- $mail_content = '尊敬的用户,您好:您的SSL证书已成功部署到'.$typename.'!
自动部署账户:['.$account['id'].']'.$typename.'('.($account['remark']?$account['remark']:$account['name']).')
关联SSL证书:['.$row['oid'].']'.implode('、', $domainList).'
任务备注:'.($row['remark']?$row['remark']:'无');
+ $mail_content = '尊敬的用户,您好:您的SSL证书已成功部署到'.$typename.'!
自动部署账户: ['.$account['id'].']'.$typename.'('.($account['remark']?$account['remark']:$account['name']).')
关联SSL证书: ['.$row['oid'].']'.implode('、', $domainList).'
任务备注: '.($row['remark']?$row['remark']:'无');
} else {
- $mail_content = '尊敬的用户,您好:您的SSL证书部署失败!
失败原因:'.$row['error'].'
自动部署账户:['.$account['id'].']'.$typename.'('.($account['remark']?$account['remark']:$account['name']).')
关联SSL证书:['.$row['oid'].']'.implode('、', $domainList).'
任务备注:'.($row['remark']?$row['remark']:'无');
+ $mail_content = '尊敬的用户,您好:您的SSL证书部署失败!
失败原因: '.$row['error'].'
自动部署账户: ['.$account['id'].']'.$typename.'('.($account['remark']?$account['remark']:$account['name']).')
关联SSL证书: ['.$row['oid'].']'.implode('、', $domainList).'
任务备注: '.($row['remark']?$row['remark']:'无');
}
- $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s');
+ $mail_content .= '
'.self::$sitename.'
'.date('Y-m-d H:i:s').'';
+ self::cert_send($mail_title, $mail_content, $result);
+ Db::name('cert_deploy')->where('id', $id)->update(['issend' => 1]);
+ }
+
+ private static function cert_send($mail_title, $mail_content, $result)
+ {
if (config_get('cert_notice_mail') == 1 || config_get('cert_notice_mail') == 2 && !$result) {
$mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name');
self::send_mail($mail_name, $mail_title, $mail_content);
}
if (config_get('cert_notice_wxtpl') == 1 || config_get('cert_notice_wxtpl') == 2 && !$result) {
$content = str_replace(['
', '', ''], ["\n\n", '**', '**'], $mail_content);
- self::send_wechat_tplmsg($mail_title, $content);
+ self::send_wechat_tplmsg($mail_title, strip_tags($content));
}
if (config_get('cert_notice_tgbot') == 1 || config_get('cert_notice_tgbot') == 2 && !$result) {
$content = str_replace('
', "\n", $mail_content);
- $content = "" . $mail_title . "\n" . $content;
+ $content = "".$mail_title."\n".strip_tags($content);
self::send_telegram_bot($content);
}
- Db::name('cert_deploy')->where('id', $id)->update(['issend' => 1]);
+ if (config_get('cert_notice_webhook') == 1) {
+ $content = str_replace(['*', '
', '', ''], ['\*', "\n", '**', '**'], $mail_content);
+ self::send_webhook($mail_title, $content);
+ }
}
public static function send_mail($to, $sub, $msg)
@@ -187,7 +188,7 @@ class MsgNotice
if (isset($arr['success']) && $arr['success'] == true) {
return true;
} else {
- return $arr['msg'];
+ return isset($arr['msg']) ? $arr['msg'] : '请求失败';
}
}
@@ -203,7 +204,48 @@ class MsgNotice
if (isset($arr['ok']) && $arr['ok'] == true) {
return true;
} else {
- return $arr['description'];
+ return isset($arr['description']) ? $arr['description'] : '请求失败';
+ }
+ }
+
+ public static function send_webhook($title, $content)
+ {
+ $url = config_get('webhook_url');
+ if (!$url || !parse_url($url)) return false;
+ if (strpos($url, 'oapi.dingtalk.com')) {
+ $content = '### '.$title." \n ".str_replace("\n", " \n ", $content);
+ $post = [
+ 'msgtype' => 'markdown',
+ 'markdown' => [
+ 'title' => $title,
+ 'text' => $content,
+ ],
+ ];
+ } elseif (strpos($url, 'qyapi.weixin.qq.com')) {
+ $content = '## '.$title."\n".$content;
+ $post = [
+ 'msgtype' => 'markdown',
+ 'markdown' => [
+ 'content' => $content,
+ ],
+ ];
+ } elseif (strpos($url, 'open.feishu.cn')) {
+ $content = str_replace(['\*', '**'], ['*', ''], strip_tags($content));
+ $post = [
+ 'msg_type' => 'text',
+ 'content' => [
+ 'text' => $content,
+ ],
+ ];
+ } else {
+ return '不支持的Webhook地址';
+ }
+ $result = get_curl($url, json_encode($post), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=UTF-8']);
+ $arr = json_decode($result, true);
+ if (isset($arr['errcode']) && $arr['errcode'] == 0 || isset($arr['code']) && $arr['code'] == 0) {
+ return true;
+ } else {
+ return isset($arr['errmsg']) ? $arr['errmsg'] : (isset($arr['msg']) ? $arr['msg'] : '请求失败');
}
}
@@ -233,6 +275,7 @@ class MsgNotice
curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type);
}
curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$httpheader[] = "Accept: */*";
diff --git a/app/view/cert/certset.html b/app/view/cert/certset.html
index a7fa391..26af945 100644
--- a/app/view/cert/certset.html
+++ b/app/view/cert/certset.html
@@ -64,6 +64,10 @@