diff --git a/app/lib/DnsHelper.php b/app/lib/DnsHelper.php index f04e2dc..8123edd 100644 --- a/app/lib/DnsHelper.php +++ b/app/lib/DnsHelper.php @@ -518,6 +518,41 @@ class DnsHelper 'page' => true, 'add' => true, ], + 'technitium' => [ + 'name' => 'Technitium', + 'icon' => 'technitium.png', + 'note' => '', + 'config' => [ + 'url' => [ + 'name' => 'Server URL', + 'type' => 'input', + 'placeholder' => 'http://127.0.0.1:5380', + 'required' => true, + ], + 'token' => [ + 'name' => 'API Token', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + ], + 'proxy' => [ + 'name' => '使用代理服务器', + 'type' => 'radio', + 'options' => [ + '0' => '否', + '1' => '是', + ], + 'value' => '0' + ], + ], + 'remark' => 2, + 'status' => true, + 'redirect' => false, + 'log' => false, + 'weight' => false, + 'page' => true, + 'add' => true, + ], 'aliyunesa' => [ 'name' => '阿里云ESA', 'icon' => 'aliyun.png', @@ -608,13 +643,13 @@ class DnsHelper 'page' => false, 'add' => false, ], - 'cccyun' => [ + 'dnsmgr' => [ 'name' => '同系统对接', 'icon' => 'logo.png', - 'note' => '通过聚合DNS管理系统接口实现域名解析管理', + 'note' => '对接其他聚合DNS管理系统站点', 'config' => [ 'base_url' => [ - 'name' => 'API 地址', + 'name' => '站点地址', 'type' => 'input', 'placeholder' => '例如:https://dns.example.com', 'required' => true, @@ -643,7 +678,7 @@ class DnsHelper ], 'remark' => 2, 'status' => true, - 'redirect' => false, + 'redirect' => true, 'log' => false, 'weight' => true, 'page' => false, diff --git a/app/lib/dns/cccyun.php b/app/lib/dns/dnsmgr.php similarity index 68% rename from app/lib/dns/cccyun.php rename to app/lib/dns/dnsmgr.php index ba2efe6..cd95de2 100644 --- a/app/lib/dns/cccyun.php +++ b/app/lib/dns/dnsmgr.php @@ -5,7 +5,7 @@ namespace app\lib\dns; use app\lib\DnsInterface; use Exception; -class cccyun implements DnsInterface +class dnsmgr implements DnsInterface { private $uid; private $key; @@ -14,6 +14,7 @@ class cccyun implements DnsInterface private $domain; private $domainid; private $proxy; + private $domainInfo; public function __construct($config) { @@ -100,8 +101,6 @@ class cccyun implements DnsInterface ]; } return ['total' => $data['total'], 'list' => $list]; - } elseif ($this->error == '记录列表为空。') { - return ['total' => 0, 'list' => []]; } return false; } @@ -114,27 +113,6 @@ class cccyun implements DnsInterface public function getDomainRecordInfo($RecordId) { - $param = [ - 'recordid' => $RecordId, - ]; - $data = $this->send_request('/api/record/data/' . $this->domainid, $param); - if ($data && isset($data['rows'][0])) { - $row = $data['rows'][0]; - return [ - 'RecordId' => $row['RecordId'], - 'Domain' => $row['Domain'], - 'Name' => $row['Name'], - 'Type' => $row['Type'], - 'Value' => $row['Value'], - 'Line' => $row['Line'], - 'TTL' => $row['TTL'], - 'MX' => $row['MX'], - 'Status' => $row['Status'], - 'Weight' => $row['Weight'], - 'Remark' => $row['Remark'], - 'UpdateTime' => $row['UpdateTime'], - ]; - } return false; } @@ -158,7 +136,7 @@ class cccyun implements DnsInterface } $data = $this->send_request('/api/record/add/' . $this->domainid, $param); - return is_array($data) && isset($data['code']) && $data['code'] == 0; + return $data !== false; } public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) @@ -182,7 +160,7 @@ class cccyun implements DnsInterface } $data = $this->send_request('/api/record/update/' . $this->domainid, $param); - return is_array($data) && isset($data['code']) && $data['code'] == 0; + return $data !== false; } public function updateDomainRecordRemark($RecordId, $Remark) @@ -193,7 +171,7 @@ class cccyun implements DnsInterface ]; $data = $this->send_request('/api/record/remark/' . $this->domainid, $param); - return is_array($data) && isset($data['code']) && $data['code'] == 0; + return $data !== false; } public function deleteDomainRecord($RecordId) @@ -203,7 +181,7 @@ class cccyun implements DnsInterface ]; $data = $this->send_request('/api/record/delete/' . $this->domainid, $param); - return is_array($data) && isset($data['code']) && $data['code'] == 0; + return $data !== false; } public function setDomainRecordStatus($RecordId, $Status) @@ -214,18 +192,17 @@ class cccyun implements DnsInterface ]; $data = $this->send_request('/api/record/status/' . $this->domainid, $param); - return is_array($data) && isset($data['code']) && $data['code'] == 0; + return $data !== false; } public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) { - $this->setError('该DNS服务商不支持查看日志'); return false; } public function getRecordLine() { - $data = $this->send_request('/api/domain/' . $this->domainid, ['loginurl' => 0]); + $data = $this->getDomainInfo(); if ($data && isset($data['recordLine'])) { $list = []; foreach ($data['recordLine'] as $row) { @@ -241,23 +218,33 @@ class cccyun implements DnsInterface public function getMinTTL() { - $data = $this->send_request('/api/domain/' . $this->domainid, ['loginurl' => 0]); + $data = $this->getDomainInfo(); if ($data && isset($data['minTTL'])) { return $data['minTTL']; } return false; } + public function getDomainInfo() + { + if (!empty($this->domainInfo)) return $this->domainInfo; + $data = $this->send_request('/api/domain/' . $this->domainid, ['loginurl' => 0]); + if ($data) { + $this->domainInfo = $data; + return $data; + } + return false; + } + public function addDomain($Domain) { - $this->setError('该DNS服务商不支持添加域名'); return false; } private function send_request($path, $param = []) { try { - $timestamp = time(); + $timestamp = (string)time(); $signStr = $this->uid . $timestamp . $this->key; $sign = md5($signStr); @@ -268,52 +255,20 @@ class cccyun implements DnsInterface $param['sign'] = $sign; $postData = http_build_query($param); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/x-www-form-urlencoded' - ]); + $response = http_request($url, $postData, null, null, null, $this->proxy); - if ($this->proxy) { - $proxy_config = Db::name('config')->where('key', 'proxy')->value('value'); - if ($proxy_config) { - $proxy_info = json_decode($proxy_config, true); - if ($proxy_info && $proxy_info['open'] == 1) { - curl_setopt($ch, CURLOPT_PROXY, $proxy_info['ip'] . ':' . $proxy_info['port']); - if (!empty($proxy_info['username']) && !empty($proxy_info['password'])) { - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_info['username'] . ':' . $proxy_info['password']); - } - } - } + $result = json_decode($response['body'], true); + if (isset($result['code']) && $result['code'] == 0) { + return isset($result['data']) ? $result['data'] : null; + } elseif (isset($result['rows']) && isset($result['total'])) { + return $result; + } elseif (isset($result['msg'])) { + $this->setError($result['msg']); + return false; + } else { + $this->setError($response['body']); + return false; } - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curlError = curl_error($ch); - curl_close($ch); - - if ($curlError) { - throw new Exception('CURL Error: ' . $curlError); - } - - if ($httpCode != 200) { - throw new Exception('HTTP Error: ' . $httpCode); - } - - $result = json_decode($response, true); - if (!$result) { - throw new Exception('JSON Decode Error: ' . $response); - } - - if (isset($result['code']) && $result['code'] != 0 && isset($result['msg'])) { - throw new Exception($result['msg']); - } - - return isset($result['data']) ? $result['data'] : $result; - } catch (Exception $e) { $this->setError($e->getMessage()); return false; @@ -324,4 +279,4 @@ class cccyun implements DnsInterface { $this->error = $message; } -} \ No newline at end of file +} diff --git a/app/lib/dns/technitium.php b/app/lib/dns/technitium.php new file mode 100644 index 0000000..b26e8b3 --- /dev/null +++ b/app/lib/dns/technitium.php @@ -0,0 +1,499 @@ +url = rtrim($config['url'], '/') . '/api'; + $this->token = $config['token']; + $this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false; + $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) + { + $data = $this->send_request('GET', '/zones/list'); + if ($data && isset($data['response']['zones'])) { + $list = []; + foreach ($data['response']['zones'] as $zone) { + $list[] = [ + 'DomainId' => $zone['name'], + 'Domain' => $zone['name'], + 'RecordCount' => 0, + ]; + } + if (!isNullOrEmpty($KeyWord)) { + $list = array_values(array_filter($list, function ($v) use ($KeyWord) { + return strpos($v['Domain'], $KeyWord) !== false; + })); + } + 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) + { + $params = ['domain' => $this->domain, 'listZone' => 'true']; + $data = $this->send_request('GET', '/zones/records/get', $params); + if ($data && isset($data['response']['records'])) { + $list = []; + $records = $data['response']['records']; + foreach ($records as $i => &$row) { + $row['id'] = $i; + $name = $row['name'] == $this->domain ? '@' : str_replace('.' . $this->domain, '', $row['name']); + $value = ''; + $mx = null; + $rData = $row['rData']; + + if ($row['type'] == 'A' || $row['type'] == 'AAAA') { + $value = isset($rData['ipAddress']) ? $rData['ipAddress'] : ''; + } elseif ($row['type'] == 'CNAME') { + $value = isset($rData['cname']) ? $rData['cname'] : ''; + } elseif ($row['type'] == 'NS') { + $value = isset($rData['nameServer']) ? $rData['nameServer'] : ''; + } elseif ($row['type'] == 'MX') { + $value = isset($rData['exchange']) ? $rData['exchange'] : ''; + $mx = isset($rData['preference']) ? $rData['preference'] : 1; + } elseif ($row['type'] == 'TXT') { + $value = isset($rData['text']) ? $rData['text'] : ''; + } elseif ($row['type'] == 'SRV') { + $value = (isset($rData['priority']) ? $rData['priority'] : 0) . ' ' . (isset($rData['weight']) ? $rData['weight'] : 0) . ' ' . (isset($rData['port']) ? $rData['port'] : 0) . ' ' . (isset($rData['target']) ? $rData['target'] : ''); + } elseif ($row['type'] == 'PTR') { + $value = isset($rData['ptrName']) ? $rData['ptrName'] : ''; + } elseif ($row['type'] == 'CAA') { + $value = (isset($rData['flags']) ? $rData['flags'] : 0) . ' ' . (isset($rData['tag']) ? $rData['tag'] : '') . ' "' . (isset($rData['value']) ? $rData['value'] : '') . '"'; + } elseif ($row['type'] == 'ANAME') { + $value = isset($rData['aname']) ? $rData['aname'] : ''; + } elseif ($row['type'] == 'DNAME') { + $value = isset($rData['dname']) ? $rData['dname'] : ''; + } elseif ($row['type'] == 'APP') { + $value = (isset($rData['appName']) ? $rData['appName'] : '') . ' ' . (isset($rData['classPath']) ? $rData['classPath'] : ''); + if (!empty($rData['recordData'])) { + $value .= ' ' . $rData['recordData']; + } + } + + $list[] = [ + 'RecordId' => $i, + 'Domain' => $this->domain, + 'Name' => $name, + 'Type' => $row['type'], + 'Value' => $value, + 'Line' => 'default', + 'TTL' => $row['ttl'], + 'MX' => $mx, + 'Status' => $row['disabled'] ? '0' : '1', + 'Weight' => null, + 'Remark' => isset($row['comments']) ? $row['comments'] : null, + 'UpdateTime' => null, + ]; + } + cache('technitium_' . $this->domain, $records, 86400); + + if (!isNullOrEmpty($SubDomain)) { + $list = array_values(array_filter($list, function ($v) use ($SubDomain) { + return strcasecmp($v['Name'], $SubDomain) === 0; + })); + } else { + if (!isNullOrEmpty($KeyWord)) { + $list = array_values(array_filter($list, function ($v) use ($KeyWord) { + return strpos($v['Name'], $KeyWord) !== false || strpos($v['Value'], $KeyWord) !== false; + })); + } + if (!isNullOrEmpty($Value)) { + $list = array_values(array_filter($list, function ($v) use ($Value) { + return $v['Value'] == $Value; + })); + } + if (!isNullOrEmpty($Type)) { + $list = array_values(array_filter($list, function ($v) use ($Type) { + return $v['Type'] == $Type; + })); + } + if (!isNullOrEmpty($Status)) { + $list = array_values(array_filter($list, function ($v) use ($Status) { + return $v['Status'] == $Status; + })); + } + } + return ['total' => count($list), '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) + { + return false; + } + + private function buildRecordParams($Type, $Value, $MX = 1) + { + $params = []; + if ($Type == 'A' || $Type == 'AAAA') { + $params['ipAddress'] = $Value; + } elseif ($Type == 'CNAME') { + $params['cname'] = $Value; + } elseif ($Type == 'NS') { + $params['nameServer'] = $Value; + } elseif ($Type == 'MX') { + $params['exchange'] = $Value; + $params['preference'] = intval($MX); + } elseif ($Type == 'TXT') { + $params['text'] = $Value; + } elseif ($Type == 'SRV') { + $parts = explode(' ', $Value); + if (count($parts) == 4) { + $params['priority'] = $parts[0]; + $params['weight'] = $parts[1]; + $params['port'] = $parts[2]; + $params['target'] = $parts[3]; + } + } elseif ($Type == 'PTR') { + $params['ptrName'] = $Value; + } elseif ($Type == 'CAA') { + $parts = explode(' ', $Value, 3); + if (count($parts) == 3) { + $params['flags'] = $parts[0]; + $params['tag'] = $parts[1]; + $params['value'] = trim($parts[2], '"'); + } + } elseif ($Type == 'ANAME') { + $params['aname'] = $Value; + } elseif ($Type == 'DNAME') { + $params['dname'] = $Value; + } elseif ($Type == 'APP') { + $parts = explode(' ', $Value, 3); + if (count($parts) >= 2) { + $params['appName'] = $parts[0]; + $params['classPath'] = $parts[1]; + $params['recordData'] = rtrim(isset($parts[2]) ? $parts[2] : ''); + } else { + $params['appName'] = rtrim($Value); + } + } + return $params; + } + + private function getOldValueParams($Type, $rData) + { + $params = []; + if ($Type == 'A' || $Type == 'AAAA') { + $params['ipAddress'] = isset($rData['ipAddress']) ? $rData['ipAddress'] : ''; + } elseif ($Type == 'CNAME') { + $params['cname'] = isset($rData['cname']) ? $rData['cname'] : ''; + } elseif ($Type == 'NS') { + $params['nameServer'] = isset($rData['nameServer']) ? $rData['nameServer'] : ''; + } elseif ($Type == 'MX') { + $params['exchange'] = isset($rData['exchange']) ? $rData['exchange'] : ''; + $params['preference'] = isset($rData['preference']) ? $rData['preference'] : 1; + } elseif ($Type == 'TXT') { + $params['text'] = isset($rData['text']) ? $rData['text'] : ''; + } elseif ($Type == 'SRV') { + $params['priority'] = isset($rData['priority']) ? $rData['priority'] : 0; + $params['weight'] = isset($rData['weight']) ? $rData['weight'] : 0; + $params['port'] = isset($rData['port']) ? $rData['port'] : 0; + $params['target'] = isset($rData['target']) ? $rData['target'] : ''; + } elseif ($Type == 'PTR') { + $params['ptrName'] = isset($rData['ptrName']) ? $rData['ptrName'] : ''; + } elseif ($Type == 'CAA') { + $params['flags'] = isset($rData['flags']) ? $rData['flags'] : 0; + $params['tag'] = isset($rData['tag']) ? $rData['tag'] : ''; + $params['value'] = isset($rData['value']) ? $rData['value'] : ''; + } elseif ($Type == 'ANAME') { + $params['aname'] = isset($rData['aname']) ? $rData['aname'] : ''; + } elseif ($Type == 'DNAME') { + $params['dname'] = isset($rData['dname']) ? $rData['dname'] : ''; + } elseif ($Type == 'APP') { + $params['appName'] = isset($rData['appName']) ? $rData['appName'] : ''; + $params['classPath'] = isset($rData['classPath']) ? $rData['classPath'] : ''; + if (!empty($rData['recordData'])) { + $params['recordData'] = $rData['recordData']; + } + } + return $params; + } + + private function getNewValueParams($Type, $Value, $MX = 1) + { + $params = []; + if ($Type == 'A' || $Type == 'AAAA') { + $params['newIpAddress'] = $Value; + } elseif ($Type == 'CNAME') { + $params['newCname'] = $Value; + } elseif ($Type == 'NS') { + $params['newNameServer'] = $Value; + } elseif ($Type == 'MX') { + $params['newExchange'] = $Value; + $params['newPreference'] = intval($MX); + } elseif ($Type == 'TXT') { + $params['newText'] = $Value; + } elseif ($Type == 'SRV') { + $parts = explode(' ', $Value); + if (count($parts) == 4) { + $params['newPriority'] = $parts[0]; + $params['newWeight'] = $parts[1]; + $params['newPort'] = $parts[2]; + $params['newTarget'] = $parts[3]; + } + } elseif ($Type == 'PTR') { + $params['newPtrName'] = $Value; + } elseif ($Type == 'CAA') { + $parts = explode(' ', $Value, 3); + if (count($parts) == 3) { + $params['newFlags'] = $parts[0]; + $params['newTag'] = $parts[1]; + $params['newValue'] = trim($parts[2], '"'); + } + } elseif ($Type == 'ANAME') { + $params['newAName'] = $Value; + } elseif ($Type == 'DNAME') { + $params['newDName'] = $Value; + } elseif ($Type == 'APP') { + $parts = explode(' ', $Value, 3); + if (count($parts) >= 2) { + $params['appName'] = $parts[0]; + $params['classPath'] = $parts[1]; + $params['recordData'] = rtrim(isset($parts[2]) ? $parts[2] : ''); + } else { + $params['appName'] = rtrim($Value); + } + } + return $params; + } + + public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $domain = $Name == '@' ? $this->domain : $Name . '.' . $this->domain; + $params = [ + 'domain' => $domain, + 'zone' => $this->domain, + 'type' => $Type, + 'ttl' => intval($TTL) + ]; + if (!isNullOrEmpty($Remark)) { + $params['comments'] = $Remark; + } + $valParams = $this->buildRecordParams($Type, $Value, $MX); + if (empty($valParams) && $Type != 'SOA') { + $this->setError('不受支持的记录类型或参数解析失败'); + return false; + } + $params = array_merge($params, $valParams); + + $result = $this->send_request('POST', '/zones/records/add', $params); + return $result !== false; + } + + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $records = cache('technitium_' . $this->domain); + if (!$records || !isset($records[$RecordId])) { + $this->setError('记录不存在,请刷新页面重试'); + return false; + } + + $oldRecord = $records[$RecordId]; + $domain = $oldRecord['name']; + $newDomain = $Name == '@' ? $this->domain : $Name . '.' . $this->domain; + + if ($oldRecord['type'] == 'APP') { + $oldValue = (isset($oldRecord['rData']['appName']) ? $oldRecord['rData']['appName'] : '') . ' ' . (isset($oldRecord['rData']['classPath']) ? $oldRecord['rData']['classPath'] : ''); + if (!empty($oldRecord['rData']['recordData'])) { + $oldValue .= ' ' . $oldRecord['rData']['recordData']; + } + if ($oldValue != rtrim($Value) || $domain != $newDomain) { + $this->deleteDomainRecord($RecordId); + return $this->addDomainRecord($Name, $Type, $Value, $Line, $TTL, $MX, $Weight, $Remark); + } + } + + $params = [ + 'domain' => $domain, + 'zone' => $this->domain, + 'type' => $oldRecord['type'], + 'ttl' => intval($TTL), + ]; + + if ($domain != $newDomain) { + $params['newDomain'] = $newDomain; + } + + $params['comments'] = empty($Remark) ? "" : $Remark; + + $oldValParams = $this->getOldValueParams($oldRecord['type'], $oldRecord['rData']); + $newValParams = $this->getNewValueParams($Type, $Value, $MX); + + $params = array_merge($params, $oldValParams, $newValParams); + $result = $this->send_request('POST', '/zones/records/update', $params); + return $result !== false; + } + + public function updateDomainRecordRemark($RecordId, $Remark) + { + $records = cache('technitium_' . $this->domain); + if (!$records || !isset($records[$RecordId])) { + $this->setError('记录不存在,请刷新页面重试'); + return false; + } + + $oldRecord = $records[$RecordId]; + $domain = $oldRecord['name']; + + $params = [ + 'domain' => $domain, + 'zone' => $this->domain, + 'type' => $oldRecord['type'], + 'comments' => $Remark, + ]; + $oldValParams = $this->getOldValueParams($oldRecord['type'], $oldRecord['rData']); + $params = array_merge($params, $oldValParams); + + $result = $this->send_request('POST', '/zones/records/update', $params); + return $result !== false; + } + + public function deleteDomainRecord($RecordId) + { + $records = cache('technitium_' . $this->domain); + if (!$records || !isset($records[$RecordId])) { + $this->setError('记录不存在,请刷新页面重试'); + return false; + } + + $oldRecord = $records[$RecordId]; + $domain = $oldRecord['name']; + + $params = [ + 'domain' => $domain, + 'zone' => $this->domain, + 'type' => $oldRecord['type'], + ]; + + $oldValParams = $this->getOldValueParams($oldRecord['type'], $oldRecord['rData']); + $params = array_merge($params, $oldValParams); + + $result = $this->send_request('POST', '/zones/records/delete', $params); + return $result !== false; + } + + public function setDomainRecordStatus($RecordId, $Status) + { + $records = cache('technitium_' . $this->domain); + if (!$records || !isset($records[$RecordId])) { + $this->setError('记录不存在,请刷新页面重试'); + return false; + } + + $oldRecord = $records[$RecordId]; + $domain = $oldRecord['name']; + + $params = [ + 'domain' => $domain, + 'zone' => $this->domain, + 'type' => $oldRecord['type'], + 'disable' => $Status == '0' ? 'true' : 'false', + ]; + + $oldValParams = $this->getOldValueParams($oldRecord['type'], $oldRecord['rData']); + $params = array_merge($params, $oldValParams); + + $result = $this->send_request('POST', '/zones/records/update', $params); + return $result !== false; + } + + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + public function getRecordLine() + { + return ['default' => ['name' => '默认', 'parent' => null]]; + } + + public function getMinTTL() + { + return false; + } + + public function addDomain($Domain) + { + $params = [ + 'zone' => $Domain, + 'type' => 'Primary' + ]; + $result = $this->send_request('POST', '/zones/create', $params); + if ($result && isset($result['response']['domain'])) { + return ['id' => $result['response']['domain'], 'name' => $result['response']['domain']]; + } + return false; + } + + private function send_request($method, $path, $params = []) + { + $url = $this->url . $path; + $params['token'] = $this->token; + + $body = null; + if ($method == 'GET' || $method == 'DELETE') { + $url .= '?' . http_build_query($params); + } else { + $body = http_build_query($params); + } + + try { + $response = http_request($url, $body, null, null, null, $this->proxy, $method); + } catch (Exception $e) { + $this->setError($e->getMessage()); + return false; + } + + $arr = json_decode($response['body'], true); + if (isset($arr['status']) && $arr['status'] == 'ok') { + return $arr; + } elseif (isset($arr['errorMessage'])) { + $this->setError($arr['errorMessage']); + return false; + } else { + $this->setError('API 请求失败'); + return false; + } + } + + private function setError($message) + { + $this->error = $message; + } +} diff --git a/public/static/images/technitium.png b/public/static/images/technitium.png new file mode 100644 index 0000000..1bdbde1 Binary files /dev/null and b/public/static/images/technitium.png differ