mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-02-21 15:31:12 +08:00
469 lines
19 KiB
HTML
469 lines
19 KiB
HTML
{extend name="common/layout" /}
|
||
{block name="title"}SSL证书订单列表{/block}
|
||
{block name="main"}
|
||
<style>
|
||
tbody tr>td:nth-child(5){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
|
||
.tips{cursor:pointer;}
|
||
textarea.form-control{margin-bottom: 3px;}
|
||
hr{margin-top: 10px;margin-bottom: 15px;border-top: 1px solid #eee;}
|
||
.modal-log{padding: 15px 15px 0 15px}
|
||
pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51, 51, 51, 1);white-space: pre-line;color: rgba(236, 236, 236, 1)}
|
||
</style>
|
||
<div class="row">
|
||
<div class="col-xs-12 center-block" style="float: none;">
|
||
<div class="panel panel-default panel-intro">
|
||
<div class="panel-body">
|
||
|
||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
|
||
<input type="hidden" name="id" value="">
|
||
<div class="form-group">
|
||
<label>搜索</label>
|
||
<div class="form-group">
|
||
<input type="text" class="form-control" name="domain" placeholder="域名">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<select name="type" class="form-control"><option value="">所有平台</option>{foreach $types as $k=>$v}
|
||
<option value="{$k}">{$v}</option>
|
||
{/foreach}</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<select name="status" class="form-control"><option value="">所有状态</option><option value="0">待提交</option><option value="1">待验证</option><option value="2">正在验证</option><option value="5">失败</option><option value="3">已签发</option><option value="4">已吊销</option><option value="6">即将过期</option><option value="7">已过期</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>
|
||
<div class="btn-group">
|
||
<a href="/cert/order/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||
<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="/cert/order/import">导入已有证书</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="javascript:operation('delete')">删除</a></li><li><a href="javascript:operation('reset')">重置订单</a></li><li><a href="javascript:operation('open')">开启续签</a></li><li><a href="javascript:operation('close')">关闭续签</a></li></ul>
|
||
</div>
|
||
|
||
</form>
|
||
|
||
<table id="listTable">
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/block}
|
||
{block name="script"}
|
||
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
|
||
<script src="{$cdnpublic}bootstrap-table/1.21.4/bootstrap-table.min.js"></script>
|
||
<script src="{$cdnpublic}bootstrap-table/1.21.4/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||
<script src="{$cdnpublic}FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
||
<script src="/static/js/custom.js"></script>
|
||
<script>
|
||
$(document).ready(function(){
|
||
updateToolbar();
|
||
const defaultPageSize = 10;
|
||
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
|
||
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
|
||
|
||
$("#listTable").bootstrapTable({
|
||
url: '/cert/order/data',
|
||
pageNumber: pageNumber,
|
||
pageSize: pageSize,
|
||
classes: 'table table-striped table-hover table-bordered',
|
||
uniqueId: 'id',
|
||
columns: [
|
||
{
|
||
field: '',
|
||
checkbox: true
|
||
},
|
||
{
|
||
field: 'id',
|
||
title: 'ID'
|
||
},
|
||
{
|
||
field: 'typename',
|
||
title: '证书账户',
|
||
formatter: function(value, row, index) {
|
||
return '<span title="'+row.aremark+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+value+'('+row.aid+')</span>';
|
||
}
|
||
},
|
||
{
|
||
field: 'domains',
|
||
title: '绑定域名',
|
||
formatter: function(value, row, index) {
|
||
return value.join('<br/>');
|
||
}
|
||
},
|
||
{
|
||
field: 'keytype',
|
||
title: '证书信息',
|
||
formatter: function(value, row, index) {
|
||
return '<span class="text-muted">签名算法:</span>'+row.keytype+'('+row.keysize+')'+(row.issuer?'<br/><span class="text-muted">颁发机构:</span>'+row.issuer:'');
|
||
}
|
||
},
|
||
{
|
||
field: 'isauto',
|
||
title: '自动续签',
|
||
formatter: function(value, row, index) {
|
||
if(value == 1){
|
||
return '<div class="material-switch"><input id="isauto'+row.id+'" type="checkbox" checked onchange="setAuto('+row.id+',0)"/><label for="isauto'+row.id+'" class="label-primary"></label></div>';
|
||
}else{
|
||
return '<div class="material-switch"><input id="isauto'+row.id+'" type="checkbox" onchange="setAuto('+row.id+',1)"/><label for="isauto'+row.id+'" class="label-primary"></label></div>';
|
||
}
|
||
}
|
||
},
|
||
{
|
||
field: 'issuetime',
|
||
title: '签发时间',
|
||
formatter: function(value, row, index) {
|
||
return value ? value.substring(0,10) : '暂未签发';
|
||
}
|
||
},
|
||
{
|
||
field: 'end_day',
|
||
title: '到期时间',
|
||
formatter: function(value, row, index) {
|
||
if(value){
|
||
if(value > 7){
|
||
return '<span title="'+row.expiretime+'" data-toggle="tooltip" data-placement="right" style="color:green">剩余' + value + '天<span>';
|
||
}else if(value > 0){
|
||
return '<span title="'+row.expiretime+'" data-toggle="tooltip" data-placement="right" style="color:#ff7f00">剩余' + value + '天<span>';
|
||
}else{
|
||
return '<span title="'+row.expiretime+'" data-toggle="tooltip" data-placement="right" style="color:red">已过期<span>';
|
||
}
|
||
}else{
|
||
return '暂未签发';
|
||
}
|
||
}
|
||
},
|
||
{
|
||
field: 'status',
|
||
title: '状态',
|
||
formatter: function(value, row, index) {
|
||
if(value == 4) {
|
||
return '<span class="label" style="background-color: #a5a5a5;">已吊销</span>';
|
||
} else if(value == 3) {
|
||
return '<span class="label label-success">已签发</span>';
|
||
} else if(value == 2) {
|
||
if(row.retrytime != null){
|
||
var now = new Date().getTime();
|
||
var retry = new Date(row.retrytime).getTime();
|
||
var diff = retry - now;
|
||
if(diff > 0){
|
||
var min = Math.floor(diff / 60000);
|
||
var sec = Math.floor((diff - min * 60000) / 1000);
|
||
return '<span title="'+min+'分'+sec+'秒后自动验证" data-toggle="tooltip" data-placement="top" class="label" style="background-color: #3e76fb;">正在验证</span>';
|
||
}
|
||
}
|
||
return '<span class="label" style="background-color: #3e76fb;">正在验证</span>';
|
||
} else if(value == 1) {
|
||
if(row.retrytime != null){
|
||
var now = new Date().getTime();
|
||
var retry = new Date(row.retrytime).getTime();
|
||
var diff = retry - now;
|
||
if(diff > 0){
|
||
var min = Math.floor(diff / 60000);
|
||
var sec = Math.floor((diff - min * 60000) / 1000);
|
||
return '<span title="'+min+'分'+sec+'秒后自动验证" data-toggle="tooltip" data-placement="top" class="label" style="background-color: #3e76fb;">待验证</span>';
|
||
}
|
||
}
|
||
return '<span class="label" style="background-color: #3e76fb;">待验证</span>';
|
||
} else if(value == 0) {
|
||
return '<span class="label label-info">待提交</span>';
|
||
} else {
|
||
var title = '失败';
|
||
if(value == -1) title = '购买证书失败';
|
||
else if(value == -2) title = '创建订单失败';
|
||
else if(value == -3) title = '添加DNS失败';
|
||
else if(value == -4) title = '验证DNS失败';
|
||
else if(value == -5) title = '验证订单失败';
|
||
else if(value == -6) title = '订单验证未通过';
|
||
else if(value == -7) title = '签发证书失败';
|
||
if(row.retrytime != null){
|
||
var now = new Date().getTime();
|
||
var retry = new Date(row.retrytime).getTime();
|
||
var diff = retry - now;
|
||
if(diff > 0){
|
||
var min = Math.floor(diff / 60000);
|
||
var sec = Math.floor((diff - min * 60000) / 1000);
|
||
return '<span title="'+min+'分'+sec+'秒后自动重试" data-toggle="tooltip" data-placement="top" class="label label-danger">'+title+'</span>'+(row.error?' <span onclick="showmsg(\''+row.error+'\')" class="tips" title="失败原因"><i class="fa fa-info-circle"></i></span>':'');
|
||
}
|
||
}
|
||
return '<span class="label label-danger">'+title+'</span>'+(row.error?' <span onclick="showmsg(\''+row.error+'\')" class="tips" title="失败原因"><i class="fa fa-info-circle"></i></span>':'');
|
||
}
|
||
}
|
||
},
|
||
{
|
||
field: '',
|
||
title: '操作',
|
||
formatter: function(value, row, index) {
|
||
var html = '';
|
||
if(row.status == 0) {
|
||
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-play-circle"></i> 立即提交</a> ';
|
||
}else if(row.status == 1) {
|
||
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-check-circle"></i> 提交验证</a> ';
|
||
}else if(row.status == 2) {
|
||
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-check-circle"></i> 继续验证</a> ';
|
||
}else if(row.status == 3) {
|
||
html += '<a href="javascript:download(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-download"></i> 下载</a> <a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-warning btn-xs"><i class="fa fa-refresh"></i> 续签</a> ';
|
||
}else if(row.status == 4) {
|
||
html += '<a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-play-circle"></i> 重新申请</a> ';
|
||
}else{
|
||
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-repeat"></i> 重试</a> ';
|
||
}
|
||
html += '<a href="/cert/order/edit?id='+row.id+'" class="btn btn-primary btn-xs"><i class="fa fa-edit"></i> 修改</a> ';
|
||
html += '<div class="btn-group dropdown-group" role="group"><button type="button" class="btn btn-info btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">更多 <span class="caret"></span></button><ul class="dropdown-menu">';
|
||
html += '<li><a href="javascript:showLog(\''+row.processid+'\')">查看日志</a></li>';
|
||
if(row.status == 3){
|
||
html += '<li><a href="/cert/deploytask?oid='+row.id+'">部署任务</a></li>';
|
||
html += '<li><a href="javascript:revokeOrder(\''+row.id+'\')">吊销证书</a></li>';
|
||
}else if(row.status < 0){
|
||
html += '<li><a href="javascript:resetOrder(\''+row.id+'\')">重置订单</a></li>';
|
||
}else if(row.status == 1 || row.status == 2){
|
||
html += '<li><a href="javascript:resetOrder(\''+row.id+'\')">取消订单</a></li>';
|
||
}
|
||
html += '<li><a href="javascript:delItem('+row.id+','+row.status+')">删除</a></li>';
|
||
html += '</ul></div>';
|
||
return html;
|
||
}
|
||
},
|
||
],
|
||
onLoadSuccess: function(data) {
|
||
$('[data-toggle="tooltip"]').tooltip()
|
||
$('.dropdown-group').on('show.bs.dropdown', function (e) {
|
||
var btnPos = $(e.target)[0].getBoundingClientRect();
|
||
var screenWidth = $(window).width();
|
||
var screenHeight = $(window).height();
|
||
var childrenWidth = $(e.target).children('.dropdown-menu').width();
|
||
var childrenHeight = $(e.target).children('.dropdown-menu').height();
|
||
var top = btnPos.bottom;
|
||
if(top + childrenHeight + 12 > screenHeight){
|
||
top = btnPos.top - childrenHeight - 12;
|
||
}
|
||
var left = btnPos.left;
|
||
if(left + childrenWidth + 7 > screenWidth){
|
||
left = screenWidth - childrenWidth - 7;
|
||
}
|
||
$(e.target).children('.dropdown-menu').css({position:'fixed', top:top, left:left});
|
||
});
|
||
}
|
||
})
|
||
})
|
||
function showmsg(msg){
|
||
layer.alert(msg, {icon: 0, title: '失败原因'});
|
||
}
|
||
function setAuto(id, status){
|
||
$.post('/cert/order/setauto', {id: id, isauto: status}, function(data){
|
||
if(data.code == 0) {
|
||
layer.msg('已'+(status==1?'开启':'关闭')+'自动续签', {icon: 1, time:800});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
} else {
|
||
layer.msg(data.msg, {icon: 2});
|
||
}
|
||
}, 'json');
|
||
}
|
||
function delItem(id,status){
|
||
var msg = '确定要删除此证书订单吗?';
|
||
if(status == 3) msg += '删除后将无法再次下载证书';
|
||
layer.confirm(msg, {
|
||
btn: ['确定','取消']
|
||
}, function(){
|
||
$.post('/cert/order/del', {id: id}, function(data){
|
||
if(data.code == 0) {
|
||
layer.msg('删除成功', {icon: 1, time:800});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
} else {
|
||
layer.msg(data.msg, {icon: 2});
|
||
}
|
||
}, 'json');
|
||
});
|
||
}
|
||
function doOrder(id, reset){
|
||
reset = reset || 0;
|
||
var ii = layer.msg('正在处理证书订单...', {icon: 16,shade: 0.1,time: 0});
|
||
$.ajax({
|
||
type: "POST",
|
||
url: "/cert/order/process",
|
||
data: {id: id, reset: reset},
|
||
dataType: 'json',
|
||
success: function(data) {
|
||
layer.close(ii);
|
||
if(data.code == 0) {
|
||
layer.alert(data.msg, {icon: 1});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
} else {
|
||
if(data.msg == '订单正在处理中,请稍后再试'){
|
||
layer.alert(data.msg, {icon: 2}, function(){
|
||
layer.closeAll();
|
||
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
|
||
showLog(row.processid)
|
||
});
|
||
}else{
|
||
layer.alert(data.msg, {icon: 2});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
}
|
||
}
|
||
},
|
||
error: function(data){
|
||
layer.close(ii);
|
||
layer.msg('执行超时,请稍后刷新列表或查看日志', {icon:0});
|
||
}
|
||
});
|
||
}
|
||
function resetOrder(id){
|
||
layer.confirm('重置订单后,订单将变成待提交状态,是否确定?', {
|
||
btn: ['确定','取消'], title: '重置订单', icon: 0
|
||
}, function(){
|
||
$.post('/cert/order/reset', {id: id}, function(data){
|
||
if(data.code == 0) {
|
||
layer.msg('重置订单状态成功', {icon: 1, time:800});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
} else {
|
||
layer.msg(data.msg, {icon: 2});
|
||
}
|
||
}, 'json');
|
||
});
|
||
}
|
||
function revokeOrder(id){
|
||
layer.confirm('是否确定要吊销该证书?吊销后浏览器将不再信任该证书', {
|
||
btn: ['确定','取消'], title: '吊销证书', icon: 0
|
||
}, function(){
|
||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||
$.post('/cert/order/revoke', {id: id}, function(data){
|
||
layer.close(ii);
|
||
if(data.code == 0) {
|
||
layer.alert('吊销证书成功!', {icon: 1});
|
||
$('#listTable').bootstrapTable('refresh');
|
||
} else {
|
||
layer.msg(data.msg, {icon: 2});
|
||
}
|
||
}, 'json');
|
||
});
|
||
}
|
||
function renewOrder(id){
|
||
layer.confirm('是否确定重新申请该证书?', {
|
||
btn: ['确定','取消'], title: '续签证书', icon: 0
|
||
}, function(){
|
||
doOrder(id, 1);
|
||
});
|
||
}
|
||
function download(id){
|
||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||
$.post('/cert/order/get', {id: id}, function(data){
|
||
layer.close(ii);
|
||
if(data.code == 0) {
|
||
layer.open({
|
||
type: 1,
|
||
title: '下载证书',
|
||
area: [$(window).width() > 768 ? '600px' : '100%', '360px'],
|
||
shadeClose: true,
|
||
content: '<div class="modal-body"><div class="row text-center"><div class="form-group col-xs-6"><label>证书(PEM格式)</label><textarea rows="6" class="form-control" name="fullchain">'+data.data.fullchain+'</textarea><a onclick="copy(\'fullchain\')" class="btn btn-default"><i class="fa fa-copy"></i> 复制</a> <a onclick="downloadFile(\'fullchain.crt\',\'fullchain\')" class="btn btn-default"><i class="fa fa-download"></i> 下载</a></div><div class="form-group col-xs-6"><label>私钥(PEM格式)</label><textarea rows="6" class="form-control" name="privatekey">'+data.data.privatekey+'</textarea><a onclick="copy(\'privatekey\')" class="btn btn-default"><i class="fa fa-copy"></i> 复制</a> <a onclick="downloadFile(\'private.key\',\'privatekey\')" class="btn btn-default"><i class="fa fa-download"></i> 下载</a></div></div><hr/><label>IIS服务器(pfx格式):</label><input type="hidden" name="pfx" value="'+data.data.pfx+'"><a onclick="downloadPFX()" class="btn btn-default"><i class="fa fa-download"></i> 下载</a>(密码为123456)</div>',
|
||
});
|
||
} else {
|
||
layer.alert(data.msg, {icon: 2});
|
||
}
|
||
}, 'json');
|
||
}
|
||
function copy(obj){
|
||
$("textarea[name='"+obj+"']").select();
|
||
document.execCommand("Copy");
|
||
layer.msg('复制成功', {icon:1, time:600});
|
||
}
|
||
function downloadFile(filename,obj){
|
||
var content = $("textarea[name='"+obj+"']").val();
|
||
if(!content) return;
|
||
var blob = new Blob([content], {type: "application/force-download"});
|
||
saveAs(blob, filename);
|
||
}
|
||
function downloadPFX(id){
|
||
var content = $("input[name='pfx']").val();
|
||
if(!content) return;
|
||
var bstr = atob(content),
|
||
n = bstr.length,
|
||
u8arr = new Uint8Array(n)
|
||
while (n--) {
|
||
u8arr[n] = bstr.charCodeAt(n)
|
||
}
|
||
var filename = 'cert.pfx';
|
||
var blob = new Blob([u8arr], {type: "application/force-download"});
|
||
saveAs(blob, filename);
|
||
}
|
||
var intverval;
|
||
function showLog(processid){
|
||
if(processid == '' || processid == 'null'){
|
||
layer.msg('暂无日志', {time: 600});
|
||
return;
|
||
}
|
||
$.post('/cert/order/show_log', {processid: processid}, function(data){
|
||
if(data.code == 0) {
|
||
layer.closeAll();
|
||
var filemtime = data.time;
|
||
layer.open({
|
||
type: 1,
|
||
title: '查看日志',
|
||
area: [$(window).width() > 768 ? '600px' : '100%', '400px'],
|
||
shadeClose: true,
|
||
resize: false,
|
||
content: '<div class="modal-log"><pre class="pre-log" id="execLog">'+data.data+'</pre></div>',
|
||
success: function(){
|
||
var exec_log = $('#execLog');
|
||
exec_log[0].scrollTop = exec_log[0].scrollHeight
|
||
intverval = setInterval(function(){
|
||
$.post('/cert/order/show_log', {processid: processid}, function(data){
|
||
if(data.code == 0 && data.time != filemtime) {
|
||
var exec_log = $('#execLog');
|
||
exec_log.html(data.data);
|
||
filemtime = data.time;
|
||
exec_log[0].scrollTop = exec_log[0].scrollHeight
|
||
}
|
||
}, 'json');
|
||
}, 1500)
|
||
},
|
||
end: function(){
|
||
clearInterval(intverval);
|
||
}
|
||
});
|
||
} else {
|
||
layer.msg(data.msg, {icon: 2, time: 600});
|
||
}
|
||
}, 'json');
|
||
}
|
||
function operation(action){
|
||
var rows = $("#listTable").bootstrapTable('getSelections');
|
||
if(rows.length == 0){
|
||
layer.msg('请选择要操作的订单');
|
||
return;
|
||
}
|
||
var ids = [];
|
||
for(var i in rows){
|
||
ids.push(rows[i].id);
|
||
}
|
||
if(action == 'delete'){
|
||
if(!confirm('确定要删除所选证书吗?删除后将无法再次下载')) return;
|
||
}else if(action == 'reset'){
|
||
if(!confirm('重置订单后,订单将变成待提交状态,是否确定重置?')) return;
|
||
}
|
||
|
||
var ii = layer.load(2);
|
||
$.ajax({
|
||
type : 'POST',
|
||
url : '/cert/order/operation',
|
||
data : {action: action, 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});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
{/block} |