修复申请中文域名证书,支持批量删域名

This commit is contained in:
net909 2025-02-24 22:34:54 +08:00
parent fe9a50469d
commit 521275ee33
13 changed files with 187 additions and 54 deletions

View File

@ -456,4 +456,19 @@ function curl_set_proxy(&$ch)
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_userpwd);
}
curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type);
}
function convertDomainToAscii($domain) {
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $domain)) {
return idn_to_ascii($domain);
} else {
return $domain;
}
}
function convertDomainToUtf8($domain) {
if (preg_match('/^xn--/', $domain)) {
return idn_to_utf8($domain);
} else {
return $domain;
}
}

View File

@ -272,7 +272,7 @@ class Cert extends BaseController
foreach($domains as $domain){
$domainList[] = [
'oid' => $id,
'domain' => $domain,
'domain' => convertDomainToAscii($domain),
'sort' => $i++,
];
}
@ -310,7 +310,7 @@ class Cert extends BaseController
foreach($domains as $domain){
$domainList[] = [
'oid' => $id,
'domain' => $domain,
'domain' => convertDomainToAscii($domain),
'sort' => $i++,
];
}
@ -441,7 +441,8 @@ class Cert extends BaseController
}
foreach($domains as $domain){
if(!$wildcard && strpos($domain, '*') !== false) return ['code' => -1, 'msg' => '该证书账户类型不支持泛域名'];
if (!$wildcard && strpos($domain, '*') !== false) return ['code' => -1, 'msg' => '该证书账户类型不支持泛域名'];
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $domain) && !function_exists('idn_to_ascii')) return ['code' => -1, 'msg' => '域名包含中文请开启intl扩展'];
$mainDomain = getMainDomain($domain);
$drow = Db::name('domain')->where('name', $mainDomain)->find();
if (!$drow) {

View File

@ -87,6 +87,7 @@ class Dmonitor extends BaseController
'cycle' => input('post.cycle/d'),
'timeout' => input('post.timeout/d'),
'proxy' => input('post.proxy/d'),
'cdn' => input('post.cdn') == 'true' || input('post.cdn') == '1' ? 1 : 0,
'remark' => input('post.remark', null, 'trim'),
'recordinfo' => input('post.recordinfo', null, 'trim'),
'addtime' => time(),
@ -123,6 +124,7 @@ class Dmonitor extends BaseController
'cycle' => input('post.cycle/d'),
'timeout' => input('post.timeout/d'),
'proxy' => input('post.proxy/d'),
'cdn' => input('post.cdn') == 'true' || input('post.cdn') == '1' ? 1 : 0,
'remark' => input('post.remark', null, 'trim'),
'recordinfo' => input('post.recordinfo', null, 'trim'),
];
@ -163,8 +165,9 @@ class Dmonitor extends BaseController
}
$domains = [];
foreach (Db::name('domain')->select() as $row) {
$domains[$row['id']] = $row['name'];
$domainList = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.id,A.name,B.type')->select();
foreach ($domainList as $row) {
$domains[] = ['id'=>$row['id'], 'name'=>$row['name'], 'type'=>$row['type']];
}
View::assign('domains', $domains);

View File

@ -241,6 +241,7 @@ class Domain extends BaseController
$is_hide = input('post.is_hide/d');
$is_sso = input('post.is_sso/d');
$remark = input('post.remark', null, 'trim');
if (empty($remark)) $remark = null;
Db::name('domain')->where('id', $id)->update([
'is_hide' => $is_hide,
'is_sso' => $is_sso,
@ -273,6 +274,22 @@ class Domain extends BaseController
}
Db::name('domain')->insertAll($data);
return json(['code' => 0, 'msg' => '成功添加' . count($data) . '个域名!']);
} elseif ($act == 'batchedit') {
if (!checkPermission(2)) return $this->alert('error', '无权限');
$ids = input('post.ids');
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
$remark = input('post.remark', null, 'trim');
if (empty($remark)) $remark = null;
Db::name('domain')->where('id', 'in', $ids)->update(['remark' => $remark]);
return json(['code' => 0, 'msg' => '成功修改' . count($ids) . '个域名!']);
} elseif ($act == 'batchdel') {
if (!checkPermission(2)) return $this->alert('error', '无权限');
$ids = input('post.ids');
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
Db::name('domain')->where('id', 'in', $ids)->delete();
Db::name('dmtask')->where('did', 'in', $ids)->delete();
Db::name('optimizeip')->where('did', 'in', $ids)->delete();
return json(['code' => 0, 'msg' => '成功删除' . count($ids) . '个域名!']);
}
return json(['code' => -3]);
}

View File

@ -68,7 +68,11 @@ class CertOrderService
$cname = CertHelper::$cert_config[$this->atype]['cname'];
foreach($this->domainList as $domain){
$mainDomain = getMainDomain($domain);
if (!Db::name('domain')->where('name', $mainDomain)->find()) {
$drow = Db::name('domain')->where('name', $mainDomain)->find();
if (!$drow && preg_match('/^xn--/', $mainDomain)) {
$drow = Db::name('domain')->where('name', idn_to_utf8($mainDomain))->find();
}
if (!$drow) {
if (substr($domain, 0, 2) == '*.') $domain = substr($domain, 2);
$cname_row = Db::name('cert_cname')->where('domain', $domain)->where('status', 1)->find();
if (!$cname || !$cname_row) {

View File

@ -83,6 +83,9 @@ class TaskRunner
if ($row['type'] == 2) {
$dns = DnsHelper::getModel2($drow);
$recordinfo = json_decode($row['recordinfo'], true);
if ($drow['type'] == 'cloudflare' && $row['cdn'] == 1) {
$recordinfo['Line'] = '1';
}
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['backup_value']), $row['backup_value'], $recordinfo['Line'], $recordinfo['TTL']);
if (!$res) {
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
@ -98,6 +101,9 @@ class TaskRunner
if ($row['type'] == 2) {
$dns = DnsHelper::getModel2($drow);
$recordinfo = json_decode($row['recordinfo'], true);
if ($drow['type'] == 'cloudflare' && $row['cdn'] == 1) {
$recordinfo['Line'] = '0';
}
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['main_value']), $row['main_value'], $recordinfo['Line'], $recordinfo['TTL']);
if (!$res) {
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);

View File

@ -5,7 +5,7 @@ CREATE TABLE `dnsmgr_config` (
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `dnsmgr_config` VALUES ('version', '1021');
INSERT INTO `dnsmgr_config` VALUES ('version', '1028');
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');
@ -96,6 +96,7 @@ CREATE TABLE `dnsmgr_dmtask` (
`timeout` tinyint(5) NOT NULL DEFAULT 2,
`remark` varchar(100) DEFAULT NULL,
`proxy` tinyint(1) NOT NULL DEFAULT 0,
`cdn` tinyint(1) NOT NULL DEFAULT 0,
`addtime` int(11) NOT NULL DEFAULT 0,
`checktime` int(11) NOT NULL DEFAULT 0,
`checknexttime` int(11) NOT NULL DEFAULT 0,

View File

@ -152,4 +152,7 @@ CREATE TABLE IF NOT EXISTS `dnsmgr_cert_cname` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `dnsmgr_account`
ADD COLUMN `proxy` tinyint(1) NOT NULL DEFAULT '0';
ADD COLUMN `proxy` tinyint(1) NOT NULL DEFAULT '0';
ALTER TABLE `dnsmgr_dmtask`
ADD COLUMN `cdn` tinyint(1) NOT NULL DEFAULT 0;

View File

@ -13,6 +13,9 @@ class CertDnsUtils
$cnameDomainList = [];
foreach ($dnsList as $mainDomain => $list) {
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.name', $mainDomain)->field('A.*,B.type')->find();
if (!$drow && preg_match('/^xn--/', $mainDomain)) {
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.name', idn_to_utf8($mainDomain))->field('A.*,B.type')->find();
}
if (!$drow) {
if ($cname) {
foreach ($list as $key => $row) {
@ -102,6 +105,9 @@ class CertDnsUtils
$cnameDomainList = [];
foreach ($dnsList as $mainDomain => $list) {
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.name', $mainDomain)->field('A.*,B.type')->find();
if (!$drow && preg_match('/^xn--/', $mainDomain)) {
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.name', idn_to_utf8($mainDomain))->field('A.*,B.type')->find();
}
if (!$drow) {
if ($cname) {
foreach ($list as $key => $row) {

View File

@ -119,8 +119,8 @@
</span>
</a>
<ul class="treeview-menu">
<li><a href="/dmonitor/overview"><i class="fa fa-circle-o"></i> 运行概览</a></li>
<li><a href="/dmonitor/task"><i class="fa fa-circle-o"></i> 切换策略</a></li>
<li class="{:checkIfActive('overview')}"><a href="/dmonitor/overview"><i class="fa fa-circle-o"></i> 运行概览</a></li>
<li class="{:checkIfActive('task,taskform')}"><a href="/dmonitor/task"><i class="fa fa-circle-o"></i> 切换策略</a></li>
</ul>
</li>
<li class="treeview {:checkIfActive('opipset,opiplist,opipform')}">
@ -132,8 +132,8 @@
</span>
</a>
<ul class="treeview-menu">
<li><a href="/optimizeip/opipset"><i class="fa fa-circle-o"></i> 优选设置</a></li>
<li><a href="/optimizeip/opiplist"><i class="fa fa-circle-o"></i> 任务管理</a></li>
<li class="{:checkIfActive('opipset')}"><a href="/optimizeip/opipset"><i class="fa fa-circle-o"></i> 优选设置</a></li>
<li class="{:checkIfActive('opiplist,opipform')}"><a href="/optimizeip/opiplist"><i class="fa fa-circle-o"></i> 任务管理</a></li>
</ul>
</li>
<li class="treeview {:checkIfActive('certaccount,account_form,certorder,order_form,order_import,deployaccount,deploytask,deploy_form,certset,cname')}">
@ -145,12 +145,12 @@
</span>
</a>
<ul class="treeview-menu">
<li><a href="/cert/certaccount"><i class="fa fa-circle-o"></i> SSL证书账户</a></li>
<li><a href="/cert/certorder"><i class="fa fa-circle-o"></i> SSL证书订单</a></li>
<li><a href="/cert/deployaccount"><i class="fa fa-circle-o"></i> 自动部署账户</a></li>
<li><a href="/cert/deploytask"><i class="fa fa-circle-o"></i> 自动部署任务</a></li>
<li><a href="/cert/cname"><i class="fa fa-circle-o"></i> CNAME代理</a></li>
<li><a href="/cert/certset"><i class="fa fa-circle-o"></i> 计划任务设置</a></li>
<li class="{:checkIfActive('certaccount')}"><a href="/cert/certaccount"><i class="fa fa-circle-o"></i> SSL证书账户</a></li>
<li class="{:checkIfActive('certorder,order_form,order_import')}"><a href="/cert/certorder"><i class="fa fa-circle-o"></i> SSL证书订单</a></li>
<li class="{:checkIfActive('deployaccount')}"><a href="/cert/deployaccount"><i class="fa fa-circle-o"></i> 自动部署账户</a></li>
<li class="{:checkIfActive('deploytask,deploy_form')}"><a href="/cert/deploytask"><i class="fa fa-circle-o"></i> 自动部署任务</a></li>
<li class="{:checkIfActive('cname')}"><a href="/cert/cname"><i class="fa fa-circle-o"></i> CNAME代理</a></li>
<li class="{:checkIfActive('certset')}"><a href="/cert/certset"><i class="fa fa-circle-o"></i> 计划任务设置</a></li>
</ul>
</li>
<li class="treeview {:checkIfActive('noticeset,proxyset')}">
@ -162,8 +162,8 @@
</span>
</a>
<ul class="treeview-menu">
<li><a href="/system/noticeset"><i class="fa fa-circle-o"></i> 通知设置</a></li>
<li><a href="/system/proxyset"><i class="fa fa-circle-o"></i> 代理设置</a></li>
<li class="{:checkIfActive('noticeset')}"><a href="/system/noticeset"><i class="fa fa-circle-o"></i> 通知设置</a></li>
<li class="{:checkIfActive('proxyset')}"><a href="/system/proxyset"><i class="fa fa-circle-o"></i> 代理设置</a></li>
<li><a href="https://www.showdoc.com.cn/dnsmgr/11058996709621562" target="_blank" rel="noreferrer"><i class="fa fa-circle-o"></i> <span>接口文档</span></a></li>
</ul>
</li>

View File

@ -20,9 +20,7 @@
<div class="col-sm-3 col-xs-5"><input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required></div>
<div class="col-sm-3 col-xs-7 dselect"><select name="did" v-model="set.did" class="form-control" required>
<option value="">--主域名--</option>
{foreach $domains as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
<option v-for="option in domainList" :value="option.id">{{option.name}}</option>
</select></div>
</div>
<div class="form-group">
@ -50,6 +48,15 @@
<input type="text" name="backup_value" v-model="set.backup_value" placeholder="支持填写IPv4或CNAME地址" class="form-control" required>
</div>
</div>
<div class="form-group" v-show="set.type==2&&dnstype=='cloudflare'">
<div class="col-sm-offset-3 col-sm-7">
<div class="checkbox">
<label>
<input type="checkbox" name="name" v-model="set.cdn"> 切换时同时开启Cloudflare代理模式
</label>
</div>
</div>
</div>
<div class="form-group" v-show="set.type<=2">
<label class="col-sm-3 control-label no-padding-right">检测协议</label>
<div class="col-sm-6">
@ -131,6 +138,7 @@
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
var domainList = {$domains|json_encode|raw};
var support_ping = '{$support_ping}';
new Vue({
el: '#app',
@ -153,7 +161,10 @@ new Vue({
timeout: 2,
cycle: 3,
proxy: 0,
cdn: 0,
},
dnstype: null,
domainList: domainList,
recordList: [],
typeList: [
{value:0, label:'无操作'},
@ -176,6 +187,10 @@ new Vue({
if(typeof record.Value == 'object') this.set.main_value = record.Value[0];
else this.set.main_value = record.Value;
}
},
'set.did': function(val){
if(val == '') return;
this.dnstype = this.domainList.find(item => item.id == val).type;
}
},
mounted() {

View File

@ -26,7 +26,6 @@
<label class="col-sm-3 control-label">选择域名</label>
<div class="col-sm-9">
<select name="domain" id="domainList" class="form-control"></select>
<span class="pull-right"><a href="/domain/add">批量添加</a></span>
</div>
</div>
@ -107,7 +106,11 @@
</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>{/if}
{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('delete')">删除</a></li></ul>
</div>{/if}
</form>
<table id="listTable">
@ -137,7 +140,12 @@ $(document).ready(function(){
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'id',
columns: [
{
field: '',
checkbox: true
},
{
field: 'id',
title: 'ID'
@ -246,25 +254,12 @@ function saveAdd(){
});
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/get',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store2").modal('show');
$("#form-store2 input[name=id]").val(data.data.id);
$("#form-store2 select[name=is_hide]").val(data.data.is_hide);
$("#form-store2 select[name=is_sso]").val(data.data.is_sso);
$("#form-store2 input[name=remark]").val(data.data.remark);
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
$("#modal-store2").modal('show');
$("#form-store2 input[name=id]").val(row.id);
$("#form-store2 select[name=is_hide]").val(row.is_hide);
$("#form-store2 select[name=is_sso]").val(row.is_sso);
$("#form-store2 input[name=remark]").val(row.remark);
}
function saveEdit(){
var ii = layer.load(2);
@ -276,14 +271,9 @@ function saveEdit(){
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store2").modal('hide');
searchRefresh();
});
layer.msg(data.msg, {icon:1, time:800});
$("#modal-store2").modal('hide');
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2})
}
@ -358,6 +348,78 @@ function getDomainList(){
cache:false
});
}
function operation(action){
var rows = $("#listTable").bootstrapTable('getSelections');
if(rows.length == 0){
layer.msg('请选择要操作的记录');
return;
}
var ids = [];
$.each(rows, function(index, item){
ids.push(item.id);
})
if(action == 'editremark'){
batch_edit_remark(ids)
return;
}
var confirmobj = layer.confirm('确定要删除所选记录吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/batchdel',
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});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
function batch_edit_remark(ids) {
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="remark2" value="" autocomplete="off" placeholder="备注信息"></div></div>',
btn: ['确认', '取消'],
yes: function(){
var remark = $("input[name='remark2']").val();
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/domain/op/act/batchedit',
data : {ids:ids, remark:remark},
dataType : 'json',
success : function(data) {
layer.close(ii);
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
searchRefresh();
});
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
});
}
function loading(){
layer.load(2);
}

View File

@ -31,7 +31,7 @@ return [
'show_error_msg' => true,
'exception_tmpl' => \think\facade\App::getAppPath() . 'view/exception.tpl',
'version' => '1029',
'version' => '1028',
'dbversion' => '1023'
'dbversion' => '1028'
];