mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-05-09 23:16:27 +02:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1eaaec650 | ||
|
|
224c27d796 | ||
|
|
6aea445259 | ||
|
|
86c557face | ||
|
|
70d3922013 | ||
|
|
e56122d7d0 | ||
|
|
6694631a9a | ||
|
|
2c03dedba0 | ||
|
|
095063dcad | ||
|
|
b6eec27d06 | ||
|
|
b400a62ef9 | ||
|
|
36a731d672 | ||
|
|
dcd829586c | ||
|
|
fb69ed702b | ||
|
|
137193d465 | ||
|
|
d0eb096873 | ||
|
|
ebdc34cf4b | ||
|
|
b19cabcbfd | ||
|
|
64b5221787 | ||
|
|
41e719720c | ||
|
|
16a9c03b6c | ||
|
|
1beb731a6e | ||
|
|
6b026ce4e4 | ||
|
|
96ff262333 | ||
|
|
17ffe5704f | ||
|
|
d4c11b520f | ||
|
|
ba418da84c | ||
|
|
b58db855ca | ||
|
|
3bd45367b0 | ||
|
|
a22bc4fa37 |
@@ -605,3 +605,10 @@ function getDomainDate($domain)
|
||||
throw new Exception('查询域名whois失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function checkTableExists($table)
|
||||
{
|
||||
$prefix = env('database.prefix', 'dnsmgr_');
|
||||
$res = Db::query("SHOW TABLES LIKE '" . $prefix . $table . "'");
|
||||
return !empty($res);
|
||||
}
|
||||
@@ -16,7 +16,6 @@ class Domain extends BaseController
|
||||
public function account()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
View::assign('dnsconfig', DnsHelper::$dns_config);
|
||||
return view();
|
||||
}
|
||||
|
||||
@@ -29,7 +28,7 @@ class Domain extends BaseController
|
||||
|
||||
$select = Db::name('account');
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('ak|remark', '%' . $kw . '%');
|
||||
$select->whereLike('name|remark', '%' . $kw . '%');
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
@@ -37,39 +36,49 @@ class Domain extends BaseController
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$row['typename'] = DnsHelper::$dns_config[$row['type']]['name'];
|
||||
$row['icon'] = DnsHelper::$dns_config[$row['type']]['icon'];
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function account_add()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$action = input('param.action');
|
||||
|
||||
$account = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$account = Db::name('account')->where('id', $id)->find();
|
||||
if (empty($account)) return $this->alert('error', '域名账户不存在');
|
||||
}
|
||||
|
||||
View::assign('info', $account);
|
||||
View::assign('typeList', DnsHelper::getList());
|
||||
View::assign('action', $action);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function account_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$act = input('param.act');
|
||||
if ($act == 'get') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('account')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '域名账户不存在']);
|
||||
return json(['code' => 0, 'data' => $row]);
|
||||
} elseif ($act == 'add') {
|
||||
$action = input('param.action');
|
||||
if ($action == 'add') {
|
||||
$type = input('post.type');
|
||||
$ak = input('post.ak', null, 'trim');
|
||||
$sk = input('post.sk', null, 'trim');
|
||||
$ext = input('post.ext', null, 'trim');
|
||||
$name = input('post.name', null, 'trim');
|
||||
$config = input('post.config', null, 'trim');
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
$proxy = input('post.proxy/d', 0);
|
||||
if (empty($ak) || empty($sk)) return json(['code' => -1, 'msg' => 'AccessKey和SecretKey不能为空']);
|
||||
if (Db::name('account')->where('type', $type)->where('ak', $ak)->find()) {
|
||||
if (empty($name) || empty($config)) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
if (Db::name('account')->where('type', $type)->where('name', $name)->find()) {
|
||||
return json(['code' => -1, 'msg' => '域名账户已存在']);
|
||||
}
|
||||
Db::startTrans();
|
||||
$id = Db::name('account')->insertGetId([
|
||||
'type' => $type,
|
||||
'ak' => $ak,
|
||||
'sk' => $sk,
|
||||
'ext' => $ext,
|
||||
'proxy' => $proxy,
|
||||
'name' => $name,
|
||||
'config' => $config,
|
||||
'remark' => $remark,
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
@@ -86,27 +95,24 @@ class Domain extends BaseController
|
||||
Db::rollback();
|
||||
return json(['code' => -1, 'msg' => 'DNS模块(' . $type . ')不存在']);
|
||||
}
|
||||
} elseif ($act == 'edit') {
|
||||
} elseif ($action == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('account')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '域名账户不存在']);
|
||||
$type = input('post.type');
|
||||
$ak = input('post.ak', null, 'trim');
|
||||
$sk = input('post.sk', null, 'trim');
|
||||
$ext = input('post.ext', null, 'trim');
|
||||
$name = input('post.name', null, 'trim');
|
||||
$config = input('post.config', null, 'trim');
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
$proxy = input('post.proxy/d', 0);
|
||||
if (empty($ak) || empty($sk)) return json(['code' => -1, 'msg' => 'AccessKey和SecretKey不能为空']);
|
||||
if (Db::name('account')->where('type', $type)->where('ak', $ak)->where('id', '<>', $id)->find()) {
|
||||
if (empty($name) || empty($config)) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
if (Db::name('account')->where('type', $type)->where('name', $name)->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '域名账户已存在']);
|
||||
}
|
||||
Db::startTrans();
|
||||
Db::name('account')->where('id', $id)->update([
|
||||
'type' => $type,
|
||||
'ak' => $ak,
|
||||
'sk' => $sk,
|
||||
'ext' => $ext,
|
||||
'proxy' => $proxy,
|
||||
'name' => $name,
|
||||
'config' => $config,
|
||||
'remark' => $remark,
|
||||
'remark' => $remark,
|
||||
]);
|
||||
$dns = DnsHelper::getModel($id);
|
||||
@@ -122,7 +128,7 @@ class Domain extends BaseController
|
||||
Db::rollback();
|
||||
return json(['code' => -1, 'msg' => 'DNS模块(' . $type . ')不存在']);
|
||||
}
|
||||
} elseif ($act == 'del') {
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
$dcount = DB::name('domain')->where('aid', $id)->count();
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该域名账户下存在域名,无法删除']);
|
||||
@@ -182,13 +188,21 @@ class Domain extends BaseController
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$order = input('post.order', null, 'trim');
|
||||
$offset = input('post.offset/d', 0);
|
||||
$limit = input('post.limit/d', 10);
|
||||
$id = input('post.id');
|
||||
$aid = input('post.aid', null, 'trim');
|
||||
|
||||
$select = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id');
|
||||
if (!empty($kw)) {
|
||||
if (!empty($id)) {
|
||||
$select->where('A.id', $id);
|
||||
} elseif (!empty($kw)) {
|
||||
$select->whereLike('name|A.remark', '%' . $kw . '%');
|
||||
}
|
||||
if (!empty($aid)) {
|
||||
$select->where('A.aid', $aid);
|
||||
}
|
||||
if (!empty($type)) {
|
||||
$select->whereLike('B.type', $type);
|
||||
}
|
||||
@@ -203,11 +217,28 @@ class Domain extends BaseController
|
||||
}
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->fieldRaw('A.*,B.type,B.remark aremark')->order('A.id', 'desc')->limit($offset, $limit)->select();
|
||||
switch ($order) {
|
||||
case '1':
|
||||
$select->order('A.regtime', 'asc');
|
||||
break;
|
||||
case '2':
|
||||
$select->order('A.regtime', 'desc');
|
||||
break;
|
||||
case '3':
|
||||
$select->order('A.expiretime', 'asc');
|
||||
break;
|
||||
case '4':
|
||||
$select->order('A.expiretime', 'desc');
|
||||
break;
|
||||
default:
|
||||
$select->order('A.id', 'desc');
|
||||
}
|
||||
$rows = $select->fieldRaw('A.*,B.type,B.remark aremark')->limit($offset, $limit)->select();
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$row['typename'] = DnsHelper::$dns_config[$row['type']]['name'];
|
||||
$row['icon'] = DnsHelper::$dns_config[$row['type']]['icon'];
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
@@ -321,6 +352,12 @@ class Domain extends BaseController
|
||||
Db::name('optimizeip')->where('did', 'in', $ids)->delete();
|
||||
Db::name('sctask')->where('did', 'in', $ids)->delete();
|
||||
return json(['code' => 0, 'msg' => '成功删除' . count($ids) . '个域名!']);
|
||||
} elseif ($act == 'updateexpire') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$ids = input('post.ids');
|
||||
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
$count = Db::name('domain')->where('id', 'in', $ids)->update(['checkstatus' => 0]);
|
||||
return json(['code' => 0, 'msg' => '已提交' . $count . '个域名,约' . ceil($count / 5) . '分钟后刷新完成。']);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
@@ -420,6 +457,9 @@ class Domain extends BaseController
|
||||
View::assign('recordLine', $recordLineArr);
|
||||
View::assign('minTTL', $minTTL ? $minTTL : 1);
|
||||
View::assign('dnsconfig', $dnsconfig);
|
||||
if ($dnstype == 'qingcloud') {
|
||||
return view('qingcloud');
|
||||
}
|
||||
return view();
|
||||
}
|
||||
|
||||
@@ -800,6 +840,7 @@ class Domain extends BaseController
|
||||
$line = input('post.line', null, 'trim');
|
||||
$ttl = input('post.ttl/d', 600);
|
||||
$mx = input('post.mx/d', 1);
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
$recordlist = explode("\n", $record);
|
||||
|
||||
if (empty($record) || empty($recordlist)) {
|
||||
@@ -821,7 +862,7 @@ class Domain extends BaseController
|
||||
$arr = explode(' ', $record);
|
||||
if (empty($record) || empty($arr[0]) || empty($arr[1])) continue;
|
||||
$thistype = empty($type) ? getDnsType($arr[1]) : $type;
|
||||
$recordid = $dns->addDomainRecord($arr[0], $thistype, $arr[1], $line, $ttl, $mx);
|
||||
$recordid = $dns->addDomainRecord($arr[0], $thistype, $arr[1], $line, $ttl, $mx, null, $remark);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '添加解析', $arr[0].' ['.$thistype.'] '.$arr[1].' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
$success++;
|
||||
|
||||
@@ -54,8 +54,6 @@ class Index extends BaseController
|
||||
|
||||
if (config('app.dbversion') && config_get('version') != config('app.dbversion')) {
|
||||
$this->db_update();
|
||||
config_set('version', config('app.dbversion'));
|
||||
Cache::clear();
|
||||
}
|
||||
|
||||
$tmp = 'version()';
|
||||
@@ -64,7 +62,7 @@ class Index extends BaseController
|
||||
'framework_version' => app()->version(),
|
||||
'php_version' => PHP_VERSION,
|
||||
'mysql_version' => $mysqlVersion,
|
||||
'software' => $_SERVER['SERVER_SOFTWARE'],
|
||||
'software' => $_SERVER['SERVER_SOFTWARE'] ?? '未知',
|
||||
'os' => php_uname(),
|
||||
'date' => date("Y-m-d H:i:s"),
|
||||
];
|
||||
@@ -87,6 +85,27 @@ class Index extends BaseController
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
config_set('version', config('app.dbversion'));
|
||||
Cache::clear();
|
||||
if(Db::name('account')->count() > 0 && Db::name('account')->whereNotNull('config')->count() == 0) {
|
||||
$accounts = Db::name('account')->select();
|
||||
foreach ($accounts as $account) {
|
||||
if (!empty($account['config']) || !isset(\app\lib\DnsHelper::$dns_config[$account['type']])) continue;
|
||||
$config = [];
|
||||
$account_fields = ['name', 'sk', 'ext'];
|
||||
$i = 0;
|
||||
foreach(\app\lib\DnsHelper::$dns_config[$account['type']]['config'] as $field => $item) {
|
||||
if ($field == 'proxy') {
|
||||
$config[$field] = $account['proxy'];
|
||||
break;
|
||||
}
|
||||
if ($i >= 3) break;
|
||||
$account_field = $account_fields[$i++];
|
||||
$config[$field] = isset($account[$account_field]) ? $account[$account_field] : '';
|
||||
}
|
||||
Db::name('account')->where('id', $account['id'])->update(['config' => json_encode($config)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function changeskin()
|
||||
|
||||
@@ -7,82 +7,128 @@ use Exception;
|
||||
use app\BaseController;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Request;
|
||||
use think\facade\View;
|
||||
use think\facade\Db;
|
||||
|
||||
class Install extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$dbconfig = '0';
|
||||
if (file_exists(app()->getRootPath() . '.env')) {
|
||||
return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件';
|
||||
if (checkTableExists('config') || checkTableExists('user') || checkTableExists('domain')) {
|
||||
return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件';
|
||||
} else {
|
||||
$dbconfig = '1';
|
||||
}
|
||||
}
|
||||
if (Request::isPost()) {
|
||||
$mysql_host = input('post.mysql_host', null, 'trim');
|
||||
$mysql_port = intval(input('post.mysql_port', '3306'));
|
||||
$mysql_user = input('post.mysql_user', null, 'trim');
|
||||
$mysql_pwd = input('post.mysql_pwd', null, 'trim');
|
||||
$mysql_name = input('post.mysql_name', null, 'trim');
|
||||
$mysql_prefix = input('post.mysql_prefix', 'cloud_', 'trim');
|
||||
$admin_username = input('post.admin_username', null, 'trim');
|
||||
$admin_password = input('post.admin_password', null, 'trim');
|
||||
if ($dbconfig == '1') {
|
||||
$admin_username = input('post.admin_username', null, 'trim');
|
||||
$admin_password = input('post.admin_password', null, 'trim');
|
||||
|
||||
if (!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password) {
|
||||
return json(['code' => 0, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if (!$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);
|
||||
$sqls = file_get_contents(app()->getAppPath() . 'sql/install.sql');
|
||||
$sqls = explode(';', $sqls);
|
||||
$mysql_prefix = env('database.prefix', 'dnsmgr_');
|
||||
|
||||
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 = '连接数据库失败:数据库名不存在!';
|
||||
$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)";
|
||||
|
||||
$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::execute($value) === false) {
|
||||
$error++;
|
||||
$dberror = Db::getErrorInfo();
|
||||
$errorMsg .= $dberror . "\n";
|
||||
} else {
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
if (empty($errorMsg)) {
|
||||
Cache::clear();
|
||||
return json(['code' => 1, 'msg' => '安装完成!成功执行SQL语句' . $success . '条']);
|
||||
} 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);
|
||||
|
||||
$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)";
|
||||
|
||||
$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) {
|
||||
$error++;
|
||||
$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' => '保存失败,请确保网站根目录有写入权限']);
|
||||
}
|
||||
Cache::clear();
|
||||
return json(['code' => 1, 'msg' => '安装完成!成功执行SQL语句' . $success . '条']);
|
||||
} else {
|
||||
return json(['code' => 0, 'msg' => $errorMsg]);
|
||||
$mysql_host = input('post.mysql_host', null, 'trim');
|
||||
$mysql_port = intval(input('post.mysql_port', '3306'));
|
||||
$mysql_user = input('post.mysql_user', null, 'trim');
|
||||
$mysql_pwd = input('post.mysql_pwd', null, 'trim');
|
||||
$mysql_name = input('post.mysql_name', null, 'trim');
|
||||
$mysql_prefix = input('post.mysql_prefix', 'cloud_', 'trim');
|
||||
$admin_username = input('post.admin_username', null, 'trim');
|
||||
$admin_password = input('post.admin_password', null, 'trim');
|
||||
|
||||
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);
|
||||
|
||||
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]);
|
||||
}
|
||||
$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);
|
||||
|
||||
$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)";
|
||||
|
||||
$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) {
|
||||
$error++;
|
||||
$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' => '保存失败,请确保网站根目录有写入权限']);
|
||||
}
|
||||
Cache::clear();
|
||||
return json(['code' => 1, 'msg' => '安装完成!成功执行SQL语句' . $success . '条']);
|
||||
} else {
|
||||
return json(['code' => 0, 'msg' => $errorMsg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
View::assign('dbconfig', $dbconfig);
|
||||
return view();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -174,6 +174,44 @@ location / {
|
||||
],
|
||||
]
|
||||
],
|
||||
'litessl' => [
|
||||
'name' => 'LiteSSL',
|
||||
'class' => 1,
|
||||
'icon' => 'litessl.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => '<a href="https://freessl.cn/automation/eab-manager" target="_blank" rel="noreferrer">LiteSSL密钥获取</a>',
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'EAB申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'EAB KID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'EAB HMAC Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'tencent' => [
|
||||
'name' => '腾讯云免费SSL',
|
||||
'class' => 2,
|
||||
|
||||
@@ -370,9 +370,8 @@ class DeployHelper
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'placeholder' => '留空则为添加证书',
|
||||
'note' => '在网站管理->证书管理查看证书的ID,注意域名是否与证书匹配',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -435,9 +434,8 @@ class DeployHelper
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'placeholder' => '留空则为添加证书',
|
||||
'note' => '在站点->证书管理查看证书的ID,注意域名是否与证书匹配',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -559,7 +557,7 @@ class DeployHelper
|
||||
'icon' => 'opanel.png',
|
||||
'desc' => '更新面板证书管理内的SSL证书',
|
||||
'note' => null,
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
@@ -581,7 +579,7 @@ class DeployHelper
|
||||
'v1' => '1.x',
|
||||
'v2' => '2.x',
|
||||
],
|
||||
'value' => 'v1',
|
||||
'value' => 'v2',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
@@ -594,7 +592,32 @@ class DeployHelper
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [],
|
||||
'taskinputs' => [
|
||||
'type' => [
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '更新已有证书',
|
||||
'3' => '面板本身的证书',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '在证书列表查看ID',
|
||||
'note' => '留空为根据关联SSL证书的域名,自动更新对应证书',
|
||||
'show' => 'type==0',
|
||||
],
|
||||
'node_name' => [
|
||||
'name' => '节点名称',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '每行一个子节点名称',
|
||||
'note' => '不填写时:只更新主节点证书;填写时:同时更新主节点和所有指定的子节点证书。每行填写一个子节点名称',
|
||||
'show' => 'type==0',
|
||||
],
|
||||
],
|
||||
],
|
||||
'mwpanel' => [
|
||||
'name' => 'MW面板',
|
||||
@@ -857,7 +880,7 @@ class DeployHelper
|
||||
sudo visudo<br/>
|
||||
#在文件最后一行增加以下内容,需要将username替换成自己的用户名<br/>
|
||||
username ALL=(ALL) NOPASSWD: NOPASSWD: ALL<br/>
|
||||
ctrl+x 保存退出',
|
||||
ctrl+x 保存退出<br/>',
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
'host' => [
|
||||
@@ -1035,6 +1058,7 @@ ctrl+x 保存退出',
|
||||
['value'=>'vod', 'label'=>'视频点播'],
|
||||
['value'=>'fc', 'label'=>'函数计算3.0'],
|
||||
['value'=>'fc2', 'label'=>'函数计算2.0'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
@@ -1146,7 +1170,7 @@ ctrl+x 保存退出',
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product!=\'esa\'&&product!=\'clb\'&&product!=\'alb\'&&product!=\'nlb\'',
|
||||
'show' => 'product!=\'esa\'&&product!=\'clb\'&&product!=\'alb\'&&product!=\'nlb\'&&product!=\'upload\'',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
@@ -1199,6 +1223,8 @@ ctrl+x 保存退出',
|
||||
['value'=>'tse', 'label'=>'云原生API网关TSE'],
|
||||
['value'=>'tcb', 'label'=>'云开发TCB'],
|
||||
['value'=>'lighthouse', 'label'=>'轻量应用服务器'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
['value'=>'update', 'label'=>'更新证书内容(证书ID不变)'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
@@ -1298,10 +1324,18 @@ ctrl+x 保存退出',
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product!=\'clb\'&&product!=\'tke\'',
|
||||
'show' => 'product!=\'clb\'&&product!=\'tke\'&&product!=\'upload\'',
|
||||
'note' => 'CDN、EO、WAF多个域名可用,隔开,其他只能填写1个域名',
|
||||
'required' => true,
|
||||
],
|
||||
'cert_id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '要更新的证书ID,在我的证书列表查看',
|
||||
'show' => 'product==\'update\'',
|
||||
'required' => true,
|
||||
'note' => '当前接口需联系加白使用',
|
||||
],
|
||||
],
|
||||
],
|
||||
'huawei' => [
|
||||
@@ -1341,15 +1375,31 @@ ctrl+x 保存退出',
|
||||
['value'=>'cdn', 'label'=>'内容分发网络CDN'],
|
||||
['value'=>'elb', 'label'=>'弹性负载均衡ELB'],
|
||||
['value'=>'waf', 'label'=>'Web应用防火墙WAF'],
|
||||
['value'=>'obs', 'label'=>'对象存储服务OBS'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
],
|
||||
'obs_endpoint' => [
|
||||
'name' => 'Endpoint地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '填写示例:obs.cn-north-4.myhuaweicloud.com',
|
||||
'show' => 'product==\'obs\'',
|
||||
'required' => true,
|
||||
],
|
||||
'obs_bucket' => [
|
||||
'name' => '桶名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'obs\'',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product==\'cdn\'',
|
||||
'show' => 'product==\'cdn\'||product==\'obs\'',
|
||||
'required' => true,
|
||||
],
|
||||
'project_id' => [
|
||||
@@ -1444,6 +1494,7 @@ ctrl+x 保存退出',
|
||||
['value'=>'cdn', 'label'=>'CDN'],
|
||||
['value'=>'oss', 'label'=>'OSS'],
|
||||
['value'=>'pili', 'label'=>'视频直播'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
@@ -1459,6 +1510,7 @@ ctrl+x 保存退出',
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product!=\'upload\'',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
@@ -1569,6 +1621,7 @@ ctrl+x 保存退出',
|
||||
['value'=>'cdn', 'label'=>'CDN'],
|
||||
['value'=>'blb', 'label'=>'普通型BLB'],
|
||||
['value'=>'appblb', 'label'=>'应用型BLB'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
@@ -1703,6 +1756,7 @@ ctrl+x 保存退出',
|
||||
['value'=>'tos', 'label'=>'对象存储TOS'],
|
||||
['value'=>'live', 'label'=>'视频直播'],
|
||||
['value'=>'imagex', 'label'=>'veImageX'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
@@ -1718,7 +1772,7 @@ ctrl+x 保存退出',
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product!=\'clb\'&&product!=\'alb\'',
|
||||
'show' => 'product!=\'clb\'&&product!=\'alb\'&&product!=\'upload\'',
|
||||
'required' => true,
|
||||
],
|
||||
'listener_id' => [
|
||||
@@ -1914,10 +1968,22 @@ ctrl+x 保存退出',
|
||||
['value'=>'cdn', 'label'=>'CDN加速'],
|
||||
['value'=>'icdn', 'label'=>'全站加速'],
|
||||
['value'=>'accessone', 'label'=>'边缘安全加速平台'],
|
||||
['value'=>'cf', 'label'=>'函数计算'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
],
|
||||
'region_id' => [
|
||||
'name' => '所属地域',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
['value'=>'bb9fdb42056f11eda1610242ac110002', 'label'=>'华东1'],
|
||||
['value'=>'200000002368', 'label'=>'西南1'],
|
||||
],
|
||||
'value' => 'bb9fdb42056f11eda1610242ac110002',
|
||||
'show' => 'product==\'cf\'',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
@@ -2000,9 +2066,8 @@ ctrl+x 保存退出',
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'placeholder' => '留空则为添加证书',
|
||||
'note' => '在SSL证书->我的证书页面查看,注意域名是否与证书匹配',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -2248,6 +2313,12 @@ ctrl+x 保存退出',
|
||||
'required' => true,
|
||||
'show' => 'auth==1',
|
||||
],
|
||||
'passphrase' => [
|
||||
'name' => '私钥密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '若私钥有设置密码,请填写此项',
|
||||
'show' => 'auth==1',
|
||||
],
|
||||
'windows' => [
|
||||
'name' => '是否Windows',
|
||||
'type' => 'radio',
|
||||
|
||||
@@ -9,9 +9,30 @@ class DnsHelper
|
||||
public static $dns_config = [
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'icon' => 'aliyun.png',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'AccessKeySecret',
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccessKeySecret' => [
|
||||
'name' => 'AccessKeySecret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置
|
||||
'status' => true, //是否支持启用暂停
|
||||
@@ -23,9 +44,30 @@ class DnsHelper
|
||||
],
|
||||
'dnspod' => [
|
||||
'name' => '腾讯云',
|
||||
'icon' => 'dnspod.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'SecretId',
|
||||
'sk' => 'SecretKey',
|
||||
'SecretId' => [
|
||||
'name' => 'SecretId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretKey' => [
|
||||
'name' => 'SecretKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 1,
|
||||
'status' => true,
|
||||
@@ -37,9 +79,30 @@ class DnsHelper
|
||||
],
|
||||
'huawei' => [
|
||||
'name' => '华为云',
|
||||
'icon' => 'huawei.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretAccessKey' => [
|
||||
'name' => 'SecretAccessKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
@@ -51,9 +114,30 @@ class DnsHelper
|
||||
],
|
||||
'baidu' => [
|
||||
'name' => '百度云',
|
||||
'icon' => 'baidu.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'AccessKey',
|
||||
'sk' => 'SecretKey',
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretAccessKey' => [
|
||||
'name' => 'SecretAccessKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => false,
|
||||
@@ -65,9 +149,30 @@ class DnsHelper
|
||||
],
|
||||
'west' => [
|
||||
'name' => '西部数码',
|
||||
'icon' => 'west.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => '用户名',
|
||||
'sk' => 'API密码',
|
||||
'username' => [
|
||||
'name' => '用户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'api_password' => [
|
||||
'name' => 'API密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
@@ -79,9 +184,30 @@ class DnsHelper
|
||||
],
|
||||
'huoshan' => [
|
||||
'name' => '火山引擎',
|
||||
'icon' => 'huoshan.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretAccessKey' => [
|
||||
'name' => 'SecretAccessKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
@@ -93,9 +219,30 @@ class DnsHelper
|
||||
],
|
||||
'jdcloud' => [
|
||||
'name' => '京东云',
|
||||
'icon' => 'jdcloud.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'AccessKeySecret',
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccessKeySecret' => [
|
||||
'name' => 'AccessKeySecret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
@@ -107,9 +254,30 @@ class DnsHelper
|
||||
],
|
||||
'dnsla' => [
|
||||
'name' => 'DNSLA',
|
||||
'icon' => 'dnsla.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'APIID',
|
||||
'sk' => 'API密钥',
|
||||
'apiid' => [
|
||||
'name' => 'APIID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'apisecret' => [
|
||||
'name' => 'API密钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
@@ -119,11 +287,117 @@ class DnsHelper
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'qingcloud' => [
|
||||
'name' => '青云',
|
||||
'icon' => 'qingcloud.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'access_key_id' => [
|
||||
'name' => 'Access Key ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'secret_access_key' => [
|
||||
'name' => 'Secret Access Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'bt' => [
|
||||
'name' => '宝塔域名',
|
||||
'icon' => 'bt.png',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'AccessKey' => [
|
||||
'name' => 'Access Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretKey' => [
|
||||
'name' => 'Secret Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccountID' => [
|
||||
'name' => 'Account ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'cloudflare' => [
|
||||
'name' => 'Cloudflare',
|
||||
'icon' => 'cloudflare.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => '邮箱地址',
|
||||
'sk' => 'API密钥/令牌',
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'auth' => [
|
||||
'name' => '认证方式',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => 'API密钥',
|
||||
'1' => 'API令牌',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API密钥/令牌',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
@@ -135,9 +409,30 @@ class DnsHelper
|
||||
],
|
||||
'namesilo' => [
|
||||
'name' => 'NameSilo',
|
||||
'icon' => 'namesilo.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => '账户名',
|
||||
'sk' => 'API Key',
|
||||
'username' => [
|
||||
'name' => '账户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
@@ -147,12 +442,73 @@ class DnsHelper
|
||||
'page' => true,
|
||||
'add' => false,
|
||||
],
|
||||
'spaceship' => [
|
||||
'name' => 'Spaceship',
|
||||
'icon' => 'spaceship.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'apikey' => [
|
||||
'name' => 'API Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'apisecret' => [
|
||||
'name' => 'API Secret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'powerdns' => [
|
||||
'name' => 'PowerDNS',
|
||||
'icon' => 'powerdns.ico',
|
||||
'note' => '',
|
||||
'config' => [
|
||||
'ak' => 'IP地址',
|
||||
'sk' => '端口',
|
||||
'ext' => 'API KEY',
|
||||
'ip' => [
|
||||
'name' => 'IP地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'port' => [
|
||||
'name' => '端口',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API KEY',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
@@ -162,6 +518,96 @@ class DnsHelper
|
||||
'page' => true,
|
||||
'add' => true,
|
||||
],
|
||||
'aliyunesa' => [
|
||||
'name' => '阿里云ESA',
|
||||
'icon' => 'aliyun.png',
|
||||
'note' => '仅支持以NS方式接入阿里云ESA的域名',
|
||||
'config' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccessKeySecret' => [
|
||||
'name' => 'AccessKeySecret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'region' => [
|
||||
'name' => 'API接入点',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
['value' => 'cn-hangzhou', 'label' => '中国内地'],
|
||||
['value' => 'ap-southeast-1', 'label' => '非中国内地'],
|
||||
],
|
||||
'value' => 'cn-hangzhou',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => false,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'tencenteo' => [
|
||||
'name' => '腾讯云EO',
|
||||
'icon' => 'tencent.png',
|
||||
'note' => '仅支持以NS方式接入腾讯云EO的域名',
|
||||
'config' => [
|
||||
'SecretId' => [
|
||||
'name' => 'SecretId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretKey' => [
|
||||
'name' => 'SecretKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'site_type' => [
|
||||
'name' => 'API接入点',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
['value' => 'cn', 'label' => '中国内地'],
|
||||
['value' => 'intl', 'label' => '非中国内地'],
|
||||
],
|
||||
'value' => 'cn',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
];
|
||||
|
||||
public static $line_name = [
|
||||
@@ -173,9 +619,14 @@ class DnsHelper
|
||||
'huoshan' => ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'],
|
||||
'baidu' => ['DEF' => 'default', 'CT' => 'ct', 'CU' => 'cnc', 'CM' => 'cmnet', 'AB' => ''],
|
||||
'jdcloud' => ['DEF' => '-1', 'CT' => '1', 'CU' => '2', 'CM' => '3', 'AB' => '4'],
|
||||
'bt' => ['DEF' => '0', 'CT' => '285344768', 'CU' => '285345792', 'CM' => '285346816'],
|
||||
'qingcloud' => ['DEF' => '0', 'CT' => '2', 'CU' => '3', 'CM' => '4', 'AB' => '8'],
|
||||
'cloudflare' => ['DEF' => '0'],
|
||||
'namesilo' => ['DEF' => 'default'],
|
||||
'powerdns' => ['DEF' => 'default'],
|
||||
'spaceship' => ['DEF' => 'default'],
|
||||
'aliyunesa' => ['DEF' => '0'],
|
||||
'tencenteo' => ['DEF' => 'Default'],
|
||||
];
|
||||
|
||||
public static function getList()
|
||||
@@ -195,11 +646,12 @@ class DnsHelper
|
||||
*/
|
||||
public static function getModel($aid, $domain = null, $domainid = null)
|
||||
{
|
||||
$config = self::getConfig($aid);
|
||||
if (!$config) return false;
|
||||
$dnstype = $config['type'];
|
||||
$account = self::getConfig($aid);
|
||||
if (!$account) return false;
|
||||
$dnstype = $account['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config = json_decode($account['config'], true);
|
||||
$config['domain'] = $domain;
|
||||
$config['domainid'] = $domainid;
|
||||
$model = new $class($config);
|
||||
@@ -211,13 +663,14 @@ class DnsHelper
|
||||
/**
|
||||
* @return DnsInterface|bool
|
||||
*/
|
||||
public static function getModel2($config)
|
||||
public static function getModel2($account)
|
||||
{
|
||||
$dnstype = $config['type'];
|
||||
$dnstype = $account['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config['domain'] = $config['name'];
|
||||
$config['domainid'] = $config['thirdid'];
|
||||
$config = json_decode($account['config'], true);
|
||||
$config['domain'] = $account['name'];
|
||||
$config['domainid'] = $account['thirdid'];
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
|
||||
116
app/lib/cert/litessl.php
Normal file
116
app/lib/cert/litessl.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class litessl implements CertInterface
|
||||
{
|
||||
private $directory = 'https://acme.litessl.com/acme/v2/directory';
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($this->directory, (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
$this->ac->setAccount($ext['kid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
if (empty($this->config['kid']) || empty($this->config['key'])) {
|
||||
throw new Exception('EAB密钥不能为空');
|
||||
}
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $key];
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
$domain_config = [];
|
||||
foreach ($domainList as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$domain_config[$domain] = ['challenge' => 'dns-01'];
|
||||
}
|
||||
if (empty($domain_config)) throw new Exception('域名列表不能为空');
|
||||
|
||||
$order = $this->ac->createOrder($domain_config);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
/*if (!array_key_exists($mainDomain, $dnsList)) {
|
||||
$dnsList[$mainDomain][] = ['name' => '@', 'type' => 'CAA', 'value' => '0 issue "litessl.cn"'];
|
||||
}*/
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$this->ac->authOrder($order);
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
|
||||
if ($keytype == 'ECC') {
|
||||
if (empty($keysize)) $keysize = '384';
|
||||
$private_key = $this->ac->generateECKey($keysize);
|
||||
} else {
|
||||
if (empty($keysize)) $keysize = '2048';
|
||||
$private_key = $this->ac->generateRSAKey($keysize);
|
||||
}
|
||||
$fullchain = $this->ac->finalizeOrder($domainList, $order, $private_key);
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $certInfo['issuer']['CN'], 'subject' => $certInfo['subject']['CN'], 'validFrom' => $certInfo['validFrom_time_t'], 'validTo' => $certInfo['validTo_time_t']];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$this->ac->revoke($pem);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->ac->setLogger($func);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class Ctyun
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $path, $query = null, $params = null)
|
||||
public function request($method, $path, $query = null, $params = null, $header = null)
|
||||
{
|
||||
if (!empty($query)) {
|
||||
$query = array_filter($query, function ($a) { return $a !== null;});
|
||||
@@ -50,6 +50,11 @@ class Ctyun
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
if (!empty($header)) {
|
||||
foreach ($header as $key => $value) {
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($query, $headers, $body, $date);
|
||||
$headers['Eop-Authorization'] = $authorization;
|
||||
@@ -151,7 +156,7 @@ class Ctyun
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['statusCode']) && $arr['statusCode'] == 100000) {
|
||||
if (isset($arr['statusCode']) && ($arr['statusCode'] == 100000 || $arr['statusCode'] == 0 && $this->endpoint == 'cf-global.ctapi.ctyun.cn')) {
|
||||
return isset($arr['returnObj']) ? $arr['returnObj'] : true;
|
||||
} elseif (isset($arr['errorMessage'])) {
|
||||
throw new Exception($arr['errorMessage']);
|
||||
|
||||
232
app/lib/client/HuaweiOBS.php
Normal file
232
app/lib/client/HuaweiOBS.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 华为云OBS
|
||||
*/
|
||||
class HuaweiOBS
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $Endpoint;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $Endpoint, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
public function setBucketCustomdomain($bucket, $domain, $cert_name, $fullchain, $privatekey)
|
||||
{
|
||||
$strXml = <<<EOF
|
||||
<CustomDomainConfiguration>
|
||||
</CustomDomainConfiguration>
|
||||
EOF;
|
||||
$xml = new \SimpleXMLElement($strXml);
|
||||
$xml->addChild('Name', $cert_name);
|
||||
$xml->addChild('Certificate', $fullchain);
|
||||
$xml->addChild('PrivateKey', $privatekey);
|
||||
$body = $xml->asXML();
|
||||
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'customdomain' => $domain
|
||||
];
|
||||
return $this->request('PUT', '/', $query, $body, $options);
|
||||
}
|
||||
|
||||
public function deleteBucketCustomdomain($bucket, $domain)
|
||||
{
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'customdomain' => $domain
|
||||
];
|
||||
return $this->request('DELETE', '/', $query, '', $options);
|
||||
}
|
||||
|
||||
public function getBucketCustomdomain($bucket)
|
||||
{
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'customdomain' => '',
|
||||
];
|
||||
return $this->request('GET', '/', $query, '', $options);
|
||||
}
|
||||
|
||||
private function request($method, $path, $query, $body, $options)
|
||||
{
|
||||
$hostname = $options['bucket'] . '.' . $this->Endpoint;
|
||||
$query_string = $this->toQueryString($query);
|
||||
$query_string = empty($query_string) ? '' : '?' . $query_string;
|
||||
$requestUrl = 'https://' . $hostname . $path . $query_string;
|
||||
$headers = [
|
||||
'Content-Type' => 'application/xml',
|
||||
'Content-MD5' => base64_encode(md5($body, true)),
|
||||
'Date' => gmdate('D, d M Y H:i:s \G\M\T'),
|
||||
];
|
||||
$headers['Authorization'] = $this->getAuthorization($method, $path, $query, $headers, $options);
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $requestUrl, $body, $header);
|
||||
}
|
||||
|
||||
private function curl($method, $url, $body, $header)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
if ($this->proxy) {
|
||||
curl_set_proxy($ch);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $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) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
if (empty($response)) return true;
|
||||
return $this->xml2array($response);
|
||||
}
|
||||
$arr = $this->xml2array($response);
|
||||
if (isset($arr['Message'])) {
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
}
|
||||
|
||||
private function toQueryString($params = array())
|
||||
{
|
||||
$temp = array();
|
||||
uksort($params, 'strnatcasecmp');
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_string($key) && !is_array($value)) {
|
||||
if (strlen($value) > 0) {
|
||||
$temp[] = rawurlencode($key) . '=' . rawurlencode($value);
|
||||
} else {
|
||||
$temp[] = rawurlencode($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode('&', $temp);
|
||||
}
|
||||
|
||||
private function xml2array($xml)
|
||||
{
|
||||
if (!$xml) {
|
||||
return false;
|
||||
}
|
||||
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
private function getAuthorization($method, $url, $query, $headers, $options)
|
||||
{
|
||||
$method = strtoupper($method);
|
||||
$date = $headers['Date'];
|
||||
$resourcePath = $this->getResourcePath($options);
|
||||
$stringToSign = $this->calcStringToSign($method, $date, $headers, $resourcePath, $query);
|
||||
$signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->SecretAccessKey, true));
|
||||
return 'OBS ' . $this->AccessKeyId . ':' . $signature;
|
||||
}
|
||||
|
||||
private function getResourcePath(array $options)
|
||||
{
|
||||
$resourcePath = '/';
|
||||
if (strlen($options['bucket']) > 0) {
|
||||
$resourcePath .= $options['bucket'] . '/';
|
||||
}
|
||||
if (strlen($options['key']) > 0) {
|
||||
$resourcePath .= $options['key'];
|
||||
}
|
||||
return $resourcePath;
|
||||
}
|
||||
|
||||
private function calcStringToSign($method, $date, array $headers, $resourcePath, array $query)
|
||||
{
|
||||
/*
|
||||
SignToString =
|
||||
VERB + "\n"
|
||||
+ Content-MD5 + "\n"
|
||||
+ Content-Type + "\n"
|
||||
+ Date + "\n"
|
||||
+ CanonicalizedOSSHeaders
|
||||
+ CanonicalizedResource
|
||||
Signature = base64(hmac-sha1(AccessKeySecret, SignToString))
|
||||
*/
|
||||
$contentMd5 = '';
|
||||
$contentType = '';
|
||||
// CanonicalizedOSSHeaders
|
||||
$signheaders = array();
|
||||
foreach ($headers as $key => $value) {
|
||||
$lowk = strtolower($key);
|
||||
if (strncmp($lowk, "x-obs-", 6) == 0) {
|
||||
$signheaders[$lowk] = $value;
|
||||
} else if ($lowk === 'content-md5') {
|
||||
$contentMd5 = $value;
|
||||
} else if ($lowk === 'content-type') {
|
||||
$contentType = $value;
|
||||
}
|
||||
}
|
||||
ksort($signheaders);
|
||||
$canonicalizedOSSHeaders = '';
|
||||
foreach ($signheaders as $key => $value) {
|
||||
$canonicalizedOSSHeaders .= $key . ':' . $value . "\n";
|
||||
}
|
||||
// CanonicalizedResource
|
||||
$signquery = array();
|
||||
foreach ($query as $key => $value) {
|
||||
if (in_array($key, $this->signKeyList)) {
|
||||
$signquery[$key] = $value;
|
||||
}
|
||||
}
|
||||
ksort($signquery);
|
||||
$sortedQueryList = array();
|
||||
foreach ($signquery as $key => $value) {
|
||||
if (strlen($value) > 0) {
|
||||
$sortedQueryList[] = $key . '=' . $value;
|
||||
} else {
|
||||
$sortedQueryList[] = $key;
|
||||
}
|
||||
}
|
||||
$queryStringSorted = implode('&', $sortedQueryList);
|
||||
$canonicalizedResource = $resourcePath;
|
||||
if (!empty($queryStringSorted)) {
|
||||
$canonicalizedResource .= '?' . $queryStringSorted;
|
||||
}
|
||||
return $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n" . $canonicalizedOSSHeaders . $canonicalizedResource;
|
||||
}
|
||||
|
||||
private $signKeyList = array(
|
||||
'acl', 'policy', 'torrent', 'logging', 'location', 'storageinfo', 'quota', 'storagepolicy', 'requestpayment', 'versions', 'versioning', 'versionid', 'uploads', 'uploadid', 'partnumber', 'website', 'notification', 'lifecycle', 'deletebucket', 'delete', 'cors', 'restore', 'tagging', 'response-content-type', 'response-content-language', 'response-expires', 'response-cache-control', 'response-content-disposition', 'response-content-encoding', 'x-image-process', 'backtosource', 'storageclass', 'replication', 'append', 'position', 'x-oss-process', 'CDNNotifyConfiguration', 'attname', 'customdomain', 'directcoldaccess', 'encryption', 'inventory', 'length', 'metadata', 'modify', 'name', 'rename', 'truncate', 'x-image-save-bucket', 'x-image-save-object', 'x-obs-security-token', 'x-obs-callback'
|
||||
);
|
||||
}
|
||||
@@ -66,6 +66,7 @@ class aliyun implements DeployInterface
|
||||
$this->deploy_alb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'nlb') {
|
||||
$this->deploy_nlb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'upload') {
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
}
|
||||
@@ -311,7 +312,20 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
|
||||
$data['Listen']['CertId'] = $cert_id;
|
||||
if (empty($data['Listen']['HttpsPorts'])) $data['Listen']['HttpsPorts'] = [443];
|
||||
if (empty($data['Listen']['HttpsPorts'])) {
|
||||
$data['Listen']['HttpsPorts'] = [443];
|
||||
$data['Listen']['TLSVersion'] = 'tlsv1.1';
|
||||
$data['Listen']['EnableTLSv3'] = true;
|
||||
$data['Listen']['CipherSuite'] = 1;
|
||||
}
|
||||
if (count($data['Redirect']['BackendPorts']) == 1 && $data['Redirect']['BackendPorts'][0]['Protocol'] == 'http') {
|
||||
$data['Redirect']['BackendPorts'][] = [
|
||||
'ListenPort' => 443,
|
||||
'Protocol' => 'https',
|
||||
'BackendPort' => $data['Redirect']['BackendPorts'][0]['BackendPort'],
|
||||
];
|
||||
$data['Redirect']['FocusHttpBackend'] = true;
|
||||
}
|
||||
$data['Redirect']['Backends'] = $data['Redirect']['AllBackends'];
|
||||
$param = [
|
||||
'Action' => 'ModifyDomain',
|
||||
|
||||
@@ -39,6 +39,7 @@ class baidu implements DeployInterface
|
||||
$this->deploy_blb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'appblb') {
|
||||
$this->deploy_appblb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'upload') {
|
||||
} else {
|
||||
throw new Exception('不支持的产品类型');
|
||||
}
|
||||
|
||||
@@ -43,7 +43,39 @@ class cdnfly implements DeployInterface
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
if (empty($id)) {
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$params = [
|
||||
'type' => 'custom',
|
||||
'name' => $cert_name,
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
if ($this->auth == 1) {
|
||||
$access_token = $this->login();
|
||||
$url = $this->url . '/v1/certs';
|
||||
$body = json_encode($params);
|
||||
$headers = [
|
||||
'Access-Token' => $access_token,
|
||||
];
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, 'POST');
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
$id = $result['data'];
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception('证书添加失败,' . $result['msg']);
|
||||
} else {
|
||||
throw new Exception('证书添加失败,返回数据解析失败');
|
||||
}
|
||||
} else {
|
||||
$id = $this->request('/v1/certs', $params, 'POST');
|
||||
}
|
||||
$this->log("证书ID:{$id}添加成功!");
|
||||
$info['config']['id'] = $id;
|
||||
return;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'type' => 'custom',
|
||||
|
||||
@@ -39,6 +39,8 @@ class ctyun implements DeployInterface
|
||||
$this->deploy_icdn($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'accessone') {
|
||||
$this->deploy_accessone($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'cf') {
|
||||
$this->deploy_cf($fullchain, $privatekey, $config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +162,7 @@ class ctyun implements DeployInterface
|
||||
}
|
||||
}
|
||||
try {
|
||||
$client->request('POST', '/ctapi/v1/accessone/domain/modify_config', null, $result);
|
||||
$client->request('POST', '/ctapi/v1/scdn/domain/modify_config', null, $result);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
|
||||
throw new Exception($e->getMessage());
|
||||
@@ -170,6 +172,44 @@ class ctyun implements DeployInterface
|
||||
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_cf($fullchain, $privatekey, $config)
|
||||
{
|
||||
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'cf-global.ctapi.ctyun.cn', $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/openapi/v1/domains/customdomains/' . $config['domain'], null, null, ['regionId' => $config['region_id']]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取自定义域名配置失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (isset($data['certConfig']['certificate']) && trim($data['certConfig']['certificate']) == trim($fullchain)) {
|
||||
$this->log('函数计算域名 ' . $config['domain'] . ' 证书已部署,无需重复操作!');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($data['protocol'] == 'HTTP') $data['protocol'] = 'HTTP,HTTPS';
|
||||
$param = [
|
||||
'domainName' => $config['domain'],
|
||||
'description' => $data['description'],
|
||||
'protocol' => $data['protocol'],
|
||||
'certConfig' => [
|
||||
'certName' => 'cert' . substr($config['cert_name'], strpos($config['cert_name'], '-') + 1),
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
],
|
||||
'authConfig' => $data['authConfig'],
|
||||
'routeConfig' => $data['routeConfig'],
|
||||
];
|
||||
try {
|
||||
$client->request('PUT', '/openapi/v1/domains/customdomains/' . $config['domain'], null, $param, ['regionId' => $config['region_id']]);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->log('函数计算域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
|
||||
@@ -52,7 +52,7 @@ class fnos implements DeployInterface
|
||||
$this->exec($connection, '上传证书文件', "sudo tee ".$certPath." > /dev/null <<'EOF'\n".$fullchain."\nEOF");
|
||||
$this->exec($connection, '上传私钥文件', "sudo tee ".$keyPath." > /dev/null <<'EOF'\n".$privatekey."\nEOF");
|
||||
$this->exec($connection, '刷新目录权限', 'sudo chmod 0755 "'.$certDir.'" -R');
|
||||
$this->exec($connection, '更新数据表', 'sudo -u postgres psql -d trim_connect -c "UPDATE cert SET valid_to='.$certInfo['validTo_time_t'].'000,valid_from='.$certInfo['validFrom_time_t'].'000,issued_by=\''.$certInfo['issuer']['CN'].'\',updated_time='.getMillisecond().' WHERE private_key=\''.$keyPath.'\'"');
|
||||
$this->exec($connection, '更新数据表', 'cd /tmp && sudo -u postgres psql -d trim_connect -c "UPDATE cert SET valid_to='.$certInfo['validTo_time_t'].'000,valid_from='.$certInfo['validFrom_time_t'].'000,issued_by=\''.$certInfo['issuer']['CN'].'\',updated_time='.getMillisecond().' WHERE private_key=\''.$keyPath.'\'"');
|
||||
$this->log('证书 '.$row['domain'].' 更新成功');
|
||||
$success++;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\HuaweiCloud;
|
||||
use app\lib\client\HuaweiOBS;
|
||||
use Exception;
|
||||
|
||||
class huawei implements DeployInterface
|
||||
@@ -39,6 +40,11 @@ class huawei implements DeployInterface
|
||||
$this->deploy_elb($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'waf') {
|
||||
$this->deploy_waf($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'obs') {
|
||||
$this->deploy_obs($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'upload') {
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
$info['cert_id'] = $cert_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +123,19 @@ class huawei implements DeployInterface
|
||||
$this->log('WAF证书ID ' . $config['cert_id'] . ' 更新证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_obs($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
if (empty($config['obs_endpoint'])) throw new Exception('OBS Endpoint不能为空');
|
||||
if (empty($config['obs_bucket'])) throw new Exception('OBS 桶名称不能为空');
|
||||
$obsClient = new HuaweiOBS($this->AccessKeyId, $this->SecretAccessKey, $config['obs_endpoint'], $this->proxy);
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$obsClient->setBucketCustomdomain($config['obs_bucket'], $domain, $config['cert_name'], $fullchain, $privatekey);
|
||||
$this->log('OSS域名 ' . $domain . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
|
||||
@@ -191,7 +191,7 @@ class huoshan implements DeployInterface
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'certificate-service.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'Tag' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
@@ -207,10 +207,20 @@ class huoshan implements DeployInterface
|
||||
}
|
||||
if (!empty($data['InstanceId'])) {
|
||||
$cert_id = $data['InstanceId'];
|
||||
$this->log('上传证书成功 CertId=' . $cert_id);
|
||||
|
||||
$param = [
|
||||
'InstanceId' => $cert_id,
|
||||
'Options' => [
|
||||
'ExpiredNotice' => 'Disabled',
|
||||
],
|
||||
];
|
||||
$client->request('POST', 'CertificateUpdateInstance', $param);
|
||||
|
||||
} else {
|
||||
$cert_id = $data['RepeatId'];
|
||||
$this->log('找到已上传的证书 CertId=' . $cert_id);
|
||||
}
|
||||
$this->log('上传证书成功 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class kuocai implements DeployInterface
|
||||
|
||||
private function request($path, $params = null, $json = false)
|
||||
{
|
||||
$url = 'https://kuocai.cn' . $path;
|
||||
$url = 'https://www.kuocaicdn.com' . $path;
|
||||
$body = $json ? json_encode($params) : $params;
|
||||
$headers = [];
|
||||
if ($json) $headers['Content-Type'] = 'application/json';
|
||||
|
||||
@@ -41,13 +41,29 @@ class lecdn implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
if ($this->auth == 0) {
|
||||
$this->login();
|
||||
}
|
||||
|
||||
$id = $config['id'];
|
||||
if (empty($id)) {
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$params = [
|
||||
'name' => $cert_name,
|
||||
'type' => 'upload',
|
||||
'ssl_pem' => base64_encode($fullchain),
|
||||
'ssl_key' => base64_encode($privatekey),
|
||||
'auto_renewal' => false,
|
||||
];
|
||||
$data = $this->request('/prod-api/certificate', $params, 'POST');
|
||||
$id = $data['id'];
|
||||
$this->log("证书ID:{$id}添加成功!");
|
||||
$info['config']['id'] = $id;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->request('/prod-api/certificate/' . $id);
|
||||
} catch (Exception $e) {
|
||||
|
||||
@@ -27,15 +27,131 @@ class opanel implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
// 解析节点名称列表
|
||||
$nodeNames = $this->parseNodeNames($config);
|
||||
|
||||
if (isset($config['type']) && $config['type'] == '3') {
|
||||
// 面板本身的证书部署
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
'ssl' => 'Enable',
|
||||
'sslID' => null,
|
||||
'sslType' => 'import-paste',
|
||||
];
|
||||
|
||||
if (empty($nodeNames)) {
|
||||
// 没有指定节点,只部署到主控节点
|
||||
try {
|
||||
$this->request('/core/settings/ssl/update', $params);
|
||||
$this->log("面板证书更新成功!");
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("面板证书更新失败:" . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
// 同时部署到主节点和所有指定的子节点
|
||||
$successCount = 0;
|
||||
$failCount = 0;
|
||||
|
||||
// 先更新主节点
|
||||
try {
|
||||
$this->request('/core/settings/ssl/update', $params);
|
||||
$this->log("主节点面板证书更新成功!");
|
||||
$successCount++;
|
||||
} catch (Exception $e) {
|
||||
$this->log("主节点面板证书更新失败:" . $e->getMessage());
|
||||
$failCount++;
|
||||
}
|
||||
|
||||
// 然后更新所有子节点
|
||||
foreach ($nodeNames as $nodeName) {
|
||||
try {
|
||||
$this->request('/core/settings/ssl/update', $params, $nodeName);
|
||||
$this->log("节点 [{$nodeName}] 面板证书更新成功!");
|
||||
$successCount++;
|
||||
} catch (Exception $e) {
|
||||
$this->log("节点 [{$nodeName}] 面板证书更新失败:" . $e->getMessage());
|
||||
$failCount++;
|
||||
}
|
||||
}
|
||||
if ($failCount > 0 && $successCount == 0) {
|
||||
throw new Exception("所有节点证书更新失败");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有指定节点,则只部署到主控节点
|
||||
if (empty($nodeNames)) {
|
||||
$this->deployToNode($fullchain, $privatekey, $config, null);
|
||||
} else {
|
||||
// 同时部署到主节点和所有指定的子节点
|
||||
$successCount = 0;
|
||||
$failCount = 0;
|
||||
|
||||
// 先更新主节点
|
||||
try {
|
||||
$this->deployToNode($fullchain, $privatekey, $config, null);
|
||||
$successCount++;
|
||||
} catch (Exception $e) {
|
||||
$this->log("主节点部署失败:" . $e->getMessage());
|
||||
$failCount++;
|
||||
}
|
||||
|
||||
// 然后更新所有子节点
|
||||
foreach ($nodeNames as $nodeName) {
|
||||
try {
|
||||
$this->deployToNode($fullchain, $privatekey, $config, $nodeName);
|
||||
$successCount++;
|
||||
} catch (Exception $e) {
|
||||
$this->log("节点 [{$nodeName}] 部署失败:" . $e->getMessage());
|
||||
$failCount++;
|
||||
}
|
||||
}
|
||||
if ($failCount > 0 && $successCount == 0) {
|
||||
throw new Exception("所有节点部署失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部署到指定节点
|
||||
*/
|
||||
private function deployToNode($fullchain, $privatekey, $config, $nodeName = null)
|
||||
{
|
||||
if (!empty($config['id'])) {
|
||||
// 指定证书ID的情况
|
||||
$params = [
|
||||
'sslID' => intval($config['id']),
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
try {
|
||||
$this->request('/websites/ssl/upload', $params, $nodeName);
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] 证书ID:{$config['id']}更新成功!" : "证书ID:{$config['id']}更新成功!";
|
||||
$this->log($logMsg);
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] 证书ID:{$config['id']}更新失败:" : "证书ID:{$config['id']}更新失败:";
|
||||
throw new Exception($logMsg . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 根据域名自动匹配证书
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$params = ['page' => 1, 'pageSize' => 500];
|
||||
try {
|
||||
$data = $this->request("/websites/ssl/search", $params);
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
$data = $this->request("/websites/ssl/search", $params, $nodeName);
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] " : "";
|
||||
$this->log($logMsg . '获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] " : "";
|
||||
throw new Exception($logMsg . '获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
@@ -62,18 +178,29 @@ class opanel implements DeployInterface
|
||||
'description' => '',
|
||||
];
|
||||
try {
|
||||
$this->request('/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$this->request('/websites/ssl/upload', $params, $nodeName);
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] 证书ID:{$row['id']}更新成功!" : "证书ID:{$row['id']}更新成功!";
|
||||
$this->log($logMsg);
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] 证书ID:{$row['id']}更新失败:" : "证书ID:{$row['id']}更新失败:";
|
||||
$this->log($logMsg . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '没有要更新的证书');
|
||||
$params = [
|
||||
'sslID' => 0,
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
$this->request('/websites/ssl/upload', $params, $nodeName);
|
||||
$logMsg = $nodeName ? "节点 [{$nodeName}] 证书上传成功!" : "证书上传成功!";
|
||||
$this->log($logMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +216,32 @@ class opanel implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
/**
|
||||
* 解析节点名称列表
|
||||
*/
|
||||
private function parseNodeNames($config)
|
||||
{
|
||||
if (!isset($config['node_name']) || empty($config['node_name'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$nodeNameStr = trim($config['node_name']);
|
||||
if (empty($nodeNameStr)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 按行分割,过滤空行
|
||||
$nodeNames = array_filter(
|
||||
array_map('trim', explode("\n", $nodeNameStr)),
|
||||
function($name) {
|
||||
return !empty($name);
|
||||
}
|
||||
);
|
||||
|
||||
return array_values($nodeNames);
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $nodeName = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
@@ -97,8 +249,12 @@ class opanel implements DeployInterface
|
||||
$token = md5('1panel' . $this->key . $timestamp);
|
||||
$headers = [
|
||||
'1Panel-Token' => $token,
|
||||
'1Panel-Timestamp' => $timestamp
|
||||
'1Panel-Timestamp' => $timestamp,
|
||||
];
|
||||
// 只有子节点时才设置 CurrentNode 头,主节点时不设置该头
|
||||
if (!empty($nodeName)) {
|
||||
$headers['CurrentNode'] = $nodeName;
|
||||
}
|
||||
$body = $params ? json_encode($params) : '{}';
|
||||
if ($body) $headers['Content-Type'] = 'application/json';
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
|
||||
@@ -37,6 +37,9 @@ class qiniu implements DeployInterface
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $certInfo['subject']['CN'], $cert_name);
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $cert_name;
|
||||
if ($config['product'] == 'upload') return;
|
||||
|
||||
foreach (explode(',', $domains) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
@@ -50,8 +53,6 @@ class qiniu implements DeployInterface
|
||||
throw new Exception('未知的产品类型');
|
||||
}
|
||||
}
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $cert_name;
|
||||
}
|
||||
|
||||
private function deploy_cdn($domain, $cert_id)
|
||||
|
||||
@@ -26,19 +26,43 @@ class rainyun implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['id'])) throw new Exception('证书ID不能为空');
|
||||
if (empty($config['id'])) {
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/product/sslcenter/', $params, 'POST');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败,' . $e->getMessage());
|
||||
}
|
||||
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/product/sslcenter/' . $config['id'], $params, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
$params = [
|
||||
'options' => '{"columnFilters":{"Domain":""},"sort":[],"page":1,"perPage":1}',
|
||||
];
|
||||
try {
|
||||
$data = $this->request('/product/sslcenter/?' . http_build_query($params), null, 'GET');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败,' . $e->getMessage());
|
||||
}
|
||||
if (empty($data['Records'])) throw new Exception('未找到已上传的证书');
|
||||
$cert_id = $data['Records'][0]['ID'];
|
||||
$info['config']['id'] = $cert_id;
|
||||
|
||||
$this->log('证书ID:' . $cert_id . '添加成功!');
|
||||
} else {
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/product/sslcenter/' . $config['id'], $params, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书ID:' . $config['id'] . '更新成功!');
|
||||
}
|
||||
|
||||
$this->log('证书ID:' . $config['id'] . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
@@ -55,7 +79,7 @@ class rainyun implements DeployInterface
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return $result;
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\CertHelper;
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
@@ -49,7 +50,8 @@ class ssh implements DeployInterface
|
||||
fclose($stream);
|
||||
$this->log('私钥已保存到:' . $config['pem_key_file']);
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
$pfx_pass = $config['pfx_pass'] ?? null;
|
||||
$pfx = CertHelper::getPfx($fullchain, $privatekey, $pfx_pass);
|
||||
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pfx_file']}", 'w');
|
||||
if (!$stream) {
|
||||
@@ -157,8 +159,14 @@ class ssh implements DeployInterface
|
||||
file_put_contents($privateKeyPath, $this->config['privatekey']);
|
||||
file_put_contents($publicKeyPath, $publicKey);
|
||||
umask($umask);
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
if (!empty($this->config['passphrase'])) {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath, $this->config['passphrase'])) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_password($connection, $this->config['username'], $this->config['password'])) {
|
||||
|
||||
@@ -31,8 +31,12 @@ class tencent implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['product'] == 'update') {
|
||||
return $this->update_cert($fullchain, $privatekey, $config);
|
||||
}
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('证书ID获取失败');
|
||||
$info['cert_id'] = $cert_id;
|
||||
if ($config['product'] == 'cos') {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['cos_bucket'])) throw new Exception('存储桶名称不能为空');
|
||||
@@ -62,6 +66,8 @@ class tencent implements DeployInterface
|
||||
return $this->deploy_scf($cert_id, $config);
|
||||
} elseif ($config['product'] == 'teo' && isset($config['site_id'])) {
|
||||
return $this->deploy_teo($cert_id, $config);
|
||||
} elseif ($config['product'] == 'upload') {
|
||||
return;
|
||||
} else {
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
if ($config['product'] == 'waf') {
|
||||
@@ -74,7 +80,6 @@ class tencent implements DeployInterface
|
||||
}
|
||||
try {
|
||||
$record_id = $this->deploy_common($config['product'], $cert_id, $instance_id);
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['record_id'] = $record_id;
|
||||
} catch (Exception $e) {
|
||||
if (isset($info['record_id'])) {
|
||||
@@ -281,6 +286,95 @@ class tencent implements DeployInterface
|
||||
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function update_cert($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['cert_id'])) throw new Exception('证书ID不能为空');
|
||||
|
||||
$param = [
|
||||
'CertificateIds' => [$config['cert_id']],
|
||||
'IsCache' => 1,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('CreateCertificateBindResourceSyncTask', $param);
|
||||
if (empty($data['CertTaskIds'])) throw new Exception('返回任务ID为空');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('创建关联云资源查询任务失败:' . $e->getMessage());
|
||||
}
|
||||
$task_id = $data['CertTaskIds'][0]['TaskId'];
|
||||
$this->log('创建关联云资源查询任务成功 TaskId=' . $task_id);
|
||||
|
||||
$retry = 0;
|
||||
$resource_result = null;
|
||||
while ($retry++ < 30) {
|
||||
sleep(2);
|
||||
$param = [
|
||||
'TaskIds' => [$task_id],
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('DescribeCertificateBindResourceTaskResult', $param);
|
||||
if (empty($data['SyncTaskBindResourceResult'])) throw new Exception('返回结果为空');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查询关联云资源任务结果失败:' . $e->getMessage());
|
||||
}
|
||||
$taskResult = $data['SyncTaskBindResourceResult'][0];
|
||||
if ($taskResult['Status'] == 1) {
|
||||
$resource_result = $taskResult['BindResourceResult'];
|
||||
break;
|
||||
} elseif ($taskResult['Status'] == 2) {
|
||||
throw new Exception('关联云资源查询任务执行失败:' . isset($taskResult['Error']) ? $taskResult['Error']['Message'] : '未知错误');
|
||||
}
|
||||
};
|
||||
if (!$resource_result) {
|
||||
throw new Exception('关联云资源查询任务超时未完成,请稍后重试');
|
||||
}
|
||||
|
||||
$resourceTypes = [];
|
||||
$resourceTypesRegions = [];
|
||||
foreach ($resource_result as $res) {
|
||||
if ($res['ResourceType'] != 'clb') continue;
|
||||
$totalCount = 0;
|
||||
$regions = [];
|
||||
foreach ($res['BindResourceRegionResult'] as $regionRes) {
|
||||
if ($regionRes['TotalCount'] > 0) {
|
||||
$totalCount += $regionRes['TotalCount'];
|
||||
if (!empty($regionRes['Region'])) {
|
||||
$regions[] = $regionRes['Region'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($totalCount > 0) {
|
||||
$resourceTypes[] = $res['ResourceType'];
|
||||
if (!empty($regions)) {
|
||||
$resourceTypesRegions[] = [
|
||||
'ResourceType' => $res['ResourceType'],
|
||||
'Regions' => $regions,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'OldCertificateId' => $config['cert_id'],
|
||||
'CertificatePublicKey' => $fullchain,
|
||||
'CertificatePrivateKey' => $privatekey,
|
||||
'ResourceTypes' => $resourceTypes,
|
||||
'ResourceTypesRegions' => $resourceTypesRegions,
|
||||
];
|
||||
$retry = 0;
|
||||
while ($retry++ < 10) {
|
||||
try {
|
||||
$data = $this->client->request('UploadUpdateCertificateInstance', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('更新证书内容失败:' . $e->getMessage());
|
||||
}
|
||||
if ($data['DeployStatus'] == 1) {
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
$this->log('更新证书内容成功,可能需要一些时间完成各资源的证书更新部署');
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
|
||||
@@ -31,9 +31,15 @@ class upyun implements DeployInterface
|
||||
$this->login();
|
||||
|
||||
$url = 'https://console.upyun.com/api/https/certificate/';
|
||||
// 如果是 EC 证书,调整私钥头为 EC PRIVATE KEY
|
||||
$privatekey_send = $privatekey;
|
||||
if ($this->isEcCertificate($fullchain)) {
|
||||
$privatekey_send = str_replace('-----BEGIN PRIVATE KEY-----', '-----BEGIN EC PRIVATE KEY-----', $privatekey_send);
|
||||
$privatekey_send = str_replace('-----END PRIVATE KEY-----', '-----END EC PRIVATE KEY-----', $privatekey_send);
|
||||
}
|
||||
$params = [
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
'private_key' => $privatekey_send,
|
||||
];
|
||||
$response = http_request($url, http_build_query($params), null, $this->cookie, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
@@ -86,8 +92,11 @@ class upyun implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
if ($i == 0) throw new Exception('未找到可迁移的证书');
|
||||
$this->log('共迁移' . $i . '个证书,关联域名' . $d . '个');
|
||||
if ($i == 0) {
|
||||
$this->log('未找到可迁移的证书');
|
||||
} else {
|
||||
$this->log('共迁移' . $i . '个证书,关联域名' . $d . '个');
|
||||
}
|
||||
}
|
||||
|
||||
private function login()
|
||||
@@ -130,4 +139,22 @@ class upyun implements DeployInterface
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为 EC (ECDSA) 证书
|
||||
*/
|
||||
private function isEcCertificate($fullchain)
|
||||
{
|
||||
// 提取第一个证书
|
||||
if (!preg_match('/-----BEGIN CERTIFICATE-----\s*(.+?)\s*-----END CERTIFICATE-----/s', $fullchain, $m)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$pubKey = openssl_pkey_get_public($m[0]);
|
||||
if (!$pubKey) return false;
|
||||
|
||||
$details = openssl_pkey_get_details($pubKey);
|
||||
|
||||
return $details && ($details['type'] ?? 0) === OPENSSL_KEYTYPE_EC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ class aliyun implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->AccessKeySecret = $config['sk'];
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->AccessKeySecret = $config['AccessKeySecret'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $this->Endpoint, $this->Version, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
|
||||
284
app/lib/dns/aliyunesa.php
Normal file
284
app/lib/dns/aliyunesa.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\Aliyun as AliyunClient;
|
||||
use Exception;
|
||||
|
||||
class aliyunesa implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint = 'esa.cn-hangzhou.aliyuncs.com'; //API接入域名
|
||||
private $Version = '2024-09-10'; //API版本号
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private AliyunClient $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->AccessKeySecret = $config['AccessKeySecret'];
|
||||
if (!empty($config['region'])) {
|
||||
$this->Endpoint = 'esa.'.$config['region'].'.aliyuncs.com';
|
||||
}
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $this->Endpoint, $this->Version, $proxy);
|
||||
$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 = ['Action' => 'ListSites', 'SiteName' => $KeyWord, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'AccessType' => 'NS'];
|
||||
$data = $this->request($param, 'GET', true);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['Sites'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['SiteId'],
|
||||
'Domain' => $row['SiteName'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
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' => 'ListRecords', 'SiteId' => $this->domainid, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$RecordName = $SubDomain == '@' ? $this->domain : $SubDomain . '.' . $this->domain;
|
||||
$param += ['RecordName' => $RecordName];
|
||||
} elseif (!isNullOrEmpty($KeyWord)) {
|
||||
$RecordName = $KeyWord == '@' ? $this->domain : $KeyWord . '.' . $this->domain;
|
||||
$param += ['RecordName' => $RecordName];
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) {
|
||||
if ($Type == 'A' || $Type == 'AAAA') $Type = 'A/AAAA';
|
||||
$param += ['Type' => $Type];
|
||||
}
|
||||
if (!isNullOrEmpty($Line)) {
|
||||
$param += ['Proxied' => $Line == '1' ? 'true' : 'false'];
|
||||
}
|
||||
$data = $this->request($param, 'GET', true);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['Records'] as $row) {
|
||||
$name = substr($row['RecordName'], 0, - (strlen($this->domain) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
$value = $row['Data']['Value'];
|
||||
if ($row['RecordType'] == 'CAA') $value = $row['Data']['Flag'] . ' ' . $row['Data']['Tag'] . ' ' . $row['Data']['Value'];
|
||||
else if ($row['RecordType'] == 'SRV') $value = $row['Data']['Priority'] . ' ' . $row['Data']['Weight'] . ' ' . $row['Data']['Port'] . ' ' . $row['Data']['Value'];
|
||||
if ($row['RecordType'] == 'A/AAAA') {
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$row['RecordType'] = 'A';
|
||||
} elseif (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$row['RecordType'] = 'AAAA';
|
||||
}
|
||||
}
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['RecordType'],
|
||||
'Value' => $value,
|
||||
'Line' => $row['Proxied'] ? '1' : '0',
|
||||
'TTL' => $row['Ttl'],
|
||||
'MX' => isset($row['Data']['Priority']) ? $row['Data']['Priority'] : null,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => isset($row['Comment']) ? $row['Comment'] : null,
|
||||
'UpdateTime' => isset($row['UpdateTime']) ? date('Y-m-d H:i:s', strtotime($row['UpdateTime'])) : null,
|
||||
];
|
||||
}
|
||||
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)
|
||||
{
|
||||
$param = ['Action' => 'GetRecord', 'RecordId' => $RecordId];
|
||||
$data = $this->request($param, 'GET', true);
|
||||
if ($data) {
|
||||
$row = $data['RecordModel'];
|
||||
$name = substr($row['RecordName'], 0, - (strlen($this->domain) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
$value = $row['Data']['Value'];
|
||||
if ($row['RecordType'] == 'CAA') $value = $row['Data']['Flag'] . ' ' . $row['Data']['Tag'] . ' ' . $row['Data']['Value'];
|
||||
else if ($row['RecordType'] == 'SRV') $value = $row['Data']['Priority'] . ' ' . $row['Data']['Weight'] . ' ' . $row['Data']['Port'] . ' ' . $row['Data']['Value'];
|
||||
if ($row['RecordType'] == 'A/AAAA') {
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$row['RecordType'] = 'A';
|
||||
} elseif (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$row['RecordType'] = 'AAAA';
|
||||
}
|
||||
}
|
||||
return [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['RecordType'],
|
||||
'Value' => $value,
|
||||
'Line' => $row['Proxied'] ? '1' : '0',
|
||||
'TTL' => $row['Ttl'],
|
||||
'MX' => isset($row['Data']['Priority']) ? $row['Data']['Priority'] : null,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => isset($row['Comment']) ? $row['Comment'] : null,
|
||||
'UpdateTime' => isset($row['UpdateTime']) ? date('Y-m-d H:i:s', strtotime($row['UpdateTime'])) : null,
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Weight = null, $Remark = null)
|
||||
{
|
||||
if ($Name == '@') {
|
||||
$Name = $this->domain;
|
||||
} else {
|
||||
$Name = $Name . '.' . $this->domain;
|
||||
}
|
||||
if ($Type == 'A' || $Type == 'AAAA') $Type = 'A/AAAA';
|
||||
$data = ['Value' => $Value];
|
||||
if ($Type == 'CAA') {
|
||||
list($flag, $tag, $val) = explode(' ', $Value, 3);
|
||||
$data = ['Flag' => intval($flag), 'Tag' => $tag, 'Value' => $val];
|
||||
} elseif ($Type == 'SRV') {
|
||||
list($priority, $weight, $port, $val) = explode(' ', $Value, 4);
|
||||
$data = ['Priority' => intval($priority), 'Weight' => intval($weight), 'Port' => intval($port), 'Value' => $val];
|
||||
} elseif ($Type == 'MX') {
|
||||
$data['Priority'] = intval($MX);
|
||||
}
|
||||
$param = ['Action' => 'CreateRecord', 'SiteId' => $this->domainid, 'RecordName' => $Name, 'Type' => $Type, 'Proxied' => $Line == '1' ? 'true' : 'false', 'Ttl' => intval($TTL), 'Data' => json_encode($data), 'Comment' => $Remark];
|
||||
if ($Line == '1') $param['BizName'] = 'web';
|
||||
$data = $this->request($param, 'POST', 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)
|
||||
{
|
||||
if ($Name == '@') {
|
||||
$Name = $this->domain;
|
||||
} else {
|
||||
$Name = $Name . '.' . $this->domain;
|
||||
}
|
||||
if ($Type == 'A' || $Type == 'AAAA') $Type = 'A/AAAA';
|
||||
$data = ['Value' => $Value];
|
||||
if ($Type == 'CAA') {
|
||||
list($flag, $tag, $val) = explode(' ', $Value, 3);
|
||||
$data = ['Flag' => intval($flag), 'Tag' => $tag, 'Value' => $val];
|
||||
} elseif ($Type == 'SRV') {
|
||||
list($priority, $weight, $port, $val) = explode(' ', $Value, 4);
|
||||
$data = ['Priority' => intval($priority), 'Weight' => intval($weight), 'Port' => intval($port), 'Value' => $val];
|
||||
} elseif ($Type == 'MX') {
|
||||
$data['Priority'] = intval($MX);
|
||||
}
|
||||
$param = ['Action' => 'UpdateRecord', 'RecordId' => $RecordId, 'Type' => $Type, 'Proxied' => $Line == '1' ? 'true' : 'false', 'Ttl' => intval($TTL), 'Data' => json_encode($data), 'Comment' => $Remark];
|
||||
if ($Line == '1') $param['BizName'] = 'web';
|
||||
return $this->request($param, 'POST');
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['Action' => 'DeleteRecord', 'RecordId' => $RecordId];
|
||||
return $this->request($param, 'POST');
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return ['0' => ['name' => '仅DNS', 'parent' => null], '1' => ['name' => '已代理', 'parent' => null]];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$param = ['Action' => 'GetSite', 'SiteId' => $this->domainid];
|
||||
$data = $this->request($param, 'GET', true);
|
||||
if ($data) {
|
||||
return $data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function request($param, $method, $returnData = false)
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false;
|
||||
try {
|
||||
$result = $this->client->request($param, $method);
|
||||
} catch (Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return $returnData ? $result : true;
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ class baidu implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
|
||||
276
app/lib/dns/bt.php
Normal file
276
app/lib/dns/bt.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class bt implements DnsInterface
|
||||
{
|
||||
private $accountId;
|
||||
private $accessKey;
|
||||
private $secretKey;
|
||||
private $baseUrl = 'https://dmp.bt.cn';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainType;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->accountId = $config['AccountID'];
|
||||
$this->accessKey = $config['AccessKey'];
|
||||
$this->secretKey = $config['SecretKey'];
|
||||
$this->domain = $config['domain'];
|
||||
if ($config['domainid']) {
|
||||
$a = explode('|', $config['domainid']);
|
||||
$this->domainid = intval($a[0]);
|
||||
$this->domainType = isset($a[1]) ? intval($a[1]) : 1;
|
||||
}
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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 = ['p' => $PageNumber, 'rows' => $PageSize, 'keyword' => $KeyWord];
|
||||
$data = $this->execute('/api/v1/dns/manage/list_domains', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['data'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['local_id'] . '|' . $row['domain_type'],
|
||||
'Domain' => $row['full_domain'],
|
||||
'RecordCount' => $row['record_count'],
|
||||
];
|
||||
}
|
||||
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 = ['domain_id' => $this->domainid, 'domain_type' => $this->domainType, 'p' => $PageNumber, 'rows' => $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$param['searchKey'] = 'record';
|
||||
$param['searchValue'] = $SubDomain;
|
||||
} elseif (!isNullOrEmpty($KeyWord)) {
|
||||
$param['searchKey'] = 'record';
|
||||
$param['searchValue'] = $KeyWord;
|
||||
} elseif (!isNullOrEmpty($Value)) {
|
||||
$param['searchKey'] = 'value';
|
||||
$param['searchValue'] = $Value;
|
||||
} elseif (!isNullOrEmpty($Type)) {
|
||||
$param['searchKey'] = 'type';
|
||||
$param['searchValue'] = $Type;
|
||||
} elseif (!isNullOrEmpty($Status)) {
|
||||
$param['searchKey'] = 'state';
|
||||
$param['searchValue'] = $Status == '0' ? '1' : '0';
|
||||
} elseif (!isNullOrEmpty($Line)) {
|
||||
$param['searchKey'] = 'line';
|
||||
$param['searchValue'] = $Line;
|
||||
}
|
||||
$data = $this->execute('/api/v1/dns/record/list', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['data'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['record_id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['record'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['viewID'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => $row['MX'],
|
||||
'Status' => $row['state'] == 1 ? '0' : '1',
|
||||
'Weight' => $row['MX'],
|
||||
'Remark' => $row['remark'],
|
||||
'UpdateTime' => date('Y-m-d H:i:s', strtotime($row['created_at'])),
|
||||
];
|
||||
}
|
||||
return ['total' => $data['count'], '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 = ['domain_id' => $this->domainid, 'domain_type' => $this->domainType, 'type' => $Type, 'record' => $Name, 'value' => $Value, 'ttl' => intval($TTL), 'view_id' => intval($Line), 'remark' => $Remark];
|
||||
if (!$Weight) $Weight = 1;
|
||||
if ($Type == 'MX') $param['mx'] = intval($MX);
|
||||
else $param['mx'] = intval($Weight);
|
||||
$data = $this->execute('/api/v1/dns/record/create', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['record_id' => $RecordId, 'domain_id' => $this->domainid, 'domain_type' => $this->domainType, 'type' => $Type, 'record' => $Name, 'value' => $Value, 'ttl' => intval($TTL), 'view_id' => intval($Line), 'remark' => $Remark];
|
||||
if (!$Weight) $Weight = 1;
|
||||
if ($Type == 'MX') $param['mx'] = intval($MX);
|
||||
else $param['mx'] = intval($Weight);
|
||||
$data = $this->execute('/api/v1/dns/record/update', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['id' => $RecordId, 'domain_id' => $this->domainid, 'domain_type' => $this->domainType];
|
||||
$data = $this->execute('/api/v1/dns/record/delete', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$param = ['record_id' => $RecordId, 'domain_id' => $this->domainid, 'domain_type' => $this->domainType];
|
||||
$data = $this->execute($Status == '0' ? '/api/v1/dns/record/pause' : '/api/v1/dns/record/start', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$param = [];
|
||||
$data = $this->execute('/api/v1/dns/record/get_views', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$this->processLineList($list, $data, null);
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if ($row['free'] && !isset($list[$row['viewId']])) {
|
||||
$list[$row['viewId']] = ['name' => $row['name'], 'parent' => $parent];
|
||||
if ($row['children']) {
|
||||
$this->processLineList($list, $row['children'], $row['viewId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$param = ['full_domain' => $Domain];
|
||||
$data = $this->execute('/api/v1/dns/manage/add_external_domain', $param);
|
||||
if ($data) {
|
||||
return ['id' => $data['domain_id'], 'name' => $data['full_domain']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function execute($path, $params)
|
||||
{
|
||||
$method = 'POST';
|
||||
$timestamp = (string)time();
|
||||
$body = json_encode($params);
|
||||
$signingString = implode("\n", [
|
||||
$this->accountId,
|
||||
$timestamp,
|
||||
$method,
|
||||
$path,
|
||||
$body
|
||||
]);
|
||||
$signature = hash_hmac('sha256', $signingString, $this->secretKey);
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Account-ID' => $this->accountId,
|
||||
'X-Access-Key' => $this->accessKey,
|
||||
'X-Timestamp' => $timestamp,
|
||||
'X-Signature' => $signature
|
||||
];
|
||||
$response = $this->curl($method, $path, $headers, $body);
|
||||
if (!$response) {
|
||||
return false;
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['code'] == 0) {
|
||||
return $arr['data'];
|
||||
} else {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function curl($method, $path, $header, $body = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $header, $this->proxy, $method);
|
||||
} catch (\Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return $response['body'];
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ class cloudflare implements DnsInterface
|
||||
{
|
||||
private $Email;
|
||||
private $ApiKey;
|
||||
private $auth;
|
||||
private $baseUrl = 'https://api.cloudflare.com/client/v4';
|
||||
private $error;
|
||||
private $domain;
|
||||
@@ -16,11 +17,12 @@ class cloudflare implements DnsInterface
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->Email = $config['ak'];
|
||||
$this->ApiKey = $config['sk'];
|
||||
$this->Email = $config['email'];
|
||||
$this->ApiKey = $config['apikey'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->auth = isset($config['auth']) ? intval($config['auth']) : (preg_match('/^[0-9a-f]+$/i', $this->ApiKey) ? 0 : 1);
|
||||
}
|
||||
|
||||
public function getError()
|
||||
@@ -75,9 +77,12 @@ class cloudflare implements DnsInterface
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$name = $this->domain == $row['name'] ? '@' : str_replace('.'.$this->domain, '', $row['name']);
|
||||
$name = $this->domain == $row['name'] ? '@' : substr($row['name'], 0, -(strlen($this->domain) + 1));
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
if ($row['type'] == 'SRV' && isset($row['priority'])) {
|
||||
$row['content'] = $row['priority'] . ' ' . $row['content'];
|
||||
}
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
@@ -109,9 +114,12 @@ class cloudflare implements DnsInterface
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
if ($data) {
|
||||
$name = $this->domain == $data['result']['name'] ? '@' : str_replace('.' . $this->domain, '', $data['result']['name']);
|
||||
$name = $this->domain == $data['result']['name'] ? '@' : substr($data['result']['name'], 0, -(strlen($this->domain) + 1));
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
if ($data['result']['type'] == 'SRV' && isset($data['result']['priority'])) {
|
||||
$data['result']['content'] = $data['result']['priority'] . ' ' . $data['result']['content'];
|
||||
}
|
||||
return [
|
||||
'RecordId' => $data['result']['id'],
|
||||
'Domain' => $this->domain,
|
||||
@@ -257,14 +265,14 @@ class cloudflare implements DnsInterface
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
|
||||
if (preg_match('/^[0-9a-z]+$/i', $this->ApiKey)) {
|
||||
if ($this->auth == 0) {
|
||||
$headers = [
|
||||
'X-Auth-Email: ' . $this->Email,
|
||||
'X-Auth-Key: ' . $this->ApiKey,
|
||||
'X-Auth-Email' => $this->Email,
|
||||
'X-Auth-Key' => $this->ApiKey,
|
||||
];
|
||||
} else {
|
||||
$headers = [
|
||||
'Authorization: Bearer ' . $this->ApiKey,
|
||||
'Authorization' => 'Bearer ' . $this->ApiKey,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -275,39 +283,17 @@ class cloudflare implements DnsInterface
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
if ($this->proxy) {
|
||||
curl_set_proxy($ch);
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
} catch (\Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PUT') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PATCH') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'DELETE') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
}
|
||||
$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);
|
||||
$arr = json_decode($response['body'], true);
|
||||
if ($arr) {
|
||||
if ($arr['success']) {
|
||||
return $arr;
|
||||
|
||||
@@ -17,8 +17,8 @@ class dnsla implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apiid = $config['ak'];
|
||||
$this->apisecret = $config['sk'];
|
||||
$this->apiid = $config['apiid'];
|
||||
$this->apisecret = $config['apisecret'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
@@ -60,19 +60,19 @@ class dnsla implements DnsInterface
|
||||
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))) {
|
||||
if (!isNullOrEmpty($KeyWord)) {
|
||||
$param['host'] = $KeyWord;
|
||||
}
|
||||
if (!isNullOrEmpty(($Type))) {
|
||||
if (!isNullOrEmpty($Type)) {
|
||||
$param['type'] = $this->convertType($Type);
|
||||
}
|
||||
if (!isNullOrEmpty(($Line))) {
|
||||
if (!isNullOrEmpty($Line)) {
|
||||
$param['lineId'] = $Line;
|
||||
}
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
if (!isNullOrEmpty(($Value))) {
|
||||
if (!isNullOrEmpty($Value)) {
|
||||
$param['data'] = $Value;
|
||||
}
|
||||
$data = $this->execute('GET', '/api/recordList', $param);
|
||||
@@ -235,7 +235,10 @@ class dnsla implements DnsInterface
|
||||
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'];
|
||||
$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 {
|
||||
@@ -264,34 +267,19 @@ class dnsla implements DnsInterface
|
||||
private function curl($method, $path, $header, $body = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
$ch = curl_init($url);
|
||||
if ($this->proxy) {
|
||||
curl_set_proxy($ch);
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $header, $this->proxy, $method);
|
||||
} catch (\Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
if ($response['code'] == 200) {
|
||||
return $response['body'];
|
||||
} elseif ($response['code'] == 401) {
|
||||
$this->setError('认证失败');
|
||||
return false;
|
||||
} else {
|
||||
$this->setError('http code: '.$httpCode);
|
||||
$this->setError('http code: '.$response['code']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ class dnspod implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['ak'];
|
||||
$this->SecretKey = $config['sk'];
|
||||
$this->SecretId = $config['SecretId'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, $this->endpoint, $this->service, $this->version, null, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
|
||||
@@ -18,8 +18,8 @@ class huawei implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
@@ -77,12 +77,13 @@ class huawei implements DnsInterface
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['recordsets'] as $row) {
|
||||
if ($row['name'] == $row['zone_name']) $row['name'] = '@';
|
||||
$name = substr($row['name'], 0, -(strlen($row['zone_name']) + 1));
|
||||
if ($name == '') $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']),
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['records'],
|
||||
'Line' => $row['line'],
|
||||
@@ -110,12 +111,13 @@ class huawei implements DnsInterface
|
||||
{
|
||||
$data = $this->send_request('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
|
||||
if ($data) {
|
||||
if ($data['name'] == $data['zone_name']) $data['name'] = '@';
|
||||
$name = substr($data['name'], 0, -(strlen($data['zone_name']) + 1));
|
||||
if ($name == '') $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']),
|
||||
'Name' => $name,
|
||||
'Type' => $data['type'],
|
||||
'Value' => $data['records'],
|
||||
'Line' => $data['line'],
|
||||
|
||||
@@ -30,8 +30,8 @@ class huoshan implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $this->service, $this->version, $this->region, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
|
||||
@@ -23,8 +23,8 @@ class jdcloud implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->AccessKeySecret = $config['sk'];
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->AccessKeySecret = $config['AccessKeySecret'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new JdcloudClient($this->AccessKeyId, $this->AccessKeySecret, $this->endpoint, $this->service, $this->region, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
|
||||
@@ -16,7 +16,7 @@ class namesilo implements DnsInterface
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->apikey = $config['sk'];
|
||||
$this->apikey = $config['apikey'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
@@ -63,11 +63,10 @@ class namesilo implements DnsInterface
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['resource_record'] as $row) {
|
||||
$name = $row['host'] == $this->domain ? '@' : str_replace('.'.$this->domain, '', $row['host']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['record_id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Name' => $row['host'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => 'default',
|
||||
|
||||
@@ -17,8 +17,8 @@ class powerdns implements DnsInterface
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->url = 'http://' . $config['ak'] . ':' . $config['sk'] . '/api/v1';
|
||||
$this->apikey = $config['ext'];
|
||||
$this->url = 'http://' . $config['ip'] . ':' . $config['port'] . '/api/v1';
|
||||
$this->apikey = $config['apikey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
|
||||
387
app/lib/dns/qingcloud.php
Normal file
387
app/lib/dns/qingcloud.php
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class qingcloud implements DnsInterface
|
||||
{
|
||||
private $access_key_id;
|
||||
private $secret_access_key;
|
||||
private $baseUrl = 'http://api.routewize.com';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->access_key_id = $config['access_key_id'];
|
||||
$this->secret_access_key = $config['secret_access_key'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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;
|
||||
$param = ['offset' => $offset, 'limit' => $PageSize];
|
||||
if (!empty($KeyWord)) {
|
||||
$param['zone_name'] = $KeyWord;
|
||||
}
|
||||
$data = $this->execute('GET', '/v1/user/zones', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['zone_name'],
|
||||
'Domain' => rtrim($row['zone_name'], '.'),
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['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)
|
||||
{
|
||||
if ($SubDomain) {
|
||||
return $this->getHostRecords($SubDomain);
|
||||
}
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$param = ['zone_name' => $this->domainid, 'offset' => $offset, 'limit' => $PageSize];
|
||||
if (!isNullOrEmpty($KeyWord)) {
|
||||
$param['search_word'] = $KeyWord;
|
||||
}
|
||||
$data = $this->execute('GET', '/v1/dns/host/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['domains'] as $row) {
|
||||
$name = substr($row['domain_name'], 0, -(strlen($row['zone_name']) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
$list[] = [
|
||||
'RecordId' => $row['domain_name'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => null,
|
||||
'Value' => null,
|
||||
'Line' => null,
|
||||
'TTL' => null,
|
||||
'MX' => null,
|
||||
'Status' => $row['status'] == 'enabled' ? '0' : '1',
|
||||
'Weight' => null,
|
||||
'Remark' => $row['description'],
|
||||
'UpdateTime' => $row['create_time'],
|
||||
'Count' => $row['count'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getHostRecords($SubDomain)
|
||||
{
|
||||
$param = ['zone_name' => $this->domainid, 'domain_name' => $SubDomain];
|
||||
$data = $this->execute('GET', '/v1/dns/host_info/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['records'] as $record) {
|
||||
$name = substr($record['domain_name'], 0, -(strlen($record['zone_name']) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
foreach ($record['record'] as $record_group) {
|
||||
foreach ($record_group['data'] as $row) {
|
||||
$mx = null;
|
||||
if ($record['rd_type'] == 'MX') {
|
||||
$value = explode(' ', $row['value'], 2);
|
||||
$row['value'] = isset($value[1]) ? $value[1] : '';
|
||||
$mx = intval($value[0]);
|
||||
}
|
||||
if ($record['rd_type'] == 'TXT') {
|
||||
$row['value'] = trim($row['value'], '"');
|
||||
}
|
||||
$list[] = [
|
||||
'RecordId' => $record['domain_record_id'].'_'.$row['record_value_id'],
|
||||
'Domain' => $record['domain_name'],
|
||||
'Name' => $name,
|
||||
'Type' => $record['rd_type'],
|
||||
'Mode' => $record['mode'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $record['view_id'],
|
||||
'TTL' => $record['ttl'],
|
||||
'MX' => $mx,
|
||||
'Status' => $row['status'] == 1 ? '1' : '0',
|
||||
'Weight' => $record_group['weight'] > 0 ? $record_group['weight'] : null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => $record['create_time'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['total' => $data['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
$SubDomain = $this->getHost($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)
|
||||
{
|
||||
$mode = input('post.mode', '1');
|
||||
|
||||
if ($Type == 'MX') {
|
||||
$Value = intval($MX).' '.$Value;
|
||||
} elseif ($Type == 'TXT' && substr($Value, 0, 1) != '"') {
|
||||
$Value = '"'.$Value.'"';
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach (explode(',', $Value) as $val) {
|
||||
$values[] = ['value' => trim($val), 'status' => 1];
|
||||
}
|
||||
if (($Type == 'A' || $Type == 'CNAME') && $mode == '3') $Weight = intval($Weight);
|
||||
else $Weight = 0;
|
||||
$record = [['weight' => $Weight, 'values' => $values]];
|
||||
|
||||
$param = ['zone_name' => $this->domainid, 'domain_name' => $Name, 'view_id' => intval($Line), 'type' => $Type, 'ttl' => intval($TTL), 'record' => json_encode($record), 'mode' => intval($mode), 'auto_merge' => 2];
|
||||
$data = $this->execute('POST', '/v1/record/', $param);
|
||||
return is_array($data) ? $data['domain_record_id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$mode = input('post.mode', '1');
|
||||
|
||||
if ($Type == 'MX') {
|
||||
$Value = intval($MX).' '.$Value;
|
||||
} elseif ($Type == 'TXT' && substr($Value, 0, 1) != '"') {
|
||||
$Value = '"'.$Value.'"';
|
||||
}
|
||||
|
||||
$recordId = explode('_', $RecordId);
|
||||
$domain_record_id = $recordId[0];
|
||||
$record_value_id = $recordId[1];
|
||||
$data = $this->execute('GET', '/v1/dr_id/'.$domain_record_id);
|
||||
if (!$data) return false;
|
||||
|
||||
if (($Type == 'A' || $Type == 'CNAME') && $mode == '3') $Weight = intval($Weight);
|
||||
else $Weight = 0;
|
||||
$record = [];
|
||||
foreach ($data['data']['record'] as $record_group) {
|
||||
$values = [];
|
||||
$flag = false;
|
||||
foreach ($record_group['data'] as $row) {
|
||||
if ($row['record_value_id'] == $record_value_id) {
|
||||
$row['value'] = $Value;
|
||||
$flag = true;
|
||||
}
|
||||
$values[] = ['value' => $row['value'], 'status' => $row['status']];
|
||||
}
|
||||
if (count($values) > 0) {
|
||||
$record[] = ['weight' => $flag ? $Weight : $record_group['weight'], 'values' => $values];
|
||||
}
|
||||
}
|
||||
|
||||
$param = ['zone_name' => $this->domainid, 'domain_name' => $Name, 'view_id' => intval($Line), 'type' => $Type, 'ttl' => intval($TTL), 'record' => json_encode($record), 'mode' => intval($mode)];
|
||||
$data = $this->execute('POST', '/v1/dr_id/'.$domain_record_id, $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
$param = ['zone_name' => $this->domainid, 'domain_name' => $RecordId, 'description' => $Remark];
|
||||
$data = $this->execute('POST', '/v1/dns/host/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
if (strpos($RecordId, $this->domainid) !== false) {
|
||||
$param = ['domain_names' => json_encode([$RecordId]), 'zone_name' => $this->domainid];
|
||||
$data = $this->execute('DELETE', '/v1/domain/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
$recordId = explode('_', $RecordId);
|
||||
$domain_record_id = $recordId[0];
|
||||
$record_value_id = $recordId[1];
|
||||
$data = $this->execute('GET', '/v1/dr_id/'.$domain_record_id);
|
||||
if (!$data) return false;
|
||||
|
||||
$record = [];
|
||||
foreach ($data['data']['record'] as $record_group) {
|
||||
$values = [];
|
||||
foreach ($record_group['data'] as $row) {
|
||||
if ($row['record_value_id'] == $record_value_id) {
|
||||
continue;
|
||||
}
|
||||
$values[] = ['value' => $row['value'], 'status' => $row['status']];
|
||||
}
|
||||
if (count($values) > 0) {
|
||||
$record[] = ['weight' => $record_group['weight'], 'values' => $values];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($record) == 0) {
|
||||
$param = ['ids' => json_encode([$domain_record_id]), 'target' => 'record', 'action' => 'delete'];
|
||||
$data = $this->execute('POST', '/v1/change_record_status/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
$name = substr($data['data']['domain_name'], 0, -(strlen($data['data']['zone_name']) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
$param = ['zone_name' => $this->domainid, 'domain_name' => $name, 'view_id' => $data['data']['view_id'], 'type' => $data['data']['rd_type'], 'ttl' => $data['data']['ttl'], 'record' => json_encode($record), 'mode' => $data['data']['mode']];
|
||||
$data = $this->execute('POST', '/v1/dr_id/'.$domain_record_id, $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$recordId = explode('_', $RecordId);
|
||||
$record_value_id = $recordId[1];
|
||||
$param = ['ids' => json_encode([$record_value_id]), 'target' => 'value', 'action' => $Status == '0' ? 'stop' : 'enable'];
|
||||
$data = $this->execute('POST', '/v1/change_record_status/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$param = ['zone_name' => $this->domainid, 'type' => 'GET_FULL'];
|
||||
$data = $this->execute('GET', '/v1/zone/view/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zone_views'] as $row) {
|
||||
if ($row['name'] == '*') $row['name'] = '默认';
|
||||
$list[$row['id']] = ['name' => $row['name'], 'parent' => null];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$param = ['zone_name' => $Domain];
|
||||
$data = $this->execute('POST', '/v1/zone/', $param);
|
||||
if ($data) {
|
||||
return ['id' => $data['zone_name'], 'name' => $Domain];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getHost($Name)
|
||||
{
|
||||
if ($Name == '@' || $Name == '') $Name = '';
|
||||
else $Name .= '.';
|
||||
$Name .= $this->domain . '.';
|
||||
return $Name;
|
||||
}
|
||||
|
||||
private function execute($method, $path, $params = null)
|
||||
{
|
||||
$date = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$string_to_sign = $method."\n".$date."\n".$path;
|
||||
if($method == 'GET' && $params){
|
||||
ksort($params);
|
||||
$string_to_sign .= '?'.http_build_query($params);
|
||||
}
|
||||
$signature = base64_encode(hash_hmac('sha256', $string_to_sign, $this->secret_access_key, true));
|
||||
$authorization = 'QC-HMAC-SHA256 '.$this->access_key_id.':'.$signature;
|
||||
$header = [
|
||||
'Authorization' => $authorization,
|
||||
'Date' => $date,
|
||||
];
|
||||
if ($method == 'POST' || $method == 'PUT' || $method == 'DELETE') {
|
||||
$header['Content-Type'] = 'application/json; charset=utf-8';
|
||||
$response = $this->curl($method, $path, $header, json_encode($params));
|
||||
} else {
|
||||
if ($params) {
|
||||
$path .= '?'.http_build_query($params);
|
||||
}
|
||||
$response = $this->curl($method, $path, $header);
|
||||
}
|
||||
$arr = json_decode($response['body'], true);
|
||||
if (isset($arr['code']) && $arr['code'] == 0 || isset($arr['domains']) || $method == 'DELETE' && $response['code'] == 204) {
|
||||
return $arr;
|
||||
} elseif(isset($arr['message'])) {
|
||||
$this->setError($arr['message']);
|
||||
return false;
|
||||
} elseif(isset($arr['msg'])) {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function curl($method, $path, $header, $body = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $header, $this->proxy, $method);
|
||||
} catch (\Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
294
app/lib/dns/spaceship.php
Normal file
294
app/lib/dns/spaceship.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
/**
|
||||
* @see https://docs.spaceship.dev/
|
||||
*/
|
||||
class spaceship implements DnsInterface
|
||||
{
|
||||
private $apiKey;
|
||||
private $apiSecret;
|
||||
private $baseUrl = 'https://spaceship.dev/api/v1';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apiKey = $config['apikey'];
|
||||
$this->apiSecret = $config['apisecret'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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 = 100)
|
||||
{
|
||||
$param = ['take' => $PageSize, 'skip' => ($PageNumber - 1) * $PageSize];
|
||||
$data = $this->send_reuqest('GET', '/domains', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['name'],
|
||||
'Domain' => $row['name'],
|
||||
'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 = ['take' => $PageSize, 'skip' => ($PageNumber - 1) * $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$param['take'] = 100;
|
||||
$param['skip'] = 0;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/dns/records/' . $this->domain, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$type = $row['type'];
|
||||
$name = $row['name'];
|
||||
$mx = 0;
|
||||
if ('MX' == $type) {
|
||||
$address = $row['exchange'];
|
||||
$mx = $row['preference'];
|
||||
} else if ('CNAME' == $type) {
|
||||
$address = $row['cname'];
|
||||
} else if ('TXT' == $type) {
|
||||
$address = $row['value'];
|
||||
} else if ('PTR' == $type) {
|
||||
$address = $row['pointer'];
|
||||
} else if ('NS' == $type) {
|
||||
$address = $row['nameserver'];
|
||||
} else if ('CAA' == $type) {
|
||||
$address = $row['flag'] . ' ' . $row['tag'] . ' ' . $row['value'];
|
||||
} else if ('SRV' == $type) {
|
||||
$address = $row['priority'] . ' ' . $row['weight'] . ' ' . $row['port'] . ' ' . $row['target'];
|
||||
} else if ('ALIAS' == $type) {
|
||||
$address = $row['aliasName'];
|
||||
} else {
|
||||
$address = $row['address'];
|
||||
}
|
||||
|
||||
$list[] = [
|
||||
'RecordId' => $row['type'] . '|' . $name . '|' . $address . '|' . $mx,
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['name'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $address,
|
||||
'TTL' => $row['ttl'],
|
||||
'Line' => 'default',
|
||||
'MX' => $mx,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
if(!isNullOrEmpty($SubDomain)){
|
||||
$list = array_values(array_filter($list, function($v) use ($SubDomain){
|
||||
return strcasecmp($v['Name'], $SubDomain) === 0;
|
||||
}));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private function convertRecordItem($Name, $Type, $Value, $MX)
|
||||
{
|
||||
$item = [
|
||||
'type' => $Type,
|
||||
'name' => $Name,
|
||||
];
|
||||
if ($Type == 'MX') {
|
||||
$item['exchange'] = $Value;
|
||||
$item['preference'] = (int)$MX;
|
||||
} else if ($Type == 'TXT') {
|
||||
$item['value'] = $Value;
|
||||
} else if ($Type == 'CNAME') {
|
||||
$item['cname'] = $Value;
|
||||
} else if ($Type == 'ALIAS') {
|
||||
$item['aliasName'] = $Value;
|
||||
} else if ($Type == 'NS') {
|
||||
$item['nameserver'] = $Value;
|
||||
} else if ($Type == 'PTR') {
|
||||
$item['pointer'] = $Value;
|
||||
} else if ($Type == 'CAA') {
|
||||
$parts = explode(' ', $Value, 3);
|
||||
if (count($parts) >= 3) {
|
||||
$item['flag'] = (int)$parts[0];
|
||||
$item['tag'] = $parts[1];
|
||||
$item['value'] = trim($parts[2], '"');
|
||||
}
|
||||
} else if ($Type == 'SRV') {
|
||||
$parts = explode(' ', $Value, 4);
|
||||
if (count($parts) >= 4) {
|
||||
$item['priority'] = (int)$parts[0];
|
||||
$item['weight'] = (int)$parts[1];
|
||||
$item['port'] = (int)$parts[2];
|
||||
$item['target'] = $parts[3];
|
||||
}
|
||||
} else {
|
||||
$item['address'] = $Value;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$item = $this->convertRecordItem($Name, $Type, $Value, $MX);
|
||||
$item['ttl'] = (int)$TTL;
|
||||
$param = [
|
||||
'force' => false,
|
||||
'items' => [
|
||||
$item
|
||||
]
|
||||
];
|
||||
$data = $this->send_reuqest('PUT', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$item = $this->convertRecordItem($Name, $Type, $Value, $MX);
|
||||
$item['ttl'] = (int)$TTL;
|
||||
$param = [
|
||||
'force' => true,
|
||||
'items' => [
|
||||
$item
|
||||
]
|
||||
];
|
||||
$data = $this->send_reuqest('PUT', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$array = explode("|", $RecordId);
|
||||
$type = $array[0];
|
||||
$name = $array[1];
|
||||
$address = $array[2];
|
||||
$mx = $array[3];
|
||||
$item = $this->convertRecordItem($name, $type, $address, $mx);
|
||||
$param = [$item];
|
||||
$data = $this->send_reuqest('DELETE', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return 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 getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $params = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
$headers = [
|
||||
'X-API-Key' => $this->apiKey,
|
||||
'X-API-Secret' => $this->apiSecret,
|
||||
];
|
||||
$body = '';
|
||||
if ($method == 'GET') {
|
||||
if ($params) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
} catch (\Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
$arr = json_decode($response['body'], true);
|
||||
if ($response['code'] == 200 || $response['code'] == 204) {
|
||||
return $arr;
|
||||
} elseif (isset($arr['detail'])) {
|
||||
$this->setError($arr['detail']);
|
||||
return false;
|
||||
} else {
|
||||
$this->setError('http code: ' . $response['code']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
}
|
||||
}
|
||||
254
app/lib/dns/tencenteo.php
Normal file
254
app/lib/dns/tencenteo.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class tencenteo implements DnsInterface
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $endpoint = "teo.tencentcloudapi.com";
|
||||
private $service = "teo";
|
||||
private $version = "2022-09-01";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private TencentCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['SecretId'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
if (isset($config['site_type']) && $config['site_type'] == 'intl') {
|
||||
$this->endpoint = "teo.intl.tencentcloudapi.com";
|
||||
}
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, $this->endpoint, $this->service, $this->version, null, $proxy);
|
||||
$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)
|
||||
{
|
||||
$action = 'DescribeZones';
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$filters = [['Name' => 'zone-type', 'Values' => ['full']]];
|
||||
if (!isNullOrEmpty($KeyWord)) {
|
||||
$filters[] = ['Name' => 'zone-name', 'Values' => [$KeyWord]];
|
||||
}
|
||||
$param = ['Offset' => $offset, 'Limit' => $PageSize, 'Filters' => $filters];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['Zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['ZoneId'],
|
||||
'Domain' => $row['ZoneName'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
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)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$action = 'DescribeDnsRecords';
|
||||
$filters = [];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$name = $SubDomain == '@' ? $this->domain : $SubDomain . '.' . $this->domain;
|
||||
$filters[] = ['Name' => 'name', 'Values' => [$name]];
|
||||
} elseif (!isNullOrEmpty($KeyWord)) {
|
||||
$name = $KeyWord == '@' ? $this->domain : $KeyWord . '.' . $this->domain;
|
||||
$filters[] = ['Name' => 'name', 'Values' => [$name]];
|
||||
}
|
||||
if (!isNullOrEmpty($Value)) {
|
||||
$filters[] = ['Name' => 'content', 'Values' => [$Value], 'Fuzzy' => true];
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) {
|
||||
$filters[] = ['Name' => 'type', 'Values' => [$Type]];
|
||||
}
|
||||
$param = ['ZoneId' => $this->domainid, 'Offset' => $offset, 'Limit' => $PageSize, 'Filters' => $filters];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['DnsRecords'] as $row) {
|
||||
$name = substr($row['Name'], 0, - (strlen($this->domain) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['Type'],
|
||||
'Value' => $row['Content'],
|
||||
'Line' => $row['Location'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => $row['Priority'],
|
||||
'Status' => $row['Status'] == 'enable' ? '1' : '0',
|
||||
'Weight' => $row['Weight'] == -1 ? null : $row['Weight'],
|
||||
'Remark' => null,
|
||||
'UpdateTime' => $row['ModifiedOn'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['TotalCount'], '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)
|
||||
{
|
||||
$action = 'DescribeDnsRecords';
|
||||
$param = ['ZoneId' => $this->domainid, 'Filters' => [['Name' => 'id', 'Values' => [$RecordId]]]];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$row = $data['DnsRecords'][0];
|
||||
$name = substr($row['Name'], 0, - (strlen($this->domain) + 1));
|
||||
if ($name == '') $name = '@';
|
||||
return [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['Type'],
|
||||
'Value' => $row['Content'],
|
||||
'Line' => $row['Location'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => $row['Priority'],
|
||||
'Status' => $row['Status'] == 'enable' ? '1' : '0',
|
||||
'Weight' => $row['Weight'] == -1 ? null : $row['Weight'],
|
||||
'Remark' => null,
|
||||
'UpdateTime' => $row['ModifiedOn'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$action = 'CreateDnsRecord';
|
||||
if ($Name == '@') {
|
||||
$Name = $this->domain;
|
||||
} else {
|
||||
$Name = $Name . '.' . $this->domain;
|
||||
}
|
||||
$param = ['ZoneId' => $this->domainid, 'Name' => $Name, 'Type' => $Type, 'Content' => $Value, 'Location' => $Line, 'TTL' => intval($TTL), 'Weight' => empty($Weight) ? -1 : intval($Weight)];
|
||||
if ($Type == 'MX') $param['Priority'] = intval($MX);
|
||||
$data = $this->send_request($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 = 'ModifyDnsRecord';
|
||||
if ($Name == '@') {
|
||||
$Name = $this->domain;
|
||||
} else {
|
||||
$Name = $Name . '.' . $this->domain;
|
||||
}
|
||||
$param = ['ZoneId' => $this->domainid, 'DnsRecordId' => $RecordId, 'Name' => $Name, 'Type' => $Type, 'Content' => $Value, 'Location' => $Line, 'TTL' => intval($TTL), 'Weight' => empty($Weight) ? -1 : intval($Weight)];
|
||||
if ($Type == 'MX') $param['Priority'] = intval($MX);
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$action = 'DeleteDnsRecords';
|
||||
$param = ['ZoneId' => $this->domainid, 'RecordIds' => [$RecordId]];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$action = 'ModifyDnsRecordsStatus';
|
||||
$param = ['ZoneId' => $this->domainid];
|
||||
if ($Status == '1') $param['RecordsToEnable'] = [$RecordId];
|
||||
else $param['RecordsToDisable'] = [$RecordId];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
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 getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send_request($action, $param)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($action, $param);
|
||||
}catch(Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ class west implements DnsInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['ak'];
|
||||
$this->api_password = $config['sk'];
|
||||
$this->username = $config['username'];
|
||||
$this->api_password = $config['api_password'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class AuthUser
|
||||
$islogin = false;
|
||||
$cookie = cookie('user_token');
|
||||
$user = null;
|
||||
if ($cookie && config_get('sys_key')) {
|
||||
if ($cookie && config_get('sys_key') && strpos($request->url(), '/install') === false) {
|
||||
$token = authcode($cookie, 'DECODE', config_get('sys_key'));
|
||||
if ($token) {
|
||||
list($type, $uid, $sid, $expiretime) = explode("\t", $token);
|
||||
|
||||
@@ -30,6 +30,16 @@ class LoadConfig
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
if (!checkTableExists('config') && !checkTableExists('user')) {
|
||||
if (strpos($request->url(), '/install') === false) {
|
||||
return redirect((string)url('/install'))->header([
|
||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
]);
|
||||
} else {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
|
||||
@@ -70,8 +70,15 @@ class CertDeployService
|
||||
$this->saveResult(-1, $e->getMessage(), date('Y-m-d H:i:s', time() + (array_key_exists($this->task['retry'], self::$retry_interval) ? self::$retry_interval[$this->task['retry']] : 3600)));
|
||||
throw $e;
|
||||
} finally {
|
||||
if($this->info){
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['info' => json_encode($this->info)]);
|
||||
if ($this->info && is_array($this->info)) {
|
||||
if (isset($this->info['config']) && is_array($this->info['config'])) {
|
||||
$config = array_merge(json_decode($this->task['config'], true), $this->info['config']);
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['config' => json_encode($config)]);
|
||||
unset($this->info['config']);
|
||||
}
|
||||
if (!empty($this->info)) {
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['info' => json_encode($this->info)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class ExpireNoticeService
|
||||
|
||||
private function refreshDomainList()
|
||||
{
|
||||
$domainList = Db::name('domain')->field('id,name')->where('expiretime', null)->where('checkstatus', 0)->select();
|
||||
$domainList = Db::name('domain')->field('id,name')->where('checkstatus', 0)->select();
|
||||
$count = 0;
|
||||
foreach ($domainList as $domain) {
|
||||
$res = $this->updateDomainDate($domain['id'], $domain['name']);
|
||||
|
||||
@@ -132,7 +132,7 @@ class OptimizeService
|
||||
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();
|
||||
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type')->find();
|
||||
if (!$drow) {
|
||||
throw new Exception('域名不存在(ID:'.$row['did'].')');
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class ScheduleService
|
||||
|
||||
public function execute_one($row)
|
||||
{
|
||||
$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();
|
||||
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.config')->find();
|
||||
if (!$drow) throw new Exception('域名不存在');
|
||||
|
||||
Db::name('sctask')->where('id', $row['id'])->update(['updatetime' => time()]);
|
||||
|
||||
@@ -71,7 +71,7 @@ class TaskRunner
|
||||
}
|
||||
|
||||
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();
|
||||
$drow = $this->db()->name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.config')->find();
|
||||
if (!$drow) {
|
||||
echo '域名不存在(ID:'.$row['did'].')'."\n";
|
||||
$this->closeDb();
|
||||
|
||||
@@ -5,7 +5,7 @@ CREATE TABLE `dnsmgr_config` (
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT INTO `dnsmgr_config` VALUES ('version', '1040');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('version', '1045');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_mail', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_wxtpl', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('mail_smtp', 'smtp.qq.com');
|
||||
@@ -15,10 +15,8 @@ DROP TABLE IF EXISTS `dnsmgr_account`;
|
||||
CREATE TABLE `dnsmgr_account` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
`type` varchar(20) NOT NULL,
|
||||
`ak` varchar(256) DEFAULT NULL,
|
||||
`sk` varchar(256) DEFAULT NULL,
|
||||
`ext` varchar(256) DEFAULT NULL,
|
||||
`proxy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`config` text DEFAULT NULL,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
`addtime` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
|
||||
@@ -185,4 +185,8 @@ CREATE TABLE IF NOT EXISTS `dnsmgr_sctask` (
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
ALTER TABLE `dnsmgr_account`
|
||||
ADD COLUMN `config` text DEFAULT NULL,
|
||||
CHANGE COLUMN `ak` `name` varchar(255) NOT NULL;
|
||||
@@ -257,6 +257,7 @@ class MsgNotice
|
||||
public static function send_webhook($title, $content)
|
||||
{
|
||||
$url = config_get('webhook_url');
|
||||
$atuser = config_get('webhook_user');
|
||||
if (!$url || !parse_url($url)) return false;
|
||||
if (strpos($url, 'oapi.dingtalk.com')) {
|
||||
$content = '### '.$title." \n ".str_replace("\n", " \n ", $content);
|
||||
@@ -267,6 +268,14 @@ class MsgNotice
|
||||
'text' => $content,
|
||||
],
|
||||
];
|
||||
if (!empty($atuser)) {
|
||||
if ($atuser == 'all') {
|
||||
$post['at'] = ['isAtAll' => true];
|
||||
} else {
|
||||
$atusers = explode(',', $atuser);
|
||||
$post['at'] = ['atMobiles' => $atusers, 'isAtAll' => false];
|
||||
}
|
||||
}
|
||||
} elseif (strpos($url, 'qyapi.weixin.qq.com')) {
|
||||
$content = '## '.$title."\n".$content;
|
||||
$post = [
|
||||
@@ -276,11 +285,63 @@ class MsgNotice
|
||||
],
|
||||
];
|
||||
} elseif (strpos($url, 'open.feishu.cn') || strpos($url, 'open.larksuite.com')) {
|
||||
$content = str_replace(['\*', '**'], ['*', ''], strip_tags($content));
|
||||
$content = str_replace('<font color="warning">', '<font color="red">', $content);
|
||||
if (!empty($atuser)) {
|
||||
if ($atuser == 'all') {
|
||||
$content .= "\n".'<at id=all></at> ';
|
||||
} else {
|
||||
$atusers = explode(',', $atuser);
|
||||
$content .= "\n";
|
||||
foreach ($atusers as $u) {
|
||||
$content .= '<at user_id="'.$u.'"></at> ';
|
||||
}
|
||||
}
|
||||
}
|
||||
$template = 'blue';
|
||||
if(strpos($title, '发生告警') !== false || strpos($title, '失败') !== false) $template = 'red';
|
||||
else if(strpos($title, '恢复正常') !== false) $template = 'green';
|
||||
else if(strpos($title, '到期提醒') !== false) $template = 'yellow';
|
||||
$post = [
|
||||
'msg_type' => 'text',
|
||||
'content' => [
|
||||
'text' => $content,
|
||||
'msg_type' => 'interactive',
|
||||
'card' => [
|
||||
'schema' => '2.0',
|
||||
'config' => [
|
||||
'update_multi' => true,
|
||||
'style' => [
|
||||
'text_size' => [
|
||||
'normal_v2' => [
|
||||
'default' => 'normal',
|
||||
'pc' => 'normal',
|
||||
'mobile' => 'heading',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'header' => [
|
||||
'title' => [
|
||||
'tag' => 'plain_text',
|
||||
'content' => $title,
|
||||
],
|
||||
'subtitle' => [
|
||||
'tag' => 'plain_text',
|
||||
'content' => '',
|
||||
],
|
||||
'template' => $template,
|
||||
'padding' => '12px 12px 12px 12px',
|
||||
],
|
||||
'body' => [
|
||||
'direction' => 'vertical',
|
||||
'padding' => '12px 12px 12px 12px',
|
||||
'elements' => [
|
||||
[
|
||||
'tag' => 'markdown',
|
||||
'content' => $content,
|
||||
'text_align' => 'left',
|
||||
'text_size' => 'normal_v2',
|
||||
'margin' => '0px 0px 0px 0px',
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
} else {
|
||||
|
||||
@@ -124,7 +124,7 @@ $(document).ready(function(){
|
||||
field: 'end_day',
|
||||
title: '到期时间',
|
||||
formatter: function(value, row, index) {
|
||||
if(value){
|
||||
if(value != null){
|
||||
if(value > 7){
|
||||
return '<span title="'+row.expiretime+'" data-toggle="tooltip" data-placement="right" style="color:green">剩余' + value + '天<span>';
|
||||
}else if(value > 0){
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
<a href="/domain"><i class="fa fa-list-ul fa-fw"></i> <span>域名管理</span></a>
|
||||
</li>
|
||||
{if request()->user['level'] eq 2}
|
||||
<li class="{:checkIfActive('account')}">
|
||||
<li class="{:checkIfActive('account,account_add')}">
|
||||
<a href="/account"><i class="fa fa-lock fa-fw"></i> <span>域名账户</span></a>
|
||||
</li>
|
||||
<li class="treeview {:checkIfActive('overview,task,taskinfo,taskform')}">
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
{block name="script"}
|
||||
<script src="/static/js/vue-2.7.16.min.js"></script>
|
||||
<script src="/static/js/layer/layer.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js?v=2"></script>
|
||||
<script>
|
||||
var action = '{$action}';
|
||||
var info = {$info|json_encode|raw};
|
||||
|
||||
@@ -1,70 +1,6 @@
|
||||
{extend name="common/layout" /}
|
||||
{block name="title"}域名账户{/block}
|
||||
{block name="main"}
|
||||
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content animated flipInX">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span
|
||||
aria-hidden="true">×</span><span
|
||||
class="sr-only">Close</span></button>
|
||||
<h4 class="modal-title" id="modal-title">添加/修改域名账户</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" id="form-store">
|
||||
<input type="hidden" name="action"/>
|
||||
<input type="hidden" name="id"/>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">所属平台</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="type" class="form-control">
|
||||
{foreach $dnsconfig as $k=>$v}
|
||||
<option value="{$k}">{$v['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" id="ak_name">AccessKey</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="ak" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" id="sk_name">SecretKey</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="sk" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="ext_name_div" style="display:none;">
|
||||
<label class="col-sm-3 control-label no-padding-right" id="ext_name">扩展字段</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="ext" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" id="ext_name">使用代理</label>
|
||||
<div class="col-sm-9">
|
||||
<label class="radio-inline"><input type="radio" name="proxy" value="0"> 否
|
||||
</label><label class="radio-inline"><input type="radio" name="proxy" value="1"> 是
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">备注</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="remark" placeholder="备注选填">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-primary" id="store" onclick="save()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
<div class="panel panel-default panel-intro">
|
||||
@@ -77,7 +13,7 @@
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名账户列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||||
<a href="/account/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||||
</form>
|
||||
|
||||
<table id="listTable">
|
||||
@@ -93,7 +29,6 @@
|
||||
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
var dnsconfig = {$dnsconfig|json_encode|raw};
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 15;
|
||||
@@ -114,11 +49,11 @@ $(document).ready(function(){
|
||||
field: 'typename',
|
||||
title: '所属平台',
|
||||
formatter: function(value, row, index) {
|
||||
return '<img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+value;
|
||||
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+value;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'ak',
|
||||
field: 'name',
|
||||
title: 'AccessKey'
|
||||
},
|
||||
{
|
||||
@@ -133,101 +68,19 @@ $(document).ready(function(){
|
||||
field: 'action',
|
||||
title: '操作',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '<a href="javascript:editframe('+row.id+')" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
|
||||
var html = '<a href="/account/edit?id='+row.id+'" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a> <a href="/domain?aid='+row.id+'" class="btn btn-default btn-xs">域名</a>';
|
||||
return html;
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
$("select[name=type]").change(function(){
|
||||
var type = $(this).val();
|
||||
if(dnsconfig[type] == undefined) return;
|
||||
$("#ak_name").html(dnsconfig[type].config.ak);
|
||||
$("#sk_name").html(dnsconfig[type].config.sk);
|
||||
if(dnsconfig[type].config.ext == undefined){
|
||||
$("#ext_name_div").hide();
|
||||
}else{
|
||||
$("#ext_name_div").show();
|
||||
$("#ext_name").html(dnsconfig[type].config.ext);
|
||||
}
|
||||
});
|
||||
})
|
||||
function addframe(){
|
||||
$("#modal-store").modal('show');
|
||||
$("#modal-title").html("添加域名账户");
|
||||
$("#form-store input[name=action]").val("add");
|
||||
$("#form-store input[name=id]").val('');
|
||||
$("#form-store input[name=ak]").val('');
|
||||
$("#form-store input[name=sk]").val('');
|
||||
$("#form-store input[name=ext]").val('');
|
||||
$("#form-store input[name=proxy]").eq(0).prop('checked',true);
|
||||
$("#form-store input[name=remark]").val('');
|
||||
$("select[name=type]").change();
|
||||
}
|
||||
function editframe(id){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/account/op/act/get',
|
||||
data : {id: id},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
$("#modal-store").modal('show');
|
||||
$("#modal-title").html("修改域名账户");
|
||||
$("#form-store input[name=action]").val("edit");
|
||||
$("#form-store input[name=id]").val(data.data.id);
|
||||
$("#form-store select[name=type]").val(data.data.type);
|
||||
$("#form-store input[name=ak]").val(data.data.ak);
|
||||
$("#form-store input[name=sk]").val(data.data.sk);
|
||||
$("#form-store input[name=ext]").val(data.data.ext);
|
||||
$("#form-store input[name=proxy]").eq(data.data.proxy).prop('checked',true);
|
||||
$("#form-store input[name=remark]").val(data.data.remark);
|
||||
$("select[name=type]").change();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function save(){
|
||||
if($("#form-store input[name=username]").val()==''){
|
||||
layer.alert('请确保各项不能为空!');return false;
|
||||
}
|
||||
var act = $("#form-store input[name=action]").val();
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/account/op/act/'+act,
|
||||
data : $("#form-store").serialize(),
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg,{
|
||||
icon: 1,
|
||||
closeBtn: false
|
||||
}, function(){
|
||||
layer.closeAll();
|
||||
$("#modal-store").modal('hide');
|
||||
searchRefresh();
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function delItem(id) {
|
||||
var confirmobj = layer.confirm('确定要删除此域名账户吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要删除此域名账户吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/account/op/act/del',
|
||||
url : '/account/del',
|
||||
data : {id: id},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
@@ -240,8 +93,6 @@ function delItem(id) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
221
app/view/domain/account_add.html
Normal file
221
app/view/domain/account_add.html
Normal file
@@ -0,0 +1,221 @@
|
||||
{extend name="common/layout" /}
|
||||
{block name="title"}域名账户{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
.tips{color: #f6a838; padding-left: 5px;}
|
||||
.input-note{color: green;}
|
||||
.control-label[is-required]:before {
|
||||
content: "*";
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
<div class="row" id="app">
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h3 class="panel-title"><a href="javascript:window.history.back()" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>{if $action=='edit'}编辑{else}添加{/if}域名账户</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="accountform">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>账户类型</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="type" class="form-control" v-model="set.type">
|
||||
<option v-for="(item, key) in typeList" :value="key">{{item.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item,name) in inputs" v-show="isShow(item.show)">
|
||||
<div class="form-group" v-if="item.type=='input'">
|
||||
<label class="col-sm-3 control-label no-padding-right" :is-required="item.required">{{item.name}}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" :name="name" v-model="config[name]" :placeholder="item.placeholder" :required="item.required" :disabled="item.disabled" :data-bv-id="item.validator=='id'" :data-bv-phone="item.validator=='phone'" :data-bv-numeric="item.validator=='numeric'" :data-bv-digits="item.validator=='digits'" :data-bv-integer="item.validator=='integer'" :data-bv-email="item.validator=='email'" :data-bv-uri="item.validator=='uri'" :min="item.min" :max="item.max"><span v-if="item.note" class="input-note" v-html="item.note"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="item.type=='textarea'">
|
||||
<label class="col-sm-3 control-label no-padding-right" :is-required="item.required">{{item.name}}</label>
|
||||
<div class="col-sm-6">
|
||||
<textarea class="form-control" :name="name" v-model="config[name]" :placeholder="item.placeholder" :required="item.required" :disabled="item.disabled"></textarea><span v-if="item.note" class="input-note" v-html="item.note"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="item.type=='select'">
|
||||
<label class="col-sm-3 control-label no-padding-right" :is-required="item.required">{{item.name}}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-control" :name="name" v-model="config[name]" :required="item.required" :disabled="item.disabled" :placeholder="item.placeholder">
|
||||
<option v-for="option in item.options" :value="option.value">{{option.label}}</option>
|
||||
</select><span v-if="item.note" class="input-note" v-html="item.note"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="item.type=='radio'">
|
||||
<label class="col-sm-3 control-label no-padding-right" :is-required="item.required">{{item.name}}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="radio-inline" v-for="(optionname, optionvalue) in item.options">
|
||||
<input type="radio" :name="name" :value="optionvalue" v-model="config[name]" :disabled="item.disabled"> {{optionname}}
|
||||
</label><br/><span v-if="item.note" class="input-note" v-html="item.note"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="item.type=='checkbox'">
|
||||
<div class="col-sm-offset-3 col-sm-7">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" :name="name" v-model="config[name]" :disabled="item.disabled"> {{item.name}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="item.type=='checkboxes'">
|
||||
<label class="col-sm-3 control-label no-padding-right" :is-required="item.required">{{item.name}}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="checkbox-inline" v-for="(optionname, optionvalue) in item.options">
|
||||
<input type="checkbox" :name="name" :value="optionvalue" v-model="config[name]" :disabled="item.disabled"> {{optionname}}
|
||||
</label><br/><span v-if="item.note" class="input-note" v-html="item.note"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">备注</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" name="remark" v-model="set.remark" placeholder="可留空" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="note">
|
||||
<div class="col-sm-offset-3 col-sm-6">
|
||||
<div class="alert alert-dismissible alert-info">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<strong>提示:</strong><span v-html="note"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" @click="submit">提交</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
{block name="script"}
|
||||
<script src="/static/js/vue-2.7.16.min.js"></script>
|
||||
<script src="/static/js/layer/layer.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js"></script>
|
||||
<script>
|
||||
var info = {$info|json_encode|raw};
|
||||
var typeList = {$typeList|json_encode|raw};
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
action: '{$action}',
|
||||
set: {
|
||||
id: '',
|
||||
type: '',
|
||||
name: '',
|
||||
config : '',
|
||||
remark: '',
|
||||
},
|
||||
inputs: {},
|
||||
config: {},
|
||||
typeList: typeList,
|
||||
note: '',
|
||||
},
|
||||
watch: {
|
||||
'set.type': function(val){
|
||||
if(this.action == 'add' && val && typeList[val]){
|
||||
this.inputs = typeList[val].config;
|
||||
this.note = typeList[val].note;
|
||||
this.config = {};
|
||||
$.each(this.inputs, (name, item) => {
|
||||
if(typeof item.value == 'undefined'){
|
||||
if(item.type == 'checkbox'){
|
||||
item.value = false;
|
||||
}else if(item.type == 'checkboxes'){
|
||||
item.value = [];
|
||||
}else{
|
||||
item.value = null;
|
||||
}
|
||||
}
|
||||
this.$set(this.config, name, item.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.action == 'edit'){
|
||||
Object.keys(info).forEach((key) => {
|
||||
this.set[key] = info[key]
|
||||
})
|
||||
var config = JSON.parse(info.config);
|
||||
this.inputs = typeList[this.set.type].config;
|
||||
this.note = typeList[this.set.type].note;
|
||||
$.each(this.inputs, (name, item) => {
|
||||
if(typeof config[name] != 'undefined'){
|
||||
item.value = config[name];
|
||||
}
|
||||
if(typeof item.value == 'undefined'){
|
||||
if(item.type == 'checkbox'){
|
||||
item.value = false;
|
||||
}else if(item.type == 'checkboxes'){
|
||||
item.value = [];
|
||||
}else{
|
||||
item.value = null;
|
||||
}
|
||||
}
|
||||
this.$set(this.config, name, item.value)
|
||||
})
|
||||
}else{
|
||||
this.set.type = Object.keys(typeList)[0]
|
||||
}
|
||||
|
||||
this.$nextTick(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
submit(){
|
||||
var that=this;
|
||||
Object.keys(this.config).forEach((key) => {
|
||||
if(this.config[key] && typeof this.config[key] == 'string'){
|
||||
this.config[key] = this.trim(this.config[key]);
|
||||
}
|
||||
})
|
||||
this.set.config = JSON.stringify(this.config);
|
||||
this.set.name = this.config[Object.keys(this.config)[0]];
|
||||
let loading = layer.msg('正在进行账户有效性检查', {icon: 16,shade: 0.1,time: 0});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "",
|
||||
data: this.set,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
layer.close(loading);
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon: 1}, function(){
|
||||
window.location.href = '/account';
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
layer.close(loading);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
},
|
||||
isShow(show){
|
||||
if(typeof show == 'boolean' && show){
|
||||
return show;
|
||||
}else if(typeof show == 'string' && show){
|
||||
var that=this;
|
||||
Object.keys(this.config).forEach((key) => {
|
||||
show = show.replace(new RegExp(key, 'g'), 'that.config["'+key+'"]')
|
||||
})
|
||||
return eval(show);
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
trim(str){
|
||||
return str.replace(/(^\s*)|(\s*$)/g, "");
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
@@ -48,6 +48,12 @@
|
||||
<input type="text" class="form-control" name="ttl" value="600" placeholder="指解析结果在DNS服务器中的缓存时间" required min="{$minTTL}">
|
||||
</div>
|
||||
</div>
|
||||
{if $dnsconfig.remark == 2}<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">备注</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="remark" placeholder="">
|
||||
</div>
|
||||
</div>{/if}
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" onclick="save()">添加</button></div>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ tbody tr>td:nth-child(3){min-width:300px;word-break:break-all;}
|
||||
<tbody>
|
||||
<tr v-for="item in domainList">
|
||||
<td>{{item.id}}</td>
|
||||
<td><img :src="'/static/images/'+item.type+'.ico'" class="type-logo"></img><a :href="'/record/'+item.id" target="_blank">{{item.name}}</a></td>
|
||||
<td><img :src="'/static/images/'+item.icon+''" class="type-logo"></img><a :href="'/record/'+item.id" target="_blank">{{item.name}}</a></td>
|
||||
<td v-html="item.result"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
<div class="panel-body">
|
||||
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
|
||||
<input type="hidden" name="id" value="">
|
||||
<input type="hidden" name="aid" value="">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="kw" placeholder="域名或备注">
|
||||
@@ -139,12 +141,15 @@
|
||||
<div class="form-group">
|
||||
<select name="status" class="form-control"><option value="">所有状态</option><option value="1">即将到期</option><option value="2">已到期</option></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="order" class="form-control"><option value="">默认排序</option><option value="1">注册时间↑</option><option value="2">注册时间↓</option><option value="3">到期时间↑</option><option value="4">到期时间↓</option></select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
{if request()->user['level'] eq 2}<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">批量操作 <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu"><li><a href="/domain/add">添加域名</a></li><li><a href="javascript:operation('editremark')">修改域名备注</a></li><li><a href="javascript:operation('opennotice')">开启到期提醒</a></li><li><a href="javascript:operation('closenotice')">关闭到期提醒</a></li><li><a href="javascript:operation('delete')">删除域名</a></li><li role="separator" class="divider"></li><li><a href="javascript:operation('addrecord')">添加解析</a></li><li><a href="javascript:operation('editrecord')">修改解析</a></li></ul>
|
||||
<ul class="dropdown-menu"><li><a href="/domain/add">添加域名</a></li><li><a href="javascript:operation('editremark')">修改域名备注</a></li><li><a href="javascript:operation('opennotice')">开启到期提醒</a></li><li><a href="javascript:operation('closenotice')">关闭到期提醒</a></li><li><a href="javascript:operation('updateexpire')">刷新到期时间</a></li><li><a href="javascript:operation('delete')">删除域名</a></li><li role="separator" class="divider"></li><li><a href="javascript:operation('addrecord')">添加解析</a></li><li><a href="javascript:operation('editrecord')">修改解析</a></li></ul>
|
||||
</div>
|
||||
<a href="/domain/expirenotice" class="btn btn-default">到期提醒设置</a>{/if}
|
||||
</form>
|
||||
@@ -193,7 +198,7 @@ $(document).ready(function(){
|
||||
field: 'typename',
|
||||
title: '平台账户',
|
||||
formatter: function(value, row, index) {
|
||||
return '<img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+(row.aremark?row.aremark:value+'('+row.aid+')');
|
||||
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+(row.aremark?row.aremark:value+'('+row.aid+')');
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -423,9 +428,7 @@ function saveEdit(){
|
||||
});
|
||||
}
|
||||
function delItem(id) {
|
||||
var confirmobj = layer.confirm('确定要删除此域名吗?删除域名不会影响已添加的解析', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要删除此域名吗?删除域名不会影响已添加的解析', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
@@ -442,8 +445,6 @@ function delItem(id) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}
|
||||
function getDomainList(){
|
||||
@@ -512,9 +513,7 @@ function operation(action){
|
||||
window.location.href = '/record/batchedit';
|
||||
return;
|
||||
}else if(action == 'delete'){
|
||||
var confirmobj = layer.confirm('确定要删除所选域名吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要删除所选域名吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
@@ -532,8 +531,26 @@ function operation(action){
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}else if(action == 'updateexpire'){
|
||||
layer.confirm('提交后将异步刷新所选域名的到期时间', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/domain/op/act/updateexpire',
|
||||
data : {ids: ids},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.closeAll();
|
||||
layer.alert(data.msg, {icon: 1});
|
||||
searchRefresh();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}else{
|
||||
var is_notice = action == 'opennotice' ? 1 : 0;
|
||||
|
||||
556
app/view/domain/qingcloud.html
Normal file
556
app/view/domain/qingcloud.html
Normal file
@@ -0,0 +1,556 @@
|
||||
{extend name="common/layout" /}
|
||||
{block name="title"}解析管理 - {$domainName}{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:360px;}
|
||||
.dns-parent-row { cursor: pointer; }
|
||||
.dns-child-row td { background: #fafafa; }
|
||||
.dns-child-empty td { background:#fafafa; }
|
||||
.glyphicon-spin { animation: spin 1s infinite linear; }
|
||||
@keyframes spin { from {transform:rotate(0deg);} to {transform:rotate(360deg);} }
|
||||
.form-group .radio-inline {position: unset;}
|
||||
.tips {color: #f6a838;padding-left: 5px;}
|
||||
.text-remark {margin-left: 10px;color: #329a29;font-size: 12px;}
|
||||
</style>
|
||||
<div class="row" id="app">
|
||||
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content animated flipInX">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span
|
||||
aria-hidden="true">×</span><span
|
||||
class="sr-only">Close</span></button>
|
||||
<h4 class="modal-title" id="modal-title">{{form.action=='add'?'添加解析':'修改解析'}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" id="form-store">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">主机记录</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="name" placeholder="填写域名前缀,支持多级" v-model="form.name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">记录类型</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="type" class="form-control" v-model="form.type">
|
||||
<option value="A">A</option>
|
||||
<option value="CNAME">CNAME</option>
|
||||
<option value="AAAA">AAAA</option>
|
||||
<option value="NS">NS</option>
|
||||
<option value="MX">MX</option>
|
||||
<option value="TXT">TXT</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">线路类型</label>
|
||||
<div class="col-sm-9" id="line_list">
|
||||
<select name="line" class="form-control" v-model="form.line">
|
||||
<option v-for="line in recordLine" :value="line.id">{{line.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="form.type=='A' || form.type=='CNAME'">
|
||||
<label class="col-sm-3 control-label">模式</label>
|
||||
<div class="col-sm-9" id="line_list">
|
||||
<label class="radio-inline"><input type="radio" name="mode" value="1" v-model="form.mode"> 普通<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="每次权威 DNS 查询都将按您填写的顺序返回解析结果,查询性能更好。" class="tips"><i class="fa fa-question-circle"></i></span></label>
|
||||
<label class="radio-inline" v-show="form.type=='A'"><input type="radio" name="mode" value="2" v-model="form.mode"> 轮询<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="每次权威 DNS 查询的解析结果排序都将会较上一次发生变化,业务负载更均衡。" class="tips"><i class="fa fa-question-circle"></i></span></label>
|
||||
<label class="radio-inline" v-show="form.type=='A'"><input type="radio" name="mode" value="4" v-model="form.mode"> 智能<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="根据访问来源的运营商及地理位置将解析结果按匹配度排序并最多返回前 5 个,可减少您对精细化线路配置的烦恼。" class="tips"><i class="fa fa-question-circle"></i></span></label>
|
||||
<label class="radio-inline"><input type="radio" name="mode" value="3" v-model="form.mode"> 权重<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="每次权威 DNS 查询都将根据每组解析结果的权值按比例返回,使得业务负载可以随心所欲。" class="tips"><i class="fa fa-question-circle"></i></span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">记录值</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="value" :placeholder="'输入记录值' + (form.type=='A'&&form.action=='add'?'(多个IP用,间隔)':'')" v-model="form.value" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="form.type=='MX'">
|
||||
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="mx" v-model="form.mx">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">TTL</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="ttl" v-model="form.ttl" placeholder="指解析结果在DNS服务器中的缓存时间" required min="{$minTTL}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="(form.type=='A' || form.type=='CNAME') && form.mode=='3'">
|
||||
<label class="col-sm-3 control-label no-padding-right">权重</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="weight" v-model="form.weight" placeholder="权重值(1-99)" min="1" max="99">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-primary" id="store" @click="save">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{if request()->user['type'] eq 'user'}<a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>{/if}{$domainName}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<form class="form-inline" id="searchToolbar" @submit.prevent>
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="keyword" placeholder="输入主机记录" v-model="keyword" @keyup.enter="loadParents">
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @click="loadParents"><i class="fa fa-search"></i> 搜索</button>
|
||||
<button type="button" class="btn btn-default" title="刷新解析记录列表" @click="keyword=null;loadParents()"><i class="fa fa-refresh"></i> 刷新</button>
|
||||
<button type="button" class="btn btn-success" @click="addRecord"><i class="fa fa-plus"></i> 添加记录</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">批量操作 <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu"><li><a href="/record/batchadd/{$domainId}">添加</a></li></ul>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">日志 <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu"><li><a href="/log?domain={$domainName}">本站日志</a></li></ul>
|
||||
</div>
|
||||
</form>
|
||||
<div class="table-responsive" style="margin-top:15px;">
|
||||
<table class="table table-striped table-hover table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>主机记录</th>
|
||||
<th>线路类型</th>
|
||||
<th>记录类型</th>
|
||||
<th>模式</th>
|
||||
<th style="min-width:150px">记录值</th>
|
||||
<th>TTL</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<template v-if="loading">
|
||||
<tr>
|
||||
<td colspan="8" class="text-muted text-center"><i class="glyphicon glyphicon-refresh glyphicon-spin"></i> 正在加载...</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-if="!loading && parents.length === 0">
|
||||
<tr>
|
||||
<td colspan="8" class="text-muted text-center">暂无记录</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-if="!loading" v-for="p in parents">
|
||||
|
||||
<tr :key="'p-' + p.RecordId" class="dns-parent-row" @click="toggleParent(p)">
|
||||
<td colspan="7">
|
||||
<span class="text-muted" style="display:inline-block;width:18px;">
|
||||
<i class="glyphicon"
|
||||
:class="expandedMap[p.RecordId] ? 'glyphicon-chevron-down' : 'glyphicon-chevron-right'"></i>
|
||||
</span>
|
||||
<strong>{{ p.Name }}</strong><span class="text-muted">(共 {{ p.Count }} 条记录)</span><span class="text-remark" v-if="p.Remark"><i class="glyphicon glyphicon-list-alt"></i> {{ p.Remark }}</span>
|
||||
</td>
|
||||
<td @click.stop>
|
||||
<button class="btn btn-xs btn-success" @click="addRecord(p)">添加</button>
|
||||
<button class="btn btn-xs btn-info" @click="editHostRemark(p)">备注</button>
|
||||
<button class="btn btn-xs btn-danger" @click="deleteHost(p)">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-if="expandedMap[p.RecordId] && loadingMap[p.RecordId]" class="dns-child-empty">
|
||||
<td colspan="8" class="text-muted text-center"><i class="glyphicon glyphicon-refresh glyphicon-spin"></i> 正在加载...</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="c in (expandedMap[p.RecordId] ? (childrenMap[p.RecordId] || []) : [])"
|
||||
:key="'c-' + c.RecordId"
|
||||
v-if="expandedMap[p.RecordId] && !loadingMap[p.RecordId]"
|
||||
class="dns-child-row">
|
||||
<td></td>
|
||||
<td>{{ c.LineName }}</td>
|
||||
<td>{{ c.Type }}</td>
|
||||
<td>{{ c.Type == 'A' || c.Type == 'CNAME' ? modeList[c.Mode] : '-' }} <span class="label label-info" v-if="(c.Type == 'A' || c.Type == 'CNAME') && c.Mode == 3">{{c.Weight}}</span></td>
|
||||
<td>{{ c.Value + (c.Type == 'MX' ? ' | ' + c.MX : '') }}<a href="javascript:void(0);" title="复制记录值" @click="copyToClipboard(c, $event)" style="padding-left:6px;"><i class="fa fa-copy"></i></a></td>
|
||||
<td>{{ c.TTL }}</td>
|
||||
<td><font color="green" v-if="c.Status=='1'"><i class="fa fa-check-circle"></i>启用</font><font color="orange" v-if="c.Status!='1'"><i class="fa fa-pause-circle"></i>暂停</font></td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-primary" @click="editRecord(c, p.RecordId)">修改</button>
|
||||
<button class="btn btn-xs btn-warning" @click="setRecordStatus(c, p.RecordId, '0')" v-if="c.Status=='1'">暂停</button>
|
||||
<button class="btn btn-xs btn-success" @click="setRecordStatus(c, p.RecordId, '1')" v-if="c.Status!='1'">启用</button>
|
||||
<button class="btn btn-xs btn-danger" @click="deleteRecord(c, p.RecordId)">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-if="expandedMap[p.RecordId] && !loadingMap[p.RecordId] && (childrenMap[p.RecordId] || []).length === 0"
|
||||
:key="'empty-' + p.RecordId"
|
||||
class="dns-child-empty">
|
||||
<td colspan="8" class="text-muted text-center">暂无记录</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row" style="margin-top:10px;">
|
||||
<div class="col-sm-6 text-muted" style="padding-top:6px;">
|
||||
共 {{ total }} 条,当前第 {{ currentPage }} / {{ totalPages }} 页
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 text-right">
|
||||
<ul class="pagination pagination-sm" style="margin:0;">
|
||||
<li :class="{disabled: currentPage === 1}">
|
||||
<a href="javascript:;" @click="goPage(1)">«</a>
|
||||
</li>
|
||||
<li :class="{disabled: currentPage === 1}">
|
||||
<a href="javascript:;" @click="goPage(currentPage - 1)">上一页</a>
|
||||
</li>
|
||||
|
||||
<li v-for="n in pageList" :key="'pg-' + n" :class="{active: n === currentPage}">
|
||||
<a href="javascript:;" @click="goPage(n)">{{ n }}</a>
|
||||
</li>
|
||||
|
||||
<li :class="{disabled: currentPage === totalPages}">
|
||||
<a href="javascript:;" @click="goPage(currentPage + 1)">下一页</a>
|
||||
</li>
|
||||
<li :class="{disabled: currentPage === totalPages}">
|
||||
<a href="javascript:;" @click="goPage(totalPages)">»</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
{block name="script"}
|
||||
<script src="/static/js/vue-2.7.16.min.js"></script>
|
||||
<script src="/static/js/layer/layer.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js"></script>
|
||||
<script>
|
||||
var recordLine = {$recordLine|json_encode|raw};
|
||||
var dnsconfig = {$dnsconfig|json_encode|raw};
|
||||
var defaultLine = recordLine[0].id;
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: function () {
|
||||
return {
|
||||
loading: false,
|
||||
recordLine: recordLine,
|
||||
form: {
|
||||
action: '',
|
||||
recordid: '',
|
||||
recordinfo: '',
|
||||
parentid: '',
|
||||
name: '',
|
||||
type: '',
|
||||
line: '',
|
||||
mode: '',
|
||||
value: '',
|
||||
ttl: 600,
|
||||
weight: '',
|
||||
mx: 10,
|
||||
},
|
||||
keyword: '',
|
||||
total: 0,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
parents: [],
|
||||
expandedMap: {}, // {pid: true/false}
|
||||
loadingMap: {}, // {pid: true/false}
|
||||
childrenMap: {}, // {pid: []} 缓存子列表
|
||||
modeList: [
|
||||
'默认',
|
||||
'普通',
|
||||
'轮询',
|
||||
'权重',
|
||||
'智能',
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentPage: function () {
|
||||
return Math.floor(this.offset / this.limit) + 1;
|
||||
},
|
||||
totalPages: function () {
|
||||
return Math.max(1, Math.ceil(this.total / this.limit));
|
||||
},
|
||||
pageList: function () {
|
||||
// 显示最多 5 个页码,居中
|
||||
var totalPages = this.totalPages;
|
||||
var cur = this.currentPage;
|
||||
var windowSize = 5;
|
||||
var half = Math.floor(windowSize / 2);
|
||||
|
||||
var start = Math.max(1, cur - half);
|
||||
var end = Math.min(totalPages, start + windowSize - 1);
|
||||
start = Math.max(1, end - windowSize + 1);
|
||||
|
||||
var arr = [];
|
||||
for (var i = start; i <= end; i++) arr.push(i);
|
||||
return arr;
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.loadParents();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$("#form-store").bootstrapValidator();
|
||||
},
|
||||
methods: {
|
||||
loadParents: function () {
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
vm.expandedMap = {};
|
||||
vm.loadingMap = {};
|
||||
vm.childrenMap = {};
|
||||
$.ajax({
|
||||
url: '/record/data/{$domainId}',
|
||||
method: 'POST',
|
||||
data: { keyword: vm.keyword, offset: vm.offset, limit: vm.limit },
|
||||
dataType: 'json'
|
||||
}).done(function (res) {
|
||||
vm.loading = false;
|
||||
vm.total = res.total || 0;
|
||||
vm.parents = res.rows || [];
|
||||
}).fail(function () {
|
||||
layer.msg('加载父级列表失败');
|
||||
});
|
||||
},
|
||||
|
||||
goPage: function (page) {
|
||||
if (!page) return;
|
||||
page = Math.max(1, Math.min(this.totalPages, page));
|
||||
if (page === this.currentPage) return;
|
||||
|
||||
this.offset = (page - 1) * this.limit;
|
||||
this.loadParents();
|
||||
},
|
||||
|
||||
toggleParent: function (p) {
|
||||
var pid = p.RecordId;
|
||||
|
||||
// 收起
|
||||
if (this.expandedMap[pid]) {
|
||||
this.$set(this.expandedMap, pid, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 展开
|
||||
this.$set(this.expandedMap, pid, true);
|
||||
|
||||
// 已有缓存就不再请求
|
||||
if (this.childrenMap[pid]) return;
|
||||
|
||||
this.loadChildren(pid);
|
||||
},
|
||||
|
||||
loadChildren: function (pid) {
|
||||
var vm = this;
|
||||
vm.$set(vm.loadingMap, pid, true);
|
||||
|
||||
$.ajax({
|
||||
url: '/record/data/{$domainId}',
|
||||
method: 'POST',
|
||||
data: { subdomain: pid },
|
||||
dataType: 'json'
|
||||
}).done(function (res) {
|
||||
vm.$set(vm.childrenMap, pid, (res && res.rows) ? res.rows : []);
|
||||
}).fail(function () {
|
||||
layer.msg('加载子级列表失败');
|
||||
vm.$set(vm.childrenMap, pid, []);
|
||||
}).always(function () {
|
||||
vm.$set(vm.loadingMap, pid, false);
|
||||
});
|
||||
},
|
||||
|
||||
addRecord: function (p) {
|
||||
this.form.action = 'add';
|
||||
this.form.recordid = '';
|
||||
this.form.recordinfo = '';
|
||||
this.form.parentid = p.RecordId || '';
|
||||
this.form.name = p.Name || '';
|
||||
this.form.type = 'A';
|
||||
this.form.line = defaultLine;
|
||||
this.form.mode = '1';
|
||||
this.form.value = '';
|
||||
this.form.ttl = 600;
|
||||
this.form.weight = '';
|
||||
this.form.mx = 10;
|
||||
$("#modal-store").modal('show');
|
||||
$("#form-store").data("bootstrapValidator").resetForm();
|
||||
},
|
||||
editRecord: function (c, parentid) {
|
||||
this.form.action = 'update';
|
||||
this.form.recordid = c.RecordId;
|
||||
this.form.recordinfo = JSON.stringify(c);
|
||||
this.form.parentid = parentid || '';
|
||||
this.form.name = c.Name;
|
||||
this.form.type = c.Type;
|
||||
this.form.line = c.Line;
|
||||
this.form.mode = c.Mode;
|
||||
this.form.value = c.Value;
|
||||
this.form.ttl = c.TTL;
|
||||
this.form.weight = c.Weight;
|
||||
this.form.mx = c.MX || 10;
|
||||
$("#modal-store").modal('show');
|
||||
$("#form-store").data("bootstrapValidator").resetForm();
|
||||
},
|
||||
save: function () {
|
||||
$("#form-store").data("bootstrapValidator").validate();
|
||||
if(!$("#form-store").data("bootstrapValidator").isValid()){
|
||||
return;
|
||||
}
|
||||
var vm = this;
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/record/'+vm.form.action+'/{$domainId}',
|
||||
data : vm.form,
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg,{
|
||||
icon: 1,
|
||||
closeBtn: false
|
||||
}, function(){
|
||||
layer.closeAll();
|
||||
$("#modal-store").modal('hide');
|
||||
if(vm.form.parentid){
|
||||
vm.loadChildren(vm.form.parentid);
|
||||
}else{
|
||||
vm.loadParents();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setRecordStatus: function (c, parentid, status) {
|
||||
var vm = this;
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/record/status/{$domainId}',
|
||||
data : { recordid: c.RecordId, status: status, recordinfo: JSON.stringify(c) },
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.closeAll();
|
||||
layer.msg(status=='1'?'开启成功':'暂停成功', {icon: 1, time:500});
|
||||
vm.loadChildren(parentid);
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
deleteRecord: function (c, parentid) {
|
||||
var vm = this;
|
||||
layer.confirm('确定要删除此解析记录吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/record/delete/{$domainId}',
|
||||
data : { recordid: c.RecordId, recordinfo: JSON.stringify(c) },
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.closeAll();
|
||||
layer.msg('删除成功', {icon: 1, time:800});
|
||||
vm.loadChildren(parentid);
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
deleteHost: function (p) {
|
||||
var vm = this;
|
||||
layer.confirm('确定要删除此主机名下所有解析记录吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/record/delete/{$domainId}',
|
||||
data : { recordid: p.RecordId },
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.closeAll();
|
||||
layer.msg('删除成功', {icon: 1, time:800});
|
||||
vm.loadParents();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
editHostRemark: function (p) {
|
||||
var vm = this;
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['350px'],
|
||||
closeBtn: 2,
|
||||
title: '编辑备注',
|
||||
content: '<div style="padding:15px"><div class="form-group"><input class="form-control" type="text" name="remark" value="'+(p.Remark==null?'':p.Remark)+'" autocomplete="off" placeholder="备注信息"></div></div>',
|
||||
btn: ['确认', '取消'],
|
||||
yes: function(){
|
||||
var remark = $("input[name='remark']").val();
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/record/remark/{$domainId}',
|
||||
data : {recordid:p.RecordId, remark:remark},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.closeAll();
|
||||
layer.msg('保存成功', {icon: 1, time:800});
|
||||
vm.loadParents();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
copyToClipboard: function (c, event) {
|
||||
var text = c.Value;
|
||||
var tempInput = document.createElement('input');
|
||||
tempInput.style.position = 'absolute';
|
||||
tempInput.style.left = '-9999px';
|
||||
tempInput.value = text;
|
||||
document.body.appendChild(tempInput);
|
||||
tempInput.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(tempInput);
|
||||
$(event.target).toggleClass('fa-copy fa-check');
|
||||
setTimeout(function(){
|
||||
$(event.target).toggleClass('fa-check fa-copy');
|
||||
}, 1000);
|
||||
layer.msg('已复制到剪贴板', {icon: 1, time: 600});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
@@ -292,7 +292,6 @@ $(document).ready(function(){
|
||||
formatter: function(value, row, index) {
|
||||
var copyId = 'copy-value-' + row.RecordId;
|
||||
if(row.Type == 'MX') {
|
||||
// 只复制 mx.yandex.net,按钮在其右侧,优先级单独显示
|
||||
return '<span id="'+copyId+'" data-value="'+htmlEscape(value)+'">'+value+'</span>'
|
||||
+ '<a href="javascript:void(0);" title="复制记录值" onclick="copyToClipboard(null, \'#'+copyId+'\')" style="padding-left:6px;"><i class=\"fa fa-copy\"></i></a>'
|
||||
+ '<span class="mx-priority"> | '+row.MX+'</span>';
|
||||
@@ -349,10 +348,12 @@ $(document).ready(function(){
|
||||
if(dnsconfig.remark == 1){
|
||||
html += '<a href="javascript:setRemark(\''+row.RecordId+'\')" class="btn btn-info btn-xs">备注</a> ';
|
||||
}
|
||||
if(row.Name === "@") var domain = "{$domainName}";
|
||||
else var domain = row.Name + ".{$domainName}";
|
||||
domain = domain.replace(/\*/g, 'www');
|
||||
html += '<a href="http://' + domain + '" target="_blank" title="访问域名" class="btn btn-default btn-xs"><i class="fa fa-external-link"></i></a>';
|
||||
if(row.Type == 'A' || row.Type == 'CNAME' || row.Type == 'AAAA' || row.Type == 'REDIRECT_URL' || row.Type == 'FORWARD_URL'){
|
||||
if(row.Name === "@") var domain = "{$domainName}";
|
||||
else var domain = row.Name + ".{$domainName}";
|
||||
domain = domain.replace(/\*/g, 'www');
|
||||
html += '<a href="http://' + domain + '" target="_blank" title="访问域名" class="btn btn-default btn-xs"><i class="fa fa-external-link"></i></a>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
},
|
||||
@@ -511,9 +512,7 @@ function setStatus(recordid, status){
|
||||
}
|
||||
function delItem(recordid) {
|
||||
var row = $("#listTable").bootstrapTable('getRowByUniqueId', recordid);
|
||||
var confirmobj = layer.confirm('确定要删除此解析记录吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要删除此解析记录吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
@@ -531,8 +530,6 @@ function delItem(recordid) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}
|
||||
function setRemark(recordid) {
|
||||
@@ -587,9 +584,7 @@ function operation(action){
|
||||
return;
|
||||
}
|
||||
|
||||
var confirmobj = layer.confirm('确定要'+(action=='open'?'启用':(action=='pause'?'暂停':'删除'))+'所选记录吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要'+(action=='open'?'启用':(action=='pause'?'暂停':'删除'))+'所选记录吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
@@ -607,8 +602,6 @@ function operation(action){
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}
|
||||
function batch_edit(records){
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<form method="post">
|
||||
<div id="error" style="display:none"></div>
|
||||
<div id="success" style="display:none"></div>
|
||||
|
||||
{if $dbconfig!='1'}
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>MySQL 数据库地址</label>
|
||||
@@ -194,6 +194,7 @@
|
||||
<input type="text" name="mysql_prefix" value="dnsmgr_">
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<div class="form-group" v-show="set.switchtype==0">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>记录值</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" name="value" v-model="set.value" placeholder="支持填写IPv4或CNAME地址" class="form-control" required>
|
||||
<input type="text" name="value" v-model="set.value" placeholder="支持填写IP或CNAME地址" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="set.switchtype==0&&dnstype=='cloudflare'">
|
||||
|
||||
@@ -121,6 +121,10 @@
|
||||
<label class="col-sm-3 control-label">Webhook地址</label>
|
||||
<div class="col-sm-9"><input type="text" name="webhook_url" value="{:config_get('webhook_url')}" class="form-control"/></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">@用户手机号</label>
|
||||
<div class="col-sm-9"><input type="text" name="webhook_user" value="{:config_get('webhook_user')}" class="form-control" placeholder="非必填,可填写用户的手机号,@全体填写all"/></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-primary btn-block"/>
|
||||
@@ -130,7 +134,9 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
仅支持填写企业微信、钉钉、飞书群机器人的Webhook地址
|
||||
仅支持填写企业微信、钉钉、飞书群机器人的Webhook地址<br/>
|
||||
认证方式可以选自定义关键词“DNS”或IP白名单。<br/>
|
||||
@用户不支持企业微信,飞书用户手机号需要填写<a href="https://open.feishu.cn/document/home/user-identity-introduction/open-id" target="_blank" rel="noreferrer">用户ID</a>。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -279,9 +279,7 @@ function setStatus(id,status) {
|
||||
});
|
||||
}
|
||||
function delItem(id) {
|
||||
var confirmobj = layer.confirm('确定要删除此用户吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.confirm('确定要删除此用户吗?', {title: '提示', icon: 0}, function(){
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
@@ -298,8 +296,6 @@ function delItem(id) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirmobj);
|
||||
});
|
||||
}
|
||||
var CreatePassword = function (len)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"cccyun/php-whois": "^1.0",
|
||||
"cccyun/think-captcha": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"phpmailer/phpmailer": "^6.10",
|
||||
"phpmailer/phpmailer": "^7.0",
|
||||
"symfony/polyfill-intl-idn": "^1.32",
|
||||
"symfony/polyfill-mbstring": "^1.32",
|
||||
"symfony/polyfill-php81": "^1.32",
|
||||
|
||||
103
composer.lock
generated
103
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f7c4abfaf4cb80cd99107e9e1763e75c",
|
||||
"content-hash": "34b2ff614d9cf3cc515823086a4f091b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "cccyun/php-whois",
|
||||
@@ -445,16 +445,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v6.11.1",
|
||||
"version": "v7.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "d9e3b36b47f04b497a0164c5a20f92acb4593284"
|
||||
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d9e3b36b47f04b497a0164c5a20f92acb4593284",
|
||||
"reference": "d9e3b36b47f04b497a0164c5a20f92acb4593284",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -468,13 +468,13 @@
|
||||
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||
"phpcompatibility/php-compatibility": "^9.3.5",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"squizlabs/php_codesniffer": "^3.7.2",
|
||||
"phpcompatibility/php-compatibility": "^10.0.0@dev",
|
||||
"squizlabs/php_codesniffer": "^3.13.5",
|
||||
"yoast/phpunit-polyfills": "^1.0.4"
|
||||
},
|
||||
"suggest": {
|
||||
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||
"directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example",
|
||||
"ext-imap": "Needed to support advanced email address parsing according to RFC822",
|
||||
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||
@@ -515,7 +515,7 @@
|
||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.11.1"
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -523,7 +523,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-30T11:54:53+00:00"
|
||||
"time": "2026-01-09T18:02:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -687,16 +687,16 @@
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.1",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -705,7 +705,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -720,7 +720,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
@@ -734,9 +734,9 @@
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:50:52+00:00"
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
@@ -1452,16 +1452,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v7.3.3",
|
||||
"version": "v7.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d"
|
||||
"reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d",
|
||||
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/90208e2fc6f68f613eae7ca25a2458a931b1bacc",
|
||||
"reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1504,7 +1504,7 @@
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.3"
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1524,20 +1524,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-27T11:34:33+00:00"
|
||||
"time": "2025-09-27T09:00:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/framework",
|
||||
"version": "v8.1.3",
|
||||
"version": "v8.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/framework.git",
|
||||
"reference": "e4207e98b66f92d26097ed6efd535930cba90e8f"
|
||||
"reference": "8e7b2b2364047cbf71a38c4e397a9ca0d4ef2b01"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/framework/zipball/e4207e98b66f92d26097ed6efd535930cba90e8f",
|
||||
"reference": "e4207e98b66f92d26097ed6efd535930cba90e8f",
|
||||
"url": "https://api.github.com/repos/top-think/framework/zipball/8e7b2b2364047cbf71a38c4e397a9ca0d4ef2b01",
|
||||
"reference": "8e7b2b2364047cbf71a38c4e397a9ca0d4ef2b01",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1545,7 +1545,7 @@
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.0.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-message": "^1.0|^2.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
"psr/simple-cache": "^1.0|^2.0|^3.0",
|
||||
"topthink/think-container": "^3.0",
|
||||
@@ -1554,6 +1554,7 @@
|
||||
"topthink/think-validate": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.92",
|
||||
"guzzlehttp/psr7": "^2.1.0",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.2",
|
||||
@@ -1589,9 +1590,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/framework/issues",
|
||||
"source": "https://github.com/top-think/framework/tree/v8.1.3"
|
||||
"source": "https://github.com/top-think/framework/tree/v8.1.4"
|
||||
},
|
||||
"time": "2025-07-14T03:48:44+00:00"
|
||||
"time": "2026-01-15T02:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-container",
|
||||
@@ -1641,16 +1642,16 @@
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-helper",
|
||||
"version": "v3.1.11",
|
||||
"version": "v3.1.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-helper.git",
|
||||
"reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c"
|
||||
"reference": "fe277121112a8f1c872e169a733ca80bb11c4acb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-helper/zipball/1d6ada9b9f3130046bf6922fe1bd159c8d88a33c",
|
||||
"reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c",
|
||||
"url": "https://api.github.com/repos/top-think/think-helper/zipball/fe277121112a8f1c872e169a733ca80bb11c4acb",
|
||||
"reference": "fe277121112a8f1c872e169a733ca80bb11c4acb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1681,22 +1682,22 @@
|
||||
"description": "The ThinkPHP6 Helper Package",
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-helper/issues",
|
||||
"source": "https://github.com/top-think/think-helper/tree/v3.1.11"
|
||||
"source": "https://github.com/top-think/think-helper/tree/v3.1.12"
|
||||
},
|
||||
"time": "2025-04-07T06:55:59+00:00"
|
||||
"time": "2025-12-26T09:58:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-orm",
|
||||
"version": "v4.0.50",
|
||||
"version": "v4.0.51",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-orm.git",
|
||||
"reference": "ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc"
|
||||
"reference": "46abe2f824eb3bcb117d4c0ce93b203b592b79f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-orm/zipball/ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc",
|
||||
"reference": "ddae72d5ff4d953d3d8cc526fd9c50e8862ce2cc",
|
||||
"url": "https://api.github.com/repos/top-think/think-orm/zipball/46abe2f824eb3bcb117d4c0ce93b203b592b79f7",
|
||||
"reference": "46abe2f824eb3bcb117d4c0ce93b203b592b79f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1741,9 +1742,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-orm/issues",
|
||||
"source": "https://github.com/top-think/think-orm/tree/v4.0.50"
|
||||
"source": "https://github.com/top-think/think-orm/tree/v4.0.51"
|
||||
},
|
||||
"time": "2025-08-26T05:32:22+00:00"
|
||||
"time": "2025-12-18T13:11:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-template",
|
||||
@@ -1907,16 +1908,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v7.3.4",
|
||||
"version": "v7.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb"
|
||||
"reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb",
|
||||
"reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d",
|
||||
"reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1970,7 +1971,7 @@
|
||||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.3.4"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.3.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1990,7 +1991,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T10:12:26+00:00"
|
||||
"time": "2025-09-27T09:00:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-trace",
|
||||
@@ -2046,7 +2047,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@@ -2060,6 +2061,6 @@
|
||||
"ext-sockets": "*",
|
||||
"ext-ssh2": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
'show_error_msg' => true,
|
||||
'exception_tmpl' => \think\facade\App::getAppPath() . 'view/exception.tpl',
|
||||
|
||||
'version' => '1042',
|
||||
'version' => '1046',
|
||||
|
||||
'dbversion' => '1040'
|
||||
'dbversion' => '1045'
|
||||
];
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
public/static/images/litessl.ico
Normal file
BIN
public/static/images/litessl.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/static/images/qingcloud.ico
Normal file
BIN
public/static/images/qingcloud.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
BIN
public/static/images/spaceship.ico
Normal file
BIN
public/static/images/spaceship.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
2
public/static/js/bootstrapValidator.min.js
vendored
2
public/static/js/bootstrapValidator.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -47,7 +47,8 @@ Route::group(function () {
|
||||
Route::get('/log', 'user/log');
|
||||
|
||||
Route::post('/account/data', 'domain/account_data');
|
||||
Route::post('/account/op', 'domain/account_op');
|
||||
Route::post('/account/:action', 'domain/account_op');
|
||||
Route::get('/account/:action', 'domain/account_add');
|
||||
Route::get('/account', 'domain/account');
|
||||
|
||||
Route::any('/domain/expirenotice', 'domain/expire_notice');
|
||||
|
||||
Reference in New Issue
Block a user