From ec51a2e6b32ef20791b81872eb52e0b8a8faf505 Mon Sep 17 00:00:00 2001 From: coolxitech Date: Sat, 9 Nov 2024 11:40:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/AppService.php | 3 +- app/BaseController.php | 17 +- app/ExceptionHandle.php | 1 + app/Request.php | 2 +- app/command/Dmtask.php | 162 +-- app/command/Opiptask.php | 65 +- app/common.php | 409 +++--- app/controller/Auth.php | 104 +- app/controller/Dmonitor.php | 610 +++++---- app/controller/Index.php | 137 +- app/controller/Install.php | 76 +- app/controller/Optimizeip.php | 366 ++--- app/controller/User.php | 360 ++--- app/event.php | 1 + app/lib/CheckUtils.php | 232 ++-- app/lib/DnsHelper.php | 296 ++-- app/lib/DnsInterface.php | 70 +- app/lib/MsgNotice.php | 353 ++--- app/lib/NewDb.php | 1 + app/lib/NewDbManager.php | 3 +- app/lib/OptimizeService.php | 456 +++--- app/lib/TaskRunner.php | 251 ++-- app/lib/dns/aliyun.php | 602 ++++---- app/lib/dns/baidu.php | 692 +++++----- app/lib/dns/dnsla.php | 561 ++++---- app/lib/dns/dnspod.php | 865 ++++++------ app/lib/dns/huawei.php | 775 ++++++----- app/lib/dns/huoshan.php | 827 +++++------ app/lib/dns/west.php | 444 +++--- app/lib/mail/Aliyun.php | 135 +- app/lib/mail/PHPMailer/Exception.php | 11 +- app/lib/mail/PHPMailer/PHPMailer.php | 1903 +++++++++++++++++++++++++- app/lib/mail/PHPMailer/SMTP.php | 484 ++++++- app/lib/mail/Sendcloud.php | 64 +- app/middleware.php | 1 + app/middleware/AuthApi.php | 87 +- app/middleware/AuthUser.php | 105 +- app/middleware/CheckLogin.php | 38 +- app/middleware/LoadConfig.php | 21 +- app/middleware/RefererCheck.php | 49 +- app/middleware/ViewOutput.php | 51 +- app/provider.php | 1 + app/view/auth/login.html | 2 +- app/view/dmonitor/taskinfo.html | 4 +- composer.json | 19 +- config/captcha.php | 5 +- route/app.php | 20 +- 47 files changed, 7347 insertions(+), 4394 deletions(-) diff --git a/app/AppService.php b/app/AppService.php index 96556e8..392dfed 100644 --- a/app/AppService.php +++ b/app/AppService.php @@ -1,5 +1,6 @@ app->route->buildUrl($url); } - if(empty($msg)) $msg = '未知错误'; + if (empty($msg)) { + $msg = '未知错误'; + } - if (request()->isApi) { - return json(['code'=>$code=='success'?0:-1, 'msg'=>$msg]); + if ($this->request->isApi) { + return json(['code' => $code == 'success' ? 0 : -1, 'msg' => $msg]); } - if (request()->isAjax()) { - return json(['code'=>$code=='success'?0:-1, 'msg'=>$msg, 'url'=>$url]); + if ($this->request->isAjax()) { + return json(['code' => $code == 'success' ? 0 : -1, 'msg' => $msg, 'url' => $url]); } - + View::assign([ 'code' => $code, 'msg' => $msg, diff --git a/app/ExceptionHandle.php b/app/ExceptionHandle.php index 453d126..42a49aa 100644 --- a/app/ExceptionHandle.php +++ b/app/ExceptionHandle.php @@ -1,4 +1,5 @@ setName('dmtask') - ->setDescription('容灾切换任务'); - } - - protected function execute(Input $input, Output $output) - { - $res = Db::name('config')->cache('configs',0)->column('value','key'); - Config::set($res, 'sys'); - - config_set('run_error', ''); - if(!extension_loaded('swoole')){ - $output->writeln('[Error] 未安装Swoole扩展'); - config_set('run_error', '未安装Swoole扩展'); - return; - } - try{ - $output->writeln('进程启动成功.'); - $this->runtask(); - }catch(Exception $e){ - $output->writeln('[Error] '.$e->getMessage()); - config_set('run_error', $e->getMessage()); - } - } - - private function runtask(){ - \Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]); - \Co\run(function() { - $date = date("Ymd"); - $count = config_get('run_count', null, true) ?? 0; - while(true){ - sleep(1); - if($date != date("Ymd")){ - $count = 0; - $date = date("Ymd"); - } - - $rows = Db::name('dmtask')->where('checknexttime', '<=', time())->where('active', 1)->order('id', 'ASC')->select(); - foreach($rows as $row){ - \go(function () use($row) { - try{ - (new TaskRunner())->execute($row); - } catch (\Swoole\ExitException $e) { - echo $e->getStatus()."\n"; - } catch (Exception $e) { - echo $e->__toString()."\n"; - } - }); - Db::name('dmtask')->where('id', $row['id'])->update([ - 'checktime' => time(), - 'checknexttime' => time() + $row['frequency'] - ]); - $count++; - } - - config_set('run_time', date("Y-m-d H:i:s")); - config_set('run_count', $count); - } - }); - } -} +setName('dmtask') + ->setDescription('容灾切换任务'); + } + + protected function execute(Input $input, Output $output) + { + $res = Db::name('config')->cache('configs', 0)->column('value', 'key'); + Config::set($res, 'sys'); + + config_set('run_error', ''); + if (!extension_loaded('swoole')) { + $output->writeln('[Error] 未安装Swoole扩展'); + config_set('run_error', '未安装Swoole扩展'); + return; + } + try { + $output->writeln('进程启动成功.'); + $this->runtask(); + } catch (Exception $e) { + $output->writeln('[Error] '.$e->getMessage()); + config_set('run_error', $e->getMessage()); + } + } + + private function runtask() + { + \Co::set(['hook_flags' => SWOOLE_HOOK_ALL]); + \Co\run(function () { + $date = date("Ymd"); + $count = config_get('run_count', null, true) ?? 0; + while (true) { + sleep(1); + if ($date != date("Ymd")) { + $count = 0; + $date = date("Ymd"); + } + + $rows = Db::name('dmtask')->where('checknexttime', '<=', time())->where('active', 1)->order('id', 'ASC')->select(); + foreach ($rows as $row) { + \go(function () use ($row) { + try { + (new TaskRunner())->execute($row); + } catch (\Swoole\ExitException $e) { + echo $e->getStatus()."\n"; + } catch (Exception $e) { + echo $e->__toString()."\n"; + } + }); + Db::name('dmtask')->where('id', $row['id'])->update([ + 'checktime' => time(), + 'checknexttime' => time() + $row['frequency'] + ]); + $count++; + } + + config_set('run_time', date("Y-m-d H:i:s")); + config_set('run_count', $count); + } + }); + } +} diff --git a/app/command/Opiptask.php b/app/command/Opiptask.php index e227a56..dd62cc8 100644 --- a/app/command/Opiptask.php +++ b/app/command/Opiptask.php @@ -1,32 +1,33 @@ -setName('opiptask') - ->setDescription('CF优选IP任务'); - } - - protected function execute(Input $input, Output $output) - { - $res = Db::name('config')->cache('configs',0)->column('value','key'); - Config::set($res, 'sys'); - - (new OptimizeService())->execute(); - } -} +setName('opiptask') + ->setDescription('CF优选IP任务'); + } + + protected function execute(Input $input, Output $output) + { + $res = Db::name('config')->cache('configs', 0)->column('value', 'key'); + Config::set($res, 'sys'); + + (new OptimizeService())->execute(); + } +} diff --git a/app/common.php b/app/common.php index b63c4a0..3eaba98 100644 --- a/app/common.php +++ b/app/common.php @@ -1,228 +1,257 @@ get($url); + } else { + $response = $client->post($url, [ + 'form_params' => $post, + 'headers' => $header, + 'verify' => false + ]); + } + + if ($nobody) { + $ret = $response->getHeaders(); + } else { + $ret = $response->getBody()->getContents(); + } + return $ret; } -function real_ip($type=0){ +function real_ip($type = 0) +{ $ip = $_SERVER['REMOTE_ADDR']; - if($type<=0 && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) { - foreach ($matches[0] AS $xip) { + if ($type <= 0 && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) { + foreach ($matches[0] as $xip) { if (filter_var($xip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $xip; break; } } - } elseif ($type<=0 && isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + } elseif ($type <= 0 && isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $_SERVER['HTTP_CLIENT_IP']; - } elseif ($type<=1 && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + } elseif ($type <= 1 && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; - } elseif ($type<=1 && isset($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + } elseif ($type <= 1 && isset($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { $ip = $_SERVER['HTTP_X_REAL_IP']; } return $ip; } -function strexists($string, $find) { - return !(strpos($string, $find) === FALSE); +function strexists($string, $find) +{ + return !(strpos($string, $find) === false); } -function dstrpos($string, $arr) { - if(empty($string)) return false; - foreach((array)$arr as $v) { - if(strpos($string, $v) !== false) { - return true; - } - } - return false; +function dstrpos($string, $arr) +{ + if (empty($string)) { + return false; + } + foreach ((array)$arr as $v) { + if (strpos($string, $v) !== false) { + return true; + } + } + return false; } -function checkmobile() { - $useragent = strtolower($_SERVER['HTTP_USER_AGENT']); - $ualist = array('android', 'midp', 'nokia', 'mobile', 'iphone', 'ipod', 'blackberry', 'windows phone'); - if((dstrpos($useragent, $ualist) || strexists($_SERVER['HTTP_ACCEPT'], "VND.WAP") || strexists($_SERVER['HTTP_VIA'],"wap"))) - return true; - else - return false; +function checkmobile() +{ + $useragent = strtolower($_SERVER['HTTP_USER_AGENT']); + $ualist = array('android', 'midp', 'nokia', 'mobile', 'iphone', 'ipod', 'blackberry', 'windows phone'); + if ((dstrpos($useragent, $ualist) || strexists($_SERVER['HTTP_ACCEPT'], "VND.WAP") || strexists($_SERVER['HTTP_VIA'], "wap"))) { + return true; + } else { + return false; + } } -function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { - $ckey_length = 4; - $key = md5($key); - $keya = md5(substr($key, 0, 16)); - $keyb = md5(substr($key, 16, 16)); - $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; - $cryptkey = $keya.md5($keya.$keyc); - $key_length = strlen($cryptkey); - $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string; - $string_length = strlen($string); - $result = ''; - $box = range(0, 255); - $rndkey = array(); - for($i = 0; $i <= 255; $i++) { - $rndkey[$i] = ord($cryptkey[$i % $key_length]); - } - for($j = $i = 0; $i < 256; $i++) { - $j = ($j + $box[$i] + $rndkey[$i]) % 256; - $tmp = $box[$i]; - $box[$i] = $box[$j]; - $box[$j] = $tmp; - } - for($a = $j = $i = 0; $i < $string_length; $i++) { - $a = ($a + 1) % 256; - $j = ($j + $box[$a]) % 256; - $tmp = $box[$a]; - $box[$a] = $box[$j]; - $box[$j] = $tmp; - $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); - } - if($operation == 'DECODE') { - if(((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { - return substr($result, 26); - } else { - return ''; - } - } else { - return $keyc.base64_encode($result); - } +function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) +{ + $ckey_length = 4; + $key = md5($key); + $keya = md5(substr($key, 0, 16)); + $keyb = md5(substr($key, 16, 16)); + $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : ''; + $cryptkey = $keya.md5($keya.$keyc); + $key_length = strlen($cryptkey); + $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string; + $string_length = strlen($string); + $result = ''; + $box = range(0, 255); + $rndkey = array(); + for ($i = 0; $i <= 255; $i++) { + $rndkey[$i] = ord($cryptkey[$i % $key_length]); + } + for ($j = $i = 0; $i < 256; $i++) { + $j = ($j + $box[$i] + $rndkey[$i]) % 256; + $tmp = $box[$i]; + $box[$i] = $box[$j]; + $box[$j] = $tmp; + } + for ($a = $j = $i = 0; $i < $string_length; $i++) { + $a = ($a + 1) % 256; + $j = ($j + $box[$a]) % 256; + $tmp = $box[$a]; + $box[$a] = $box[$j]; + $box[$j] = $tmp; + $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); + } + if ($operation == 'DECODE') { + if (((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { + return substr($result, 26); + } else { + return ''; + } + } else { + return $keyc.base64_encode($result); + } } -function random($length, $numeric = 0) { - $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); - $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); - $hash = ''; - $max = strlen($seed) - 1; - for($i = 0; $i < $length; $i++) { - $hash .= $seed[mt_rand(0, $max)]; - } - return $hash; +function random($length, $numeric = 0) +{ + $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); + $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); + $hash = ''; + $max = strlen($seed) - 1; + for ($i = 0; $i < $length; $i++) { + $hash .= $seed[mt_rand(0, $max)]; + } + return $hash; } -function checkDomain($domain){ - if(empty($domain) || !preg_match('/^[-$a-z0-9_*.]{2,512}$/i', $domain) || (stripos($domain, '.') === false) || substr($domain, -1) == '.' || substr($domain, 0 ,1) == '.' || substr($domain, 0 ,1) == '*' && substr($domain, 1 ,1) != '.' || substr_count($domain, '*')>1 || strpos($domain, '*')>0 || strlen($domain)<4) return false; - return true; +function checkDomain($domain) +{ + if (empty($domain) || !preg_match('/^[-$a-z0-9_*.]{2,512}$/i', $domain) || (stripos($domain, '.') === false) || str_ends_with($domain, '.') || str_starts_with($domain, '.') || str_starts_with($domain, '*') && substr($domain, 1, 1) != '.' || substr_count($domain, '*') > 1 || strpos($domain, '*') > 0 || strlen($domain) < 4) { + return false; + } + return true; } function getSubstr($str, $leftStr, $rightStr) { - $left = strpos($str, $leftStr); - $start = $left+strlen($leftStr); - $right = strpos($str, $rightStr, $start); - if($left < 0) return ''; - if($right>0){ - return substr($str, $start, $right-$start); - }else{ - return substr($str, $start); - } + $left = strpos($str, $leftStr); + $start = $left + strlen($leftStr); + $right = strpos($str, $rightStr, $start); + if ($left < 0) { + return ''; + } + if ($right > 0) { + return substr($str, $start, $right - $start); + } else { + return substr($str, $start); + } } -function checkRefererHost(){ - if(!request()->header('referer'))return false; - $url_arr = parse_url(request()->header('referer')); - $http_host = request()->header('host'); - if(strpos($http_host,':'))$http_host = substr($http_host, 0, strpos($http_host, ':')); +function checkRefererHost() +{ + if (!Request::header('referer')) { + return false; + } + $url_arr = parse_url(Request::header('referer')); + $http_host = Request::header('host'); + if (strpos($http_host, ':')) { + $http_host = substr($http_host, 0, strpos($http_host, ':')); + } return $url_arr['host'] === $http_host; } -function checkIfActive($string) { - $array=explode(',',$string); - $action = request()->action(); - if (in_array($action,$array)){ - return 'active'; - }else - return null; +function checkIfActive($string) +{ + $array = explode(',', $string); + $action = Request::action(); + if (in_array($action, $array)) { + return 'active'; + } else { + return null; + } } -function getSid() { +function getSid() +{ return md5(uniqid(mt_rand(), true) . microtime()); } -function getMd5Pwd($pwd, $salt=null) { +function getMd5Pwd($pwd, $salt = null) +{ return md5(md5($pwd) . md5('1277180438'.$salt)); } -function isNullOrEmpty($str){ - return $str === null || $str === ''; +function isNullOrEmpty($str) +{ + return $str === null || $str === ''; } -function checkPermission($type, $domain = null){ - $user = request()->user; - if(empty($user)) return false; - if($user['level'] == 2) return true; - if($type == 1 && $user['level'] == 1 || $type == 0 && $user['level'] >= 0){ - if($domain == null) return true; - if(in_array($domain, $user['permission'])){ - return true; - } - } - return false; +function checkPermission($type, $domain = null) +{ + $user = Request()->user; + if (empty($user)) { + return false; + } + if ($user['level'] == 2) { + return true; + } + if ($type == 1 && $user['level'] == 1 || $type == 0 && $user['level'] >= 0) { + if ($domain == null) { + return true; + } + if (in_array($domain, $user['permission'])) { + return true; + } + } + return false; } -function getAdminSkin(){ - $skin = cookie('admin_skin'); - if(empty($skin)){ - $skin = config_get('admin_skin'); - } - if(empty($skin)){ - $skin = 'skin-black-blue'; - } - return $skin; +function getAdminSkin() +{ + $skin = cookie('admin_skin'); + if (empty($skin)) { + $skin = config_get('admin_skin'); + } + if (empty($skin)) { + $skin = 'skin-black-blue'; + } + return $skin; } function config_get($key, $default = null, $force = false) { - if ($force) { - $value = Db::name('config')->where('key', $key)->value('value'); - } else { - $value = config('sys.'.$key); - } + if ($force) { + $value = Config::where('key', $key)->value('value'); + } else { + $value = config("sys.$key"); + } return $value ?: $default; } function config_set($key, $value) { - $res = Db::name('config')->replace()->insert(['key'=>$key, 'value'=>$value]); - return $res!==false; + $res = Db::name('config')->replace()->insert(['key' => $key, 'value' => $value]); + return $res !== false; } function getMillisecond() @@ -231,24 +260,30 @@ function getMillisecond() return (int)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); } -function getDnsType($value){ - if(filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))return 'A'; - else if(filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))return 'AAAA'; - else return 'CNAME'; +function getDnsType($value) +{ + if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return 'A'; + } elseif (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return 'AAAA'; + } else { + return 'CNAME'; + } } -function convert_second($s){ - $m = floor($s/60); - if($m == 0){ - return $s.'秒'; - }else{ - $s = $s%60; - $h = floor($m/60); - if($h == 0){ - return $m.'分钟'.$s.'秒'; - }else{ - $m = $m%60; - return $h.'小时'.$m.'分钟'.$s.'秒'; - } - } -} \ No newline at end of file +function convert_second($s) +{ + $m = floor($s / 60); + if ($m == 0) { + return $s.'秒'; + } else { + $s = $s % 60; + $h = floor($m / 60); + if ($h == 0) { + return $m.'分钟'.$s.'秒'; + } else { + $m = $m % 60; + return $h.'小时'.$m.'分钟'.$s.'秒'; + } + } +} diff --git a/app/controller/Auth.php b/app/controller/Auth.php index 389cb2c..05e4835 100644 --- a/app/controller/Auth.php +++ b/app/controller/Auth.php @@ -1,61 +1,60 @@ getRuntimePath().'@login.lock'; + $login_limit_file = app()->getRuntimePath() . '@login.lock'; - if(request()->islogin){ + if ($this->request->islogin) { return redirect('/'); } - if(request()->isAjax()){ - $username = input('post.username',null,'trim'); - $password = input('post.password',null,'trim'); - $code = input('post.code',null,'trim'); - - if(empty($username) || empty($password)){ - return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']); + if ($this->request->isAjax()) { + $username = $this->request->post('username', null, 'trim'); + $password = $this->request->post('password', null, 'trim'); + $code = $this->request->post('code', null, 'trim'); + + if (empty($username) || empty($password)) { + return json(['code' => -1, 'msg' => '用户名或密码不能为空']); } - if(!captcha_check($code)){ - return json(['code'=>-1, 'msg'=>'验证码错误', 'vcode'=>1]); + if (!Captcha::check($code)) { + return json(['code' => -1, 'msg' => '验证码错误', 'vcode' => 1]); } if (file_exists($login_limit_file)) { $login_limit = unserialize(file_get_contents($login_limit_file)); if ($login_limit['count'] >= $login_limit_count && $login_limit['time'] > time() - 7200) { - exit(json_encode(['code' => -1, 'msg' => '多次登录失败,暂时禁止登录。可删除/runtime/@login.lock文件解除限制', 'vcode'=>1])); + return json(['code' => -1, 'msg' => '多次登录失败,暂时禁止登录。可删除/runtime/@login.lock文件解除限制', 'vcode' => 1]); } } - $user = Db::name('user')->where('username', $username)->find(); - if($user && password_verify($password, $user['password'])){ - if($user['status'] == 0) return json(['code'=>-1, 'msg'=>'此用户已被封禁', 'vcode'=>1]); - Db::name('log')->insert(['uid' => $user['id'], 'action' => '登录后台', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]); - DB::name('user')->where('id', $user['id'])->update(['lasttime' => date("Y-m-d H:i:s")]); - $session = md5($user['id'].$user['password']); - $expiretime = time()+2562000; + $user = User::where('username', $username)->find(); + if ($user && password_verify($password, $user['password'])) { + if ($user['status'] == 0) { + return json(['code' => -1, 'msg' => '此用户已被封禁', 'vcode' => 1]); + } + Log::insert(['uid' => $user['id'], 'action' => '登录后台', 'data' => 'IP:' . $this->clientip, 'addtime' => date("Y-m-d H:i:s")]); + User::where('id', $user['id'])->update(['lasttime' => date("Y-m-d H:i:s")]); + $session = md5($user['id'] . $user['password']); + $expiretime = time() + 2562000; $token = authcode("user\t{$user['id']}\t{$session}\t{$expiretime}", 'ENCODE', config_get('sys_key')); cookie('user_token', $token, ['expire' => $expiretime, 'httponly' => true]); if (file_exists($login_limit_file)) { unlink($login_limit_file); } - return json(['code'=>0]); - }else{ - if($user){ - Db::name('log')->insert(['uid' => $user['id'], 'action' => '登录失败', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]); + return json(['code' => 0]); + } else { + if ($user) { + Log::insert(['uid' => $user['id'], 'action' => '登录失败', 'data' => 'IP:' . $this->clientip, 'addtime' => date("Y-m-d H:i:s")]); } if (!file_exists($login_limit_file)) { $login_limit = ['count' => 0, 'time' => 0]; @@ -75,44 +74,49 @@ class Auth extends BaseController return view(); } - public function logout() + public function logout(): response { cookie('user_token', null); return redirect('/login'); } - public function quicklogin() + public function quicklogin(): response { - $domain = input('get.domain',null,'trim'); - $timestamp = input('get.timestamp',null,'trim'); - $token = input('get.token',null,'trim'); - $sign = input('get.sign',null,'trim'); - if(empty($domain) || empty($timestamp) || empty($token) || empty($sign)){ + $domain = $this->request->get('domain', null, 'trim'); + $timestamp = $this->request->get('timestamp', null, 'trim'); + $token = $this->request->get('token', null, 'trim'); + $sign = $this->request->get('sign', null, 'trim'); + if (empty($domain) || empty($timestamp) || empty($token) || empty($sign)) { return $this->alert('error', '参数错误'); } - if($timestamp < time()-300 || $timestamp > time()+300){ + if ($timestamp < time() - 300 || $timestamp > time() + 300) { return $this->alert('error', '时间戳无效'); } - if(md5(config_get('sys_key').$domain.$timestamp.$token.config_get('sys_key')) !== $sign){ + if (md5(config_get('sys_key') . $domain . $timestamp . $token . config_get('sys_key')) !== $sign) { return $this->alert('error', '签名错误'); } - if($token != cache('quicklogin_'.$domain)){ + if ($token != cache('quicklogin_' . $domain)) { return $this->alert('error', 'Token无效'); } - $row = Db::name('domain')->where('name', $domain)->find(); - if(!$row){ + $row = DomainModel::where('name', $domain)->find(); + if (!$row) { return $this->alert('error', '该域名不存在'); } - if(!$row['is_sso']){ + if (!$row['is_sso']) { return $this->alert('error', '该域名不支持快捷登录'); } - Db::name('log')->insert(['uid' => 0, 'action' => '域名快捷登录', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s"), 'domain' => $domain]); + Log::insert(['uid' => 0, 'action' => '域名快捷登录', 'data' => 'IP:' . $this->clientip, 'addtime' => date("Y-m-d H:i:s"), 'domain' => $domain]); - $session = md5($row['id'].$row['name']); - $expiretime = time()+2562000; + $session = md5($row['id'] . $row['name']); + $expiretime = time() + 2562000; $token = authcode("domain\t{$row['id']}\t{$session}\t{$expiretime}", 'ENCODE', config_get('sys_key')); cookie('user_token', $token, ['expire' => $expiretime, 'httponly' => true]); - return redirect('/record/'.$row['id']); + return redirect('/record/' . $row['id']); + } + + public function verifycode() + { + return Captcha::create(); } } diff --git a/app/controller/Dmonitor.php b/app/controller/Dmonitor.php index a3c0344..180c27c 100644 --- a/app/controller/Dmonitor.php +++ b/app/controller/Dmonitor.php @@ -1,301 +1,309 @@ -alert('error', '无权限'); - $switch_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->count(); - $fail_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->where('action', 1)->count(); - - $run_time = config_get('run_time', null, true); - $run_state = $run_time ? (time()-strtotime($run_time) > 10 ? 0 : 1) : 0; - View::assign('info', [ - 'run_count' => config_get('run_count', null, true) ?? 0, - 'run_time' => $run_time ?? '无', - 'run_state' => $run_state, - 'run_error' => config_get('run_error', null, true), - 'switch_count' => $switch_count, - 'fail_count' => $fail_count, - 'swoole' => extension_loaded('swoole') ? '已安装' : '未安装', - ]); - return View::fetch(); - } - - public function task() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - return View::fetch(); - } - - public function task_data(){ - if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]); - $type = input('post.type/d', 1); - $kw = input('post.kw', null, 'trim'); - $offset = input('post.offset/d'); - $limit = input('post.limit/d'); - - $select = Db::name('dmtask')->alias('A')->join('domain B','A.did = B.id'); - if(!empty($kw)){ - if($type == 1){ - $select->whereLike('rr|B.name', '%'.$kw.'%'); - }elseif($type == 2){ - $select->where('recordid', $kw); - }elseif($type == 3){ - $select->where('main_value', $kw); - }elseif($type == 4){ - $select->where('backup_value', $kw); - }elseif($type == 5){ - $select->whereLike('remark', '%'.$kw.'%'); - } - } - $total = $select->count(); - $list = $select->order('A.id','desc')->limit($offset, $limit)->field('A.*,B.name domain')->select()->toArray(); - - foreach($list as &$row){ - $row['checktimestr'] = date('Y-m-d H:i:s', $row['checktime']); - } - - return json(['total'=>$total, 'rows'=>$list]); - } - - public function taskform() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $action = input('param.action'); - if(request()->isPost()){ - if($action == 'add'){ - $task = [ - 'did' => input('post.did/d'), - 'rr' => input('post.rr', null, 'trim'), - 'recordid' => input('post.recordid', null, 'trim'), - 'type' => input('post.type/d'), - 'main_value' => input('post.main_value', null, 'trim'), - 'backup_value' => input('post.backup_value', null, 'trim'), - 'checktype' => input('post.checktype/d'), - 'checkurl' => input('post.checkurl', null, 'trim'), - 'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null, - 'frequency' => input('post.frequency/d'), - 'cycle' => input('post.cycle/d'), - 'timeout' => input('post.timeout/d'), - 'proxy' => input('post.proxy/d'), - 'remark' => input('post.remark', null, 'trim'), - 'recordinfo' => input('post.recordinfo', null, 'trim'), - 'addtime' => time(), - 'active' => 1 - ]; - - if(empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])){ - return json(['code'=>-1, 'msg'=>'必填项不能为空']); - } - if($task['checktype'] > 0 && $task['timeout'] > $task['frequency']){ - return json(['code'=>-1, 'msg'=>'为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']); - } - if($task['type'] == 2 && $task['backup_value'] == $task['main_value']){ - return json(['code'=>-1, 'msg'=>'主备地址不能相同']); - } - if(Db::name('dmtask')->where('recordid', $task['recordid'])->find()){ - return json(['code'=>-1, 'msg'=>'当前容灾切换策略已存在']); - } - Db::name('dmtask')->insert($task); - return json(['code'=>0, 'msg'=>'添加成功']); - }elseif($action == 'edit'){ - $id = input('post.id/d'); - $task = [ - 'did' => input('post.did/d'), - 'rr' => input('post.rr', null, 'trim'), - 'recordid' => input('post.recordid', null, 'trim'), - 'type' => input('post.type/d'), - 'main_value' => input('post.main_value', null, 'trim'), - 'backup_value' => input('post.backup_value', null, 'trim'), - 'checktype' => input('post.checktype/d'), - 'checkurl' => input('post.checkurl', null, 'trim'), - 'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null, - 'frequency' => input('post.frequency/d'), - 'cycle' => input('post.cycle/d'), - 'timeout' => input('post.timeout/d'), - 'proxy' => input('post.proxy/d'), - 'remark' => input('post.remark', null, 'trim'), - 'recordinfo' => input('post.recordinfo', null, 'trim'), - ]; - - if(empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])){ - return json(['code'=>-1, 'msg'=>'必填项不能为空']); - } - if($task['checktype'] > 0 && $task['timeout'] > $task['frequency']){ - return json(['code'=>-1, 'msg'=>'为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']); - } - if($task['type'] == 2 && $task['backup_value'] == $task['main_value']){ - return json(['code'=>-1, 'msg'=>'主备地址不能相同']); - } - if(Db::name('dmtask')->where('recordid', $task['recordid'])->where('id', '<>', $id)->find()){ - return json(['code'=>-1, 'msg'=>'当前容灾切换策略已存在']); - } - Db::name('dmtask')->where('id', $id)->update($task); - return json(['code'=>0, 'msg'=>'修改成功']); - }elseif($action == 'setactive'){ - $id = input('post.id/d'); - $active = input('post.active/d'); - Db::name('dmtask')->where('id', $id)->update(['active'=>$active]); - return json(['code'=>0, 'msg'=>'设置成功']); - }elseif($action == 'del'){ - $id = input('post.id/d'); - Db::name('dmtask')->where('id', $id)->delete(); - Db::name('dmlog')->where('taskid', $id)->delete(); - return json(['code'=>0, 'msg'=>'删除成功']); - }else{ - return json(['code'=>-1, 'msg'=>'参数错误']); - } - } - $task = null; - if($action == 'edit'){ - $id = input('get.id/d'); - $task = Db::name('dmtask')->where('id', $id)->find(); - if(empty($task)) return $this->alert('error', '切换策略不存在'); - } - - $domains = []; - foreach(Db::name('domain')->select() as $row){ - $domains[$row['id']] = $row['name']; - } - View::assign('domains', $domains); - - View::assign('info', $task); - View::assign('action', $action); - View::assign('support_ping', function_exists('exec')?'1':'0'); - return View::fetch(); - } - - public function taskinfo() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $id = input('param.id/d'); - $task = Db::name('dmtask')->where('id', $id)->find(); - if(empty($task)) return $this->alert('error', '切换策略不存在'); - - $switch_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->count(); - $fail_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->where('action', 1)->count(); - - $task['switch_count'] = $switch_count; - $task['fail_count'] = $fail_count; - if($task['type'] == 3){ - $task['action_name'] = ['未知', '开启解析', '暂停解析']; - }elseif($task['type'] == 2){ - $task['action_name'] = ['未知', '切换备用解析记录', '恢复主解析记录']; - }else{ - $task['action_name'] = ['未知', '暂停解析', '启用解析']; - } - View::assign('info', $task); - return View::fetch(); - } - - public function tasklog_data(){ - if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]); - $taskid = input('param.id/d'); - $offset = input('post.offset/d'); - $limit = input('post.limit/d'); - $action = input('post.action/d', 0); - - $select = Db::name('dmlog')->where('taskid', $taskid); - if($action > 0){ - $select->where('action', $action); - } - $total = $select->count(); - $list = $select->order('id','desc')->limit($offset, $limit)->select(); - - return json(['total'=>$total, 'rows'=>$list]); - } - - public function noticeset() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - if(request()->isPost()){ - $params = input('post.'); - if(isset($params['mail_type']) && isset($params['mail_name2']) && $params['mail_type'] > 0){ - $params['mail_name'] = $params['mail_name2']; - unset($params['mail_name2']); - } - foreach ($params as $key=>$value){ - if (empty($key)) { - continue; - } - config_set($key, $value); - Cache::delete('configs'); - } - return json(['code'=>0, 'msg'=>'succ']); - } - return View::fetch(); - } - - public function proxyset() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - if(request()->isPost()){ - $params = input('post.'); - foreach ($params as $key=>$value){ - if (empty($key)) { - continue; - } - config_set($key, $value); - Cache::delete('configs'); - } - return json(['code'=>0, 'msg'=>'succ']); - } - return View::fetch(); - } - - public function mailtest() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $mail_name = config_get('mail_recv')?config_get('mail_recv'):config_get('mail_name'); - if(empty($mail_name)) return json(['code'=>-1, 'msg'=>'您还未设置邮箱!']); - $result = \app\lib\MsgNotice::send_mail($mail_name,'邮件发送测试。','这是一封测试邮件!

来自:'.request()->root(true)); - if($result === true){ - return json(['code'=>0, 'msg'=>'邮件发送成功!']); - }else{ - return json(['code'=>-1, 'msg'=>'邮件发送失败!'.$result]); - } - } - - public function tgbottest() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $tgbot_token = config_get('tgbot_token'); - $tgbot_chatid = config_get('tgbot_chatid'); - if(empty($tgbot_token) || empty($tgbot_chatid)) return json(['code'=>-1, 'msg'=>'请先保存设置']); - $content = "消息发送测试\n\n这是一封测试消息!\n\n来自:".request()->root(true); - $result = \app\lib\MsgNotice::send_telegram_bot($content); - if($result === true){ - return json(['code'=>0, 'msg'=>'消息发送成功!']); - }else{ - return json(['code'=>-1, 'msg'=>'消息发送失败!'.$result]); - } - } - - public function clean() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - if(request()->isPost()){ - $days = input('post.days/d'); - if(!$days || $days < 0) return json(['code'=>-1, 'msg'=>'参数错误']); - Db::execute("DELETE FROM `".config('database.connections.mysql.prefix')."dmlog` WHERE `date`<'".date("Y-m-d H:i:s",strtotime("-".$days." days"))."'"); - Db::execute("OPTIMIZE TABLE `".config('database.connections.mysql.prefix')."dmlog`"); - return json(['code'=>0, 'msg'=>'清理成功']); - } - } - - public function status() - { - $run_time = config_get('run_time', null, true); - $run_state = $run_time ? (time()-strtotime($run_time) > 10 ? 0 : 1) : 0; - return $run_state == 1 ? 'ok' : 'error'; - } -} \ No newline at end of file +alert('error', '无权限'); + } + $switch_count = Dmlog::where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count(); + $fail_count = Dmlog::where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count(); + + $run_time = config_get('run_time', null, true); + $run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0; + View::assign('info', [ + 'run_count' => config_get('run_count', null, true) ?? 0, + 'run_time' => $run_time ?? '无', + 'run_state' => $run_state, + 'run_error' => config_get('run_error', null, true), + 'switch_count' => $switch_count, + 'fail_count' => $fail_count, + 'swoole' => extension_loaded('swoole') ? '已安装' : '未安装', + ]); + return View::fetch(); + } + + public function task() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + return View::fetch(); + } + + public function task_data() + { + if (!checkPermission(2)) { + return json(['total' => 0, 'rows' => []]); + } + $type = $this->request->post('type', 1); + $kw = $this->request->post('kw', null, 'trim'); + $offset = $this->request->post('offset'); + $limit = $this->request->post('limit'); + + $select = Dmtask::alias('A')->join('domain B', 'A.did = B.id'); + if (!empty($kw)) { + $select = match ($type) { + 1 => $select->where('rr', $kw), + 2 => $select->where('recordid', $kw), + 3 => $select->where('main_value', $kw), + 4 => $select->where('backup_value', $kw), + 5 => $select->where('remark', $kw), + default => throw new Exception('参数错误'), + }; + } + $total = $select->count(); + $list = $select->order('A.id', 'desc')->limit($offset, $limit)->field('A.*,B.name domain')->select()->toArray(); + + foreach ($list as &$row) { + $row['checktimestr'] = date('Y-m-d H:i:s', $row['checktime']); + } + + return json(['total' => $total, 'rows' => $list]); + } + + public function taskform() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $action = $this->request->param('action'); + if ($this->request->isPost()) { + if ($action == 'add') { + $task = $this->request->post(); + $task['addtime'] = time(); + $task['active'] = 1; + + if (!isset($task['did'], $task['rr'], $task['recordid'], $task['main_value'], $task['frequency'], $task['cycle'])) { + return json(['code' => -1, 'msg' => '必填项不能为空']); + } + if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) { + return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']); + } + if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) { + return json(['code' => -1, 'msg' => '主备地址不能相同']); + } + if (Db::name('dmtask')->where('recordid', $task['recordid'])->find()) { + return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']); + } + Db::name('dmtask')->insert($task); + return json(['code' => 0, 'msg' => '添加成功']); + } elseif ($action == 'edit') { + $id = $this->request->post('id'); + $task = $this->request->post(); + unset($task['id']); + + if (!isset($task['did'], $task['rr'], $task['recordid'], $task['main_value'], $task['frequency'], $task['cycle'])) { + return json(['code' => -1, 'msg' => '必填项不能为空']); + } + if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) { + return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']); + } + if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) { + return json(['code' => -1, 'msg' => '主备地址不能相同']); + } + if (Dmtask::where('recordid', $task['recordid'])->where('id', '<>', $id)->find()) { + return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']); + } + Dmtask::where('id', $id)->update($task); + return json(['code' => 0, 'msg' => '修改成功']); + } elseif ($action == 'setactive') { + $id = $this->request->post('id'); + $active = $this->request->post('active'); + Dmtask::where('id', $id)->update(['active' => $active]); + return json(['code' => 0, 'msg' => '设置成功']); + } elseif ($action == 'del') { + $id = $this->request->post('id'); + Dmtask::where('id', $id)->delete(); + Dmlog::where('taskid', $id)->delete(); + return json(['code' => 0, 'msg' => '删除成功']); + } else { + return json(['code' => -1, 'msg' => '参数错误']); + } + } + $task = null; + if ($action == 'edit') { + $id = $this->request->get('id'); + $task = Dmtask::where('id', $id)->find(); + if (empty($task)) { + return $this->alert('error', '切换策略不存在'); + } + } + + $domains = []; + foreach (Domain::select() as $row) { + $domains[$row['id']] = $row['name']; + } + View::assign('domains', $domains); + + View::assign('info', $task); + View::assign('action', $action); + View::assign('support_ping', function_exists('exec') ? '1' : '0'); + return View::fetch(); + } + + public function taskinfo() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $id = $this->request->param('id'); + $task = Dmtask::where('id', $id)->find(); + if (empty($task)) { + return $this->alert('error', '切换策略不存在'); + } + + $switch_count = Dmlog::where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count(); + $fail_count = Dmlog::where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count(); + + $task['switch_count'] = $switch_count; + $task['fail_count'] = $fail_count; + if ($task['type'] == 3) { + $task['action_name'] = ['未知', '开启解析', '暂停解析']; + } elseif ($task['type'] == 2) { + $task['action_name'] = ['未知', '切换备用解析记录', '恢复主解析记录']; + } else { + $task['action_name'] = ['未知', '暂停解析', '启用解析']; + } + View::assign('info', $task); + return View::fetch(); + } + + public function tasklog_data() + { + if (!checkPermission(2)) { + return json(['total' => 0, 'rows' => []]); + } + $taskid = $this->request->param('id'); + $offset = $this->request->post('offset'); + $limit = $this->request->post('limit'); + $action = $this->request->post('action', 0); + + $select = Dmlog::where('taskid', $taskid); + if ($action > 0) { + $select->where('action', $action); + } + $total = $select->count(); + $list = $select->order('id', 'desc')->limit($offset, $limit)->select(); + + return json(['total' => $total, 'rows' => $list]); + } + + public function noticeset() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + if ($this->request->isPost()) { + $params = $this->request->post(); + if (isset($params['mail_type']) && isset($params['mail_name2']) && $params['mail_type'] > 0) { + $params['mail_name'] = $params['mail_name2']; + unset($params['mail_name2']); + } + foreach ($params as $key => $value) { + if (empty($key)) { + continue; + } + config_set($key, $value); + Cache::delete('configs'); + } + return json(['code' => 0, 'msg' => 'succ']); + } + return View::fetch(); + } + + public function proxyset() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + if ($this->request->isPost()) { + $params = $this->request->post(); + foreach ($params as $key => $value) { + if (empty($key)) { + continue; + } + config_set($key, $value); + Cache::delete('configs'); + } + return json(['code' => 0, 'msg' => 'succ']); + } + return View::fetch(); + } + + public function mailtest() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name'); + if (empty($mail_name)) { + return json(['code' => -1, 'msg' => '您还未设置邮箱!']); + } + $result = MsgNotice::send_mail($mail_name, '邮件发送测试。', '这是一封测试邮件!

来自:' . $this->request->root(true)); + if ($result === true) { + return json(['code' => 0, 'msg' => '邮件发送成功!']); + } else { + return json(['code' => -1, 'msg' => '邮件发送失败!' . $result]); + } + } + + public function tgbottest() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $tgbot_token = config_get('tgbot_token'); + $tgbot_chatid = config_get('tgbot_chatid'); + if (empty($tgbot_token) || empty($tgbot_chatid)) { + return json(['code' => -1, 'msg' => '请先保存设置']); + } + $content = "消息发送测试\n\n这是一封测试消息!\n\n来自:" . $this->request->root(true); + $result = MsgNotice::send_telegram_bot($content); + if ($result === true) { + return json(['code' => 0, 'msg' => '消息发送成功!']); + } else { + return json(['code' => -1, 'msg' => '消息发送失败!' . $result]); + } + } + + public function clean() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + if (Request::isPost()) { + $days = $this->request->post('days'); + if (!$days || $days < 0) { + return json(['code' => -1, 'msg' => '参数错误']); + } + Dmlog::where('date', '<', date("Y-m-d H:i:s", strtotime("-" . $days . " days")))->delete(); + Db::execute("OPTIMIZE TABLE `" . config('database.connections.mysql.prefix') . "dmlog`"); + return json(['code' => 0, 'msg' => '清理成功']); + } + } + + public function status() + { + $run_time = config_get('run_time', null, true); + $run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0; + return $run_state == 1 ? 'ok' : 'error'; + } +} diff --git a/app/controller/Index.php b/app/controller/Index.php index 339cec6..a31c7eb 100644 --- a/app/controller/Index.php +++ b/app/controller/Index.php @@ -1,38 +1,41 @@ user['type'] == 'domain'){ - return redirect('/record/'.request()->user['id']); + if ($this->request->user['type'] == 'domain') { + return redirect('/record/' . $this->request->user['id']); } - if(request()->isAjax()){ - if(input('post.do') == 'stat'){ - $stat = ['domains'=>0, 'users'=>0, 'records'=>0, 'types'=>count(DnsHelper::$dns_config)]; - if(request()->user['level'] == 2){ + if ($this->request->isAjax()) { + if (input('post.do') == 'stat') { + $stat = ['domains' => 0, 'users' => 0, 'records' => 0, 'types' => count(DnsHelper::$dns_config)]; + if ($this->request->user['level'] == 2) { $stat['domains'] = Db::name('domain')->count(); $stat['users'] = Db::name('user')->count(); $stat['records'] = Db::name('domain')->sum('recordcount'); - }else{ - $stat['domains'] = Db::name('domain')->where('name', 'in', request()->user['permission'])->count(); + } else { + $stat['domains'] = Db::name('domain')->where('name', 'in', $this->request->user['permission'])->count(); $stat['users'] = 1; - $stat['records'] = Db::name('domain')->where('name', 'in', request()->user['permission'])->sum('recordcount'); + $stat['records'] = Db::name('domain')->where('name', 'in', $this->request->user['permission'])->sum('recordcount'); } return json($stat); } - return json(['code'=>-3]); + return json(['code' => -3]); } - if(config('app.dbversion') && config_get('version') != config('app.dbversion')){ + if (config('app.dbversion') && config_get('version') != config('app.dbversion')) { $this->db_update(); config_set('version', config('app.dbversion')); Cache::clear(); @@ -49,74 +52,120 @@ class Index extends BaseController 'date' => date("Y-m-d H:i:s"), ]; View::assign('info', $info); - View::assign('checkupdate', '//auth.cccyun.cc/app/dnsmgr.php?ver='.config('app.version')); + View::assign('checkupdate', '//auth.cccyun.cc/app/dnsmgr.php?ver=' . config('app.version')); return view(); } - private function db_update(){ - $sqls=file_get_contents(app()->getAppPath().'sql/update.sql'); + private function db_update() + { + $sqls = file_get_contents(app()->getAppPath() . 'sql/update.sql'); $mysql_prefix = env('database.prefix', 'dnsmgr_'); - $sqls=explode(';', $sqls); + $sqls = explode(';', $sqls); foreach ($sqls as $value) { - $value=trim($value); - if(empty($value))continue; - $value = str_replace('dnsmgr_',$mysql_prefix,$value); - try{ + $value = trim($value); + if (empty($value)) { + continue; + } + $value = str_replace('dnsmgr_', $mysql_prefix, $value); + try { Db::execute($value); - }catch(Exception $e){ - + } catch (Exception $e) { + } } } - public function changeskin(){ + public function changeskin() + { $skin = input('post.skin'); - if(request()->user['level'] == 2){ - if(cookie('admin_skin')){ + if ($this->request->user['level'] == 2) { + if (cookie('admin_skin')) { cookie('admin_skin', null); } config_set('admin_skin', $skin); Cache::delete('configs'); - }else{ + } else { cookie('admin_skin', $skin); } - return json(['code'=>0,'msg'=>'succ']); + return json(['code' => 0, 'msg' => 'succ']); } - public function cleancache(){ - if(!checkPermission(1)) return $this->alert('error', '无权限'); + public function cleancache() + { + if (!checkPermission(1)) { + return $this->alert('error', '无权限'); + } Cache::clear(); - return json(['code'=>0,'msg'=>'succ']); + $this->clearDirectory(app()->getRuntimePath().'cache/'); + $this->clearDirectory(app()->getRuntimePath().'temp/'); + return json(['code' => 0, 'msg' => 'succ']); } - public function doc(){ - if(!checkPermission(1)) return $this->alert('error', '无权限'); - View::assign('siteurl', request()->root(true)); + public function doc() + { + if (!checkPermission(1)) { + return $this->alert('error', '无权限'); + } + View::assign('siteurl', $this->request->root(true)); return view(); } - public function setpwd(){ - if(!checkPermission(1)) return $this->alert('error', '无权限'); - if(request()->isPost()){ + public function setpwd() + { + if (!checkPermission(1)) { + return $this->alert('error', '无权限'); + } + if ($this->request->isPost()) { $oldpwd = input('post.oldpwd'); $newpwd = input('post.newpwd'); $newpwd2 = input('post.newpwd2'); - if(empty($oldpwd) || empty($newpwd) || empty($newpwd2)){ - return json(['code'=>-1, 'msg'=>'密码不能为空']); + if (empty($oldpwd) || empty($newpwd) || empty($newpwd2)) { + return json(['code' => -1, 'msg' => '密码不能为空']); } - if($newpwd != $newpwd2){ - return json(['code'=>-1, 'msg'=>'两次输入的密码不一致']); + if ($newpwd != $newpwd2) { + return json(['code' => -1, 'msg' => '两次输入的密码不一致']); } - if(!password_verify($oldpwd, request()->user['password'])){ - return json(['code'=>-1, 'msg'=>'原密码错误']); + if (!password_verify($oldpwd, $this->request->user['password'])) { + return json(['code' => -1, 'msg' => '原密码错误']); } - Db::name('user')->where('id', request()->user['id'])->update(['password'=>password_hash($newpwd, PASSWORD_DEFAULT)]); - return json(['code'=>0, 'msg'=>'succ']); + Db::name('user')->where('id', $this->request->user['id'])->update(['password' => password_hash($newpwd, PASSWORD_DEFAULT)]); + return json(['code' => 0, 'msg' => 'succ']); } return view(); } - public function test(){ + public function test() + { } + private function clearDirectory($dir) + { + // 确保路径是目录 + if (!is_dir($dir)) { + return false; + } + + // 打开目录 + $items = scandir($dir); + foreach ($items as $item) { + // 跳过 '.' 和 '..' + if ($item == '.' || $item == '..') { + continue; + } + + // 完整路径 + $path = $dir . DIRECTORY_SEPARATOR . $item; + + // 如果是目录,递归删除其内容 + if (is_dir($path)) { + clearDirectory($path); + // 删除空目录 + rmdir($path); + } else { + // 删除文件 + unlink($path); + } + } + return true; + } } diff --git a/app/controller/Install.php b/app/controller/Install.php index c3dcae4..853e435 100644 --- a/app/controller/Install.php +++ b/app/controller/Install.php @@ -1,4 +1,5 @@ getRootPath().'.env')){ + if (file_exists(app()->getRootPath() . '.env')) { return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件'; } - if(request()->isPost()){ + if (Request::isPost()) { $mysql_host = Request::post('mysql_host'); $mysql_port = intval(Request::post('mysql_port', '3306')); $mysql_user = Request::post('mysql_user', null, 'trim'); @@ -25,65 +26,66 @@ class Install extends BaseController $admin_username = Request::post('admin_username', null, 'trim'); $admin_password = Request::post('admin_password', null, 'trim'); - if(!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password){ - return json(['code'=>0, 'msg'=>'必填项不能为空']); + if (!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password) { + return json(['code' => 0, 'msg' => '必填项不能为空']); } - $configData = file_get_contents(app()->getRootPath().'.example.env'); - $configData = str_replace(['{dbhost}','{dbname}','{dbuser}','{dbpwd}','{dbport}','{dbprefix}'], [$mysql_host, $mysql_name, $mysql_user, $mysql_pwd, $mysql_port, $mysql_prefix], $configData); + $configData = file_get_contents(app()->getRootPath() . '.example.env'); + $configData = str_replace(['{dbhost}', '{dbname}', '{dbuser}', '{dbpwd}', '{dbport}', '{dbprefix}'], [$mysql_host, $mysql_name, $mysql_user, $mysql_pwd, $mysql_port, $mysql_prefix], $configData); - try{ - $DB = Db::connect(); - $DB=new PDO("mysql:host=".$mysql_host.";dbname=".$mysql_name.";port=".$mysql_port,$mysql_user,$mysql_pwd); - }catch(Exception $e){ - if($e->getCode() == 2002){ - $errorMsg='连接数据库失败:数据库地址填写错误!'; - }elseif($e->getCode() == 1045){ - $errorMsg='连接数据库失败:数据库用户名或密码填写错误!'; - }elseif($e->getCode() == 1049){ - $errorMsg='连接数据库失败:数据库名不存在!'; - }else{ - $errorMsg='连接数据库失败:'.$e->getMessage(); + try { + $DB = new PDO("mysql:host=" . $mysql_host . ";dbname=" . $mysql_name . ";port=" . $mysql_port, $mysql_user, $mysql_pwd); + } catch (Exception $e) { + if ($e->getCode() == 2002) { + $errorMsg = '连接数据库失败:数据库地址填写错误!'; + } elseif ($e->getCode() == 1045) { + $errorMsg = '连接数据库失败:数据库用户名或密码填写错误!'; + } elseif ($e->getCode() == 1049) { + $errorMsg = '连接数据库失败:数据库名不存在!'; + } else { + $errorMsg = '连接数据库失败:' . $e->getMessage(); } - return json(['code'=>0, 'msg'=>$errorMsg]); + return json(['code' => 0, 'msg' => $errorMsg]); } $DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $DB->exec("set sql_mode = ''"); $DB->exec("set names utf8"); - $sqls=file_get_contents(app()->getAppPath().'sql/install.sql'); - $sqls=explode(';', $sqls); + $sqls = file_get_contents(app()->getAppPath() . 'sql/install.sql'); + $sqls = explode(';', $sqls); $password = password_hash($admin_password, PASSWORD_DEFAULT); - $sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('sys_key', '".random(16)."')"; - $sqls[]="INSERT INTO `".$mysql_prefix."user` (`username`,`password`,`level`,`regtime`,`lasttime`,`status`) VALUES ('".addslashes($admin_username)."', '$password', 2, NOW(), NOW(), 1)"; + $sqls[] = "REPLACE INTO `" . $mysql_prefix . "config` VALUES ('sys_key', '" . random(16) . "')"; + $sqls[] = "INSERT INTO `" . $mysql_prefix . "user` (`username`,`password`,`level`,`regtime`,`lasttime`,`status`) VALUES ('" . addslashes($admin_username) . "', '$password', 2, NOW(), NOW(), 1)"; $success = 0; $error = 0; $errorMsg = null; foreach ($sqls as $value) { - $value=trim($value); - if(empty($value))continue; - $value = str_replace('dnsmgr_',$mysql_prefix,$value); - if($DB->exec($value)===false){ + $value = trim($value); + if (empty($value)) { + continue; + } + $value = str_replace('dnsmgr_', $mysql_prefix, $value); + if ($DB->exec($value) === false) { $error++; - $dberror=$DB->errorInfo(); - $errorMsg.=$dberror[2]."\n"; - }else{ + $dberror = $DB->errorInfo(); + $errorMsg .= $dberror[2] . "\n"; + } else { $success++; } } - if(empty($errorMsg)){ - if(!file_put_contents(app()->getRootPath().'.env', $configData)){ - return json(['code'=>0, 'msg'=>'保存失败,请确保网站根目录有写入权限']); + if (empty($errorMsg)) { + if (!file_put_contents(app()->getRootPath() . '.env', $configData)) { + return json(['code' => 0, 'msg' => '保存失败,请确保网站根目录有写入权限']); } Cache::clear(); - return json(['code'=>1, 'msg'=>'安装完成!成功执行SQL语句'.$success.'条']); - }else{ - return json(['code'=>0, 'msg'=>$errorMsg]); + return json(['code' => 1, 'msg' => '安装完成!成功执行SQL语句' . $success . '条']); + } else { + return json(['code' => 0, 'msg' => $errorMsg]); } } return view(); } - + } diff --git a/app/controller/Optimizeip.php b/app/controller/Optimizeip.php index 695182d..0430279 100644 --- a/app/controller/Optimizeip.php +++ b/app/controller/Optimizeip.php @@ -1,174 +1,192 @@ -alert('error', '无权限'); - if(request()->isPost()){ - $params = input('post.'); - foreach ($params as $key=>$value){ - if (empty($key)) { - continue; - } - config_set($key, $value); - Cache::delete('configs'); - } - return json(['code'=>0, 'msg'=>'succ']); - } - return View::fetch(); - } - - public function opiplist() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - return View::fetch(); - } - - public function opiplist_data(){ - if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]); - $type = input('post.type/d', 1); - $kw = input('post.kw', null, 'trim'); - $offset = input('post.offset/d'); - $limit = input('post.limit/d'); - - $select = Db::name('optimizeip')->alias('A')->join('domain B','A.did = B.id'); - if(!empty($kw)){ - if($type == 1){ - $select->whereLike('rr|B.name', '%'.$kw.'%'); - }elseif($type == 2){ - $select->whereLike('remark', '%'.$kw.'%'); - } - } - $total = $select->count(); - $list = $select->order('A.id','desc')->limit($offset, $limit)->field('A.*,B.name domain')->select(); - - return json(['total'=>$total, 'rows'=>$list]); - } - - public function opipform() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $action = input('param.action'); - if(request()->isPost()){ - if($action == 'add'){ - $task = [ - 'did' => input('post.did/d'), - 'rr' => input('post.rr', null, 'trim'), - 'type' => input('post.type/d'), - 'ip_type' => input('post.ip_type', null, 'trim'), - 'cdn_type' => input('post.cdn_type/d'), - 'recordnum' => input('post.recordnum/d'), - 'ttl' => input('post.ttl/d'), - 'remark' => input('post.remark', null, 'trim'), - 'addtime' => date('Y-m-d H:i:s'), - 'active' => 1 - ]; - - if(empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])){ - return json(['code'=>-1, 'msg'=>'必填项不能为空']); - } - if($task['recordnum'] > 5){ - return json(['code'=>-1, 'msg'=>'解析数量不能超过5个']); - } - if(Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->find()){ - return json(['code'=>-1, 'msg'=>'当前域名的优选IP任务已存在']); - } - Db::name('optimizeip')->insert($task); - return json(['code'=>0, 'msg'=>'添加成功']); - }elseif($action == 'edit'){ - $id = input('post.id/d'); - $task = [ - 'did' => input('post.did/d'), - 'rr' => input('post.rr', null, 'trim'), - 'type' => input('post.type/d'), - 'ip_type' => input('post.ip_type', null, 'trim'), - 'cdn_type' => input('post.cdn_type/d'), - 'recordnum' => input('post.recordnum/d'), - 'ttl' => input('post.ttl/d'), - 'remark' => input('post.remark', null, 'trim'), - ]; - - if(empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])){ - return json(['code'=>-1, 'msg'=>'必填项不能为空']); - } - if($task['recordnum'] > 5){ - return json(['code'=>-1, 'msg'=>'解析数量不能超过5个']); - } - if(Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->where('id', '<>', $id)->find()){ - return json(['code'=>-1, 'msg'=>'当前域名的优选IP任务已存在']); - } - Db::name('optimizeip')->where('id', $id)->update($task); - return json(['code'=>0, 'msg'=>'修改成功']); - }elseif($action == 'setactive'){ - $id = input('post.id/d'); - $active = input('post.active/d'); - Db::name('optimizeip')->where('id', $id)->update(['active'=>$active]); - return json(['code'=>0, 'msg'=>'设置成功']); - }elseif($action == 'del'){ - $id = input('post.id/d'); - Db::name('optimizeip')->where('id', $id)->delete(); - return json(['code'=>0, 'msg'=>'删除成功']); - }elseif($action == 'run'){ - $id = input('post.id/d'); - $task = Db::name('optimizeip')->where('id', $id)->find(); - if(empty($task)) return json(['code'=>-1, 'msg'=>'任务不存在']); - try{ - $result = (new OptimizeService())->execute_one($task); - Db::name('optimizeip')->where('id', $id)->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]); - return json(['code'=>0, 'msg'=>'优选任务执行成功:'.$result]); - }catch(Exception $e){ - Db::name('optimizeip')->where('id', $id)->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]); - return json(['code'=>-1, 'msg'=>'优选任务执行失败:'.$e->getMessage(), 'stack'=>$e->__toString()]); - } - }else{ - return json(['code'=>-1, 'msg'=>'参数错误']); - } - } - $task = null; - if($action == 'edit'){ - $id = input('get.id/d'); - $task = Db::name('optimizeip')->where('id', $id)->find(); - if(empty($task)) return $this->alert('error', '任务不存在'); - } - - $domains = []; - foreach(Db::name('domain')->alias('A')->join('account B','A.aid = B.id')->field('A.*')->where('B.type', '<>', 'cloudflare')->select() as $row){ - $domains[$row['id']] = $row['name']; - } - View::assign('domains', $domains); - - View::assign('info', $task); - View::assign('action', $action); - return View::fetch(); - } - - public function queryapi() - { - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $optimize_ip_api = input('post.optimize_ip_api/d'); - $optimize_ip_key = input('post.optimize_ip_key', null, 'trim'); - if(empty($optimize_ip_key)) return json(['code'=>-1, 'msg'=>'参数不能为空']); - try{ - $result = (new OptimizeService())->get_license($optimize_ip_api, $optimize_ip_key); - return json(['code'=>0, 'msg'=>'当前积分余额:'.$result]); - }catch(Exception $e){ - return json(['code'=>-1, 'msg'=>$e->getMessage()]); - } - } - - public function status() - { - $run_time = Db::name('optimizeip')->where('active', 1)->order('updatetime', 'desc')->value('updatetime'); - $run_state = $run_time ? (time()-strtotime($run_time) > 3600 ? 0 : 1) : 0; - return $run_state == 1 ? 'ok' : 'error'; - } -} \ No newline at end of file +alert('error', '无权限'); + } + if ($this->request->isPost()) { + $params = input('post.'); + foreach ($params as $key => $value) { + if (empty($key)) { + continue; + } + config_set($key, $value); + Cache::delete('configs'); + } + return json(['code' => 0, 'msg' => 'succ']); + } + return View::fetch(); + } + + public function opiplist() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + return View::fetch(); + } + + public function opiplist_data() + { + if (!checkPermission(2)) { + return json(['total' => 0, 'rows' => []]); + } + $type = input('post.type/d', 1); + $kw = input('post.kw', null, 'trim'); + $offset = input('post.offset/d'); + $limit = input('post.limit/d'); + + $select = Db::name('optimizeip')->alias('A')->join('domain B', 'A.did = B.id'); + if (!empty($kw)) { + if ($type == 1) { + $select->whereLike('rr|B.name', '%' . $kw . '%'); + } elseif ($type == 2) { + $select->whereLike('remark', '%' . $kw . '%'); + } + } + $total = $select->count(); + $list = $select->order('A.id', 'desc')->limit($offset, $limit)->field('A.*,B.name domain')->select(); + + return json(['total' => $total, 'rows' => $list]); + } + + public function opipform() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $action = input('param.action'); + if ($this->request->isPost()) { + if ($action == 'add') { + $task = [ + 'did' => input('post.did/d'), + 'rr' => input('post.rr', null, 'trim'), + 'type' => input('post.type/d'), + 'ip_type' => input('post.ip_type', null, 'trim'), + 'cdn_type' => input('post.cdn_type/d'), + 'recordnum' => input('post.recordnum/d'), + 'ttl' => input('post.ttl/d'), + 'remark' => input('post.remark', null, 'trim'), + 'addtime' => date('Y-m-d H:i:s'), + 'active' => 1 + ]; + + if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) { + return json(['code' => -1, 'msg' => '必填项不能为空']); + } + if ($task['recordnum'] > 5) { + return json(['code' => -1, 'msg' => '解析数量不能超过5个']); + } + if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->find()) { + return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']); + } + Db::name('optimizeip')->insert($task); + return json(['code' => 0, 'msg' => '添加成功']); + } elseif ($action == 'edit') { + $id = input('post.id/d'); + $task = [ + 'did' => input('post.did/d'), + 'rr' => input('post.rr', null, 'trim'), + 'type' => input('post.type/d'), + 'ip_type' => input('post.ip_type', null, 'trim'), + 'cdn_type' => input('post.cdn_type/d'), + 'recordnum' => input('post.recordnum/d'), + 'ttl' => input('post.ttl/d'), + 'remark' => input('post.remark', null, 'trim'), + ]; + + if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) { + return json(['code' => -1, 'msg' => '必填项不能为空']); + } + if ($task['recordnum'] > 5) { + return json(['code' => -1, 'msg' => '解析数量不能超过5个']); + } + if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->where('id', '<>', $id)->find()) { + return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']); + } + Db::name('optimizeip')->where('id', $id)->update($task); + return json(['code' => 0, 'msg' => '修改成功']); + } elseif ($action == 'setactive') { + $id = input('post.id/d'); + $active = input('post.active/d'); + Db::name('optimizeip')->where('id', $id)->update(['active' => $active]); + return json(['code' => 0, 'msg' => '设置成功']); + } elseif ($action == 'del') { + $id = input('post.id/d'); + Db::name('optimizeip')->where('id', $id)->delete(); + return json(['code' => 0, 'msg' => '删除成功']); + } elseif ($action == 'run') { + $id = input('post.id/d'); + $task = Db::name('optimizeip')->where('id', $id)->find(); + if (empty($task)) { + return json(['code' => -1, 'msg' => '任务不存在']); + } + try { + $result = (new OptimizeService())->execute_one($task); + Db::name('optimizeip')->where('id', $id)->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]); + return json(['code' => 0, 'msg' => '优选任务执行成功:' . $result]); + } catch (Exception $e) { + Db::name('optimizeip')->where('id', $id)->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]); + return json(['code' => -1, 'msg' => '优选任务执行失败:' . $e->getMessage(), 'stack' => $e->__toString()]); + } + } else { + return json(['code' => -1, 'msg' => '参数错误']); + } + } + $task = null; + if ($action == 'edit') { + $id = input('get.id/d'); + $task = Db::name('optimizeip')->where('id', $id)->find(); + if (empty($task)) { + return $this->alert('error', '任务不存在'); + } + } + + $domains = []; + foreach (Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.*')->where('B.type', '<>', 'cloudflare')->select() as $row) { + $domains[$row['id']] = $row['name']; + } + View::assign('domains', $domains); + + View::assign('info', $task); + View::assign('action', $action); + return View::fetch(); + } + + public function queryapi() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $optimize_ip_api = input('post.optimize_ip_api/d'); + $optimize_ip_key = input('post.optimize_ip_key', null, 'trim'); + if (empty($optimize_ip_key)) { + return json(['code' => -1, 'msg' => '参数不能为空']); + } + try { + $result = (new OptimizeService())->get_license($optimize_ip_api, $optimize_ip_key); + return json(['code' => 0, 'msg' => '当前积分余额:' . $result]); + } catch (Exception $e) { + return json(['code' => -1, 'msg' => $e->getMessage()]); + } + } + + public function status() + { + $run_time = Db::name('optimizeip')->where('active', 1)->order('updatetime', 'desc')->value('updatetime'); + $run_state = $run_time ? (time() - strtotime($run_time) > 3600 ? 0 : 1) : 0; + return $run_state == 1 ? 'ok' : 'error'; + } +} diff --git a/app/controller/User.php b/app/controller/User.php index 99b8ab2..bffcbd1 100644 --- a/app/controller/User.php +++ b/app/controller/User.php @@ -1,164 +1,196 @@ -alert('error', '无权限'); - $list = Db::name('domain')->select(); - $domains = []; - foreach($list as $row){ - $domains[] = $row['name']; - } - View::assign('domains', $domains); - return view(); - } - - public function user_data(){ - if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]); - $kw = input('post.kw', null, 'trim'); - $offset = input('post.offset/d'); - $limit = input('post.limit/d'); - - $select = Db::name('user'); - if(!empty($kw)){ - $select->whereLike('id|username', $kw); - } - $total = $select->count(); - $rows = $select->order('id','desc')->limit($offset, $limit)->select(); - - return json(['total'=>$total, 'rows'=>$rows]); - } - - public function user_op(){ - if(!checkPermission(2)) return $this->alert('error', '无权限'); - $act = input('param.act'); - if($act == 'get'){ - $id = input('post.id/d'); - $row = Db::name('user')->where('id', $id)->find(); - if(!$row) return json(['code'=>-1, 'msg'=>'用户不存在']); - $row['permission'] = Db::name('permission')->where('uid', $id)->column('domain'); - return json(['code'=>0, 'data'=>$row]); - }elseif($act == 'add'){ - $username = input('post.username', null, 'trim'); - $password = input('post.password', null, 'trim'); - $is_api = input('post.is_api/d'); - $apikey = input('post.apikey', null, 'trim'); - $level = input('post.level/d'); - if(empty($username) || empty($password)) return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']); - if($is_api ==1 && empty($apikey)) return json(['code'=>-1, 'msg'=>'API密钥不能为空']); - if(Db::name('user')->where('username', $username)->find()){ - return json(['code'=>-1, 'msg'=>'用户名已存在']); - } - $uid = Db::name('user')->insertGetId([ - 'username' => $username, - 'password' => password_hash($password, PASSWORD_DEFAULT), - 'is_api' => $is_api, - 'apikey' => $apikey, - 'level' => $level, - 'regtime' => date('Y-m-d H:i:s'), - 'status' => 1, - ]); - if($level == 1){ - $permission = input('post.permission/a'); - if(!empty($permission)){ - $data = []; - foreach($permission as $domain){ - $data[] = ['uid'=>$uid, 'domain'=>$domain]; - } - Db::name('permission')->insertAll($data); - } - } - return json(['code'=>0, 'msg'=>'添加用户成功!']); - }elseif($act == 'edit'){ - $id = input('post.id/d'); - $row = Db::name('user')->where('id', $id)->find(); - if(!$row) return json(['code'=>-1, 'msg'=>'用户不存在']); - $username = input('post.username', null, 'trim'); - $is_api = input('post.is_api/d'); - $apikey = input('post.apikey', null, 'trim'); - $level = input('post.level/d'); - $repwd = input('post.repwd', null, 'trim'); - if(empty($username)) return json(['code'=>-1, 'msg'=>'用户名不能为空']); - if($is_api ==1 && empty($apikey)) return json(['code'=>-1, 'msg'=>'API密钥不能为空']); - if(Db::name('user')->where('username', $username)->where('id', '<>', $id)->find()){ - return json(['code'=>-1, 'msg'=>'用户名已存在']); - } - if($level == 1 && ($id == 1000 || $id == request()->user['id'])) $level = 2; - Db::name('user')->where('id', $id)->update([ - 'username' => $username, - 'is_api' => $is_api, - 'apikey' => $apikey, - 'level' => $level, - ]); - Db::name('permission')->where(['uid'=>$id])->delete(); - if($level == 1){ - $permission = input('post.permission/a'); - if(!empty($permission)){ - $data = []; - foreach($permission as $domain){ - $data[] = ['uid'=>$id, 'domain'=>$domain]; - } - Db::name('permission')->insertAll($data); - } - } - if(!empty($repwd)){ - Db::name('user')->where('id', $id)->update(['password'=>password_hash($repwd, PASSWORD_DEFAULT)]); - } - return json(['code'=>0, 'msg'=>'修改用户成功!']); - }elseif($act == 'set'){ - $id = input('post.id/d'); - $status = input('post.status/d'); - if($id == 1000) return json(['code'=>-1, 'msg'=>'此用户无法修改状态']); - if($id == request()->user['id']) return json(['code'=>-1, 'msg'=>'当前登录用户无法修改状态']); - Db::name('user')->where('id', $id)->update(['status'=>$status]); - return json(['code'=>0]); - }elseif($act == 'del'){ - $id = input('post.id/d'); - if($id == 1000) return json(['code'=>-1, 'msg'=>'此用户无法删除']); - if($id == request()->user['id']) return json(['code'=>-1, 'msg'=>'当前登录用户无法删除']); - Db::name('user')->where('id', $id)->delete(); - return json(['code'=>0]); - } - return json(['code'=>-3]); - } - - public function log(){ - return view(); - } - - public function log_data(){ - $uid = input('post.uid', null, 'trim'); - $kw = input('post.kw', null, 'trim'); - $domain = input('post.domain', null, 'trim'); - $offset = input('post.offset/d'); - $limit = input('post.limit/d'); - - $select = Db::name('log'); - if(request()->user['type'] == 'domain'){ - $select->where('domain', request()->user['name']); - }elseif(request()->user['level'] == 1){ - $select->where('uid', request()->user['id']); - }elseif(!empty($uid)){ - $select->where('uid', $uid); - } - if(!empty($kw)){ - $select->whereLike('action|data', '%'.$kw.'%'); - } - if(!empty($domain)){ - $select->where('domain', $domain); - } - $total = $select->count(); - $rows = $select->order('id','desc')->limit($offset, $limit)->select(); - - return json(['total'=>$total, 'rows'=>$rows]); - } - -} \ No newline at end of file +alert('error', '无权限'); + } + $list = Db::name('domain')->select(); + $domains = []; + foreach ($list as $row) { + $domains[] = $row['name']; + } + View::assign('domains', $domains); + return view(); + } + + public function user_data() + { + if (!checkPermission(2)) { + return json(['total' => 0, 'rows' => []]); + } + $kw = input('post.kw', null, 'trim'); + $offset = input('post.offset/d'); + $limit = input('post.limit/d'); + + $select = Db::name('user'); + if (!empty($kw)) { + $select->whereLike('id|username', $kw); + } + $total = $select->count(); + $rows = $select->order('id', 'desc')->limit($offset, $limit)->select(); + + return json(['total' => $total, 'rows' => $rows]); + } + + public function user_op() + { + if (!checkPermission(2)) { + return $this->alert('error', '无权限'); + } + $act = input('param.act'); + if ($act == 'get') { + $id = input('post.id/d'); + $row = Db::name('user')->where('id', $id)->find(); + if (!$row) { + return json(['code' => -1, 'msg' => '用户不存在']); + } + $row['permission'] = Db::name('permission')->where('uid', $id)->column('domain'); + return json(['code' => 0, 'data' => $row]); + } elseif ($act == 'add') { + $username = input('post.username', null, 'trim'); + $password = input('post.password', null, 'trim'); + $is_api = input('post.is_api/d'); + $apikey = input('post.apikey', null, 'trim'); + $level = input('post.level/d'); + if (empty($username) || empty($password)) { + return json(['code' => -1, 'msg' => '用户名或密码不能为空']); + } + if ($is_api == 1 && empty($apikey)) { + return json(['code' => -1, 'msg' => 'API密钥不能为空']); + } + if (Db::name('user')->where('username', $username)->find()) { + return json(['code' => -1, 'msg' => '用户名已存在']); + } + $uid = Db::name('user')->insertGetId([ + 'username' => $username, + 'password' => password_hash($password, PASSWORD_DEFAULT), + 'is_api' => $is_api, + 'apikey' => $apikey, + 'level' => $level, + 'regtime' => date('Y-m-d H:i:s'), + 'status' => 1, + ]); + if ($level == 1) { + $permission = input('post.permission/a'); + if (!empty($permission)) { + $data = []; + foreach ($permission as $domain) { + $data[] = ['uid' => $uid, 'domain' => $domain]; + } + Db::name('permission')->insertAll($data); + } + } + return json(['code' => 0, 'msg' => '添加用户成功!']); + } elseif ($act == 'edit') { + $id = input('post.id/d'); + $row = Db::name('user')->where('id', $id)->find(); + if (!$row) { + return json(['code' => -1, 'msg' => '用户不存在']); + } + $username = input('post.username', null, 'trim'); + $is_api = input('post.is_api/d'); + $apikey = input('post.apikey', null, 'trim'); + $level = input('post.level/d'); + $repwd = input('post.repwd', null, 'trim'); + if (empty($username)) { + return json(['code' => -1, 'msg' => '用户名不能为空']); + } + if ($is_api == 1 && empty($apikey)) { + return json(['code' => -1, 'msg' => 'API密钥不能为空']); + } + if (Db::name('user')->where('username', $username)->where('id', '<>', $id)->find()) { + return json(['code' => -1, 'msg' => '用户名已存在']); + } + if ($level == 1 && ($id == 1000 || $id == $this->request->user['id'])) { + $level = 2; + } + Db::name('user')->where('id', $id)->update([ + 'username' => $username, + 'is_api' => $is_api, + 'apikey' => $apikey, + 'level' => $level, + ]); + Db::name('permission')->where(['uid' => $id])->delete(); + if ($level == 1) { + $permission = input('post.permission/a'); + if (!empty($permission)) { + $data = []; + foreach ($permission as $domain) { + $data[] = ['uid' => $id, 'domain' => $domain]; + } + Db::name('permission')->insertAll($data); + } + } + if (!empty($repwd)) { + Db::name('user')->where('id', $id)->update(['password' => password_hash($repwd, PASSWORD_DEFAULT)]); + } + return json(['code' => 0, 'msg' => '修改用户成功!']); + } elseif ($act == 'set') { + $id = input('post.id/d'); + $status = input('post.status/d'); + if ($id == 1000) { + return json(['code' => -1, 'msg' => '此用户无法修改状态']); + } + if ($id == $this->request->user['id']) { + return json(['code' => -1, 'msg' => '当前登录用户无法修改状态']); + } + Db::name('user')->where('id', $id)->update(['status' => $status]); + return json(['code' => 0]); + } elseif ($act == 'del') { + $id = input('post.id/d'); + if ($id == 1000) { + return json(['code' => -1, 'msg' => '此用户无法删除']); + } + if ($id == $this->request->user['id']) { + return json(['code' => -1, 'msg' => '当前登录用户无法删除']); + } + Db::name('user')->where('id', $id)->delete(); + return json(['code' => 0]); + } + return json(['code' => -3]); + } + + public function log() + { + return view(); + } + + public function log_data() + { + $uid = input('post.uid', null, 'trim'); + $kw = input('post.kw', null, 'trim'); + $domain = input('post.domain', null, 'trim'); + $offset = input('post.offset/d'); + $limit = input('post.limit/d'); + + $select = Db::name('log'); + if ($this->request->user['type'] == 'domain') { + $select->where('domain', $this->request->user['name']); + } elseif ($this->request->user['level'] == 1) { + $select->where('uid', $this->request->user['id']); + } elseif (!empty($uid)) { + $select->where('uid', $uid); + } + if (!empty($kw)) { + $select->whereLike('action|data', '%' . $kw . '%'); + } + if (!empty($domain)) { + $select->where('domain', $domain); + } + $total = $select->count(); + $rows = $select->order('id', 'desc')->limit($offset, $limit)->select(); + + return json(['total' => $total, 'rows' => $rows]); + } + +} diff --git a/app/event.php b/app/event.php index e9851bb..c3393e1 100644 --- a/app/event.php +++ b/app/event.php @@ -1,4 +1,5 @@ [ diff --git a/app/lib/CheckUtils.php b/app/lib/CheckUtils.php index 2b4ba21..3c173c2 100644 --- a/app/lib/CheckUtils.php +++ b/app/lib/CheckUtils.php @@ -1,112 +1,120 @@ -= 400)){ - $status = false; - $errmsg = 'http_code='.$httpcode; - } - $usetime = round(curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000); - curl_close($ch); - return ['status'=>$status, 'errmsg'=>$errmsg, 'usetime'=>$usetime]; - } - - public static function tcp($target, $port, $timeout){ - if(!filter_var($target,FILTER_VALIDATE_IP) && checkDomain($target)){ - $target = gethostbyname($target); - if(!$target)return ['status'=>false, 'error'=>'DNS resolve failed', 'usetime'=>0]; - } - $starttime = getMillisecond(); - $fp = @fsockopen($target, $port, $errCode, $errStr, $timeout); - if ($fp) { - $status = true; - fclose($fp); - } else { - $status = false; - } - $endtime = getMillisecond(); - $usetime = $endtime-$starttime; - return ['status'=>$status, 'errmsg'=>$errStr, 'usetime'=>$usetime]; - } - - public static function ping($target){ - if(!function_exists('exec'))return ['status'=>false, 'error'=>'exec函数不可用', 'usetime'=>0]; - if(!filter_var($target,FILTER_VALIDATE_IP) && checkDomain($target)){ - $target = gethostbyname($target); - if(!$target)return ['status'=>false, 'error'=>'DNS resolve failed', 'usetime'=>0]; - } - if(!filter_var($target,FILTER_VALIDATE_IP)){ - return ['status'=>false, 'error'=>'Invalid IP address', 'usetime'=>0]; - } - $timeout = 1; - exec('ping -c 1 -w '.$timeout.' '.$target.'', $output, $return_var); - $usetime = !empty($output[1]) ? round(getSubstr($output[1], 'time=', ' ms')) : 0; - $errmsg = null; - if($return_var !== 0){ - $usetime = $usetime == 0 ? $timeout*1000 : $usetime; - $errmsg = 'ping timeout'; - } - return ['status'=>$return_var===0, 'errmsg'=>$errmsg, 'usetime'=>$usetime]; - } -} \ No newline at end of file += 400)) { + $status = false; + $errmsg = 'http_code='.$httpcode; + } + $usetime = round(curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000); + curl_close($ch); + return ['status' => $status, 'errmsg' => $errmsg, 'usetime' => $usetime]; + } + + public static function tcp($target, $port, $timeout) + { + if (!filter_var($target, FILTER_VALIDATE_IP) && checkDomain($target)) { + $target = gethostbyname($target); + if (!$target) { + return ['status' => false, 'error' => 'DNS resolve failed', 'usetime' => 0]; + } + } + $starttime = getMillisecond(); + $fp = @fsockopen($target, $port, $errCode, $errStr, $timeout); + if ($fp) { + $status = true; + fclose($fp); + } else { + $status = false; + } + $endtime = getMillisecond(); + $usetime = $endtime - $starttime; + return ['status' => $status, 'errmsg' => $errStr, 'usetime' => $usetime]; + } + + public static function ping($target) + { + if (!function_exists('exec')) { + return ['status' => false, 'error' => 'exec函数不可用', 'usetime' => 0]; + } + if (!filter_var($target, FILTER_VALIDATE_IP) && checkDomain($target)) { + $target = gethostbyname($target); + if (!$target) { + return ['status' => false, 'error' => 'DNS resolve failed', 'usetime' => 0]; + } + } + if (!filter_var($target, FILTER_VALIDATE_IP)) { + return ['status' => false, 'error' => 'Invalid IP address', 'usetime' => 0]; + } + $timeout = 1; + exec('ping -c 1 -w '.$timeout.' '.$target.'', $output, $return_var); + $usetime = !empty($output[1]) ? round(getSubstr($output[1], 'time=', ' ms')) : 0; + $errmsg = null; + if ($return_var !== 0) { + $usetime = $usetime == 0 ? $timeout * 1000 : $usetime; + $errmsg = 'ping timeout'; + } + return ['status' => $return_var === 0, 'errmsg' => $errmsg, 'usetime' => $usetime]; + } +} diff --git a/app/lib/DnsHelper.php b/app/lib/DnsHelper.php index 8bcf329..840d80c 100644 --- a/app/lib/DnsHelper.php +++ b/app/lib/DnsHelper.php @@ -1,145 +1,151 @@ - [ - 'name' => '阿里云', - 'config' => [ - 'ak' => 'AccessKeyId', - 'sk' => 'AccessKeySecret' - ], - 'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置 - 'status' => true, //是否支持启用暂停 - 'redirect' => true, //是否支持域名转发 - 'log' => true, //是否支持查看日志 - 'weight' => false, //是否支持权重 - ], - 'dnspod' => [ - 'name' => '腾讯云', - 'config' => [ - 'ak' => 'SecretId', - 'sk' => 'SecretKey' - ], - 'remark' => 1, - 'status' => true, - 'redirect' => true, - 'log' => true, - 'weight' => true, - ], - 'huawei' => [ - 'name' => '华为云', - 'config' => [ - 'ak' => 'AccessKeyId', - 'sk' => 'SecretAccessKey' - ], - 'remark' => 2, - 'status' => true, - 'redirect' => false, - 'log' => false, - 'weight' => true, - ], - 'baidu' => [ - 'name' => '百度云', - 'config' => [ - 'ak' => 'AccessKey', - 'sk' => 'SecretKey' - ], - 'remark' => 2, - 'status' => false, - 'redirect' => false, - 'log' => false, - 'weight' => false, - ], - 'west' => [ - 'name' => '西部数码', - 'config' => [ - 'ak' => '用户名', - 'sk' => 'API密码' - ], - 'remark' => 0, - 'status' => true, - 'redirect' => false, - 'log' => false, - 'weight' => false, - ], - 'huoshan' => [ - 'name' => '火山引擎', - 'config' => [ - 'ak' => 'AccessKeyId', - 'sk' => 'SecretAccessKey' - ], - 'remark' => 2, - 'status' => true, - 'redirect' => false, - 'log' => false, - 'weight' => true, - ], - 'dnsla' => [ - 'name' => 'DNSLA', - 'config' => [ - 'ak' => 'APIID', - 'sk' => 'API密钥' - ], - 'remark' => 0, - 'status' => true, - 'redirect' => true, - 'log' => false, - 'weight' => true, - ], - 'cloudflare' => [ - 'name' => 'Cloudflare', - 'config' => [ - 'ak' => '邮箱地址', - 'sk' => 'API密钥/令牌' - ], - 'remark' => 2, - 'status' => false, - 'redirect' => false, - 'log' => false, - 'weight' => false, - ], - ]; - - public static function getList() - { - return self::$dns_config; - } - - private static function getConfig($aid){ - $account = Db::name('account')->where('id', $aid)->find(); - if(!$account) return false; - return $account; - } - - public static function getModel($aid, $domain = null, $domainid = null) - { - $config = self::getConfig($aid); - if(!$config) return false; - $dnstype = $config['type']; - $class = "\\app\\lib\\dns\\{$dnstype}"; - if(class_exists($class)){ - $config['domain'] = $domain; - $config['domainid'] = $domainid; - $model = new $class($config); - return $model; - } - return false; - } - - public static function getModel2($config) - { - $dnstype = $config['type']; - $class = "\\app\\lib\\dns\\{$dnstype}"; - if(class_exists($class)){ - $config['domain'] = $config['name']; - $config['domainid'] = $config['thirdid']; - $model = new $class($config); - return $model; - } - return false; - } -} \ No newline at end of file + [ + 'name' => '阿里云', + 'config' => [ + 'ak' => 'AccessKeyId', + 'sk' => 'AccessKeySecret' + ], + 'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置 + 'status' => true, //是否支持启用暂停 + 'redirect' => true, //是否支持域名转发 + 'log' => true, //是否支持查看日志 + 'weight' => false, //是否支持权重 + ], + 'dnspod' => [ + 'name' => '腾讯云', + 'config' => [ + 'ak' => 'SecretId', + 'sk' => 'SecretKey' + ], + 'remark' => 1, + 'status' => true, + 'redirect' => true, + 'log' => true, + 'weight' => true, + ], + 'huawei' => [ + 'name' => '华为云', + 'config' => [ + 'ak' => 'AccessKeyId', + 'sk' => 'SecretAccessKey' + ], + 'remark' => 2, + 'status' => true, + 'redirect' => false, + 'log' => false, + 'weight' => true, + ], + 'baidu' => [ + 'name' => '百度云', + 'config' => [ + 'ak' => 'AccessKey', + 'sk' => 'SecretKey' + ], + 'remark' => 2, + 'status' => false, + 'redirect' => false, + 'log' => false, + 'weight' => false, + ], + 'west' => [ + 'name' => '西部数码', + 'config' => [ + 'ak' => '用户名', + 'sk' => 'API密码' + ], + 'remark' => 0, + 'status' => true, + 'redirect' => false, + 'log' => false, + 'weight' => false, + ], + 'huoshan' => [ + 'name' => '火山引擎', + 'config' => [ + 'ak' => 'AccessKeyId', + 'sk' => 'SecretAccessKey' + ], + 'remark' => 2, + 'status' => true, + 'redirect' => false, + 'log' => false, + 'weight' => true, + ], + 'dnsla' => [ + 'name' => 'DNSLA', + 'config' => [ + 'ak' => 'APIID', + 'sk' => 'API密钥' + ], + 'remark' => 0, + 'status' => true, + 'redirect' => true, + 'log' => false, + 'weight' => true, + ], + 'cloudflare' => [ + 'name' => 'Cloudflare', + 'config' => [ + 'ak' => '邮箱地址', + 'sk' => 'API密钥/令牌' + ], + 'remark' => 2, + 'status' => false, + 'redirect' => false, + 'log' => false, + 'weight' => false, + ], + ]; + + public static function getList() + { + return self::$dns_config; + } + + private static function getConfig($aid) + { + $account = Db::name('account')->where('id', $aid)->find(); + if (!$account) { + return false; + } + return $account; + } + + public static function getModel($aid, $domain = null, $domainid = null): DnsInterface|bool + { + $config = self::getConfig($aid); + if (!$config) { + return false; + } + $dnstype = $config['type']; + $class = "\\app\\lib\\dns\\$dnstype"; + if (class_exists($class)) { + $config['domain'] = $domain; + $config['domainid'] = $domainid; + $model = new $class($config); + return $model; + } + return false; + } + + public static function getModel2($config) + { + $dnstype = $config['type']; + $class = "\\app\\lib\\dns\\{$dnstype}"; + if (class_exists($class)) { + $config['domain'] = $config['name']; + $config['domainid'] = $config['thirdid']; + $model = new $class($config); + return $model; + } + return false; + } +} diff --git a/app/lib/DnsInterface.php b/app/lib/DnsInterface.php index 166030c..9411108 100644 --- a/app/lib/DnsInterface.php +++ b/app/lib/DnsInterface.php @@ -1,35 +1,35 @@ -您的域名 '.$task['domain'].''.$task['main_value'].' 记录发生了异常'; - if($task['type'] == 2){ - $mail_content .= ',已自动切换为备用解析记录 '.$task['backup_value'].' '; - }elseif($task['type'] == 1){ - $mail_content .= ',已自动暂停解析'; - }else{ - $mail_content .= ',请及时处理'; - } - if(!empty($result['errmsg'])){ - $mail_content .= '。
异常信息:'.$result['errmsg']; - } - }else{ - $mail_title = 'DNS容灾切换-恢复正常通知'; - $mail_content = '尊敬的系统管理员,您好:
您的域名 '.$task['domain'].''.$task['main_value'].' 记录已恢复正常'; - if($task['type'] == 2){ - $mail_content .= ',已自动切换回当前解析记录'; - }elseif($task['type'] == 1){ - $mail_content .= ',已自动开启解析'; - } - $lasttime = convert_second(time() - $task['switchtime']); - $mail_content .= '。
异常持续时间:'.$lasttime; - } - if(!empty($task['remark'])) $mail_title .= '('.$task['remark'].')'; - if(!empty($task['remark'])) $mail_content .= '
备注:'.$task['remark']; - $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'); - self::send_mail($mail_name, $mail_title, $mail_content); - } - if(config_get('notice_wxtpl') == 1){ - $content = str_replace(['
', '', ''], ["\n\n", '**', '**'], $mail_content); - self::send_wechat_tplmsg($mail_title, $content); - } - if(config_get('notice_tgbot') == 1){ - $content = str_replace('
', "\n", $mail_content); - $content = "".$mail_title."\n".$content; - self::send_telegram_bot($content); - } - } - - public static function send_mail($to, $sub, $msg){ - $mail_type = config_get('mail_type'); - if($mail_type == 1){ - $mail = new \app\lib\mail\Sendcloud(config_get('mail_apiuser'), config_get('mail_apikey')); - return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename); - }elseif($mail_type == 2){ - $mail = new \app\lib\mail\Aliyun(config_get('mail_apiuser'), config_get('mail_apikey')); - return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename); - }else{ - $mail_name = config_get('mail_name'); - $mail_port = intval(config_get('mail_port')); - $mail_smtp = config_get('mail_smtp'); - $mail_pwd = config_get('mail_pwd'); - if(!$mail_name || !$mail_port || !$mail_smtp || !$mail_pwd)return false; - $mail = new \app\lib\mail\PHPMailer\PHPMailer(true); - try{ - $mail->SMTPDebug = 0; - $mail->CharSet = 'UTF-8'; - $mail->Timeout = 5; - $mail->isSMTP(); - $mail->Host = $mail_smtp; - $mail->SMTPAuth = true; - $mail->Username = $mail_name; - $mail->Password = $mail_pwd; - if($mail_port == 587) $mail->SMTPSecure = 'tls'; - else if($mail_port >= 465) $mail->SMTPSecure = 'ssl'; - else $mail->SMTPAutoTLS = false; - $mail->Port = $mail_port; - $mail->setFrom($mail_name, self::$sitename); - $mail->addAddress($to); - $mail->addReplyTo($mail_name, self::$sitename); - $mail->isHTML(true); - $mail->Subject = $sub; - $mail->Body = $msg; - $mail->send(); - return true; - } catch (\Exception $e) { - return $mail->ErrorInfo; - } - } - } - - public static function send_wechat_tplmsg($title, $content){ - $wechat_apptoken = config_get('wechat_apptoken'); - $wechat_appuid = config_get('wechat_appuid'); - if(!$wechat_apptoken||!$wechat_appuid)return false; - $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,0,['Content-Type: application/json; charset=UTF-8']); - $arr = json_decode($result, true); - if(isset($arr['success']) && $arr['success']==true){ - return true; - }else{ - return $arr['msg']; - } - } - - public static function send_telegram_bot($content){ - $tgbot_token = config_get('tgbot_token'); - $tgbot_chatid = config_get('tgbot_chatid'); - if(!$tgbot_token||!$tgbot_chatid)return false; - $url = 'https://api.telegram.org/bot'.$tgbot_token.'/sendMessage'; - $post = ['chat_id'=>$tgbot_chatid, 'text'=>$content, 'parse_mode'=>'HTML']; - $result = self::telegram_curl($url, http_build_query($post)); - $arr = json_decode($result, true); - if(isset($arr['ok']) && $arr['ok']==true){ - return true; - }else{ - return $arr['description']; - } - } - - private static function telegram_curl($url, $post){ - $ch = curl_init(); - if(config_get('tgbot_proxy') == 1){ - $proxy_server = config_get('proxy_server'); - $proxy_port = intval(config_get('proxy_port')); - $proxy_userpwd = config_get('proxy_user').':'.config_get('proxy_pwd'); - $proxy_type = config_get('proxy_type'); - if($proxy_type == 'https'){ - $proxy_type = CURLPROXY_HTTPS; - }elseif($proxy_type == 'sock4'){ - $proxy_type = CURLPROXY_SOCKS4; - }elseif($proxy_type == 'sock5'){ - $proxy_type = CURLPROXY_SOCKS5; - }else{ - $proxy_type = CURLPROXY_HTTP; - } - curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); - curl_setopt($ch, CURLOPT_PROXY, $proxy_server); - curl_setopt($ch, CURLOPT_PROXYPORT, $proxy_port); - if($proxy_userpwd != ':'){ - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_userpwd); - } - curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type); - } - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - $httpheader[] = "Accept: */*"; - $httpheader[] = "Accept-Encoding: gzip,deflate,sdch"; - $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; - $httpheader[] = "Connection: close"; - curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $post); - curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0"); - curl_setopt($ch, CURLOPT_ENCODING, "gzip"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $ret = curl_exec($ch); - curl_close($ch); - return $ret; - } -} \ No newline at end of file +您的域名 '.$task['domain'].''.$task['main_value'].' 记录发生了异常'; + if ($task['type'] == 2) { + $mail_content .= ',已自动切换为备用解析记录 '.$task['backup_value'].' '; + } elseif ($task['type'] == 1) { + $mail_content .= ',已自动暂停解析'; + } else { + $mail_content .= ',请及时处理'; + } + if (!empty($result['errmsg'])) { + $mail_content .= '。
异常信息:'.$result['errmsg']; + } + } else { + $mail_title = 'DNS容灾切换-恢复正常通知'; + $mail_content = '尊敬的系统管理员,您好:
您的域名 '.$task['domain'].''.$task['main_value'].' 记录已恢复正常'; + if ($task['type'] == 2) { + $mail_content .= ',已自动切换回当前解析记录'; + } elseif ($task['type'] == 1) { + $mail_content .= ',已自动开启解析'; + } + $lasttime = convert_second(time() - $task['switchtime']); + $mail_content .= '。
异常持续时间:'.$lasttime; + } + if (!empty($task['remark'])) { + $mail_title .= '('.$task['remark'].')'; + } + if (!empty($task['remark'])) { + $mail_content .= '
备注:'.$task['remark']; + } + $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'); + self::send_mail($mail_name, $mail_title, $mail_content); + } + if (config_get('notice_wxtpl') == 1) { + $content = str_replace(['
', '', ''], ["\n\n", '**', '**'], $mail_content); + self::send_wechat_tplmsg($mail_title, $content); + } + if (config_get('notice_tgbot') == 1) { + $content = str_replace('
', "\n", $mail_content); + $content = "".$mail_title."\n".$content; + self::send_telegram_bot($content); + } + } + + public static function send_mail($to, $sub, $msg) + { + $mail_type = config_get('mail_type'); + if ($mail_type == 1) { + $mail = new \app\lib\mail\Sendcloud(config_get('mail_apiuser'), config_get('mail_apikey')); + return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename); + } elseif ($mail_type == 2) { + $mail = new \app\lib\mail\Aliyun(config_get('mail_apiuser'), config_get('mail_apikey')); + return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename); + } else { + $mail_name = config_get('mail_name'); + $mail_port = intval(config_get('mail_port')); + $mail_smtp = config_get('mail_smtp'); + $mail_pwd = config_get('mail_pwd'); + if (!$mail_name || !$mail_port || !$mail_smtp || !$mail_pwd) { + return false; + } + $mail = new \app\lib\mail\PHPMailer\PHPMailer(true); + try { + $mail->SMTPDebug = 0; + $mail->CharSet = 'UTF-8'; + $mail->Timeout = 5; + $mail->isSMTP(); + $mail->Host = $mail_smtp; + $mail->SMTPAuth = true; + $mail->Username = $mail_name; + $mail->Password = $mail_pwd; + if ($mail_port == 587) { + $mail->SMTPSecure = 'tls'; + } elseif ($mail_port >= 465) { + $mail->SMTPSecure = 'ssl'; + } else { + $mail->SMTPAutoTLS = false; + } + $mail->Port = $mail_port; + $mail->setFrom($mail_name, self::$sitename); + $mail->addAddress($to); + $mail->addReplyTo($mail_name, self::$sitename); + $mail->isHTML(true); + $mail->Subject = $sub; + $mail->Body = $msg; + $mail->send(); + return true; + } catch (\Exception $e) { + return $mail->ErrorInfo; + } + } + } + + public static function send_wechat_tplmsg($title, $content) + { + $wechat_apptoken = config_get('wechat_apptoken'); + $wechat_appuid = config_get('wechat_appuid'); + if (!$wechat_apptoken || !$wechat_appuid) { + return false; + } + $url = 'https://wxpusher.zjiecode.com/api/send/message'; + $post = ['appToken' => $wechat_apptoken, 'content' => $content, 'summary' => $title, 'contentType' => 3, 'uids' => [$wechat_appuid]]; + + $result = (new \GuzzleHttp\Client())->post($url, ['json' => $post])->getBody()->getContents(); + $arr = json_decode($result, true); + if (isset($arr['success']) && $arr['success'] == true) { + return true; + } else { + return $arr['msg']; + } + } + + public static function send_telegram_bot($content) + { + $tgbot_token = config_get('tgbot_token'); + $tgbot_chatid = config_get('tgbot_chatid'); + if (!$tgbot_token || !$tgbot_chatid) { + return false; + } + $url = 'https://api.telegram.org/bot'.$tgbot_token.'/sendMessage'; + $post = ['chat_id' => $tgbot_chatid, 'text' => $content, 'parse_mode' => 'HTML']; + $result = self::telegram_curl($url, http_build_query($post)); + $arr = json_decode($result, true); + if (isset($arr['ok']) && $arr['ok'] == true) { + return true; + } else { + return $arr['description']; + } + } + + private static function telegram_curl($url, $post) + { + $ch = curl_init(); + if (config_get('tgbot_proxy') == 1) { + $proxy_server = config_get('proxy_server'); + $proxy_port = intval(config_get('proxy_port')); + $proxy_userpwd = config_get('proxy_user').':'.config_get('proxy_pwd'); + $proxy_type = config_get('proxy_type'); + if ($proxy_type == 'https') { + $proxy_type = CURLPROXY_HTTPS; + } elseif ($proxy_type == 'sock4') { + $proxy_type = CURLPROXY_SOCKS4; + } elseif ($proxy_type == 'sock5') { + $proxy_type = CURLPROXY_SOCKS5; + } else { + $proxy_type = CURLPROXY_HTTP; + } + curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_PROXY, $proxy_server); + curl_setopt($ch, CURLOPT_PROXYPORT, $proxy_port); + if ($proxy_userpwd != ':') { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_userpwd); + } + curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type); + } + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Encoding: gzip,deflate,sdch"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0"); + curl_setopt($ch, CURLOPT_ENCODING, "gzip"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } +} diff --git a/app/lib/NewDb.php b/app/lib/NewDb.php index 2a1d6d0..1538b7e 100644 --- a/app/lib/NewDb.php +++ b/app/lib/NewDb.php @@ -1,4 +1,5 @@ ['DEF'=>'default', 'CT'=>'telecom', 'CU'=>'unicom', 'CM'=>'mobile', 'AB'=>'oversea'], - 'dnspod' => ['DEF'=>'0', 'CT'=>'10=0', 'CU'=>'10=1', 'CM'=>'10=3', 'AB'=>'3=0'], - 'huawei' => ['DEF'=>'default_view', 'CT'=>'Dianxin', 'CU'=>'Liantong', 'CM'=>'Yidong', 'AB'=>'Abroad'], - 'west' => ['DEF'=>'', 'CT'=>'LTEL', 'CU'=>'LCNC', 'CM'=>'LMOB', 'AB'=>'LFOR'], - 'dnsla' => ['DEF'=>'', 'CT'=>'84613316902921216', 'CU'=>'84613316923892736', 'CM'=>'84613316953252864', 'AB'=>''], - 'huoshan' => ['DEF'=>'default', 'CT'=>'telecom', 'CU'=>'unicom', 'CM'=>'mobile', 'AB'=>'oversea'], - 'baidu' => ['DEF'=>'default', 'CT'=>'ct', 'CU'=>'cnc', 'CM'=>'cmnet', 'AB'=>''], - ]; - - private $ip_address = []; - private $add_num = 0; - private $change_num = 0; - private $del_num = 0; - - public static function get_license($api, $key){ - if($api == 2){ - throw new Exception('当前接口暂不支持'); - }elseif($api == 1){ - $url = 'https://api.hostmonit.com/get_license?license='.$key; - }else{ - $url = 'https://www.wetest.vip/api/cf2dns/get_license?license='.$key; - } - $response = get_curl($url); - $arr = json_decode($response, true); - if(isset($arr['code']) && $arr['code'] == 200 && isset($arr['count'])){ - return $arr['count']; - }elseif(isset($arr['info'])){ - throw new Exception('获取剩余请求次数失败,'.$arr['info']); - }else{ - throw new Exception('获取剩余请求次数失败'); - } - } - - public function get_ip_address($cdn_type = 1, $ip_type = 'v4'){ - $api = config_get('optimize_ip_api', 0); - if($api == 2){ - $url = 'https://api.345673.xyz/get_data'; - }elseif($api == 1){ - $url = 'https://api.hostmonit.com/get_optimization_ip'; - }else{ - $url = 'https://www.wetest.vip/api/cf2dns/'; - if($cdn_type == 1){ - $url .= 'get_cloudflare_ip'; - }elseif($cdn_type == 2){ - $url .= 'get_cloudfront_ip'; - }elseif($cdn_type == 3){ - $url .= 'get_gcore_ip'; - } - } - $params = [ - 'key' => config_get('optimize_ip_key', 'o1zrmHAF'), - 'type' => $ip_type, - ]; - $response = get_curl($url, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=UTF-8']); - $arr = json_decode($response, true); - if(isset($arr['code']) && $arr['code'] == 200){ - return $arr['info']; - }elseif(isset($arr['info'])){ - throw new Exception('获取优选IP数据失败,'.$arr['info']); - }elseif(isset($arr['msg'])){ - throw new Exception('获取优选IP数据失败,'.$arr['msg']); - }else{ - throw new Exception('获取优选IP数据失败,原因未知'); - } - } - - public function get_ip_address2($cdn_type = 1, $ip_type = 'v4'){ - $key = $cdn_type.'_'.$ip_type; - if(!isset($this->ip_address[$key])){ - $info = $this->get_ip_address($cdn_type, $ip_type); - $res = []; - if(isset($info['DEF'])) $res['DEF'] = $info['DEF']; - if(isset($info['CT'])) $res['CT'] = $info['CT']; - if(isset($info['CU'])) $res['CU'] = $info['CU']; - if(isset($info['CM'])) $res['CM'] = $info['CM']; - $this->ip_address[$key] = $res; - } - return $this->ip_address[$key]; - } - - //批量执行优选任务 - public function execute(){ - $list = Db::name('optimizeip')->where('active', 1)->select(); - echo '开始执行IP优选任务,共获取到'.count($list).'个待执行任务'."\n"; - foreach($list as $row){ - try{ - $result = $this->execute_one($row); - Db::name('optimizeip')->where('id', $row['id'])->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]); - echo '优选任务'.$row['id'].'执行成功:'.$result."\n"; - }catch(Exception $e){ - Db::name('optimizeip')->where('id', $row['id'])->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]); - echo '优选任务'.$row['id'].'执行失败:'.$e->getMessage()."\n"; - } - } - } - - //执行单个优选任务 - public function execute_one($row){ - $this->add_num = 0; - $this->change_num = 0; - $this->del_num = 0; - $ip_types = explode(',', $row['ip_type']); - foreach($ip_types as $ip_type){ - if(empty($ip_type)) continue; - - $drow = Db::name('domain')->alias('A')->join('account B','A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find(); - if(!$drow){ - throw new Exception('域名不存在(ID:'.$row['did'].')'); - } - if(!isset(self::$line_name[$drow['type']])){ - throw new Exception('不支持的DNS服务商'); - } - - $info = $this->get_ip_address2($row['cdn_type'], $ip_type); - - $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']); - $domainRecords = $dns->getSubDomainRecords($row['rr'], 1, 100); - if(!$domainRecords){ - throw new Exception('获取记录列表失败,'.$dns->getError()); - } - - if($row['type'] == 1 && isset($info['DEF']) && !empty($info['DEF'])) $row['type'] = 0; - - foreach($info as $line=>$iplist){ - if(empty($iplist)) continue; - $get_ips = array_column($iplist, 'ip'); - if($drow['type']=='huawei') {sort($get_ips); $get_ips = [implode(',',$get_ips)]; $row['recordnum'] = 1;} - if($row['type'] == 1 && $line == 'CT') $line = 'DEF'; - $line_name = self::$line_name[$drow['type']][$line]; - $this->process_dns_line($dns, $row, $domainRecords['list'], $get_ips, $line_name, $ip_type); - } - } - - return '成功添加'.$this->add_num.'条记录,修改'.$this->change_num.'条记录,删除'.$this->del_num.'条记录'; - } - - //处理单个线路的解析记录 - private function process_dns_line($dns, $row, $record_list, $get_ips, $line_name, $ip_type){ - $record_num = $row['recordnum']; - $records = array_filter($record_list, function($v) use($line_name){ - return $v['Line'] == $line_name; - }); - - //删除CNAME记录 - $cname_records = array_filter($records, function($v){ - return $v['Type'] == 'CNAME'; - }); - if(!empty($cname_records)){ - foreach($cname_records as $record){ - $dns->deleteDomainRecord($record['RecordId']); - } - } - - //处理A/AAAA记录 - $ip_records = array_filter($records, function($v) use ($ip_type){ - return $v['Type'] == ($ip_type == 'v6' ? 'AAAA' : 'A'); - }); - - if(!empty($ip_records) && is_array($ip_records[array_key_first($ip_records)]['Value'])){ //处理华为云记录 - foreach($ip_records as &$ip_record){ - sort($ip_record['Value']); - $ip_record['Value'] = implode(',', $ip_record['Value']); - } - } - - $exist_ips = array_column($ip_records, 'Value'); - $add_ips = array_diff($get_ips, $exist_ips); - $del_ips = array_diff($exist_ips, $get_ips); - $correct_ips = array_diff($exist_ips, $del_ips); - $correct_count = count($correct_ips); - if(!empty($del_ips)){ - foreach($ip_records as $record){ - if(in_array($record['Value'], $del_ips)){ - $add_ip = array_pop($add_ips); - if($add_ip){ - $res = $dns->updateDomainRecord($record['RecordId'], $row['rr'], $ip_type == 'v6' ? 'AAAA' : 'A', $add_ip, $line_name, $row['ttl']); - if(!$res){ - throw new Exception('修改解析失败,'.$dns->getError()); - } - $this->change_num++; - $correct_count++; - }else{ - $res = $dns->deleteDomainRecord($record['RecordId']); - if(!$res){ - throw new Exception('删除解析失败,'.$dns->getError()); - } - $this->del_num++; - } - } - } - } - if($correct_count < $record_num && !empty($add_ips)){ - foreach($add_ips as $add_ip){ - $res = $dns->addDomainRecord($row['rr'], $ip_type == 'v6' ? 'AAAA' : 'A', $add_ip, $line_name, $row['ttl']); - if(!$res){ - throw new Exception('添加解析失败,'.$dns->getError()); - } - $this->add_num++; - $correct_count++; - if($correct_count >= $record_num) break; - } - } - } -} \ No newline at end of file + ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'], + 'dnspod' => ['DEF' => '0', 'CT' => '10=0', 'CU' => '10=1', 'CM' => '10=3', 'AB' => '3=0'], + 'huawei' => ['DEF' => 'default_view', 'CT' => 'Dianxin', 'CU' => 'Liantong', 'CM' => 'Yidong', 'AB' => 'Abroad'], + 'west' => ['DEF' => '', 'CT' => 'LTEL', 'CU' => 'LCNC', 'CM' => 'LMOB', 'AB' => 'LFOR'], + 'dnsla' => ['DEF' => '', 'CT' => '84613316902921216', 'CU' => '84613316923892736', 'CM' => '84613316953252864', 'AB' => ''], + 'huoshan' => ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'], + 'baidu' => ['DEF' => 'default', 'CT' => 'ct', 'CU' => 'cnc', 'CM' => 'cmnet', 'AB' => ''], + ]; + + private $ip_address = []; + private $add_num = 0; + private $change_num = 0; + private $del_num = 0; + + public static function get_license($api, $key) + { + if ($api == 2) { + throw new Exception('当前接口暂不支持'); + } elseif ($api == 1) { + $url = 'https://api.hostmonit.com/get_license?license='.$key; + } else { + $url = 'https://www.wetest.vip/api/cf2dns/get_license?license='.$key; + } + $response = (new \GuzzleHttp\Client())->get($url)->getBody()->getContents(); + $arr = json_decode($response, true); + if (isset($arr['code']) && $arr['code'] == 200 && isset($arr['count'])) { + return $arr['count']; + } elseif (isset($arr['info'])) { + throw new Exception('获取剩余请求次数失败,'.$arr['info']); + } else { + throw new Exception('获取剩余请求次数失败'); + } + } + + public function get_ip_address($cdn_type = 1, $ip_type = 'v4') + { + $api = config_get('optimize_ip_api', 0); + if ($api == 2) { + $url = 'https://api.345673.xyz/get_data'; + } elseif ($api == 1) { + $url = 'https://api.hostmonit.com/get_optimization_ip'; + } else { + $url = 'https://www.wetest.vip/api/cf2dns/'; + if ($cdn_type == 1) { + $url .= 'get_cloudflare_ip'; + } elseif ($cdn_type == 2) { + $url .= 'get_cloudfront_ip'; + } elseif ($cdn_type == 3) { + $url .= 'get_gcore_ip'; + } + } + $params = [ + 'key' => config_get('optimize_ip_key', 'o1zrmHAF'), + 'type' => $ip_type, + ]; + $response = (new \GuzzleHttp\Client())->post($url, ['json' => $params])->getBody()->getContents(); + $arr = json_decode($response, true); + if (isset($arr['code']) && $arr['code'] == 200) { + return $arr['info']; + } elseif (isset($arr['info'])) { + throw new Exception('获取优选IP数据失败,'.$arr['info']); + } elseif (isset($arr['msg'])) { + throw new Exception('获取优选IP数据失败,'.$arr['msg']); + } else { + throw new Exception('获取优选IP数据失败,原因未知'); + } + } + + public function get_ip_address2($cdn_type = 1, $ip_type = 'v4') + { + $key = $cdn_type.'_'.$ip_type; + if (!isset($this->ip_address[$key])) { + $info = $this->get_ip_address($cdn_type, $ip_type); + $res = []; + if (isset($info['DEF'])) { + $res['DEF'] = $info['DEF']; + } + if (isset($info['CT'])) { + $res['CT'] = $info['CT']; + } + if (isset($info['CU'])) { + $res['CU'] = $info['CU']; + } + if (isset($info['CM'])) { + $res['CM'] = $info['CM']; + } + $this->ip_address[$key] = $res; + } + return $this->ip_address[$key]; + } + + //批量执行优选任务 + public function execute() + { + $list = Db::name('optimizeip')->where('active', 1)->select(); + echo '开始执行IP优选任务,共获取到'.count($list).'个待执行任务'."\n"; + foreach ($list as $row) { + try { + $result = $this->execute_one($row); + Db::name('optimizeip')->where('id', $row['id'])->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]); + echo '优选任务'.$row['id'].'执行成功:'.$result."\n"; + } catch (Exception $e) { + Db::name('optimizeip')->where('id', $row['id'])->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]); + echo '优选任务'.$row['id'].'执行失败:'.$e->getMessage()."\n"; + } + } + } + + //执行单个优选任务 + public function execute_one($row) + { + $this->add_num = 0; + $this->change_num = 0; + $this->del_num = 0; + $ip_types = explode(',', $row['ip_type']); + foreach ($ip_types as $ip_type) { + if (empty($ip_type)) { + continue; + } + + $drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find(); + if (!$drow) { + throw new Exception('域名不存在(ID:'.$row['did'].')'); + } + if (!isset(self::$line_name[$drow['type']])) { + throw new Exception('不支持的DNS服务商'); + } + + $info = $this->get_ip_address2($row['cdn_type'], $ip_type); + + $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']); + $domainRecords = $dns->getSubDomainRecords($row['rr'], 1, 100); + if (!$domainRecords) { + throw new Exception('获取记录列表失败,'.$dns->getError()); + } + + if ($row['type'] == 1 && isset($info['DEF']) && !empty($info['DEF'])) { + $row['type'] = 0; + } + + foreach ($info as $line => $iplist) { + if (empty($iplist)) { + continue; + } + $get_ips = array_column($iplist, 'ip'); + if ($drow['type'] == 'huawei') { + sort($get_ips); + $get_ips = [implode(',', $get_ips)]; + $row['recordnum'] = 1; + } + if ($row['type'] == 1 && $line == 'CT') { + $line = 'DEF'; + } + $line_name = self::$line_name[$drow['type']][$line]; + $this->process_dns_line($dns, $row, $domainRecords['list'], $get_ips, $line_name, $ip_type); + } + } + + return '成功添加'.$this->add_num.'条记录,修改'.$this->change_num.'条记录,删除'.$this->del_num.'条记录'; + } + + //处理单个线路的解析记录 + private function process_dns_line($dns, $row, $record_list, $get_ips, $line_name, $ip_type) + { + $record_num = $row['recordnum']; + $records = array_filter($record_list, function ($v) use ($line_name) { + return $v['Line'] == $line_name; + }); + + //删除CNAME记录 + $cname_records = array_filter($records, function ($v) { + return $v['Type'] == 'CNAME'; + }); + if (!empty($cname_records)) { + foreach ($cname_records as $record) { + $dns->deleteDomainRecord($record['RecordId']); + } + } + + //处理A/AAAA记录 + $ip_records = array_filter($records, function ($v) use ($ip_type) { + return $v['Type'] == ($ip_type == 'v6' ? 'AAAA' : 'A'); + }); + + if (!empty($ip_records) && is_array($ip_records[array_key_first($ip_records)]['Value'])) { //处理华为云记录 + foreach ($ip_records as &$ip_record) { + sort($ip_record['Value']); + $ip_record['Value'] = implode(',', $ip_record['Value']); + } + } + + $exist_ips = array_column($ip_records, 'Value'); + $add_ips = array_diff($get_ips, $exist_ips); + $del_ips = array_diff($exist_ips, $get_ips); + $correct_ips = array_diff($exist_ips, $del_ips); + $correct_count = count($correct_ips); + if (!empty($del_ips)) { + foreach ($ip_records as $record) { + if (in_array($record['Value'], $del_ips)) { + $add_ip = array_pop($add_ips); + if ($add_ip) { + $res = $dns->updateDomainRecord($record['RecordId'], $row['rr'], $ip_type == 'v6' ? 'AAAA' : 'A', $add_ip, $line_name, $row['ttl']); + if (!$res) { + throw new Exception('修改解析失败,'.$dns->getError()); + } + $this->change_num++; + $correct_count++; + } else { + $res = $dns->deleteDomainRecord($record['RecordId']); + if (!$res) { + throw new Exception('删除解析失败,'.$dns->getError()); + } + $this->del_num++; + } + } + } + } + if ($correct_count < $record_num && !empty($add_ips)) { + foreach ($add_ips as $add_ip) { + $res = $dns->addDomainRecord($row['rr'], $ip_type == 'v6' ? 'AAAA' : 'A', $add_ip, $line_name, $row['ttl']); + if (!$res) { + throw new Exception('添加解析失败,'.$dns->getError()); + } + $this->add_num++; + $correct_count++; + if ($correct_count >= $record_num) { + break; + } + } + } + } +} diff --git a/app/lib/TaskRunner.php b/app/lib/TaskRunner.php index 8cef5b0..398c961 100644 --- a/app/lib/TaskRunner.php +++ b/app/lib/TaskRunner.php @@ -1,125 +1,126 @@ -conn){ - $this->conn = NewDb::connect(); - } - return $this->conn; - } - - private function closeDb() - { - if($this->conn){ - $this->conn->close(); - } - } - - public function execute($row) - { - if($row['type'] == 3){ //条件开启解析 - $action = 0; - $remain = $this->db()->name('dmtask')->where(['did'=>$row['did'], 'rr'=>$row['rr'], 'type'=>1, 'status'=>0])->count(); - if($remain<=$row['cycle'] && $row['status']==0){ - $action = 2; - $this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>1, 'errcount'=>0, 'switchtime'=>time()]); - }elseif($remain>$row['cycle'] && $row['status']==1){ - $action = 1; - $this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>0, 'errcount'=>0, 'switchtime'=>time()]); - } - }else{ - if($row['checktype'] == 2){ - $result = CheckUtils::curl($row['checkurl'], $row['timeout'], $row['main_value'], $row['proxy'] == 1); - }else if($row['checktype'] == 1){ - $result = CheckUtils::tcp($row['main_value'], $row['tcpport'], $row['timeout']); - }else{ - $result = CheckUtils::ping($row['main_value']); - } - - $action = 0; - if($result['status'] && $row['status']==1){ - if($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']){ - $this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>0, 'errcount'=>0, 'switchtime'=>time()]); - $action = 2; - }else{ - $this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update(); - } - }elseif(!$result['status'] && $row['status']==0){ - if($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']){ - $this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>1, 'errcount'=>0, 'switchtime'=>time()]); - $action = 1; - }else{ - $this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update(); - } - }elseif($row['errcount'] > 0){ - $this->db()->name('dmtask')->where('id', $row['id'])->update(['errcount'=>0]); - } - } - - if($action > 0){ - $drow = $this->db()->name('domain')->alias('A')->join('account B','A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find(); - if(!$drow){ - echo '域名不存在(ID:'.$row['did'].')'."\n"; - $this->closeDb(); - return; - } - $row['domain'] = $row['rr'] . '.' . $drow['name']; - } - if($action == 1){ - if($row['type'] == 2){ - $dns = DnsHelper::getModel2($drow); - $recordinfo = json_decode($row['recordinfo'], true); - $res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['backup_value']), $row['backup_value'], $recordinfo['Line'], $recordinfo['TTL']); - if(!$res){ - $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); - } - }elseif($row['type'] == 1 || $row['type'] == 3){ - $dns = DnsHelper::getModel2($drow); - $res = $dns->setDomainRecordStatus($row['recordid'], '0'); - if(!$res){ - $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '暂停解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); - } - } - }elseif($action == 2){ - if($row['type'] == 2){ - $dns = DnsHelper::getModel2($drow); - $recordinfo = json_decode($row['recordinfo'], true); - $res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['main_value']), $row['main_value'], $recordinfo['Line'], $recordinfo['TTL']); - if(!$res){ - $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); - } - }elseif($row['type'] == 1 || $row['type'] == 3){ - $dns = DnsHelper::getModel2($drow); - $res = $dns->setDomainRecordStatus($row['recordid'], '1'); - if(!$res){ - $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '启用解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); - } - } - }else{ - $this->closeDb(); - return; - } - - $this->db()->name('dmlog')->insert([ - 'taskid' => $row['id'], - 'action' => $action, - 'errmsg' => isset($result) ? ($result['status'] ? null : $result['errmsg']) : null, - 'date' => date('Y-m-d H:i:s') - ]); - $this->closeDb(); - - if($row['type'] != 3){ - MsgNotice::send($action, $row, $result); - } - } -} \ No newline at end of file +conn) { + $this->conn = NewDb::connect(); + } + return $this->conn; + } + + private function closeDb() + { + if ($this->conn) { + $this->conn->close(); + } + } + + public function execute($row) + { + if ($row['type'] == 3) { //条件开启解析 + $action = 0; + $remain = $this->db()->name('dmtask')->where(['did' => $row['did'], 'rr' => $row['rr'], 'type' => 1, 'status' => 0])->count(); + if ($remain <= $row['cycle'] && $row['status'] == 0) { + $action = 2; + $this->db()->name('dmtask')->where('id', $row['id'])->update(['status' => 1, 'errcount' => 0, 'switchtime' => time()]); + } elseif ($remain > $row['cycle'] && $row['status'] == 1) { + $action = 1; + $this->db()->name('dmtask')->where('id', $row['id'])->update(['status' => 0, 'errcount' => 0, 'switchtime' => time()]); + } + } else { + if ($row['checktype'] == 2) { + $result = CheckUtils::curl($row['checkurl'], $row['timeout'], $row['main_value'], $row['proxy'] == 1); + } elseif ($row['checktype'] == 1) { + $result = CheckUtils::tcp($row['main_value'], $row['tcpport'], $row['timeout']); + } else { + $result = CheckUtils::ping($row['main_value']); + } + + $action = 0; + if ($result['status'] && $row['status'] == 1) { + if ($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']) { + $this->db()->name('dmtask')->where('id', $row['id'])->update(['status' => 0, 'errcount' => 0, 'switchtime' => time()]); + $action = 2; + } else { + $this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update(); + } + } elseif (!$result['status'] && $row['status'] == 0) { + if ($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']) { + $this->db()->name('dmtask')->where('id', $row['id'])->update(['status' => 1, 'errcount' => 0, 'switchtime' => time()]); + $action = 1; + } else { + $this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update(); + } + } elseif ($row['errcount'] > 0) { + $this->db()->name('dmtask')->where('id', $row['id'])->update(['errcount' => 0]); + } + } + + if ($action > 0) { + $drow = $this->db()->name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find(); + if (!$drow) { + echo '域名不存在(ID:'.$row['did'].')'."\n"; + $this->closeDb(); + return; + } + $row['domain'] = $row['rr'] . '.' . $drow['name']; + } + if ($action == 1) { + if ($row['type'] == 2) { + $dns = DnsHelper::getModel2($drow); + $recordinfo = json_decode($row['recordinfo'], true); + $res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['backup_value']), $row['backup_value'], $recordinfo['Line'], $recordinfo['TTL']); + if (!$res) { + $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); + } + } elseif ($row['type'] == 1 || $row['type'] == 3) { + $dns = DnsHelper::getModel2($drow); + $res = $dns->setDomainRecordStatus($row['recordid'], '0'); + if (!$res) { + $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '暂停解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); + } + } + } elseif ($action == 2) { + if ($row['type'] == 2) { + $dns = DnsHelper::getModel2($drow); + $recordinfo = json_decode($row['recordinfo'], true); + $res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['main_value']), $row['main_value'], $recordinfo['Line'], $recordinfo['TTL']); + if (!$res) { + $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); + } + } elseif ($row['type'] == 1 || $row['type'] == 3) { + $dns = DnsHelper::getModel2($drow); + $res = $dns->setDomainRecordStatus($row['recordid'], '1'); + if (!$res) { + $this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '启用解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]); + } + } + } else { + $this->closeDb(); + return; + } + + $this->db()->name('dmlog')->insert([ + 'taskid' => $row['id'], + 'action' => $action, + 'errmsg' => isset($result) ? ($result['status'] ? null : $result['errmsg']) : null, + 'date' => date('Y-m-d H:i:s') + ]); + $this->closeDb(); + + if ($row['type'] != 3) { + MsgNotice::send($action, $row, $result); + } + } +} diff --git a/app/lib/dns/aliyun.php b/app/lib/dns/aliyun.php index 6660353..4e903cc 100644 --- a/app/lib/dns/aliyun.php +++ b/app/lib/dns/aliyun.php @@ -1,314 +1,344 @@ AccessKeyId = $config['ak']; - $this->AccessKeySecret = $config['sk']; - $this->domain = $config['domain']; - } + public function __construct($config) + { + $this->AccessKeyId = $config['ak']; + $this->AccessKeySecret = $config['sk']; + $this->domain = $config['domain']; + } - public function getError(){ - return $this->error; - } + public function getError() + { + return $this->error; + } - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $param = ['Action' => 'DescribeDomains', 'KeyWord' => $KeyWord, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize]; - $data = $this->request($param, true); - if($data){ - $list = []; - foreach($data['Domains']['Domain'] as $row){ - $list[] = [ - 'DomainId' => $row['DomainId'], - 'Domain' => $row['DomainName'], - 'RecordCount' => $row['RecordCount'], - ]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $param = ['Action' => 'DescribeDomains', 'KeyWord' => $KeyWord, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize]; + $data = $this->request($param, true); + if ($data) { + $list = []; + foreach ($data['Domains']['Domain'] as $row) { + $list[] = [ + 'DomainId' => $row['DomainId'], + 'Domain' => $row['DomainName'], + 'RecordCount' => $row['RecordCount'], + ]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $param = ['Action' => 'DescribeDomainRecords', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize]; - if(!empty($SubDomain) || !empty($Type) || !empty($Line) || !empty($Value)){ - $param += ['SearchMode' => 'ADVANCED', 'RRKeyWord' => $SubDomain, 'ValueKeyWord' => $Value, 'Type' => $Type, 'Line' => $Line]; - }elseif(!empty($KeyWord)){ - $param += ['KeyWord' => $KeyWord]; - } - if(!isNullOrEmpty($Status)){ - $Status = $Status == '1' ? 'Enable' : 'Disable'; - $param += ['Status' => $Status]; - } - $data = $this->request($param, true); - if($data){ - $list = []; - foreach($data['DomainRecords']['Record'] as $row){ - $list[] = [ - 'RecordId' => $row['RecordId'], - 'Domain' => $row['DomainName'], - 'Name' => $row['RR'], - 'Type' => $row['Type'], - 'Value' => $row['Value'], - 'Line' => $row['Line'], - 'TTL' => $row['TTL'], - 'MX' => isset($row['Priority']) ? $row['Priority'] : null, - 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', - 'Weight' => isset($row['Weight']) ? $row['Weight'] : null, - 'Remark' => isset($row['Remark']) ? $row['Remark'] : null, - 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp']/1000) : null, - ]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $param = ['Action' => 'DescribeDomainRecords', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize]; + if (!empty($SubDomain) || !empty($Type) || !empty($Line) || !empty($Value)) { + $param += ['SearchMode' => 'ADVANCED', 'RRKeyWord' => $SubDomain, 'ValueKeyWord' => $Value, 'Type' => $Type, 'Line' => $Line]; + } elseif (!empty($KeyWord)) { + $param += ['KeyWord' => $KeyWord]; + } + if (!isNullOrEmpty($Status)) { + $Status = $Status == '1' ? 'Enable' : 'Disable'; + $param += ['Status' => $Status]; + } + $data = $this->request($param, true); + if ($data) { + $list = []; + foreach ($data['DomainRecords']['Record'] as $row) { + $list[] = [ + 'RecordId' => $row['RecordId'], + 'Domain' => $row['DomainName'], + 'Name' => $row['RR'], + 'Type' => $row['Type'], + 'Value' => $row['Value'], + 'Line' => $row['Line'], + 'TTL' => $row['TTL'], + 'MX' => isset($row['Priority']) ? $row['Priority'] : null, + 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', + 'Weight' => isset($row['Weight']) ? $row['Weight'] : null, + 'Remark' => isset($row['Remark']) ? $row['Remark'] : null, + 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp'] / 1000) : null, + ]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - $param = ['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $SubDomain . '.' . $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Type' => $Type, 'Line' => $Line]; - $data = $this->request($param, true); - if($data){ - $list = []; - foreach($data['DomainRecords']['Record'] as $row){ - $list[] = [ - 'RecordId' => $row['RecordId'], - 'Domain' => $row['DomainName'], - 'Name' => $row['RR'], - 'Type' => $row['Type'], - 'Value' => $row['Value'], - 'Line' => $row['Line'], - 'TTL' => $row['TTL'], - 'MX' => isset($row['Priority']) ? $row['Priority'] : null, - 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', - 'Weight' => isset($row['Weight']) ? $row['Weight'] : null, - 'Remark' => isset($row['Remark']) ? $row['Remark'] : null, - 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp']/1000) : null, - ]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + $param = ['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $SubDomain . '.' . $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Type' => $Type, 'Line' => $Line]; + $data = $this->request($param, true); + if ($data) { + $list = []; + foreach ($data['DomainRecords']['Record'] as $row) { + $list[] = [ + 'RecordId' => $row['RecordId'], + 'Domain' => $row['DomainName'], + 'Name' => $row['RR'], + 'Type' => $row['Type'], + 'Value' => $row['Value'], + 'Line' => $row['Line'], + 'TTL' => $row['TTL'], + 'MX' => isset($row['Priority']) ? $row['Priority'] : null, + 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', + 'Weight' => isset($row['Weight']) ? $row['Weight'] : null, + 'Remark' => isset($row['Remark']) ? $row['Remark'] : null, + 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp'] / 1000) : null, + ]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - $param = ['Action' => 'DescribeDomainRecordInfo', 'RecordId' => $RecordId]; - $data = $this->request($param, true); - if($data){ - return [ - 'RecordId' => $data['RecordId'], - 'Domain' => $data['DomainName'], - 'Name' => $data['RR'], - 'Type' => $data['Type'], - 'Value' => $data['Value'], - 'Line' => $data['Line'], - 'TTL' => $data['TTL'], - 'MX' => isset($data['Priority']) ? $data['Priority'] : null, - 'Status' => $data['Status'] == 'ENABLE' ? '1' : '0', - 'Weight' => isset($data['Weight']) ? $data['Weight'] : null, - 'Remark' => isset($data['Remark']) ? $data['Remark'] : null, - 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $data['UpdateTimestamp']/1000) : null, - ]; - } - return false; - } + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + $param = ['Action' => 'DescribeDomainRecordInfo', 'RecordId' => $RecordId]; + $data = $this->request($param, true); + if ($data) { + return [ + 'RecordId' => $data['RecordId'], + 'Domain' => $data['DomainName'], + 'Name' => $data['RR'], + 'Type' => $data['Type'], + 'Value' => $data['Value'], + 'Line' => $data['Line'], + 'TTL' => $data['TTL'], + 'MX' => isset($data['Priority']) ? $data['Priority'] : null, + 'Status' => $data['Status'] == 'ENABLE' ? '1' : '0', + 'Weight' => isset($data['Weight']) ? $data['Weight'] : null, + 'Remark' => isset($data['Remark']) ? $data['Remark'] : null, + 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $data['UpdateTimestamp'] / 1000) : null, + ]; + } + return false; + } - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Weight = null, $Remark = null){ - $param = ['Action' => 'AddDomainRecord', 'DomainName' => $this->domain, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)]; - if($MX){ - $param['Priority'] = intval($MX); - } - $data = $this->request($param, true); - if($data){ - return $data['RecordId']; - } - return false; - } + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Weight = null, $Remark = null) + { + $param = ['Action' => 'AddDomainRecord', 'DomainName' => $this->domain, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)]; + if ($MX) { + $param['Priority'] = intval($MX); + } + $data = $this->request($param, true); + if ($data) { + return $data['RecordId']; + } + return false; + } - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Weight = null, $Remark = null){ - $param = ['Action' => 'UpdateDomainRecord', 'RecordId' => $RecordId, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)]; - if($MX){ - $param['Priority'] = intval($MX); - } - return $this->request($param); - } + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Weight = null, $Remark = null) + { + $param = ['Action' => 'UpdateDomainRecord', 'RecordId' => $RecordId, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)]; + if ($MX) { + $param['Priority'] = intval($MX); + } + return $this->request($param); + } - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - $param = ['Action' => 'UpdateDomainRecordRemark', 'RecordId' => $RecordId, 'Remark' => $Remark]; - return $this->request($param); - } + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + $param = ['Action' => 'UpdateDomainRecordRemark', 'RecordId' => $RecordId, 'Remark' => $Remark]; + return $this->request($param); + } - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $param = ['Action' => 'DeleteDomainRecord', 'RecordId' => $RecordId]; - return $this->request($param); - } + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $param = ['Action' => 'DeleteDomainRecord', 'RecordId' => $RecordId]; + return $this->request($param); + } - //删除子域名的解析记录 - public function deleteSubDomainRecords($SubDomain){ - $param = ['Action' => 'DeleteSubDomainRecords', 'DomainName' => $this->domain, 'RR' => $SubDomain]; - return $this->request($param); - } + //删除子域名的解析记录 + public function deleteSubDomainRecords($SubDomain) + { + $param = ['Action' => 'DeleteSubDomainRecords', 'DomainName' => $this->domain, 'RR' => $SubDomain]; + return $this->request($param); + } - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $Status = $Status == '1' ? 'Enable' : 'Disable'; - $param = ['Action' => 'SetDomainRecordStatus', 'RecordId' => $RecordId, 'Status' => $Status]; - return $this->request($param); - } + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $Status = $Status == '1' ? 'Enable' : 'Disable'; + $param = ['Action' => 'SetDomainRecordStatus', 'RecordId' => $RecordId, 'Status' => $Status]; + return $this->request($param); + } - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - $param = ['Action' => 'DescribeRecordLogs', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'KeyWord' => $KeyWord, 'StartDate' => $StartDate, 'endDate' => $endDate, 'Lang' => 'zh']; - $data = $this->request($param, true); - if($data){ - $list = []; - foreach($data['RecordLogs']['RecordLog'] as $row){ - $list[] = ['time'=>date('Y-m-d H:i:s', intval($row['ActionTimestamp']/1000)), 'data'=>$row['Message']]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + $param = ['Action' => 'DescribeRecordLogs', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'KeyWord' => $KeyWord, 'StartDate' => $StartDate, 'endDate' => $endDate, 'Lang' => 'zh']; + $data = $this->request($param, true); + if ($data) { + $list = []; + foreach ($data['RecordLogs']['RecordLog'] as $row) { + $list[] = ['time' => date('Y-m-d H:i:s', intval($row['ActionTimestamp'] / 1000)), 'data' => $row['Message']]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } - //获取解析线路列表 - public function getRecordLine(){ - $data = $this->getDomainInfo(); - if($data){ - $list = []; - foreach($data['RecordLines']['RecordLine'] as $row){ - $list[$row['LineCode']] = ['name'=>$row['LineDisplayName'], 'parent'=>isset($row['FatherCode']) ? $row['FatherCode'] : null]; - } - return $list; - } - return false; - } + //获取解析线路列表 + public function getRecordLine() + { + $data = $this->getDomainInfo(); + if ($data) { + $list = []; + foreach ($data['RecordLines']['RecordLine'] as $row) { + $list[$row['LineCode']] = ['name' => $row['LineDisplayName'], 'parent' => isset($row['FatherCode']) ? $row['FatherCode'] : null]; + } + return $list; + } + return false; + } - //获取域名信息 - public function getDomainInfo(){ - if(!empty($this->domainInfo)) return $this->domainInfo; - $param = ['Action' => 'DescribeDomainInfo', 'DomainName' => $this->domain, 'NeedDetailAttributes' => 'true']; - $data = $this->request($param, true); - if($data){ - $this->domainInfo = $data; - return $data; - } - return false; - } + //获取域名信息 + public function getDomainInfo() + { + if (!empty($this->domainInfo)) { + return $this->domainInfo; + } + $param = ['Action' => 'DescribeDomainInfo', 'DomainName' => $this->domain, 'NeedDetailAttributes' => 'true']; + $data = $this->request($param, true); + if ($data) { + $this->domainInfo = $data; + return $data; + } + return false; + } - //获取域名最低TTL - public function getMinTTL(){ - $data = $this->getDomainInfo(); - if($data){ - return $data['MinTtl']; - } - return false; - } + //获取域名最低TTL + public function getMinTTL() + { + $data = $this->getDomainInfo(); + if ($data) { + return $data['MinTtl']; + } + return false; + } - private function convertLineCode($line){ - $convert_dict = ['0'=>'default', '10=1'=>'unicom', '10=0'=>'telecom', '10=3'=>'mobile', '10=2'=>'edu', '3=0'=>'oversea', '10=22'=>'btvn', '80=0'=>'search', '7=0'=>'internal']; - if(array_key_exists($line, $convert_dict)){ - return $convert_dict[$line]; - } - return $line; - } + private function convertLineCode($line) + { + $convert_dict = ['0' => 'default', '10=1' => 'unicom', '10=0' => 'telecom', '10=3' => 'mobile', '10=2' => 'edu', '3=0' => 'oversea', '10=22' => 'btvn', '80=0' => 'search', '7=0' => 'internal']; + if (array_key_exists($line, $convert_dict)) { + return $convert_dict[$line]; + } + return $line; + } - private function aliyunSignature($parameters, $accessKeySecret, $method) - { - ksort($parameters); - $canonicalizedQueryString = ''; - foreach ($parameters as $key => $value) { - if($value === null) continue; - $canonicalizedQueryString .= '&' . $this->percentEncode($key). '=' . $this->percentEncode($value); - } - $stringToSign = $method . '&%2F&' . $this->percentEncode(substr($canonicalizedQueryString, 1)); - $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret."&", true)); + private function aliyunSignature($parameters, $accessKeySecret, $method) + { + ksort($parameters); + $canonicalizedQueryString = ''; + foreach ($parameters as $key => $value) { + if ($value === null) { + continue; + } + $canonicalizedQueryString .= '&' . $this->percentEncode($key). '=' . $this->percentEncode($value); + } + $stringToSign = $method . '&%2F&' . $this->percentEncode(substr($canonicalizedQueryString, 1)); + $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret."&", true)); - return $signature; - } - private function percentEncode($str) - { - $search = ['+', '*', '%7E']; - $replace = ['%20', '%2A', '~']; - return str_replace($search, $replace, urlencode($str)); - } - private function request($param, $returnData=false){ - if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false; - $result = $this->request_do($param, $returnData); - if(!$returnData && $result!==true){ - usleep(50000); - $result = $this->request_do($param, $returnData); - } - return $result; - } - private function request_do($param, $returnData=false){ - if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false; - $url='https://'.$this->Endpoint.'/'; - $data=array( - 'Format' => 'JSON', - 'Version' => $this->Version, - 'AccessKeyId' => $this->AccessKeyId, - 'SignatureMethod' => 'HMAC-SHA1', - 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), - 'SignatureVersion' => '1.0', - 'SignatureNonce' => random(8)); - $data=array_merge($data, $param); - $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); - $response = curl_exec($ch); - $errno = curl_errno($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; + return $signature; + } + private function percentEncode($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + private function request($param, $returnData = false) + { + if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) { + return false; + } + $result = $this->request_do($param, $returnData); + if (!$returnData && $result !== true) { + usleep(50000); + $result = $this->request_do($param, $returnData); + } + return $result; + } + private function request_do($param, $returnData = false) + { + if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) { + return false; + } + $url = 'https://'.$this->Endpoint.'/'; + $client = new Client([ + 'base_uri' => $url + ]); + $data = [ + 'Format' => 'JSON', + 'Version' => $this->Version, + 'AccessKeyId' => $this->AccessKeyId, + 'SignatureMethod' => 'HMAC-SHA1', + 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'SignatureVersion' => '1.0', + 'SignatureNonce' => random(8) + ]; + $data = array_merge($data, $param); + $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); + try { + $response = $client->post($url, [ + 'form_params' => $data, + ]); + } catch (BadResponseException $e) { + $response = $e->getResponse(); + $result = $response->getBody()->getContents(); + $data = json_decode($result, true); + $this->setError($data['Message']); + return false; + } catch (\Exception $e) { + $errno = $e->getMessage(); + $this->setError('Request error: ' . $errno); + return false; + } + $result = $response->getBody()->getContents(); + $data = json_decode($result, true); + return $returnData ? $data : true; + } - $arr = json_decode($response,true); - if($httpCode==200){ - return $returnData ? $arr : true; - }elseif($arr){ - $this->setError($arr['Message']); - return false; - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } } diff --git a/app/lib/dns/baidu.php b/app/lib/dns/baidu.php index 12796df..8318961 100644 --- a/app/lib/dns/baidu.php +++ b/app/lib/dns/baidu.php @@ -1,326 +1,366 @@ -AccessKeyId = $config['ak']; - $this->SecretAccessKey = $config['sk']; - $this->domain = $config['domain']; - $this->domainid = $config['domainid']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $query = ['name' => $KeyWord]; - $data = $this->send_reuqest('GET', '/v1/dns/zone', $query); - if($data){ - $list = []; - foreach($data['zones'] as $row){ - $list[] = [ - 'DomainId' => $row['id'], - 'Domain' => rtrim($row['name'], '.'), - 'RecordCount' => 0, - ]; - } - return ['total' => count($list), 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $query = ['rr' => $KeyWord]; - if(!isNullOrEmpty($SubDomain)){ - $query['rr'] = $SubDomain; - } - $data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query); - if($data){ - $list = []; - foreach($data['records'] as $row){ - $list[] = [ - 'RecordId' => $row['id'], - 'Domain' => $this->domain, - 'Name' => $row['rr'], - 'Type' => $row['type'], - 'Value' => $row['value'], - 'Line' => $row['line'], - 'TTL' => $row['ttl'], - 'MX' => $row['priority'], - 'Status' => $row['status'] == 'running' ? '1' : '0', - 'Weight' => null, - 'Remark' => $row['description'], - 'UpdateTime' => null, - ]; - } - return ['total' => count($list), 'list' => $list]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - if($SubDomain == '')$SubDomain='@'; - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - $query = ['id' => $RecordId]; - $data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query); - if($data && !empty($data['records'])){ - $data = $data['records'][0]; - return [ - 'RecordId' => $data['id'], - 'Domain' => rtrim($data['zone_name'], '.'), - 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']), - 'Type' => $data['type'], - 'Value' => $data['value'], - 'Line' => $data['line'], - 'TTL' => $data['ttl'], - 'MX' => $data['priority'], - 'Status' => $data['status'] == 'running' ? '1' : '0', - 'Weight' => null, - 'Remark' => $data['description'], - 'UpdateTime' => null, - ]; - } - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark]; - if($Type == 'MX')$params['priority'] = intval($MX); - $query = ['clientToken' => getSid()]; - return $this->send_reuqest('POST', '/v1/dns/zone/'.$this->domain.'/record', $query, $params); - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark]; - if($Type == 'MX')$params['priority'] = intval($MX); - $query = ['clientToken' => getSid()]; - return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query, $params); - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - return false; - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $query = ['clientToken' => getSid()]; - return $this->send_reuqest('DELETE', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query); - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $Status = $Status == '1' ? 'enable' : 'disable'; - $query = [$Status => '', 'clientToken' => getSid()]; - return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query); - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - return [ - 'default'=>['name'=>'默认', 'parent'=>null], - 'ct'=>['name'=>'电信', 'parent'=>null], - 'cnc'=>['name'=>'联通', 'parent'=>null], - 'cmnet'=>['name'=>'移动', 'parent'=>null], - 'edu'=>['name'=>'教育网', 'parent'=>null], - 'search'=>['name'=>'搜索引擎(百度)', 'parent'=>null], - ]; - } - - //获取域名概览信息 - public function getDomainInfo(){ - $res = $this->getDomainList($this->domain); - if($res && !empty($res['list'])){ - return $res['list'][0]; - } - return false; - } - - //获取域名最低TTL - public function getMinTTL(){ - return false; - } - - private function convertType($type){ - return $type; - } - - private function send_reuqest($method, $path, $query = null, $params = null){ - if(!empty($query)){ - $query = array_filter($query, function($a){ return $a!==null;}); - } - if(!empty($params)){ - $params = array_filter($params, function($a){ return $a!==null;}); - } - - $time = time(); - $date = gmdate("Y-m-d\TH:i:s\Z", $time); - $body = !empty($params) ? json_encode($params) : ''; - $headers = [ - 'Host' => $this->endpoint, - 'x-bce-date' => $date, - ]; - if($body){ - $headers['Content-Type'] = 'application/json'; - } - - $authorization = $this->generateSign($method, $path, $query, $headers, $time); - $headers['Authorization'] = $authorization; - - $url = 'https://'.$this->endpoint.$path; - if(!empty($query)){ - $url .= '?'.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, $time){ - $algorithm = "bce-auth-v1"; - - // step 1: build canonical request string - $httpRequestMethod = $method; - $canonicalUri = $this->getCanonicalUri($path); - $canonicalQueryString = $this->getCanonicalQueryString($query); - [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); - $canonicalRequest = $httpRequestMethod."\n" - .$canonicalUri."\n" - .$canonicalQueryString."\n" - .$canonicalHeaders; - - // step 2: calculate signing key - $date = gmdate("Y-m-d\TH:i:s\Z", $time); - $expirationInSeconds = 1800; - $authString = $algorithm . '/' . $this->AccessKeyId . '/' . $date . '/' . $expirationInSeconds; - $signingKey = hash_hmac('sha256', $authString, $this->SecretAccessKey); - - // step 3: sign string - $signature = hash_hmac("sha256", $canonicalRequest, $signingKey); - - // step 4: build authorization - $authorization = $authString . '/' . $signedHeaders . "/" . $signature; - - return $authorization; - } - - private function escape($str) - { - $search = ['+', '*', '%7E']; - $replace = ['%20', '%2A', '~']; - return str_replace($search, $replace, urlencode($str)); - } - - private function getCanonicalUri($path) - { - if(empty($path)) return '/'; - $uri = str_replace('%2F', '/', $this->escape($path)); - if(substr($uri, 0, 1) !== '/') $uri = '/'.$uri; - return $uri; - } - - private function getCanonicalQueryString($parameters) - { - if(empty($parameters)) return ''; - ksort($parameters); - $canonicalQueryString = ''; - foreach ($parameters as $key => $value) { - if($key == 'authorization') continue; - $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); - } - return substr($canonicalQueryString, 1); - } - - private function getCanonicalHeaders($oldheaders){ - $headers = array(); - foreach ($oldheaders as $key => $value) { - $headers[strtolower($key)] = trim($value); - } - ksort($headers); - - $canonicalHeaders = ''; - $signedHeaders = ''; - foreach ($headers as $key => $value) { - $canonicalHeaders .= $this->escape($key) . ':' . $this->escape($value) . "\n"; - $signedHeaders .= $key . ';'; - } - $canonicalHeaders = substr($canonicalHeaders, 0, -1); - $signedHeaders = substr($signedHeaders, 0, -1); - return [$canonicalHeaders, $signedHeaders]; - } - - private function curl($method, $url, $body, $header){ - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - if(!empty($body)){ - curl_setopt($ch, CURLOPT_POSTFIELDS, $body); - } - $response = curl_exec($ch); - $errno = curl_errno($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; - - if(empty($response) && $httpCode == 200){ - return true; - } - $arr=json_decode($response,true); - if($arr){ - if(isset($arr['code']) && isset($arr['message'])){ - $this->setError($arr['message']); - return false; - }else{ - return $arr; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file +AccessKeyId = $config['ak']; + $this->SecretAccessKey = $config['sk']; + $this->domain = $config['domain']; + $this->domainid = $config['domainid']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $query = ['name' => $KeyWord]; + $data = $this->send_reuqest('GET', '/v1/dns/zone', $query); + if ($data) { + $list = []; + foreach ($data['zones'] as $row) { + $list[] = [ + 'DomainId' => $row['id'], + 'Domain' => rtrim($row['name'], '.'), + 'RecordCount' => 0, + ]; + } + return ['total' => count($list), 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $query = ['rr' => $KeyWord]; + if (!isNullOrEmpty($SubDomain)) { + $query['rr'] = $SubDomain; + } + $data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query); + if ($data) { + $list = []; + foreach ($data['records'] as $row) { + $list[] = [ + 'RecordId' => $row['id'], + 'Domain' => $this->domain, + 'Name' => $row['rr'], + 'Type' => $row['type'], + 'Value' => $row['value'], + 'Line' => $row['line'], + 'TTL' => $row['ttl'], + 'MX' => $row['priority'], + 'Status' => $row['status'] == 'running' ? '1' : '0', + 'Weight' => null, + 'Remark' => $row['description'], + 'UpdateTime' => null, + ]; + } + return ['total' => count($list), 'list' => $list]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + if ($SubDomain == '') { + $SubDomain = '@'; + } + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + $query = ['id' => $RecordId]; + $data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query); + if ($data && !empty($data['records'])) { + $data = $data['records'][0]; + return [ + 'RecordId' => $data['id'], + 'Domain' => rtrim($data['zone_name'], '.'), + 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']), + 'Type' => $data['type'], + 'Value' => $data['value'], + 'Line' => $data['line'], + 'TTL' => $data['ttl'], + 'MX' => $data['priority'], + 'Status' => $data['status'] == 'running' ? '1' : '0', + 'Weight' => null, + 'Remark' => $data['description'], + 'UpdateTime' => null, + ]; + } + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark]; + if ($Type == 'MX') { + $params['priority'] = intval($MX); + } + $query = ['clientToken' => getSid()]; + return $this->send_reuqest('POST', '/v1/dns/zone/'.$this->domain.'/record', $query, $params); + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark]; + if ($Type == 'MX') { + $params['priority'] = intval($MX); + } + $query = ['clientToken' => getSid()]; + return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query, $params); + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $query = ['clientToken' => getSid()]; + return $this->send_reuqest('DELETE', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query); + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $Status = $Status == '1' ? 'enable' : 'disable'; + $query = [$Status => '', 'clientToken' => getSid()]; + return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query); + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + return [ + 'default' => ['name' => '默认', 'parent' => null], + 'ct' => ['name' => '电信', 'parent' => null], + 'cnc' => ['name' => '联通', 'parent' => null], + 'cmnet' => ['name' => '移动', 'parent' => null], + 'edu' => ['name' => '教育网', 'parent' => null], + 'search' => ['name' => '搜索引擎(百度)', 'parent' => null], + ]; + } + + //获取域名概览信息 + public function getDomainInfo() + { + $res = $this->getDomainList($this->domain); + if ($res && !empty($res['list'])) { + return $res['list'][0]; + } + return false; + } + + //获取域名最低TTL + public function getMinTTL() + { + return false; + } + + private function convertType($type) + { + return $type; + } + + private function send_reuqest($method, $path, $query = null, $params = null) + { + if (!empty($query)) { + $query = array_filter($query, function ($a) { return $a !== null;}); + } + if (!empty($params)) { + $params = array_filter($params, function ($a) { return $a !== null;}); + } + + $time = time(); + $date = gmdate("Y-m-d\TH:i:s\Z", $time); + $body = !empty($params) ? json_encode($params) : ''; + $headers = [ + 'Host' => $this->endpoint, + 'x-bce-date' => $date, + ]; + if ($body) { + $headers['Content-Type'] = 'application/json'; + } + + $authorization = $this->generateSign($method, $path, $query, $headers, $time); + $headers['Authorization'] = $authorization; + + $url = 'https://'.$this->endpoint.$path; + if (!empty($query)) { + $url .= '?'.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, $time) + { + $algorithm = "bce-auth-v1"; + + // step 1: build canonical request string + $httpRequestMethod = $method; + $canonicalUri = $this->getCanonicalUri($path); + $canonicalQueryString = $this->getCanonicalQueryString($query); + [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); + $canonicalRequest = $httpRequestMethod."\n" + .$canonicalUri."\n" + .$canonicalQueryString."\n" + .$canonicalHeaders; + + // step 2: calculate signing key + $date = gmdate("Y-m-d\TH:i:s\Z", $time); + $expirationInSeconds = 1800; + $authString = $algorithm . '/' . $this->AccessKeyId . '/' . $date . '/' . $expirationInSeconds; + $signingKey = hash_hmac('sha256', $authString, $this->SecretAccessKey); + + // step 3: sign string + $signature = hash_hmac("sha256", $canonicalRequest, $signingKey); + + // step 4: build authorization + $authorization = $authString . '/' . $signedHeaders . "/" . $signature; + + return $authorization; + } + + private function escape($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + + private function getCanonicalUri($path) + { + if (empty($path)) { + return '/'; + } + $uri = str_replace('%2F', '/', $this->escape($path)); + if (substr($uri, 0, 1) !== '/') { + $uri = '/'.$uri; + } + return $uri; + } + + private function getCanonicalQueryString($parameters) + { + if (empty($parameters)) { + return ''; + } + ksort($parameters); + $canonicalQueryString = ''; + foreach ($parameters as $key => $value) { + if ($key == 'authorization') { + continue; + } + $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); + } + return substr($canonicalQueryString, 1); + } + + private function getCanonicalHeaders($oldheaders) + { + $headers = array(); + foreach ($oldheaders as $key => $value) { + $headers[strtolower($key)] = trim($value); + } + ksort($headers); + + $canonicalHeaders = ''; + $signedHeaders = ''; + foreach ($headers as $key => $value) { + $canonicalHeaders .= $this->escape($key) . ':' . $this->escape($value) . "\n"; + $signedHeaders .= $key . ';'; + } + $canonicalHeaders = substr($canonicalHeaders, 0, -1); + $signedHeaders = substr($signedHeaders, 0, -1); + return [$canonicalHeaders, $signedHeaders]; + } + + private function curl($method, $url, $body, $header) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if (!empty($body)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + $response = curl_exec($ch); + $errno = curl_errno($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + curl_close($ch); + if ($errno) { + return false; + } + + if (empty($response) && $httpCode == 200) { + return true; + } + $arr = json_decode($response, true); + if ($arr) { + if (isset($arr['code']) && isset($arr['message'])) { + $this->setError($arr['message']); + return false; + } else { + return $arr; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/dns/dnsla.php b/app/lib/dns/dnsla.php index 9652b8c..3173cde 100644 --- a/app/lib/dns/dnsla.php +++ b/app/lib/dns/dnsla.php @@ -1,256 +1,305 @@ - 'A', 2 => 'NS', 5 => 'CNAME', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 33 => 'SRV', 257 => 'CAA', 256 => 'URL转发']; - private $error; - private $domain; - private $domainid; - - function __construct($config){ - $this->apiid = $config['ak']; - $this->apisecret = $config['sk']; - $this->domain = $config['domain']; - $this->domainid = $config['domainid']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $param = ['pageIndex' => $PageNumber, 'pageSize' => $PageSize]; - $data = $this->execute('GET', '/api/domainList', $param); - if($data){ - $list = []; - foreach($data['results'] as $row){ - $list[] = [ - 'DomainId' => $row['id'], - 'Domain' => rtrim($row['displayDomain'], '.'), - 'RecordCount' => 0, - ]; - } - return ['total' => $data['total'], 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $param = ['domainId' => $this->domainid, 'pageIndex' => $PageNumber, 'pageSize' => $PageSize]; - if(!isNullOrEmpty(($KeyWord))){ - $param['host'] = $KeyWord; - } - if(!isNullOrEmpty(($Type))){ - $param['type'] = $this->convertType($Type); - } - if(!isNullOrEmpty(($Line))){ - $param['lineId'] = $Line; - } - if(!isNullOrEmpty(($SubDomain))){ - $param['host'] = $SubDomain; - } - if(!isNullOrEmpty(($Value))){ - $param['data'] = $Value; - } - $data = $this->execute('GET', '/api/recordList', $param); - if($data){ - $list = []; - foreach($data['results'] as $row){ - $list[] = [ - 'RecordId' => $row['id'], - 'Domain' => $this->domain, - 'Name' => $row['host'], - 'Type' => $this->convertTypeId($row['type'], isset($row['domaint']) ? $row['domaint'] : false), - 'Value' => $row['data'], - 'Line' => $row['lineId'], - 'TTL' => $row['ttl'], - 'MX' => isset($row['preference']) ? $row['preference'] : null, - 'Status' => $row['disable'] ? '0' : '1', - 'Weight' => isset($row['weight']) ? $row['weight'] : null, - 'Remark' => null, - 'UpdateTime' => date('Y-m-d H:i:s', $row['updatedAt']), - ]; - } - return ['total' => $data['total'], 'list' => $list]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - if($SubDomain == '')$SubDomain='@'; - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $param = ['domainId' => $this->domainid, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line]; - if($Type == 'MX')$param['preference'] = intval($MX); - if($Type == 'REDIRECT_URL'){$param['type'] = 256;$param['dominant'] = true;} - elseif($Type == 'FORWARD_URL'){$param['type'] = 256;$param['dominant'] = false;} - if($Weight > 0) $param['weight'] = $Weight; - $data = $this->execute('POST', '/api/record', $param); - return is_array($data) ? $data['id'] : false; - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $param = ['id' => $RecordId, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line]; - if($Type == 'MX')$param['preference'] = intval($MX); - if($Type == 'REDIRECT_URL'){$param['type'] = 256;$param['dominant'] = true;} - elseif($Type == 'FORWARD_URL'){$param['type'] = 256;$param['dominant'] = false;} - if($Weight > 0) $param['weight'] = $Weight; - $data = $this->execute('PUT', '/api/record', $param); - return $data!==false; - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - return false; - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $param = ['id' => $RecordId]; - $data = $this->execute('DELETE', '/api/record', $param); - return $data!==false; - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $param = ['id' => $RecordId, 'disable' => $Status == '0' ? true : false]; - $data = $this->execute('PUT', '/api/recordDisable', $param); - return $data!==false; - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - $param = ['domain' => $this->domain]; - $data = $this->execute('GET', '/api/availableLine', $param); - if($data){ - array_multisort(array_column($data, 'order'), SORT_ASC, $data); - $list = []; - foreach($data as $row){ - if($row['id'] == '0') $row['id'] = ''; - $list[$row['id']] = ['name'=>$row['value'], 'parent'=>!empty($row['pid']) ? $row['pid'] : null]; - } - return $list; - } - return false; - } - - //获取域名信息 - public function getDomainInfo(){ - $param = ['id' => $this->domainid]; - $data = $this->execute('GET', '/api/domain', $param); - return $data; - } - - //获取域名最低TTL - public function getMinTTL(){ - $param = ['id' => $this->domainid]; - $data = $this->execute('GET', '/api/dnsMeasures', $param); - if($data && isset($data['minTTL'])){ - return $data['minTTL']; - } - return false; - } - - private function convertType($type){ - $typeList = array_flip($this->typeList); - return $typeList[$type]; - } - - private function convertTypeId($typeId, $domaint){ - if($typeId == 256) return $domaint ? 'REDIRECT_URL' : 'FORWARD_URL'; - return $this->typeList[$typeId]; - } - - private function execute($method, $path, $params = null){ - $token = base64_encode($this->apiid.':'.$this->apisecret); - $header = ['Authorization: Basic '.$token, 'Content-Type: application/json; charset=utf-8']; - if($method == 'POST' || $method == 'PUT'){ - $response = $this->curl($method, $path, $header, json_encode($params)); - }else{ - if($params){ - $path .= '?'.http_build_query($params); - } - $response = $this->curl($method, $path, $header); - } - if(!$response){ - return false; - } - $arr=json_decode($response,true); - if($arr){ - if($arr['code'] == 200){ - return $arr['data']; - }else{ - $this->setError($arr['msg']); - return false; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function curl($method, $path, $header, $body = null, $isPut = false){ - $url = $this->baseUrl . $path; - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - if ($body) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $body); - } - $response = curl_exec($ch); - $errno = curl_errno($ch); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - if ($errno) return false; - if($httpCode==200){ - return $response; - }elseif($httpCode==401){ - $this->setError('认证失败'); - return false; - }else{ - $this->setError('http code: '.$httpCode); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file + 'A', 2 => 'NS', 5 => 'CNAME', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 33 => 'SRV', 257 => 'CAA', 256 => 'URL转发']; + private $error; + private $domain; + private $domainid; + + public function __construct($config) + { + $this->apiid = $config['ak']; + $this->apisecret = $config['sk']; + $this->domain = $config['domain']; + $this->domainid = $config['domainid']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $param = ['pageIndex' => $PageNumber, 'pageSize' => $PageSize]; + $data = $this->execute('GET', '/api/domainList', $param); + if ($data) { + $list = []; + foreach ($data['results'] as $row) { + $list[] = [ + 'DomainId' => $row['id'], + 'Domain' => rtrim($row['displayDomain'], '.'), + 'RecordCount' => 0, + ]; + } + return ['total' => $data['total'], 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $param = ['domainId' => $this->domainid, 'pageIndex' => $PageNumber, 'pageSize' => $PageSize]; + if (!isNullOrEmpty(($KeyWord))) { + $param['host'] = $KeyWord; + } + if (!isNullOrEmpty(($Type))) { + $param['type'] = $this->convertType($Type); + } + if (!isNullOrEmpty(($Line))) { + $param['lineId'] = $Line; + } + if (!isNullOrEmpty(($SubDomain))) { + $param['host'] = $SubDomain; + } + if (!isNullOrEmpty(($Value))) { + $param['data'] = $Value; + } + $data = $this->execute('GET', '/api/recordList', $param); + if ($data) { + $list = []; + foreach ($data['results'] as $row) { + $list[] = [ + 'RecordId' => $row['id'], + 'Domain' => $this->domain, + 'Name' => $row['host'], + 'Type' => $this->convertTypeId($row['type'], isset($row['domaint']) ? $row['domaint'] : false), + 'Value' => $row['data'], + 'Line' => $row['lineId'], + 'TTL' => $row['ttl'], + 'MX' => isset($row['preference']) ? $row['preference'] : null, + 'Status' => $row['disable'] ? '0' : '1', + 'Weight' => isset($row['weight']) ? $row['weight'] : null, + 'Remark' => null, + 'UpdateTime' => date('Y-m-d H:i:s', $row['updatedAt']), + ]; + } + return ['total' => $data['total'], 'list' => $list]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + if ($SubDomain == '') { + $SubDomain = '@'; + } + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $param = ['domainId' => $this->domainid, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line]; + if ($Type == 'MX') { + $param['preference'] = intval($MX); + } + if ($Type == 'REDIRECT_URL') { + $param['type'] = 256; + $param['dominant'] = true; + } elseif ($Type == 'FORWARD_URL') { + $param['type'] = 256; + $param['dominant'] = false; + } + if ($Weight > 0) { + $param['weight'] = $Weight; + } + $data = $this->execute('POST', '/api/record', $param); + return is_array($data) ? $data['id'] : false; + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $param = ['id' => $RecordId, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line]; + if ($Type == 'MX') { + $param['preference'] = intval($MX); + } + if ($Type == 'REDIRECT_URL') { + $param['type'] = 256; + $param['dominant'] = true; + } elseif ($Type == 'FORWARD_URL') { + $param['type'] = 256; + $param['dominant'] = false; + } + if ($Weight > 0) { + $param['weight'] = $Weight; + } + $data = $this->execute('PUT', '/api/record', $param); + return $data !== false; + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $param = ['id' => $RecordId]; + $data = $this->execute('DELETE', '/api/record', $param); + return $data !== false; + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $param = ['id' => $RecordId, 'disable' => $Status == '0' ? true : false]; + $data = $this->execute('PUT', '/api/recordDisable', $param); + return $data !== false; + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + $param = ['domain' => $this->domain]; + $data = $this->execute('GET', '/api/availableLine', $param); + if ($data) { + array_multisort(array_column($data, 'order'), SORT_ASC, $data); + $list = []; + foreach ($data as $row) { + if ($row['id'] == '0') { + $row['id'] = ''; + } + $list[$row['id']] = ['name' => $row['value'], 'parent' => !empty($row['pid']) ? $row['pid'] : null]; + } + return $list; + } + return false; + } + + //获取域名信息 + public function getDomainInfo() + { + $param = ['id' => $this->domainid]; + $data = $this->execute('GET', '/api/domain', $param); + return $data; + } + + //获取域名最低TTL + public function getMinTTL() + { + $param = ['id' => $this->domainid]; + $data = $this->execute('GET', '/api/dnsMeasures', $param); + if ($data && isset($data['minTTL'])) { + return $data['minTTL']; + } + return false; + } + + private function convertType($type) + { + $typeList = array_flip($this->typeList); + return $typeList[$type]; + } + + private function convertTypeId($typeId, $domaint) + { + if ($typeId == 256) { + return $domaint ? 'REDIRECT_URL' : 'FORWARD_URL'; + } + return $this->typeList[$typeId]; + } + + private function execute($method, $path, $params = null) + { + $token = base64_encode($this->apiid.':'.$this->apisecret); + $header = ['Authorization: Basic '.$token, 'Content-Type: application/json; charset=utf-8']; + if ($method == 'POST' || $method == 'PUT') { + $response = $this->curl($method, $path, $header, json_encode($params)); + } else { + if ($params) { + $path .= '?'.http_build_query($params); + } + $response = $this->curl($method, $path, $header); + } + if (!$response) { + return false; + } + $arr = json_decode($response, true); + if ($arr) { + if ($arr['code'] == 200) { + return $arr['data']; + } else { + $this->setError($arr['msg']); + return false; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function curl($method, $path, $header, $body = null, $isPut = false) + { + $url = $this->baseUrl . $path; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if ($body) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + $response = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($errno) { + return false; + } + if ($httpCode == 200) { + return $response; + } elseif ($httpCode == 401) { + $this->setError('认证失败'); + return false; + } else { + $this->setError('http code: '.$httpCode); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/dns/dnspod.php b/app/lib/dns/dnspod.php index f3b1fe1..2d67e9e 100644 --- a/app/lib/dns/dnspod.php +++ b/app/lib/dns/dnspod.php @@ -1,407 +1,458 @@ -SecretId = $config['ak']; - $this->SecretKey = $config['sk']; - $this->domain = $config['domain']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $action = 'DescribeDomainList'; - $offset = ($PageNumber-1)*$PageSize; - $param = ['Offset' => $offset, 'Limit' => $PageSize, 'Keyword' => $KeyWord]; - $data = $this->send_reuqest($action, $param); - if($data){ - $list = []; - foreach($data['DomainList'] as $row){ - $list[] = [ - 'DomainId' => $row['DomainId'], - 'Domain' => $row['Name'], - 'RecordCount' => $row['RecordCount'], - ]; - } - return ['total' => $data['DomainCountInfo']['DomainTotal'], 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $offset = ($PageNumber-1)*$PageSize; - if(!isNullOrEmpty($Status) || !isNullOrEmpty($Value)){ - $action = 'DescribeRecordFilterList'; - $param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize, 'RecordValue' => $Value]; - if(!isNullOrEmpty($SubDomain)) $param['SubDomain'] = $SubDomain; - if(!isNullOrEmpty($KeyWord)) $param['Keyword'] = $KeyWord; - if(!isNullOrEmpty($Value)) $param['RecordValue'] = $Value; - if(!isNullOrEmpty($Status)){ - $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; - $param['RecordStatus'] = [$Status]; - } - if(!isNullOrEmpty($Type)) $param['RecordType'] = [$this->convertType($Type)]; - if(!isNullOrEmpty($Line)) $param['RecordLine'] = [$Line]; - }else{ - $action = 'DescribeRecordList'; - $param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'RecordType' => $this->convertType($Type), 'RecordLineId' => $Line, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize]; - } - $data = $this->send_reuqest($action, $param); - if($data){ - $list = []; - foreach($data['RecordList'] as $row){ - //if($row['Name'] == '@' && $row['Type'] == 'NS') continue; - $list[] = [ - 'RecordId' => $row['RecordId'], - 'Domain' => $this->domain, - 'Name' => $row['Name'], - 'Type' => $this->convertTypeId($row['Type']), - 'Value' => $row['Value'], - 'Line' => $row['LineId'], - 'TTL' => $row['TTL'], - 'MX' => $row['MX'], - 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', - 'Weight' => $row['Weight'], - 'Remark' => $row['Remark'], - 'UpdateTime' => $row['UpdatedOn'], - ]; - } - return ['total' => $data['RecordCountInfo']['TotalCount'], 'list' => $list]; - }elseif($this->error == '记录列表为空。' || $this->error == 'No records on the list.'){ - return ['total' => 0, 'list' => []]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - if($SubDomain == '')$SubDomain='@'; - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - $action = 'DescribeRecord'; - $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)]; - $data = $this->send_reuqest($action, $param); - if($data){ - return [ - 'RecordId' => $data['RecordInfo']['Id'], - 'Domain' => $this->domain, - 'Name' => $data['RecordInfo']['SubDomain'], - 'Type' => $this->convertTypeId($data['RecordInfo']['RecordType']), - 'Value' => $data['RecordInfo']['Value'], - 'Line' => $data['RecordInfo']['RecordLineId'], - 'TTL' => $data['RecordInfo']['TTL'], - 'MX' => $data['RecordInfo']['MX'], - 'Status' => $data['RecordInfo']['Enabled'] == 1 ? '1' : '0', - 'Weight' => $data['RecordInfo']['Weight'], - 'Remark' => $data['RecordInfo']['Remark'], - 'UpdateTime' => $data['RecordInfo']['UpdatedOn'], - ]; - } - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $action = 'CreateRecord'; - $param = ['Domain' => $this->domain, 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine'=>$Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight]; - if($Type == 'MX')$param['MX'] = intval($MX); - $data = $this->send_reuqest($action, $param); - return is_array($data) ? $data['RecordId'] : false; - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $action = 'ModifyRecord'; - $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine'=>$Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight]; - if($Type == 'MX')$param['MX'] = intval($MX); - $data = $this->send_reuqest($action, $param); - return is_array($data); - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - $action = 'ModifyRecordRemark'; - $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Remark' => $Remark]; - $data = $this->send_reuqest($action, $param); - return is_array($data); - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $action = 'DeleteRecord'; - $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)]; - $data = $this->send_reuqest($action, $param); - return is_array($data); - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; - $action = 'ModifyRecordStatus'; - $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Status' => $Status]; - $data = $this->send_reuqest($action, $param); - return is_array($data); - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - $action = 'DescribeDomainLogList'; - $offset = ($PageNumber-1)*$PageSize; - $param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize]; - $data = $this->send_reuqest($action, $param); - if($data){ - $list = []; - foreach($data['LogList'] as $row){ - $list[] = ['time'=>substr($row, 0, strpos($row,'(')), 'ip'=>substr($row, strpos($row,'(')+1, strpos($row,')')-strpos($row,'(')-1), 'data'=>substr($row, strpos($row,')')+1, strpos($row,' Uin:')-strpos($row,')')-1)]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - $action = 'DescribeRecordLineCategoryList'; - $param = ['Domain' => $this->domain]; - $data = $this->send_reuqest($action, $param); - if($data){ - $list = []; - $this->processLineList($list, $data['LineList'], null); - return $list; - }else{ - $data = $this->getRecordLineByGrade(); - if($data){ - $list = []; - foreach($data as $row){ - $list[$row['LineId']] = ['name'=>$row['Name'], 'parent'=>null]; - } - return $list; - } - } - return false; - } - - private function processLineList(&$list, $line_list, $parent){ - foreach($line_list as $row){ - if(isNullOrEmpty($row['LineId'])) $row['LineId'] = 'N.'.$row['LineName']; - if($row['Useful'] && !isset($list[$row['LineId']])){ - $list[$row['LineId']] = ['name'=>$row['LineName'], 'parent'=>$parent]; - if($row['SubGroup']){ - $this->processLineList($list, $row['SubGroup'], $row['LineId']); - } - } - } - } - - //获取域名概览信息 - public function getDomainInfo(){ - $action = 'DescribeDomain'; - $param = ['Domain' => $this->domain]; - $data = $this->send_reuqest($action, $param); - if($data){ - $this->domainInfo = $data['DomainInfo']; - return $data['DomainInfo']; - } - return false; - } - - //获取域名权限 - public function getDomainPurview(){ - $action = 'DescribeDomainPurview'; - $param = ['Domain' => $this->domain]; - $data = $this->send_reuqest($action, $param); - if($data){ - return $data['PurviewList']; - } - return false; - } - - //获取域名最低TTL - public function getMinTTL(){ - if($this->domainInfo){ - return $this->domainInfo['TTL']; - } - $PurviewList = $this->getDomainPurview(); - if($PurviewList){ - foreach($PurviewList as $row){ - if($row['Name'] == '记录 TTL 最低' || $row['Name'] == 'Min TTL value'){ - return intval($row['Value']); - } - } - } - return false; - } - - //获取等级允许的线路 - public function getRecordLineByGrade(){ - $action = 'DescribeRecordLineList'; - $param = ['Domain' => $this->domain, 'DomainGrade' => '']; - $data = $this->send_reuqest($action, $param); - if($data){ - $line_list = $data['LineList']; - if(!empty($data['LineGroupList'])){ - foreach($data['LineGroupList'] as $row){ - $line_list[] = ['Name' => $row['Name'], 'LineId' => $row['LineId']]; - } - } - return $line_list; - } - return false; - } - - //获取用户信息 - public function getAccountInfo(){ - $action = 'DescribeUserDetail'; - $param = []; - $data = $this->send_reuqest($action, $param); - if($data){ - return $data['UserInfo']; - } - return false; - } - - private function convertLineCode($line){ - $convert_dict = ['default'=>'0', 'unicom'=>'10=1', 'telecom'=>'10=0', 'mobile'=>'10=3', 'edu'=>'10=2', 'oversea'=>'3=0', 'btvn'=>'10=22', 'search'=>'80=0', 'internal'=>'7=0']; - if(array_key_exists($line, $convert_dict)){ - return $convert_dict[$line]; - } - return $line; - } - - private function convertType($type){ - $convert_dict = ['REDIRECT_URL'=>'显性URL', 'FORWARD_URL'=>'隐性URL']; - if(array_key_exists($type, $convert_dict)){ - return $convert_dict[$type]; - } - return $type; - } - - private function convertTypeId($type){ - $convert_dict = ['显性URL'=>'REDIRECT_URL', '隐性URL'=>'FORWARD_URL']; - if(array_key_exists($type, $convert_dict)){ - return $convert_dict[$type]; - } - return $type; - } - - - private function send_reuqest($action, $param){ - $param = array_filter($param, function($a){ return $a!==null;}); - if(!$param) $param = (object)[]; - $payload = json_encode($param); - $time = time(); - $authorization = $this->generateSign($payload, $time); - $header = [ - 'Authorization: '.$authorization, - 'Content-Type: application/json; charset=utf-8', - 'X-TC-Action: '.$action, - 'X-TC-Timestamp: '.$time, - 'X-TC-Version: '.$this->version, - ]; - return $this->curl_post($payload, $header); - } - - private function generateSign($payload, $time){ - $algorithm = "TC3-HMAC-SHA256"; - - // step 1: build canonical request string - $httpRequestMethod = "POST"; - $canonicalUri = "/"; - $canonicalQueryString = ""; - $canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n"; - $signedHeaders = "content-type;host"; - $hashedRequestPayload = hash("SHA256", $payload); - $canonicalRequest = $httpRequestMethod."\n" - .$canonicalUri."\n" - .$canonicalQueryString."\n" - .$canonicalHeaders."\n" - .$signedHeaders."\n" - .$hashedRequestPayload; - - // step 2: build string to sign - $date = gmdate("Y-m-d", $time); - $credentialScope = $date."/".$this->service."/tc3_request"; - $hashedCanonicalRequest = hash("SHA256", $canonicalRequest); - $stringToSign = $algorithm."\n" - .$time."\n" - .$credentialScope."\n" - .$hashedCanonicalRequest; - - // step 3: sign string - $secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true); - $secretService = hash_hmac("SHA256", $this->service, $secretDate, true); - $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true); - $signature = hash_hmac("SHA256", $stringToSign, $secretSigning); - - // step 4: build authorization - $authorization = $algorithm - ." Credential=".$this->SecretId."/".$credentialScope - .", SignedHeaders=content-type;host, Signature=".$signature; - - return $authorization; - } - - private function curl_post($payload, $header){ - $url = 'https://'.$this->endpoint.'/'; - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - $response = curl_exec($ch); - $errno = curl_errno($ch); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; - - $arr=json_decode($response,true); - if($arr){ - if(isset($arr['Response']['Error'])){ - $this->setError($arr['Response']['Error']['Message']); - return false; - }else{ - return $arr['Response']; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file +SecretId = $config['ak']; + $this->SecretKey = $config['sk']; + $this->domain = $config['domain']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $action = 'DescribeDomainList'; + $offset = ($PageNumber - 1) * $PageSize; + $param = ['Offset' => $offset, 'Limit' => $PageSize, 'Keyword' => $KeyWord]; + $data = $this->send_reuqest($action, $param); + if ($data) { + $list = []; + foreach ($data['DomainList'] as $row) { + $list[] = [ + 'DomainId' => $row['DomainId'], + 'Domain' => $row['Name'], + 'RecordCount' => $row['RecordCount'], + ]; + } + return ['total' => $data['DomainCountInfo']['DomainTotal'], 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $offset = ($PageNumber - 1) * $PageSize; + if (!isNullOrEmpty($Status) || !isNullOrEmpty($Value)) { + $action = 'DescribeRecordFilterList'; + $param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize, 'RecordValue' => $Value]; + if (!isNullOrEmpty($SubDomain)) { + $param['SubDomain'] = $SubDomain; + } + if (!isNullOrEmpty($KeyWord)) { + $param['Keyword'] = $KeyWord; + } + if (!isNullOrEmpty($Value)) { + $param['RecordValue'] = $Value; + } + if (!isNullOrEmpty($Status)) { + $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; + $param['RecordStatus'] = [$Status]; + } + if (!isNullOrEmpty($Type)) { + $param['RecordType'] = [$this->convertType($Type)]; + } + if (!isNullOrEmpty($Line)) { + $param['RecordLine'] = [$Line]; + } + } else { + $action = 'DescribeRecordList'; + $param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'RecordType' => $this->convertType($Type), 'RecordLineId' => $Line, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize]; + } + $data = $this->send_reuqest($action, $param); + if ($data) { + $list = []; + foreach ($data['RecordList'] as $row) { + //if($row['Name'] == '@' && $row['Type'] == 'NS') continue; + $list[] = [ + 'RecordId' => $row['RecordId'], + 'Domain' => $this->domain, + 'Name' => $row['Name'], + 'Type' => $this->convertTypeId($row['Type']), + 'Value' => $row['Value'], + 'Line' => $row['LineId'], + 'TTL' => $row['TTL'], + 'MX' => $row['MX'], + 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0', + 'Weight' => $row['Weight'], + 'Remark' => $row['Remark'], + 'UpdateTime' => $row['UpdatedOn'], + ]; + } + return ['total' => $data['RecordCountInfo']['TotalCount'], 'list' => $list]; + } elseif ($this->error == '记录列表为空。' || $this->error == 'No records on the list.') { + return ['total' => 0, 'list' => []]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + if ($SubDomain == '') { + $SubDomain = '@'; + } + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + $action = 'DescribeRecord'; + $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)]; + $data = $this->send_reuqest($action, $param); + if ($data) { + return [ + 'RecordId' => $data['RecordInfo']['Id'], + 'Domain' => $this->domain, + 'Name' => $data['RecordInfo']['SubDomain'], + 'Type' => $this->convertTypeId($data['RecordInfo']['RecordType']), + 'Value' => $data['RecordInfo']['Value'], + 'Line' => $data['RecordInfo']['RecordLineId'], + 'TTL' => $data['RecordInfo']['TTL'], + 'MX' => $data['RecordInfo']['MX'], + 'Status' => $data['RecordInfo']['Enabled'] == 1 ? '1' : '0', + 'Weight' => $data['RecordInfo']['Weight'], + 'Remark' => $data['RecordInfo']['Remark'], + 'UpdateTime' => $data['RecordInfo']['UpdatedOn'], + ]; + } + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $action = 'CreateRecord'; + $param = ['Domain' => $this->domain, 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight]; + if ($Type == 'MX') { + $param['MX'] = intval($MX); + } + $data = $this->send_reuqest($action, $param); + return is_array($data) ? $data['RecordId'] : false; + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $action = 'ModifyRecord'; + $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight]; + if ($Type == 'MX') { + $param['MX'] = intval($MX); + } + $data = $this->send_reuqest($action, $param); + return is_array($data); + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + $action = 'ModifyRecordRemark'; + $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Remark' => $Remark]; + $data = $this->send_reuqest($action, $param); + return is_array($data); + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $action = 'DeleteRecord'; + $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)]; + $data = $this->send_reuqest($action, $param); + return is_array($data); + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; + $action = 'ModifyRecordStatus'; + $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Status' => $Status]; + $data = $this->send_reuqest($action, $param); + return is_array($data); + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + $action = 'DescribeDomainLogList'; + $offset = ($PageNumber - 1) * $PageSize; + $param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize]; + $data = $this->send_reuqest($action, $param); + if ($data) { + $list = []; + foreach ($data['LogList'] as $row) { + $list[] = ['time' => substr($row, 0, strpos($row, '(')), 'ip' => substr($row, strpos($row, '(') + 1, strpos($row, ')') - strpos($row, '(') - 1), 'data' => substr($row, strpos($row, ')') + 1, strpos($row, ' Uin:') - strpos($row, ')') - 1)]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + $action = 'DescribeRecordLineCategoryList'; + $param = ['Domain' => $this->domain]; + $data = $this->send_reuqest($action, $param); + if ($data) { + $list = []; + $this->processLineList($list, $data['LineList'], null); + return $list; + } else { + $data = $this->getRecordLineByGrade(); + if ($data) { + $list = []; + foreach ($data as $row) { + $list[$row['LineId']] = ['name' => $row['Name'], 'parent' => null]; + } + return $list; + } + } + return false; + } + + private function processLineList(&$list, $line_list, $parent) + { + foreach ($line_list as $row) { + if (isNullOrEmpty($row['LineId'])) { + $row['LineId'] = 'N.'.$row['LineName']; + } + if ($row['Useful'] && !isset($list[$row['LineId']])) { + $list[$row['LineId']] = ['name' => $row['LineName'], 'parent' => $parent]; + if ($row['SubGroup']) { + $this->processLineList($list, $row['SubGroup'], $row['LineId']); + } + } + } + } + + //获取域名概览信息 + public function getDomainInfo() + { + $action = 'DescribeDomain'; + $param = ['Domain' => $this->domain]; + $data = $this->send_reuqest($action, $param); + if ($data) { + $this->domainInfo = $data['DomainInfo']; + return $data['DomainInfo']; + } + return false; + } + + //获取域名权限 + public function getDomainPurview() + { + $action = 'DescribeDomainPurview'; + $param = ['Domain' => $this->domain]; + $data = $this->send_reuqest($action, $param); + if ($data) { + return $data['PurviewList']; + } + return false; + } + + //获取域名最低TTL + public function getMinTTL() + { + if ($this->domainInfo) { + return $this->domainInfo['TTL']; + } + $PurviewList = $this->getDomainPurview(); + if ($PurviewList) { + foreach ($PurviewList as $row) { + if ($row['Name'] == '记录 TTL 最低' || $row['Name'] == 'Min TTL value') { + return intval($row['Value']); + } + } + } + return false; + } + + //获取等级允许的线路 + public function getRecordLineByGrade() + { + $action = 'DescribeRecordLineList'; + $param = ['Domain' => $this->domain, 'DomainGrade' => '']; + $data = $this->send_reuqest($action, $param); + if ($data) { + $line_list = $data['LineList']; + if (!empty($data['LineGroupList'])) { + foreach ($data['LineGroupList'] as $row) { + $line_list[] = ['Name' => $row['Name'], 'LineId' => $row['LineId']]; + } + } + return $line_list; + } + return false; + } + + //获取用户信息 + public function getAccountInfo() + { + $action = 'DescribeUserDetail'; + $param = []; + $data = $this->send_reuqest($action, $param); + if ($data) { + return $data['UserInfo']; + } + return false; + } + + private function convertLineCode($line) + { + $convert_dict = ['default' => '0', 'unicom' => '10=1', 'telecom' => '10=0', 'mobile' => '10=3', 'edu' => '10=2', 'oversea' => '3=0', 'btvn' => '10=22', 'search' => '80=0', 'internal' => '7=0']; + if (array_key_exists($line, $convert_dict)) { + return $convert_dict[$line]; + } + return $line; + } + + private function convertType($type) + { + $convert_dict = ['REDIRECT_URL' => '显性URL', 'FORWARD_URL' => '隐性URL']; + if (array_key_exists($type, $convert_dict)) { + return $convert_dict[$type]; + } + return $type; + } + + private function convertTypeId($type) + { + $convert_dict = ['显性URL' => 'REDIRECT_URL', '隐性URL' => 'FORWARD_URL']; + if (array_key_exists($type, $convert_dict)) { + return $convert_dict[$type]; + } + return $type; + } + + + private function send_reuqest($action, $param) + { + $param = array_filter($param, function ($a) { return $a !== null;}); + if (!$param) { + $param = (object)[]; + } + $payload = json_encode($param); + $time = time(); + $authorization = $this->generateSign($payload, $time); + $header = [ + 'Authorization: '.$authorization, + 'Content-Type: application/json; charset=utf-8', + 'X-TC-Action: '.$action, + 'X-TC-Timestamp: '.$time, + 'X-TC-Version: '.$this->version, + ]; + return $this->curl_post($payload, $header); + } + + private function generateSign($payload, $time) + { + $algorithm = "TC3-HMAC-SHA256"; + + // step 1: build canonical request string + $httpRequestMethod = "POST"; + $canonicalUri = "/"; + $canonicalQueryString = ""; + $canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n"; + $signedHeaders = "content-type;host"; + $hashedRequestPayload = hash("SHA256", $payload); + $canonicalRequest = $httpRequestMethod."\n" + .$canonicalUri."\n" + .$canonicalQueryString."\n" + .$canonicalHeaders."\n" + .$signedHeaders."\n" + .$hashedRequestPayload; + + // step 2: build string to sign + $date = gmdate("Y-m-d", $time); + $credentialScope = $date."/".$this->service."/tc3_request"; + $hashedCanonicalRequest = hash("SHA256", $canonicalRequest); + $stringToSign = $algorithm."\n" + .$time."\n" + .$credentialScope."\n" + .$hashedCanonicalRequest; + + // step 3: sign string + $secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true); + $secretService = hash_hmac("SHA256", $this->service, $secretDate, true); + $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true); + $signature = hash_hmac("SHA256", $stringToSign, $secretSigning); + + // step 4: build authorization + $authorization = $algorithm + ." Credential=".$this->SecretId."/".$credentialScope + .", SignedHeaders=content-type;host, Signature=".$signature; + + return $authorization; + } + + private function curl_post($payload, $header) + { + $url = 'https://'.$this->endpoint.'/'; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + $response = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + curl_close($ch); + if ($errno) { + return false; + } + + $arr = json_decode($response, true); + if ($arr) { + if (isset($arr['Response']['Error'])) { + $this->setError($arr['Response']['Error']['Message']); + return false; + } else { + return $arr['Response']; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/dns/huawei.php b/app/lib/dns/huawei.php index c17d770..06b2c80 100644 --- a/app/lib/dns/huawei.php +++ b/app/lib/dns/huawei.php @@ -1,360 +1,415 @@ -AccessKeyId = $config['ak']; - $this->SecretAccessKey = $config['sk']; - $this->domain = $config['domain']; - $this->domainid = $config['domainid']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $offset = ($PageNumber-1)*$PageSize; - $query = ['offset' => $offset, 'limit' => $PageSize, 'name' => $KeyWord]; - $data = $this->send_reuqest('GET', '/v2/zones', $query); - if($data){ - $list = []; - foreach($data['zones'] as $row){ - $list[] = [ - 'DomainId' => $row['id'], - 'Domain' => rtrim($row['name'], '.'), - 'RecordCount' => $row['record_num'], - ]; - } - return ['total' => $data['metadata']['total_count'], 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $offset = ($PageNumber-1)*$PageSize; - $query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'offset' => $offset, 'limit' => $PageSize]; - if(!isNullOrEmpty($Status)){ - $Status = $Status == '1' ? 'ACTIVE' : 'DISABLE'; - $query['status'] = $Status; - } - if(!isNullOrEmpty($SubDomain)){ - $SubDomain = $this->getHost($SubDomain); - $query['name'] = $SubDomain; - $query['search_mode'] = 'equal'; - } - $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query); - if($data){ - $list = []; - foreach($data['recordsets'] as $row){ - if($row['name'] == $row['zone_name']) $row['name'] = '@'; - if($row['type'] == 'MX') list($row['mx'], $row['records']) = explode(' ', $row['records'][0]); - $list[] = [ - 'RecordId' => $row['id'], - 'Domain' => rtrim($row['zone_name'], '.'), - 'Name' => str_replace('.'.$row['zone_name'], '', $row['name']), - 'Type' => $row['type'], - 'Value' => $row['records'], - 'Line' => $row['line'], - 'TTL' => $row['ttl'], - 'MX' => isset($row['mx']) ? $row['mx'] : null, - 'Status' => $row['status'] == 'ACTIVE' ? '1' : '0', - 'Weight' => $row['weight'], - 'Remark' => $row['description'], - 'UpdateTime' => $row['updated_at'], - ]; - } - return ['total' => $data['metadata']['total_count'], 'list' => $list]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId); - if($data){ - if($data['name'] == $data['zone_name']) $data['name'] = '@'; - if($data['type'] == 'MX') list($data['mx'], $data['records']) = explode(' ', $data['records'][0]); - return [ - 'RecordId' => $data['id'], - 'Domain' => rtrim($data['zone_name'], '.'), - 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']), - 'Type' => $data['type'], - 'Value' => $data['records'], - 'Line' => $data['line'], - 'TTL' => $data['ttl'], - 'MX' => isset($data['mx']) ? $data['mx'] : null, - 'Status' => $data['status'] == 'ACTIVE' ? '1' : '0', - 'Weight' => $data['weight'], - 'Remark' => $data['description'], - 'UpdateTime' => $data['updated_at'], - ]; - } - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $Name = $this->getHost($Name); - if($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"'.$Value.'"'; - $records = explode(',', $Value); - $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark]; - if($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value; - if($Weight > 0) $params['weight'] = intval($Weight); - $data = $this->send_reuqest('POST', '/v2.1/zones/'.$this->domainid.'/recordsets', null, $params); - return is_array($data) ? $data['id'] : false; - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $Name = $this->getHost($Name); - if($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"'.$Value.'"'; - $records = explode(',', $Value); - $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark]; - if($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value; - if($Weight > 0) $params['weight'] = intval($Weight); - $data = $this->send_reuqest('PUT', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId, null, $params); - return is_array($data); - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - return false; - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $data = $this->send_reuqest('DELETE', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId); - return is_array($data); - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; - $params = ['status' => $Status]; - $data = $this->send_reuqest('PUT', '/v2.1/recordsets/'.$RecordId.'/statuses/set', null, $params); - return is_array($data); - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - $file_path = app()->getBasePath().'data'.DIRECTORY_SEPARATOR.'huawei_line.json'; - $content = file_get_contents($file_path); - $data = json_decode($content, true); - if($data){ - return $data; - $list = [$data['DEFAULT']['id'] => ['name'=>$data['DEFAULT']['zh'], 'parent'=>null]]; - $this->processLineList($list, $data['ISP'], null, 1, 1); - $this->processLineList($list, $data['REGION'], null, null, 1); - //file_put_contents($file_path, json_encode($list, JSON_UNESCAPED_UNICODE)); - return $list; - } - return false; - } - - private function processLineList(&$list, $line_list, $parent, $rootId = null, $rootName = null){ - foreach($line_list as $row){ - if($rootId && $rootId!==1){ - $row['id'] = $rootId.'_'.$row['id']; - } - if($rootName && $rootName!==1){ - $row['zh'] = $rootName.'_'.$row['zh']; - } - $list[$row['id']] = ['name'=>$row['zh'], 'parent'=>$parent]; - if(isset($row['children']) && !empty($row['children'])){ - $this->processLineList($list, $row['children'], $row['id'], $rootId === 1 ? $row['id'] : $rootId, $rootName === 1 ? $row['zh'] : $rootName); - } - } - } - - //获取域名概览信息 - public function getDomainInfo(){ - return $this->send_reuqest('GET', '/v2/zones/'.$this->domainid); - } - - //获取域名最低TTL - public function getMinTTL(){ - return false; - } - - private function convertType($type){ - return $type; - } - - private function getHost($Name){ - if($Name == '@') $Name = ''; - else $Name .= '.'; - $Name .= $this->domain . '.'; - return $Name; - } - - private function send_reuqest($method, $path, $query = null, $params = null){ - if(!empty($query)){ - $query = array_filter($query, function($a){ return $a!==null;}); - } - if(!empty($params)){ - $params = array_filter($params, function($a){ return $a!==null;}); - } - - $time = time(); - $date = gmdate("Ymd\THis\Z", $time); - $body = !empty($params) ? json_encode($params) : ''; - $headers = [ - 'Host' => $this->endpoint, - 'X-Sdk-Date' => $date, - ]; - if($body){ - $headers['Content-Type'] = 'application/json'; - } - - $authorization = $this->generateSign($method, $path, $query, $headers, $body, $time); - $headers['Authorization'] = $authorization; - - $url = 'https://'.$this->endpoint.$path; - if(!empty($query)){ - $url .= '?'.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 = "SDK-HMAC-SHA256"; - - // step 1: build canonical request string - $httpRequestMethod = $method; - $canonicalUri = $path; - if(substr($canonicalUri, -1) != "/") $canonicalUri .= "/"; - $canonicalQueryString = $this->getCanonicalQueryString($query); - [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); - $hashedRequestPayload = hash("sha256", $body); - $canonicalRequest = $httpRequestMethod."\n" - .$canonicalUri."\n" - .$canonicalQueryString."\n" - .$canonicalHeaders."\n" - .$signedHeaders."\n" - .$hashedRequestPayload; - - // step 2: build string to sign - $date = gmdate("Ymd\THis\Z", $time); - $hashedCanonicalRequest = hash("sha256", $canonicalRequest); - $stringToSign = $algorithm."\n" - .$date."\n" - .$hashedCanonicalRequest; - - // step 3: sign string - $signature = hash_hmac("sha256", $stringToSign, $this->SecretAccessKey); - - // step 4: build authorization - $authorization = $algorithm . ' Access=' . $this->AccessKeyId . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature; - - return $authorization; - } - - private function escape($str) - { - $search = ['+', '*', '%7E']; - $replace = ['%20', '%2A', '~']; - return str_replace($search, $replace, urlencode($str)); - } - - private function getCanonicalQueryString($parameters) - { - if(empty($parameters)) return ''; - ksort($parameters); - $canonicalQueryString = ''; - foreach ($parameters as $key => $value) { - $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); - } - return substr($canonicalQueryString, 1); - } - - private function getCanonicalHeaders($oldheaders){ - $headers = array(); - 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]; - } - - private function curl($method, $url, $body, $header){ - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - if(!empty($body)){ - curl_setopt($ch, CURLOPT_POSTFIELDS, $body); - } - $response = curl_exec($ch); - $errno = curl_errno($ch); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; - - $arr=json_decode($response,true); - if($arr){ - if(isset($arr['error_msg'])){ - $this->setError($arr['error_msg']); - return false; - }elseif(isset($arr['message'])){ - $this->setError($arr['message']); - return false; - }else{ - return $arr; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file +AccessKeyId = $config['ak']; + $this->SecretAccessKey = $config['sk']; + $this->domain = $config['domain']; + $this->domainid = $config['domainid']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $offset = ($PageNumber - 1) * $PageSize; + $query = ['offset' => $offset, 'limit' => $PageSize, 'name' => $KeyWord]; + $data = $this->send_reuqest('GET', '/v2/zones', $query); + if ($data) { + $list = []; + foreach ($data['zones'] as $row) { + $list[] = [ + 'DomainId' => $row['id'], + 'Domain' => rtrim($row['name'], '.'), + 'RecordCount' => $row['record_num'], + ]; + } + return ['total' => $data['metadata']['total_count'], 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $offset = ($PageNumber - 1) * $PageSize; + $query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'offset' => $offset, 'limit' => $PageSize]; + if (!isNullOrEmpty($Status)) { + $Status = $Status == '1' ? 'ACTIVE' : 'DISABLE'; + $query['status'] = $Status; + } + if (!isNullOrEmpty($SubDomain)) { + $SubDomain = $this->getHost($SubDomain); + $query['name'] = $SubDomain; + $query['search_mode'] = 'equal'; + } + $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query); + if ($data) { + $list = []; + foreach ($data['recordsets'] as $row) { + if ($row['name'] == $row['zone_name']) { + $row['name'] = '@'; + } + if ($row['type'] == 'MX') { + list($row['mx'], $row['records']) = explode(' ', $row['records'][0]); + } + $list[] = [ + 'RecordId' => $row['id'], + 'Domain' => rtrim($row['zone_name'], '.'), + 'Name' => str_replace('.'.$row['zone_name'], '', $row['name']), + 'Type' => $row['type'], + 'Value' => $row['records'], + 'Line' => $row['line'], + 'TTL' => $row['ttl'], + 'MX' => isset($row['mx']) ? $row['mx'] : null, + 'Status' => $row['status'] == 'ACTIVE' ? '1' : '0', + 'Weight' => $row['weight'], + 'Remark' => $row['description'], + 'UpdateTime' => $row['updated_at'], + ]; + } + return ['total' => $data['metadata']['total_count'], 'list' => $list]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId); + if ($data) { + if ($data['name'] == $data['zone_name']) { + $data['name'] = '@'; + } + if ($data['type'] == 'MX') { + list($data['mx'], $data['records']) = explode(' ', $data['records'][0]); + } + return [ + 'RecordId' => $data['id'], + 'Domain' => rtrim($data['zone_name'], '.'), + 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']), + 'Type' => $data['type'], + 'Value' => $data['records'], + 'Line' => $data['line'], + 'TTL' => $data['ttl'], + 'MX' => isset($data['mx']) ? $data['mx'] : null, + 'Status' => $data['status'] == 'ACTIVE' ? '1' : '0', + 'Weight' => $data['weight'], + 'Remark' => $data['description'], + 'UpdateTime' => $data['updated_at'], + ]; + } + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $Name = $this->getHost($Name); + if ($Type == 'TXT' && substr($Value, 0, 1) != '"') { + $Value = '"'.$Value.'"'; + } + $records = explode(',', $Value); + $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark]; + if ($Type == 'MX') { + $params['records'][0] = intval($MX) . ' ' . $Value; + } + if ($Weight > 0) { + $params['weight'] = intval($Weight); + } + $data = $this->send_reuqest('POST', '/v2.1/zones/'.$this->domainid.'/recordsets', null, $params); + return is_array($data) ? $data['id'] : false; + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $Name = $this->getHost($Name); + if ($Type == 'TXT' && substr($Value, 0, 1) != '"') { + $Value = '"'.$Value.'"'; + } + $records = explode(',', $Value); + $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark]; + if ($Type == 'MX') { + $params['records'][0] = intval($MX) . ' ' . $Value; + } + if ($Weight > 0) { + $params['weight'] = intval($Weight); + } + $data = $this->send_reuqest('PUT', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId, null, $params); + return is_array($data); + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $data = $this->send_reuqest('DELETE', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId); + return is_array($data); + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $Status = $Status == '1' ? 'ENABLE' : 'DISABLE'; + $params = ['status' => $Status]; + $data = $this->send_reuqest('PUT', '/v2.1/recordsets/'.$RecordId.'/statuses/set', null, $params); + return is_array($data); + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + $file_path = app()->getBasePath().'data'.DIRECTORY_SEPARATOR.'huawei_line.json'; + $content = file_get_contents($file_path); + $data = json_decode($content, true); + if ($data) { + return $data; + $list = [$data['DEFAULT']['id'] => ['name' => $data['DEFAULT']['zh'], 'parent' => null]]; + $this->processLineList($list, $data['ISP'], null, 1, 1); + $this->processLineList($list, $data['REGION'], null, null, 1); + //file_put_contents($file_path, json_encode($list, JSON_UNESCAPED_UNICODE)); + return $list; + } + return false; + } + + private function processLineList(&$list, $line_list, $parent, $rootId = null, $rootName = null) + { + foreach ($line_list as $row) { + if ($rootId && $rootId !== 1) { + $row['id'] = $rootId.'_'.$row['id']; + } + if ($rootName && $rootName !== 1) { + $row['zh'] = $rootName.'_'.$row['zh']; + } + $list[$row['id']] = ['name' => $row['zh'], 'parent' => $parent]; + if (isset($row['children']) && !empty($row['children'])) { + $this->processLineList($list, $row['children'], $row['id'], $rootId === 1 ? $row['id'] : $rootId, $rootName === 1 ? $row['zh'] : $rootName); + } + } + } + + //获取域名概览信息 + public function getDomainInfo() + { + return $this->send_reuqest('GET', '/v2/zones/'.$this->domainid); + } + + //获取域名最低TTL + public function getMinTTL() + { + return false; + } + + private function convertType($type) + { + return $type; + } + + private function getHost($Name) + { + if ($Name == '@') { + $Name = ''; + } else { + $Name .= '.'; + } + $Name .= $this->domain . '.'; + return $Name; + } + + private function send_reuqest($method, $path, $query = null, $params = null) + { + if (!empty($query)) { + $query = array_filter($query, function ($a) { return $a !== null;}); + } + if (!empty($params)) { + $params = array_filter($params, function ($a) { return $a !== null;}); + } + + $time = time(); + $date = gmdate("Ymd\THis\Z", $time); + $body = !empty($params) ? json_encode($params) : ''; + $headers = [ + 'Host' => $this->endpoint, + 'X-Sdk-Date' => $date, + ]; + if ($body) { + $headers['Content-Type'] = 'application/json'; + } + + $authorization = $this->generateSign($method, $path, $query, $headers, $body, $time); + $headers['Authorization'] = $authorization; + + $url = 'https://'.$this->endpoint.$path; + if (!empty($query)) { + $url .= '?'.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 = "SDK-HMAC-SHA256"; + + // step 1: build canonical request string + $httpRequestMethod = $method; + $canonicalUri = $path; + if (substr($canonicalUri, -1) != "/") { + $canonicalUri .= "/"; + } + $canonicalQueryString = $this->getCanonicalQueryString($query); + [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); + $hashedRequestPayload = hash("sha256", $body); + $canonicalRequest = $httpRequestMethod."\n" + .$canonicalUri."\n" + .$canonicalQueryString."\n" + .$canonicalHeaders."\n" + .$signedHeaders."\n" + .$hashedRequestPayload; + + // step 2: build string to sign + $date = gmdate("Ymd\THis\Z", $time); + $hashedCanonicalRequest = hash("sha256", $canonicalRequest); + $stringToSign = $algorithm."\n" + .$date."\n" + .$hashedCanonicalRequest; + + // step 3: sign string + $signature = hash_hmac("sha256", $stringToSign, $this->SecretAccessKey); + + // step 4: build authorization + $authorization = $algorithm . ' Access=' . $this->AccessKeyId . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature; + + return $authorization; + } + + private function escape($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + + private function getCanonicalQueryString($parameters) + { + if (empty($parameters)) { + return ''; + } + ksort($parameters); + $canonicalQueryString = ''; + foreach ($parameters as $key => $value) { + $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); + } + return substr($canonicalQueryString, 1); + } + + private function getCanonicalHeaders($oldheaders) + { + $headers = array(); + 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]; + } + + private function curl($method, $url, $body, $header) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if (!empty($body)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + $response = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + curl_close($ch); + if ($errno) { + return false; + } + + $arr = json_decode($response, true); + if ($arr) { + if (isset($arr['error_msg'])) { + $this->setError($arr['error_msg']); + return false; + } elseif (isset($arr['message'])) { + $this->setError($arr['message']); + return false; + } else { + return $arr; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/dns/huoshan.php b/app/lib/dns/huoshan.php index 4f594fa..d7011d6 100644 --- a/app/lib/dns/huoshan.php +++ b/app/lib/dns/huoshan.php @@ -1,387 +1,440 @@ - ['level' => 1, 'name' => '免费版', 'ttl' => 600], - 'professional_inner' => ['level' => 2, 'name' => '专业版', 'ttl' => 300], - 'enterprise_inner' => ['level' => 3, 'name' => '企业版', 'ttl' => 60], - 'ultimate_inner' => ['level' => 4, 'name' => '旗舰版', 'ttl' => 1], - 'ultimate_exclusive_inner' => ['level' => 5, 'name' => '尊享版', 'ttl' => 1], - ]; - - function __construct($config){ - $this->AccessKeyId = $config['ak']; - $this->SecretAccessKey = $config['sk']; - $this->domain = $config['domain']; - $this->domainid = $config['domainid']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $query = ['PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Key' => $KeyWord]; - $data = $this->send_reuqest('GET', 'ListZones', $query); - if($data){ - $list = []; - if(!empty($data['Zones'])){ - foreach($data['Zones'] as $row){ - $list[] = [ - 'DomainId' => $row['ZID'], - 'Domain' => $row['ZoneName'], - 'RecordCount' => $row['RecordCount'], - ]; - } - } - return ['total' => $data['Total'], 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $query = ['ZID' => intval($this->domainid), 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'SearchOrder' => 'desc']; - if(!empty($SubDomain) || !empty($Type) || !empty($Line) || !empty($Value)){ - $query += ['Host' => $SubDomain, 'Value' => $Value, 'Type' => $Type, 'Line' => $Line]; - }elseif(!empty($KeyWord)){ - $query += ['Host' => $KeyWord]; - } - $data = $this->send_reuqest('GET', 'ListRecords', $query); - if($data){ - $list = []; - foreach($data['Records'] as $row){ - if($row['Type'] == 'MX') list($row['MX'], $row['Value']) = explode(' ', $row['Value']); - $list[] = [ - 'RecordId' => $row['RecordID'], - 'Domain' => $this->domain, - 'Name' => $row['Host'], - 'Type' => $row['Type'], - 'Value' => $row['Value'], - 'Line' => $row['Line'], - 'TTL' => $row['TTL'], - 'MX' => isset($row['MX']) ? $row['MX'] : null, - 'Status' => $row['Enable'] ? '1' : '0', - 'Weight' => $row['Weight'], - 'Remark' => $row['Remark'], - 'UpdateTime' => $row['UpdatedAt'], - ]; - } - return ['total' => $data['TotalCount'], 'list' => $list]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - $data = $this->send_reuqest('GET', 'QueryRecord', ['RecordID' => $RecordId]); - if($data){ - if($data['name'] == $data['zone_name']) $data['name'] = '@'; - if($data['Type'] == 'MX') list($data['MX'], $data['Value']) = explode(' ', $data['Value']); - return [ - 'RecordId' => $data['RecordID'], - 'Domain' => $this->domain, - 'Name' => $data['Host'], - 'Type' => $data['Type'], - 'Value' => $data['Value'], - 'Line' => $data['Line'], - 'TTL' => $data['TTL'], - 'MX' => isset($data['MX']) ? $data['MX'] : null, - 'Status' => $data['Enable'] ? '1' : '0', - 'Weight' => $data['Weight'], - 'Remark' => $data['Remark'], - 'UpdateTime' => $data['UpdatedAt'], - ]; - } - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $params = ['ZID' => intval($this->domainid), 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line'=>$Line, 'TTL' => intval($TTL), 'Remark' => $Remark]; - if($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value; - if($Weight > 0) $params['Weight'] = $Weight; - $data = $this->send_reuqest('POST', 'CreateRecord', $params); - return is_array($data) ? $data['RecordID'] : false; - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $params = ['RecordID' => $RecordId, 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line'=>$Line, 'TTL' => intval($TTL), 'Remark' => $Remark]; - if($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value; - if($Weight > 0) $params['Weight'] = $Weight; - $data = $this->send_reuqest('POST', 'UpdateRecord', $params); - return is_array($data); - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - return false; - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $data = $this->send_reuqest('POST', 'DeleteRecord', ['RecordID' => $RecordId]); - return $data; - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $params = ['RecordID' => $RecordId, 'Enable' => $Status == '1']; - $data = $this->send_reuqest('POST', 'UpdateRecordStatus', $params); - return is_array($data); - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - $domainInfo = $this->getDomainInfo(); - if(!$domainInfo) return false; - $level = $this->getTradeInfo($domainInfo['TradeCode'])['level']; - $data = $this->send_reuqest('GET', 'ListLines', []); - if($data){ - $list = []; - $list['default'] = ['name' => '默认', 'parent' => null]; - foreach($data['Lines'] as $row){ - if($row['Value'] == 'default') continue; - if($row['Level'] > $level) continue; - $list[$row['Value']] = ['name' => $row['Name'], 'parent' => isset($row['FatherValue']) ? $row['FatherValue'] : null]; - } - - $data = $this->send_reuqest('GET', 'ListCustomLines', []); - if($data && $data['TotalCount'] > 0){ - $list['N.customer_lines'] = ['name' => '自定义线路', 'parent' => null]; - foreach($data['CustomerLines'] as $row){ - $list[$row['Line']] = ['name' => $row['NameCN'], 'parent' => 'N.customer_lines']; - } - } - - return $list; - } - return false; - } - - //获取域名概览信息 - public function getDomainInfo(){ - if(!empty($this->domainInfo)) return $this->domainInfo; - $query = ['ZID' => intval($this->domainid)]; - $data = $this->send_reuqest('GET', 'QueryZone', $query); - if($data){ - $this->domainInfo = $data; - return $data; - } - return false; - } - - //获取域名最低TTL - public function getMinTTL(){ - $domainInfo = $this->getDomainInfo(); - if($domainInfo){ - $ttl = $this->getTradeInfo($domainInfo['TradeCode'])['ttl']; - return $ttl; - } - return false; - } - - private function convertType($type){ - return $type; - } - - private function getTradeInfo($trade_code){ - if(array_key_exists($trade_code, self::$trade_code_list)){ - $trade_code = $trade_code; - }else{ - $trade_code = 'free_inner'; - } - return self::$trade_code_list[$trade_code]; - } - - private function send_reuqest($method, $action, $params = []){ - if(!empty($params)){ - $params = array_filter($params, function($a){ return $a!==null;}); - } - - $query = [ - 'Action' => $action, - 'Version' => $this->version, - ]; - - $body = ''; - if($method == 'GET'){ - $query = array_merge($query, $params); - }else{ - $body = !empty($params) ? json_encode($params) : ''; - } - - $time = time(); - $headers = [ - 'Host' => $this->endpoint, - 'X-Date' => gmdate("Ymd\THis\Z", $time), - //'X-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"; - - // step 1: build canonical request string - $httpRequestMethod = $method; - $canonicalUri = $path; - if(substr($canonicalUri, -1) != "/") $canonicalUri .= "/"; - $canonicalQueryString = $this->getCanonicalQueryString($query); - [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); - $hashedRequestPayload = hash("sha256", $body); - $canonicalRequest = $httpRequestMethod."\n" - .$canonicalUri."\n" - .$canonicalQueryString."\n" - .$canonicalHeaders."\n" - .$signedHeaders."\n" - .$hashedRequestPayload; - - // step 2: build string to sign - $date = gmdate("Ymd\THis\Z", $time); - $shortDate = substr($date, 0, 8); - $credentialScope = $shortDate . '/' .$this->region . '/' . $this->service . '/request'; - $hashedCanonicalRequest = hash("sha256", $canonicalRequest); - $stringToSign = $algorithm."\n" - .$date."\n" - .$credentialScope."\n" - .$hashedCanonicalRequest; - - // step 3: sign string - $kDate = hash_hmac("sha256", $shortDate, $this->SecretAccessKey, true); - $kRegion = hash_hmac("sha256", $this->region, $kDate, true); - $kService = hash_hmac("sha256", $this->service, $kRegion, true); - $kSigning = hash_hmac("sha256", "request", $kService, true); - $signature = hash_hmac("sha256", $stringToSign, $kSigning); - - // step 4: build authorization - $credential = $this->AccessKeyId . '/' . $shortDate . '/' . $this->region . '/' . $this->service . '/request'; - $authorization = $algorithm . ' Credential=' . $credential . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature; - - return $authorization; - } - - private function escape($str) - { - $search = ['+', '*', '%7E']; - $replace = ['%20', '%2A', '~']; - return str_replace($search, $replace, urlencode($str)); - } - - private function getCanonicalQueryString($parameters) - { - if(empty($parameters)) return ''; - ksort($parameters); - $canonicalQueryString = ''; - foreach ($parameters as $key => $value) { - $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); - } - return substr($canonicalQueryString, 1); - } - - private function getCanonicalHeaders($oldheaders){ - $headers = array(); - 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]; - } - - private function curl($method, $url, $body, $header){ - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - if(!empty($body)){ - curl_setopt($ch, CURLOPT_POSTFIELDS, $body); - } - $response = curl_exec($ch); - $errno = curl_errno($ch); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; - - $arr=json_decode($response,true); - if($arr){ - if(isset($arr['ResponseMetadata']['Error']['MessageCN'])){ - $this->setError($arr['ResponseMetadata']['Error']['MessageCN']); - return false; - }elseif(isset($arr['ResponseMetadata']['Error']['Message'])){ - $this->setError($arr['ResponseMetadata']['Error']['Message']); - return false; - }elseif(isset($arr['Result'])){ - return $arr['Result']; - }else{ - return true; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file + ['level' => 1, 'name' => '免费版', 'ttl' => 600], + 'professional_inner' => ['level' => 2, 'name' => '专业版', 'ttl' => 300], + 'enterprise_inner' => ['level' => 3, 'name' => '企业版', 'ttl' => 60], + 'ultimate_inner' => ['level' => 4, 'name' => '旗舰版', 'ttl' => 1], + 'ultimate_exclusive_inner' => ['level' => 5, 'name' => '尊享版', 'ttl' => 1], + ]; + + public function __construct($config) + { + $this->AccessKeyId = $config['ak']; + $this->SecretAccessKey = $config['sk']; + $this->domain = $config['domain']; + $this->domainid = $config['domainid']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $query = ['PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Key' => $KeyWord]; + $data = $this->send_reuqest('GET', 'ListZones', $query); + if ($data) { + $list = []; + if (!empty($data['Zones'])) { + foreach ($data['Zones'] as $row) { + $list[] = [ + 'DomainId' => $row['ZID'], + 'Domain' => $row['ZoneName'], + 'RecordCount' => $row['RecordCount'], + ]; + } + } + return ['total' => $data['Total'], 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $query = ['ZID' => intval($this->domainid), 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'SearchOrder' => 'desc']; + if (!empty($SubDomain) || !empty($Type) || !empty($Line) || !empty($Value)) { + $query += ['Host' => $SubDomain, 'Value' => $Value, 'Type' => $Type, 'Line' => $Line]; + } elseif (!empty($KeyWord)) { + $query += ['Host' => $KeyWord]; + } + $data = $this->send_reuqest('GET', 'ListRecords', $query); + if ($data) { + $list = []; + foreach ($data['Records'] as $row) { + if ($row['Type'] == 'MX') { + list($row['MX'], $row['Value']) = explode(' ', $row['Value']); + } + $list[] = [ + 'RecordId' => $row['RecordID'], + 'Domain' => $this->domain, + 'Name' => $row['Host'], + 'Type' => $row['Type'], + 'Value' => $row['Value'], + 'Line' => $row['Line'], + 'TTL' => $row['TTL'], + 'MX' => isset($row['MX']) ? $row['MX'] : null, + 'Status' => $row['Enable'] ? '1' : '0', + 'Weight' => $row['Weight'], + 'Remark' => $row['Remark'], + 'UpdateTime' => $row['UpdatedAt'], + ]; + } + return ['total' => $data['TotalCount'], 'list' => $list]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + $data = $this->send_reuqest('GET', 'QueryRecord', ['RecordID' => $RecordId]); + if ($data) { + if ($data['name'] == $data['zone_name']) { + $data['name'] = '@'; + } + if ($data['Type'] == 'MX') { + list($data['MX'], $data['Value']) = explode(' ', $data['Value']); + } + return [ + 'RecordId' => $data['RecordID'], + 'Domain' => $this->domain, + 'Name' => $data['Host'], + 'Type' => $data['Type'], + 'Value' => $data['Value'], + 'Line' => $data['Line'], + 'TTL' => $data['TTL'], + 'MX' => isset($data['MX']) ? $data['MX'] : null, + 'Status' => $data['Enable'] ? '1' : '0', + 'Weight' => $data['Weight'], + 'Remark' => $data['Remark'], + 'UpdateTime' => $data['UpdatedAt'], + ]; + } + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $params = ['ZID' => intval($this->domainid), 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark]; + if ($Type == 'MX') { + $params['Value'] = intval($MX) . ' ' . $Value; + } + if ($Weight > 0) { + $params['Weight'] = $Weight; + } + $data = $this->send_reuqest('POST', 'CreateRecord', $params); + return is_array($data) ? $data['RecordID'] : false; + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $params = ['RecordID' => $RecordId, 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark]; + if ($Type == 'MX') { + $params['Value'] = intval($MX) . ' ' . $Value; + } + if ($Weight > 0) { + $params['Weight'] = $Weight; + } + $data = $this->send_reuqest('POST', 'UpdateRecord', $params); + return is_array($data); + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $data = $this->send_reuqest('POST', 'DeleteRecord', ['RecordID' => $RecordId]); + return $data; + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $params = ['RecordID' => $RecordId, 'Enable' => $Status == '1']; + $data = $this->send_reuqest('POST', 'UpdateRecordStatus', $params); + return is_array($data); + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + $domainInfo = $this->getDomainInfo(); + if (!$domainInfo) { + return false; + } + $level = $this->getTradeInfo($domainInfo['TradeCode'])['level']; + $data = $this->send_reuqest('GET', 'ListLines', []); + if ($data) { + $list = []; + $list['default'] = ['name' => '默认', 'parent' => null]; + foreach ($data['Lines'] as $row) { + if ($row['Value'] == 'default') { + continue; + } + if ($row['Level'] > $level) { + continue; + } + $list[$row['Value']] = ['name' => $row['Name'], 'parent' => isset($row['FatherValue']) ? $row['FatherValue'] : null]; + } + + $data = $this->send_reuqest('GET', 'ListCustomLines', []); + if ($data && $data['TotalCount'] > 0) { + $list['N.customer_lines'] = ['name' => '自定义线路', 'parent' => null]; + foreach ($data['CustomerLines'] as $row) { + $list[$row['Line']] = ['name' => $row['NameCN'], 'parent' => 'N.customer_lines']; + } + } + + return $list; + } + return false; + } + + //获取域名概览信息 + public function getDomainInfo() + { + if (!empty($this->domainInfo)) { + return $this->domainInfo; + } + $query = ['ZID' => intval($this->domainid)]; + $data = $this->send_reuqest('GET', 'QueryZone', $query); + if ($data) { + $this->domainInfo = $data; + return $data; + } + return false; + } + + //获取域名最低TTL + public function getMinTTL() + { + $domainInfo = $this->getDomainInfo(); + if ($domainInfo) { + $ttl = $this->getTradeInfo($domainInfo['TradeCode'])['ttl']; + return $ttl; + } + return false; + } + + private function convertType($type) + { + return $type; + } + + private function getTradeInfo($trade_code) + { + if (array_key_exists($trade_code, self::$trade_code_list)) { + $trade_code = $trade_code; + } else { + $trade_code = 'free_inner'; + } + return self::$trade_code_list[$trade_code]; + } + + private function send_reuqest($method, $action, $params = []) + { + if (!empty($params)) { + $params = array_filter($params, function ($a) { return $a !== null;}); + } + + $query = [ + 'Action' => $action, + 'Version' => $this->version, + ]; + + $body = ''; + if ($method == 'GET') { + $query = array_merge($query, $params); + } else { + $body = !empty($params) ? json_encode($params) : ''; + } + + $time = time(); + $headers = [ + 'Host' => $this->endpoint, + 'X-Date' => gmdate("Ymd\THis\Z", $time), + //'X-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"; + + // step 1: build canonical request string + $httpRequestMethod = $method; + $canonicalUri = $path; + if (substr($canonicalUri, -1) != "/") { + $canonicalUri .= "/"; + } + $canonicalQueryString = $this->getCanonicalQueryString($query); + [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers); + $hashedRequestPayload = hash("sha256", $body); + $canonicalRequest = $httpRequestMethod."\n" + .$canonicalUri."\n" + .$canonicalQueryString."\n" + .$canonicalHeaders."\n" + .$signedHeaders."\n" + .$hashedRequestPayload; + + // step 2: build string to sign + $date = gmdate("Ymd\THis\Z", $time); + $shortDate = substr($date, 0, 8); + $credentialScope = $shortDate . '/' .$this->region . '/' . $this->service . '/request'; + $hashedCanonicalRequest = hash("sha256", $canonicalRequest); + $stringToSign = $algorithm."\n" + .$date."\n" + .$credentialScope."\n" + .$hashedCanonicalRequest; + + // step 3: sign string + $kDate = hash_hmac("sha256", $shortDate, $this->SecretAccessKey, true); + $kRegion = hash_hmac("sha256", $this->region, $kDate, true); + $kService = hash_hmac("sha256", $this->service, $kRegion, true); + $kSigning = hash_hmac("sha256", "request", $kService, true); + $signature = hash_hmac("sha256", $stringToSign, $kSigning); + + // step 4: build authorization + $credential = $this->AccessKeyId . '/' . $shortDate . '/' . $this->region . '/' . $this->service . '/request'; + $authorization = $algorithm . ' Credential=' . $credential . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature; + + return $authorization; + } + + private function escape($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + + private function getCanonicalQueryString($parameters) + { + if (empty($parameters)) { + return ''; + } + ksort($parameters); + $canonicalQueryString = ''; + foreach ($parameters as $key => $value) { + $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value); + } + return substr($canonicalQueryString, 1); + } + + private function getCanonicalHeaders($oldheaders) + { + $headers = array(); + 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]; + } + + private function curl($method, $url, $body, $header) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if (!empty($body)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + $response = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + curl_close($ch); + if ($errno) { + return false; + } + + $arr = json_decode($response, true); + if ($arr) { + if (isset($arr['ResponseMetadata']['Error']['MessageCN'])) { + $this->setError($arr['ResponseMetadata']['Error']['MessageCN']); + return false; + } elseif (isset($arr['ResponseMetadata']['Error']['Message'])) { + $this->setError($arr['ResponseMetadata']['Error']['Message']); + return false; + } elseif (isset($arr['Result'])) { + return $arr['Result']; + } else { + return true; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/dns/west.php b/app/lib/dns/west.php index 54994e7..684487f 100644 --- a/app/lib/dns/west.php +++ b/app/lib/dns/west.php @@ -1,209 +1,235 @@ -username = $config['ak']; - $this->api_password = $config['sk']; - $this->domain = $config['domain']; - } - - public function getError(){ - return $this->error; - } - - public function check(){ - if($this->getDomainList() != false){ - return true; - } - return false; - } - - //获取域名列表 - public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){ - $param = ['act' => 'getdomains', 'page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord]; - $data = $this->execute('/domain/', $param); - if($data){ - $list = []; - foreach($data['items'] as $row){ - $list[] = [ - 'DomainId' => $row['domain'], - 'Domain' => $row['domain'], - 'RecordCount' => 0, - ]; - } - return ['total' => $data['total'], 'list' => $list]; - } - return false; - } - - //获取解析记录列表 - public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null){ - $param = ['act' => 'getdnsrecord', 'domain' => $this->domain, 'type' => $Type, 'line' => $Line, 'host' => $KeyWord, 'value' => $Value, 'pageno' => $PageNumber, 'limit' => $PageSize]; - if(!isNullOrEmpty(($SubDomain))){ - $param['host'] = $SubDomain; - } - $data = $this->execute('/domain/', $param); - if($data){ - $list = []; - foreach($data['items'] as $row){ - $list[] = [ - 'RecordId' => $row['id'], - 'Domain' => $this->domain, - 'Name' => $row['item'], - 'Type' => $row['type'], - 'Value' => $row['value'], - 'Line' => $row['line'], - 'TTL' => $row['ttl'], - 'MX' => $row['level'], - 'Status' => $row['pause'] == 1 ? '0' : '1', - 'Weight' => null, - 'Remark' => null, - 'UpdateTime' => null, - ]; - } - return ['total' => $data['total'], 'list' => $list]; - } - return false; - } - - //获取子域名解析记录列表 - public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){ - if($SubDomain == '')$SubDomain='@'; - return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); - } - - //获取解析记录详细信息 - public function getDomainRecordInfo($RecordId){ - return false; - } - - //添加解析记录 - public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $param = ['act' => 'adddnsrecord', 'domain' => $this->domain, 'host' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line]; - $data = $this->execute('/domain/', $param); - return is_array($data) ? $data['id'] : false; - } - - //修改解析记录 - public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null){ - $param = ['act' => 'moddnsrecord', 'domain' => $this->domain, 'id' => $RecordId, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line]; - $data = $this->execute('/domain/', $param); - return is_array($data); - } - - //修改解析记录备注 - public function updateDomainRecordRemark($RecordId, $Remark){ - return false; - } - - //删除解析记录 - public function deleteDomainRecord($RecordId){ - $param = ['act' => 'deldnsrecord', 'domain' => $this->domain, 'id' => $RecordId]; - $data = $this->execute('/domain/', $param); - return is_array($data); - } - - //设置解析记录状态 - public function setDomainRecordStatus($RecordId, $Status){ - $param = ['act' => 'pause', 'domain' => $this->domain, 'id' => $RecordId, 'val' => $Status == '1' ? '0' : '1']; - $data = $this->execute('/domain/', $param); - return $data !== false; - } - - //获取解析记录操作日志 - public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){ - return false; - } - - //获取解析线路列表 - public function getRecordLine(){ - return [ - ''=>['name'=>'默认', 'parent'=>null], - 'LTEL'=>['name'=>'电信', 'parent'=>null], - 'LCNC'=>['name'=>'联通', 'parent'=>null], - 'LMOB'=>['name'=>'移动', 'parent'=>null], - 'LEDU'=>['name'=>'教育网', 'parent'=>null], - 'LSEO'=>['name'=>'搜索引擎', 'parent'=>null], - 'LFOR'=>['name'=>'境外', 'parent'=>null], - ]; - } - - //获取域名信息 - public function getDomainInfo(){ - return false; - } - - //获取域名最低TTL - public function getMinTTL(){ - return false; - } - - private function convertType($type){ - return $type; - } - - private function execute($path, $params){ - $params['username'] = $this->username; - $params['time'] = $this->getMillisecond(); - $params['token'] = md5($this->username.$this->api_password.$params['time']); - $response = $this->curl($path, $params); - $response = mb_convert_encoding($response, 'UTF-8', 'GBK'); - $arr=json_decode($response,true); - if($arr){ - if($arr['result'] == 200){ - return isset($arr['data']) ? $arr['data'] : []; - }else{ - $this->setError($arr['msg']); - return false; - } - }else{ - $this->setError('返回数据解析失败'); - return false; - } - } - - private function curl($path, $params = null){ - $url = $this->baseUrl . $path; - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - if ($params) { - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); - } - $response = curl_exec($ch); - $errno = curl_errno($ch); - if ($errno) { - $this->setError('Curl error: ' . curl_error($ch)); - } - curl_close($ch); - if ($errno) return false; - return $response; - } - - private function getMillisecond() - { - list($s1, $s2) = explode(' ', microtime()); - return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); - } - - private function setError($message){ - $this->error = $message; - //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); - } -} \ No newline at end of file +username = $config['ak']; + $this->api_password = $config['sk']; + $this->domain = $config['domain']; + } + + public function getError() + { + return $this->error; + } + + public function check() + { + if ($this->getDomainList() != false) { + return true; + } + return false; + } + + //获取域名列表 + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $param = ['act' => 'getdomains', 'page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord]; + $data = $this->execute('/domain/', $param); + if ($data) { + $list = []; + foreach ($data['items'] as $row) { + $list[] = [ + 'DomainId' => $row['domain'], + 'Domain' => $row['domain'], + 'RecordCount' => 0, + ]; + } + return ['total' => $data['total'], 'list' => $list]; + } + return false; + } + + //获取解析记录列表 + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $param = ['act' => 'getdnsrecord', 'domain' => $this->domain, 'type' => $Type, 'line' => $Line, 'host' => $KeyWord, 'value' => $Value, 'pageno' => $PageNumber, 'limit' => $PageSize]; + if (!isNullOrEmpty(($SubDomain))) { + $param['host'] = $SubDomain; + } + $data = $this->execute('/domain/', $param); + if ($data) { + $list = []; + foreach ($data['items'] as $row) { + $list[] = [ + 'RecordId' => $row['id'], + 'Domain' => $this->domain, + 'Name' => $row['item'], + 'Type' => $row['type'], + 'Value' => $row['value'], + 'Line' => $row['line'], + 'TTL' => $row['ttl'], + 'MX' => $row['level'], + 'Status' => $row['pause'] == 1 ? '0' : '1', + 'Weight' => null, + 'Remark' => null, + 'UpdateTime' => null, + ]; + } + return ['total' => $data['total'], 'list' => $list]; + } + return false; + } + + //获取子域名解析记录列表 + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + if ($SubDomain == '') { + $SubDomain = '@'; + } + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + //获取解析记录详细信息 + public function getDomainRecordInfo($RecordId) + { + return false; + } + + //添加解析记录 + public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $param = ['act' => 'adddnsrecord', 'domain' => $this->domain, 'host' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line]; + $data = $this->execute('/domain/', $param); + return is_array($data) ? $data['id'] : false; + } + + //修改解析记录 + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $param = ['act' => 'moddnsrecord', 'domain' => $this->domain, 'id' => $RecordId, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line]; + $data = $this->execute('/domain/', $param); + return is_array($data); + } + + //修改解析记录备注 + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + //删除解析记录 + public function deleteDomainRecord($RecordId) + { + $param = ['act' => 'deldnsrecord', 'domain' => $this->domain, 'id' => $RecordId]; + $data = $this->execute('/domain/', $param); + return is_array($data); + } + + //设置解析记录状态 + public function setDomainRecordStatus($RecordId, $Status) + { + $param = ['act' => 'pause', 'domain' => $this->domain, 'id' => $RecordId, 'val' => $Status == '1' ? '0' : '1']; + $data = $this->execute('/domain/', $param); + return $data !== false; + } + + //获取解析记录操作日志 + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + //获取解析线路列表 + public function getRecordLine() + { + return [ + '' => ['name' => '默认', 'parent' => null], + 'LTEL' => ['name' => '电信', 'parent' => null], + 'LCNC' => ['name' => '联通', 'parent' => null], + 'LMOB' => ['name' => '移动', 'parent' => null], + 'LEDU' => ['name' => '教育网', 'parent' => null], + 'LSEO' => ['name' => '搜索引擎', 'parent' => null], + 'LFOR' => ['name' => '境外', 'parent' => null], + ]; + } + + //获取域名信息 + public function getDomainInfo() + { + return false; + } + + //获取域名最低TTL + public function getMinTTL() + { + return false; + } + + private function convertType($type) + { + return $type; + } + + private function execute($path, $params) + { + $params['username'] = $this->username; + $params['time'] = $this->getMillisecond(); + $params['token'] = md5($this->username.$this->api_password.$params['time']); + $response = $this->curl($path, $params); + $response = mb_convert_encoding($response, 'UTF-8', 'GBK'); + $arr = json_decode($response, true); + if ($arr) { + if ($arr['result'] == 200) { + return isset($arr['data']) ? $arr['data'] : []; + } else { + $this->setError($arr['msg']); + return false; + } + } else { + $this->setError('返回数据解析失败'); + return false; + } + } + + private function curl($path, $params = null) + { + $url = $this->baseUrl . $path; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + if ($params) { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); + } + $response = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $this->setError('Curl error: ' . curl_error($ch)); + } + curl_close($ch); + if ($errno) { + return false; + } + return $response; + } + + private function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } + + private function setError($message) + { + $this->error = $message; + //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND); + } +} diff --git a/app/lib/mail/Aliyun.php b/app/lib/mail/Aliyun.php index 82225f8..9506808 100644 --- a/app/lib/mail/Aliyun.php +++ b/app/lib/mail/Aliyun.php @@ -1,72 +1,77 @@ AccessKeyId = $AccessKeyId; - $this->AccessKeySecret = $AccessKeySecret; - } - private function aliyunSignature($parameters, $accessKeySecret, $method) - { - ksort($parameters); - $canonicalizedQueryString = ''; - foreach ($parameters as $key => $value) { - if($value === null) continue; - $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); - } - $stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); - $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); + public function __construct($AccessKeyId, $AccessKeySecret) + { + $this->AccessKeyId = $AccessKeyId; + $this->AccessKeySecret = $AccessKeySecret; + } + private function aliyunSignature($parameters, $accessKeySecret, $method) + { + ksort($parameters); + $canonicalizedQueryString = ''; + foreach ($parameters as $key => $value) { + if ($value === null) { + continue; + } + $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); + } + $stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); + $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); - return $signature; - } - private function percentEncode($str) - { - $search = ['+', '*', '%7E']; - $replace = ['%20', '%2A', '~']; - return str_replace($search, $replace, urlencode($str)); - } - public function send($to, $sub, $msg, $from, $from_name) - { - if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false; - $url = 'https://dm.aliyuncs.com/'; - $data = array( - 'Action' => 'SingleSendMail', - 'AccountName' => $from, - 'ReplyToAddress' => 'false', - 'AddressType' => 1, - 'ToAddress' => $to, - 'FromAlias' => $from_name, - 'Subject' => $sub, - 'HtmlBody' => $msg, - 'Format' => 'JSON', - 'Version' => '2015-11-23', - 'AccessKeyId' => $this->AccessKeyId, - 'SignatureMethod' => 'HMAC-SHA1', - 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), - 'SignatureVersion' => '1.0', - 'SignatureNonce' => random(8) - ); - $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); - $json = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - $arr = json_decode($json, true); - if ($httpCode == 200) { - return true; - } else { - return $arr['Message']; - } - } -} \ No newline at end of file + return $signature; + } + private function percentEncode($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + public function send($to, $sub, $msg, $from, $from_name) + { + if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) { + return false; + } + $url = 'https://dm.aliyuncs.com/'; + $data = array( + 'Action' => 'SingleSendMail', + 'AccountName' => $from, + 'ReplyToAddress' => 'false', + 'AddressType' => 1, + 'ToAddress' => $to, + 'FromAlias' => $from_name, + 'Subject' => $sub, + 'HtmlBody' => $msg, + 'Format' => 'JSON', + 'Version' => '2015-11-23', + 'AccessKeyId' => $this->AccessKeyId, + 'SignatureMethod' => 'HMAC-SHA1', + 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'SignatureVersion' => '1.0', + 'SignatureNonce' => random(8) + ); + $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); + $json = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + $arr = json_decode($json, true); + if ($httpCode == 200) { + return true; + } else { + return $arr['Message']; + } + } +} diff --git a/app/lib/mail/PHPMailer/Exception.php b/app/lib/mail/PHPMailer/Exception.php index f784b9f..8ed7aba 100644 --- a/app/lib/mail/PHPMailer/Exception.php +++ b/app/lib/mail/PHPMailer/Exception.php @@ -1,2 +1,11 @@ ' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; } } \ No newline at end of file + +namespace app\lib\mail\PHPMailer; + +class Exception extends \Exception +{ + public function errorMessage() + { + return '' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; + } +} diff --git a/app/lib/mail/PHPMailer/PHPMailer.php b/app/lib/mail/PHPMailer/PHPMailer.php index 1a4e34f..aab7d73 100644 --- a/app/lib/mail/PHPMailer/PHPMailer.php +++ b/app/lib/mail/PHPMailer/PHPMailer.php @@ -1,2 +1,1903 @@ exceptions = (bool) $exceptions; } $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); } public function __destruct() { $this->smtpClose(); } private function mailPassthru($to, $subject, $body, $header, $params) { if ((int)ini_get('mbstring.func_overload') & 1) { $subject = $this->secureHeader($subject); } else { $subject = $this->encodeHeader($this->secureHeader($subject)); } $this->edebug('Sending with mail()'); $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); $this->edebug("Envelope sender: {$this->Sender}"); $this->edebug("To: {$to}"); $this->edebug("Subject: {$subject}"); $this->edebug("Headers: {$header}"); if (!$this->UseSendmailOptions || null === $params) { $result = @mail($to, $subject, $body, $header); } else { $this->edebug("Additional params: {$params}"); $result = @mail($to, $subject, $body, $header, $params); } $this->edebug('Result: ' . ($result ? 'true' : 'false')); return $result; } protected function edebug($str) { if ($this->SMTPDebug <= 0) { return; } if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { $this->Debugoutput->debug(rtrim($str, "\r\n")); return; } if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { call_user_func($this->Debugoutput, $str, $this->SMTPDebug); return; } switch ($this->Debugoutput) { case 'error_log': error_log($str); break; case 'html': echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8'), "
\n"; break; case 'echo': default: $str = preg_replace('/\r\n|\r/m', "\n", $str); echo gmdate('Y-m-d H:i:s'), "\t", trim(str_replace("\n", "\n \t ", trim($str))), "\n"; } } public function isHTML($isHtml = true) { if ($isHtml) { $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; } else { $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; } } public function isSMTP() { $this->Mailer = 'smtp'; } public function isMail() { $this->Mailer = 'mail'; } public function isSendmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (false === stripos($ini_sendmail_path, 'sendmail')) { $this->Sendmail = '/usr/sbin/sendmail'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'sendmail'; } public function isQmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (false === stripos($ini_sendmail_path, 'qmail')) { $this->Sendmail = '/var/qmail/bin/qmail-inject'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'qmail'; } public function addAddress($address, $name = '') { return $this->addOrEnqueueAnAddress('to', $address, $name); } public function addCC($address, $name = '') { return $this->addOrEnqueueAnAddress('cc', $address, $name); } public function addBCC($address, $name = '') { return $this->addOrEnqueueAnAddress('bcc', $address, $name); } public function addReplyTo($address, $name = '') { return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); } protected function addOrEnqueueAnAddress($kind, $address, $name) { $pos = false; if ($address !== null) { $address = trim($address); $pos = strrpos($address, '@'); } if (false === $pos) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $kind, $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if ($name !== null && is_string($name)) { $name = trim(preg_replace('/[\r\n]+/', '', $name)); } else { $name = ''; } $params = [$kind, $address, $name]; if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) { if ('Reply-To' !== $kind) { if (!array_key_exists($address, $this->RecipientsQueue)) { $this->RecipientsQueue[$address] = $params; return true; } } elseif (!array_key_exists($address, $this->ReplyToQueue)) { $this->ReplyToQueue[$address] = $params; return true; } return false; } return call_user_func_array([$this, 'addAnAddress'], $params); } public function setBoundaries() { $this->uniqueid = $this->generateId(); $this->boundary[1] = 'b1=_' . $this->uniqueid; $this->boundary[2] = 'b2=_' . $this->uniqueid; $this->boundary[3] = 'b3=_' . $this->uniqueid; } protected function addAnAddress($kind, $address, $name = '') { if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { $error_message = sprintf('%s: %s', $this->lang('Invalid recipient kind'), $kind); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if (!static::validateAddress($address)) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $kind, $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if ('Reply-To' !== $kind) { if (!array_key_exists(strtolower($address), $this->all_recipients)) { $this->{$kind}[] = [$address, $name]; $this->all_recipients[strtolower($address)] = true; return true; } } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) { $this->ReplyTo[strtolower($address)] = [$address, $name]; return true; } return false; } public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) { $addresses = []; if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { $list = imap_rfc822_parse_adrlist($addrstr, ''); imap_errors(); foreach ($list as $address) { if ('.SYNTAX-ERROR.' !== $address->host && static::validateAddress($address->mailbox . '@' . $address->host)) { if (property_exists($address, 'personal') && defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $address->personal)) { $origCharset = mb_internal_encoding(); mb_internal_encoding($charset); $address->personal = str_replace('_', '=20', $address->personal); $address->personal = mb_decode_mimeheader($address->personal); mb_internal_encoding($origCharset); } $addresses[] = ['name' => (property_exists($address, 'personal') ? $address->personal : ''), 'address' => $address->mailbox . '@' . $address->host,]; } } } else { $list = explode(',', $addrstr); foreach ($list as $address) { $address = trim($address); if (strpos($address, '<') === false) { if (static::validateAddress($address)) { $addresses[] = ['name' => '', 'address' => $address,]; } } else { list($name, $email) = explode('<', $address); $email = trim(str_replace('>', '', $email)); $name = trim($name); if (static::validateAddress($email)) { if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { $origCharset = mb_internal_encoding(); mb_internal_encoding($charset); $name = str_replace('_', '=20', $name); $name = mb_decode_mimeheader($name); mb_internal_encoding($origCharset); } $addresses[] = ['name' => trim($name, '\'" '), 'address' => $email,]; } } } } return $addresses; } public function setFrom($address, $name = '', $auto = true) { $address = trim((string)$address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); $pos = strrpos($address, '@'); if ((false === $pos) || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) && !static::validateAddress($address))) { $error_message = sprintf('%s (From): %s', $this->lang('invalid_address'), $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } $this->From = $address; $this->FromName = $name; if ($auto && empty($this->Sender)) { $this->Sender = $address; } return true; } public function getLastMessageID() { return $this->lastMessageID; } public static function validateAddress($address, $patternselect = null) { if (null === $patternselect) { $patternselect = static::$validator; } if (is_callable($patternselect) && !is_string($patternselect)) { return call_user_func($patternselect, $address); } if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { return false; } switch ($patternselect) { case 'pcre': case 'pcre8': return (bool) preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); case 'html5': return (bool) preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address); case 'php': default: return filter_var($address, FILTER_VALIDATE_EMAIL) !== false; } } public static function idnSupported() { return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding'); } public function punyencodeAddress($address) { $pos = strrpos($address, '@'); if (!empty($this->CharSet) && false !== $pos && static::idnSupported()) { $domain = substr($address, ++$pos); if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); $errorcode = 0; if (defined('INTL_IDNA_VARIANT_UTS46')) { $punycode = idn_to_ascii($domain, \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, \INTL_IDNA_VARIANT_UTS46); } elseif (defined('INTL_IDNA_VARIANT_2003')) { $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); } else { $punycode = idn_to_ascii($domain, $errorcode); } if (false !== $punycode) { return substr($address, 0, $pos) . $punycode; } } } return $address; } public function send() { try { if (!$this->preSend()) { return false; } return $this->postSend(); } catch (Exception $exc) { $this->mailHeader = ''; $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } public function preSend() { if ('smtp' === $this->Mailer || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0))) { static::setLE(self::CRLF); } else { static::setLE(PHP_EOL); } if ('mail' === $this->Mailer && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) && ini_get('mail.add_x_header') === '1' && stripos(PHP_OS, 'WIN') === 0) { trigger_error($this->lang('buggy_php'), E_USER_WARNING); } try { $this->error_count = 0; $this->mailHeader = ''; foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { $params[1] = $this->punyencodeAddress($params[1]); call_user_func_array([$this, 'addAnAddress'], $params); } if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); } foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { if ($this->{$address_kind} === null) { $this->{$address_kind} = ''; continue; } $this->{$address_kind} = trim($this->{$address_kind}); if (empty($this->{$address_kind})) { continue; } $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind}); if (!static::validateAddress($this->{$address_kind})) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $address_kind, $this->{$address_kind}); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } } if ($this->alternativeExists()) { $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; } $this->setMessageType(); if (!$this->AllowEmpty && empty($this->Body)) { throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); } $this->Subject = trim($this->Subject); $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); $tempheaders = $this->MIMEHeader; $this->MIMEHeader = $this->createHeader(); $this->MIMEHeader .= $tempheaders; if ('mail' === $this->Mailer) { if (count($this->to) > 0) { $this->mailHeader .= $this->addrAppend('To', $this->to); } else { $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); } $this->mailHeader .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } if (!empty($this->DKIM_domain) && !empty($this->DKIM_selector) && (!empty($this->DKIM_private_string) || (!empty($this->DKIM_private) && static::isPermittedPath($this->DKIM_private) && file_exists($this->DKIM_private)))) { $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->encodeHeader($this->secureHeader($this->Subject)), $this->MIMEBody); $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE . static::normalizeBreaks($header_dkim) . static::$LE; } return true; } catch (Exception $exc) { $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } public function postSend() { try { switch ($this->Mailer) { case 'sendmail': case 'qmail': return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); case 'mail': return $this->mailSend($this->MIMEHeader, $this->MIMEBody); default: $sendMethod = $this->Mailer . 'Send'; if (method_exists($this, $sendMethod)) { return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody); } return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } } catch (Exception $exc) { $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) { $this->smtp->reset(); } if ($this->exceptions) { throw $exc; } } return false; } protected function sendmailSend($header, $body) { if ($this->Mailer === 'qmail') { $this->edebug('Sending with qmail'); } else { $this->edebug('Sending with sendmail'); } $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $sendmail_from_value = ini_get('sendmail_from'); if (empty($this->Sender) && !empty($sendmail_from_value)) { $this->Sender = ini_get('sendmail_from'); } if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { if ($this->Mailer === 'qmail') { $sendmailFmt = '%s -f%s'; } else { $sendmailFmt = '%s -oi -f%s -t'; } } else { $sendmailFmt = '%s -oi -t'; } $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); $this->edebug('Sendmail path: ' . $this->Sendmail); $this->edebug('Sendmail command: ' . $sendmail); $this->edebug('Envelope sender: ' . $this->Sender); $this->edebug("Headers: {$header}"); if ($this->SingleTo) { foreach ($this->SingleToArray as $toAddr) { $mail = @popen($sendmail, 'w'); if (!$mail) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } $this->edebug("To: {$toAddr}"); fwrite($mail, 'To: ' . $toAddr . "\n"); fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); $this->doCallback(($result === 0), [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } } else { $mail = @popen($sendmail, 'w'); if (!$mail) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); $this->doCallback(($result === 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } return true; } protected static function isShellSafe($string) { if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { return false; } if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])) { return false; } $length = strlen($string); for ($i = 0; $i < $length; ++$i) { $c = $string[$i]; if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { return false; } } return true; } protected static function isPermittedPath($path) { return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path); } protected static function fileIsAccessible($path) { if (!static::isPermittedPath($path)) { return false; } $readable = is_file($path); if (strpos($path, '\\\\') !== 0) { $readable = $readable && is_readable($path); } return $readable; } protected function mailSend($header, $body) { $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $toArr = []; foreach ($this->to as $toaddr) { $toArr[] = $this->addrFormat($toaddr); } $to = trim(implode(', ', $toArr)); if ($to === '') { $to = 'undisclosed-recipients:;'; } $params = null; $sendmail_from_value = ini_get('sendmail_from'); if (empty($this->Sender) && !empty($sendmail_from_value)) { $this->Sender = ini_get('sendmail_from'); } if (!empty($this->Sender) && static::validateAddress($this->Sender)) { if (self::isShellSafe($this->Sender)) { $params = sprintf('-f%s', $this->Sender); } $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); } $result = false; if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); $this->doCallback($result, [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); } } else { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); } if (!$result) { throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); } return true; } public function getSMTPInstance() { if (!is_object($this->smtp)) { $this->smtp = new SMTP(); } return $this->smtp; } public function setSMTPInstance(SMTP $smtp) { $this->smtp = $smtp; return $this->smtp; } public function setSMTPXclientAttribute($name, $value) { if (!in_array($name, SMTP::$xclient_allowed_attributes)) { return false; } if (isset($this->SMTPXClient[$name]) && $value === null) { unset($this->SMTPXClient[$name]); } elseif ($value !== null) { $this->SMTPXClient[$name] = $value; } return true; } public function getSMTPXclientAttributes() { return $this->SMTPXClient; } protected function smtpSend($header, $body) { $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $bad_rcpt = []; if (!$this->smtpConnect($this->SMTPOptions)) { throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); } if ('' === $this->Sender) { $smtp_from = $this->From; } else { $smtp_from = $this->Sender; } if (count($this->SMTPXClient)) { $this->smtp->xclient($this->SMTPXClient); } if (!$this->smtp->mail($smtp_from)) { $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); } $callbacks = []; foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { foreach ($togroup as $to) { if (!$this->smtp->recipient($to[0], $this->dsn)) { $error = $this->smtp->getError(); $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; $isSent = false; } else { $isSent = true; } $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; } } if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); } $smtp_transaction_id = $this->smtp->getLastTransactionID(); if ($this->SMTPKeepAlive) { $this->smtp->reset(); } else { $this->smtp->quit(); $this->smtp->close(); } foreach ($callbacks as $cb) { $this->doCallback($cb['issent'], [[$cb['to'], $cb['name']]], [], [], $this->Subject, $body, $this->From, ['smtp_transaction_id' => $smtp_transaction_id]); } if (count($bad_rcpt) > 0) { $errstr = ''; foreach ($bad_rcpt as $bad) { $errstr .= $bad['to'] . ': ' . $bad['error']; } throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE); } return true; } public function smtpConnect($options = null) { if (null === $this->smtp) { $this->smtp = $this->getSMTPInstance(); } if (null === $options) { $options = $this->SMTPOptions; } if ($this->smtp->connected()) { return true; } $this->smtp->setTimeout($this->Timeout); $this->smtp->setDebugLevel($this->SMTPDebug); $this->smtp->setDebugOutput($this->Debugoutput); $this->smtp->setVerp($this->do_verp); if ($this->Host === null) { $this->Host = 'localhost'; } $hosts = explode(';', $this->Host); $lastexception = null; foreach ($hosts as $hostentry) { $hostinfo = []; if (!preg_match('/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', trim($hostentry), $hostinfo)) { $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); continue; } if (!static::isValidHost($hostinfo[2])) { $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]); continue; } $prefix = ''; $secure = $this->SMTPSecure; $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { $prefix = 'ssl://'; $tls = false; $secure = static::ENCRYPTION_SMTPS; } elseif ('tls' === $hostinfo[1]) { $tls = true; $secure = static::ENCRYPTION_STARTTLS; } $sslext = defined('OPENSSL_ALGO_SHA256'); if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { if (!$sslext) { throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); } } $host = $hostinfo[2]; $port = $this->Port; if (array_key_exists(3, $hostinfo) && is_numeric($hostinfo[3]) && $hostinfo[3] > 0 && $hostinfo[3] < 65536) { $port = (int) $hostinfo[3]; } if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { try { if ($this->Helo) { $hello = $this->Helo; } else { $hello = $this->serverHostname(); } $this->smtp->hello($hello); if ($this->SMTPAutoTLS && $this->Host !== 'localhost' && $sslext && $secure !== 'ssl' && $this->smtp->getServerExt('STARTTLS')) { $tls = true; } if ($tls) { if (!$this->smtp->startTLS()) { $message = $this->getSmtpErrorMessage('connect_host'); throw new Exception($message); } $this->smtp->hello($hello); } if ($this->SMTPAuth && !$this->smtp->authenticate($this->Username, $this->Password, $this->AuthType, $this->oauth)) { throw new Exception($this->lang('authenticate')); } return true; } catch (Exception $exc) { $lastexception = $exc; $this->edebug($exc->getMessage()); $this->smtp->quit(); } } } $this->smtp->close(); if ($this->exceptions && null !== $lastexception) { throw $lastexception; } if ($this->exceptions) { $message = $this->getSmtpErrorMessage('connect_host'); throw new Exception($message); } return false; } public function smtpClose() { if ((null !== $this->smtp) && $this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); } } public function setLanguage($langcode = 'en', $lang_path = '') { $renamed_langcodes = ['br' => 'pt_br', 'cz' => 'cs', 'dk' => 'da', 'no' => 'nb', 'se' => 'sv', 'rs' => 'sr', 'tg' => 'tl', 'am' => 'hy',]; if (array_key_exists($langcode, $renamed_langcodes)) { $langcode = $renamed_langcodes[$langcode]; } $PHPMAILER_LANG = ['authenticate' => 'SMTP Error: Could not authenticate.', 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 'data_not_accepted' => 'SMTP Error: data not accepted.', 'empty_message' => 'Message body empty', 'encoding' => 'Unknown encoding: ', 'execute' => 'Could not execute: ', 'extension_missing' => 'Extension missing: ', 'file_access' => 'Could not access file: ', 'file_open' => 'File Error: Could not open file: ', 'from_failed' => 'The following From address failed: ', 'instantiate' => 'Could not instantiate mail function.', 'invalid_address' => 'Invalid address: ', 'invalid_header' => 'Invalid header name or value', 'invalid_hostentry' => 'Invalid hostentry: ', 'invalid_host' => 'Invalid host: ', 'mailer_not_supported' => ' mailer is not supported.', 'provide_address' => 'You must provide at least one recipient email address.', 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 'signing' => 'Signing Error: ', 'smtp_code' => 'SMTP code: ', 'smtp_code_ex' => 'Additional SMTP info: ', 'smtp_connect_failed' => 'SMTP connect() failed.', 'smtp_detail' => 'Detail: ', 'smtp_error' => 'SMTP server error: ', 'variable_set' => 'Cannot set or reset variable: ',]; $PHPMAILER_LANG['authenticate'] = 'SMTP登录失败:邮箱账号或密码错误。'; $PHPMAILER_LANG['buggy_php'] = '您的 PHP 版本存在漏洞,可能会导致消息损坏。为修复此问题,请切换到使用 SMTP 发送,在您的 php.ini 中禁用 mail.add_x_header 选项。切换到 MacOS 或 Linux,或将您的 PHP 升级到 7.0.17+ 或 7.1.3+ 版本。'; $PHPMAILER_LANG['connect_host'] = '无法连接到SMTP服务器。'; $PHPMAILER_LANG['data_not_accepted'] = '数据不被接受。'; $PHPMAILER_LANG['empty_message'] = '邮件正文为空。'; $PHPMAILER_LANG['encoding'] = '未知编码:'; $PHPMAILER_LANG['execute'] = '无法执行:'; $PHPMAILER_LANG['extension_missing'] = '缺少扩展名:'; $PHPMAILER_LANG['file_access'] = '无法访问文件:'; $PHPMAILER_LANG['file_open'] = '文件错误:无法打开文件:'; $PHPMAILER_LANG['from_failed'] = '发送地址错误:'; $PHPMAILER_LANG['instantiate'] = '未知函数调用。'; $PHPMAILER_LANG['invalid_address'] = '发送失败,电子邮箱地址是无效的:'; $PHPMAILER_LANG['mailer_not_supported'] = '发信客户端不被支持。'; $PHPMAILER_LANG['provide_address'] = '必须提供至少一个收件人地址。'; $PHPMAILER_LANG['recipients_failed'] = '收件人地址错误:'; $PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP服务器连接失败:SMTP服务器地址或端口错误。'; $PHPMAILER_LANG['smtp_error'] = 'SMTP服务器出错:'; $PHPMAILER_LANG['variable_set'] = '无法设置或重置变量:'; $PHPMAILER_LANG['invalid_header'] = '无效的标题名称或值'; $PHPMAILER_LANG['invalid_hostentry'] = '无效的hostentry: '; $PHPMAILER_LANG['invalid_host'] = '无效的主机:'; $PHPMAILER_LANG['signing'] = '签名错误:'; $PHPMAILER_LANG['smtp_code'] = 'SMTP代码: '; $PHPMAILER_LANG['smtp_code_ex'] = '附加SMTP信息: '; $PHPMAILER_LANG['smtp_detail'] = '详情:'; if (empty($lang_path)) { $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; } $foundlang = true; $langcode = strtolower($langcode); if (!preg_match('/^(?P[a-z]{2})(?P