Localize CDN assets

This commit is contained in:
耗子 2025-10-16 17:56:59 +08:00
parent 4bf87156e3
commit 135abfac6e
60 changed files with 8460 additions and 8219 deletions

View File

@ -19,8 +19,7 @@ class ViewOutput
{
View::assign('islogin', $request->islogin);
View::assign('user', $request->user);
View::assign('cdnpublic', 'https://s4.zstatic.net/ajax/libs/');
View::assign('skin', getAdminSkin());
View::assign('skin', getAdminSkin());
return $next($request);
}
}

View File

@ -1,180 +1,180 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>聚合DNS管理系统 - 登录</title>
<link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css" rel="stylesheet"/>
<script src="{$cdnpublic}jquery/3.6.4/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style type="text/css">
body{color:#999;background-color:#f1f4fd;background-size:cover}
a{color:#444}
.login-screen{max-width:430px;padding:0;margin:100px auto 0 auto}
.login-screen .well{border-radius:3px;-webkit-box-shadow:0 0 30px rgba(0,0,0,.1);box-shadow:0 0 30px rgba(0,0,0,.1);background:#fff;border:none;padding:0}
@media (max-width:767px){.login-screen{padding:20px 0}
}
.profile-img-card{width:100px;height:100px;display:block;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;margin:-93px auto 30px;border:5px solid #fff}
.profile-name-card{text-align:center}
.login-head{background:#899fe1;border-radius:3px 3px 0 0}
.login-form{padding:40px 30px;position:relative;z-index:99}
#login-form{margin-top:20px}
#login-form .input-group{margin-bottom:15px}
#login-form .form-control{font-size:14px}
#totp-form{margin-top:20px}
#totp-form .input-group{margin-bottom:15px}
#totp-form .form-control{font-size:14px}
</style>
</head>
<body>
<div class="container">
<div class="login-wrapper">
<div class="login-screen">
<div class="well">
<div class="login-head">
<img src="/static/images/login-head.png" style="width:100%;"/>
</div>
<div class="login-form">
<img id="profile-img" class="profile-img-card" src="/static/images/user.png"/>
<p id="profile-name" class="profile-name-card"></p>
<form action="" id="login-form" onsubmit="return doLogin()">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></div>
<input type="text" class="form-control" placeholder="用户名" name="username" required="required"/>
</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="password" class="form-control" placeholder="密码" name="password" required="required"/>
</div>
{if config_get('vcode', '1')=='1'}<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="text" class="form-control input-lg" placeholder="验证码" name="code" autocomplete="off" required="required"/>
<span class="input-group-addon" style="padding: 0">
<img id="verifycode" src="/verifycode" height="45" onclick="this.src='/verifycode?r='+Math.random();" title="点击更换验证码">
</span>
</div>{/if}
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" id="submit" style="background:#708eea;">登 录</button>
</div>
<div class="pull-right"><a href="javascript:findpwd()">忘记密码</a></div>
</form>
<form action="" id="totp-form" onsubmit="return doTotp()" style="display:none;">
<div class="alert alert-info" role="alert">TOTP二次验证</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="number" class="form-control input-lg" placeholder="输入动态口令" name="totp_code" id="totp_code" autocomplete="off" required="required"/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" id="submit" style="background:#708eea;">登 录</button>
</div>
<div class="pull-right"><a href="javascript:findpwd()">忘记密码</a></div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-findpwd">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">忘记密码</h4>
</div>
<div class="modal-body">
<strong>重置密码</strong>
<p>进入网站根目录,执行命令:<br/><code>php think reset pwd 用户名 密码</code></p>
<strong>关闭TOTP</strong>
<p>进入网站根目录,执行命令:<br/><code>php think reset totp 用户名</code></p>
</div>
</div>
</div>
</div>
<script src="{$cdnpublic}twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
function doLogin(){
var username = $("input[name='username']").val();
var password = $("input[name='password']").val();
var code = $("input[name='code']").val();
if(username == ''){
layer.msg('用户名不能为空', {icon: 2});
return false;
}
if(password == ''){
layer.msg('密码不能为空', {icon: 2});
return false;
}
if(code == ''){
layer.msg('验证码不能为空', {icon: 2});
return false;
}
var ii = layer.load(2);
$.ajax({
type: "POST",
url: "/login",
data: $("#login-form").serialize(),
dataType: 'json',
success: function(data){
layer.close(ii);
if(data.code == 0){
layer.msg('登录成功,正在跳转到首页', {icon: 1,shade: 0.01,time: 15000});
window.location.href = '/';
}else{
if(data.vcode==1){
$("#verifycode").attr('src', '/verifycode?r='+Math.random())
}else if(data.vcode==2){
$("#totp-form").show();
$("#login-form").hide();
$("#totp_code").focus();
return false;
}
layer.alert(data.msg, {icon: 2});
}
}
});
return false;
}
function doTotp(){
var code = $("#totp_code").val();
if(code.length != 6){
layer.msg('动态口令格式错误', {icon: 2});
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/auth/totp', {code:code}, function(res){
layer.close(ii);
if(res.code == 0){
layer.msg('登录成功,正在跳转到首页', {icon: 1,shade: 0.01,time: 15000});
window.location.href = '/';
}else{
layer.alert(res.msg, {icon: 2});
}
});
return false;
}
function findpwd(){
$('#modal-findpwd').modal('show');
}
$(document).ready(function(){
$("#totp_code").keyup(function(){
var code = $(this).val();
if(code.length == 6){
$("#totp-form").submit();
}
});
});
</script>
</body>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>聚合DNS管理系统 - 登录</title>
<link href="/static/css/bootstrap-3.4.1.min.css" rel="stylesheet"/>
<link href="/static/css/font-awesome-4.7.0.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css" rel="stylesheet"/>
<script src="/static/js/jquery-3.6.4.min.js"></script>
<!--[if lt IE 9]>
<script src="/static/js/html5shiv-3.7.3.min.js"></script>
<script src="/static/js/respond-1.4.2.min.js"></script>
<![endif]-->
<style type="text/css">
body{color:#999;background-color:#f1f4fd;background-size:cover}
a{color:#444}
.login-screen{max-width:430px;padding:0;margin:100px auto 0 auto}
.login-screen .well{border-radius:3px;-webkit-box-shadow:0 0 30px rgba(0,0,0,.1);box-shadow:0 0 30px rgba(0,0,0,.1);background:#fff;border:none;padding:0}
@media (max-width:767px){.login-screen{padding:20px 0}
}
.profile-img-card{width:100px;height:100px;display:block;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;margin:-93px auto 30px;border:5px solid #fff}
.profile-name-card{text-align:center}
.login-head{background:#899fe1;border-radius:3px 3px 0 0}
.login-form{padding:40px 30px;position:relative;z-index:99}
#login-form{margin-top:20px}
#login-form .input-group{margin-bottom:15px}
#login-form .form-control{font-size:14px}
#totp-form{margin-top:20px}
#totp-form .input-group{margin-bottom:15px}
#totp-form .form-control{font-size:14px}
</style>
</head>
<body>
<div class="container">
<div class="login-wrapper">
<div class="login-screen">
<div class="well">
<div class="login-head">
<img src="/static/images/login-head.png" style="width:100%;"/>
</div>
<div class="login-form">
<img id="profile-img" class="profile-img-card" src="/static/images/user.png"/>
<p id="profile-name" class="profile-name-card"></p>
<form action="" id="login-form" onsubmit="return doLogin()">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></div>
<input type="text" class="form-control" placeholder="用户名" name="username" required="required"/>
</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="password" class="form-control" placeholder="密码" name="password" required="required"/>
</div>
{if config_get('vcode', '1')=='1'}<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="text" class="form-control input-lg" placeholder="验证码" name="code" autocomplete="off" required="required"/>
<span class="input-group-addon" style="padding: 0">
<img id="verifycode" src="/verifycode" height="45" onclick="this.src='/verifycode?r='+Math.random();" title="点击更换验证码">
</span>
</div>{/if}
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" id="submit" style="background:#708eea;">登 录</button>
</div>
<div class="pull-right"><a href="javascript:findpwd()">忘记密码</a></div>
</form>
<form action="" id="totp-form" onsubmit="return doTotp()" style="display:none;">
<div class="alert alert-info" role="alert">TOTP二次验证</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="number" class="form-control input-lg" placeholder="输入动态口令" name="totp_code" id="totp_code" autocomplete="off" required="required"/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" id="submit" style="background:#708eea;">登 录</button>
</div>
<div class="pull-right"><a href="javascript:findpwd()">忘记密码</a></div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-findpwd">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">忘记密码</h4>
</div>
<div class="modal-body">
<strong>重置密码</strong>
<p>进入网站根目录,执行命令:<br/><code>php think reset pwd 用户名 密码</code></p>
<strong>关闭TOTP</strong>
<p>进入网站根目录,执行命令:<br/><code>php think reset totp 用户名</code></p>
</div>
</div>
</div>
</div>
<script src="/static/js/bootstrap-3.4.1.min.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script>
function doLogin(){
var username = $("input[name='username']").val();
var password = $("input[name='password']").val();
var code = $("input[name='code']").val();
if(username == ''){
layer.msg('用户名不能为空', {icon: 2});
return false;
}
if(password == ''){
layer.msg('密码不能为空', {icon: 2});
return false;
}
if(code == ''){
layer.msg('验证码不能为空', {icon: 2});
return false;
}
var ii = layer.load(2);
$.ajax({
type: "POST",
url: "/login",
data: $("#login-form").serialize(),
dataType: 'json',
success: function(data){
layer.close(ii);
if(data.code == 0){
layer.msg('登录成功,正在跳转到首页', {icon: 1,shade: 0.01,time: 15000});
window.location.href = '/';
}else{
if(data.vcode==1){
$("#verifycode").attr('src', '/verifycode?r='+Math.random())
}else if(data.vcode==2){
$("#totp-form").show();
$("#login-form").hide();
$("#totp_code").focus();
return false;
}
layer.alert(data.msg, {icon: 2});
}
}
});
return false;
}
function doTotp(){
var code = $("#totp_code").val();
if(code.length != 6){
layer.msg('动态口令格式错误', {icon: 2});
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/auth/totp', {code:code}, function(res){
layer.close(ii);
if(res.code == 0){
layer.msg('登录成功,正在跳转到首页', {icon: 1,shade: 0.01,time: 15000});
window.location.href = '/';
}else{
layer.alert(res.msg, {icon: 2});
}
});
return false;
}
function findpwd(){
$('#modal-findpwd').modal('show');
}
$(document).ready(function(){
$("#totp_code").keyup(function(){
var code = $(this).val();
if(code.length == 6){
$("#totp-form").submit();
}
});
});
</script>
</body>
</html>

View File

@ -1,340 +1,340 @@
{extend name="common/layout" /}
{block name="title"}{$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;
}
/* 账户类型卡片样式 */
.account-type-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-bottom: 20px;
}
.account-type-category {
width: 100%;
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.account-type-card {
width: calc(25% - 15px);
min-width: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
background: #fff;
height: 100px;
overflow: hidden;
}
.account-type-card:hover {
border-color: #409EFF;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.account-type-card .icon {
width: 30px;
margin: 11px 8px;
float: left;
}
.account-type-card .content {
margin-left: 38px;
}
.account-type-card .title {
font-size: 14px;
font-weight: bold;
margin-bottom: 3px;
color: #333;
}
.account-type-card .desc {
font-size: 12px;
color: #999;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 768px) {
.account-type-card {
width: calc(50% - 15px);
}
}
@media (max-width: 480px) {
.account-type-card {
width: 100%;
height: 78px;
}
.account-type-card .desc {
-webkit-line-clamp: 1;
}
}
</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}{$title}</h3></div>
<div class="panel-body">
<!-- 账户类型选择视图 -->
<div id="account-type-view" v-if="!selectedType">
<div v-for="(category, classId) in groupedTypes" :key="classId">
<div class="account-type-category">{{ category.label }}</div>
<div class="account-type-container">
<div class="account-type-card" v-for="type in category.types" :key="type.value" @click="selectType(type.value)">
<img class="icon" :src="'/static/images/' + typeList[type.value].icon" :alt="type.label">
<div class="content">
<div class="title">{{ type.label }}</div>
<div class="desc">{{ typeList[type.value].desc || ''}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 表单视图 -->
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="accountform" v-if="selectedType">
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>账户类型</label>
<div class="col-sm-6">
<div class="form-control-static">
{{ typeList[set.type].name }}
<a href="javascript:;" @click="selectedType = false" class="pull-right btn btn-default" v-if="action=='add'">重新选择</a>
</div>
<input type="hidden" name="type" v-model="set.type">
</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">&times;</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="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var info = {$info|json_encode|raw};
var typeList = {$typeList|json_encode|raw};
var classList = {$classList|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
selectedType: false,
set: {
deploy: '{$deploy}',
id: '',
type: '',
name: '',
config : '',
remark: '',
},
inputs: {},
config: {},
typeList: typeList,
classList: classList,
note: '',
typeOption: [],
},
watch: {
'set.type': function(val){
if(this.action == 'add' && val && typeList[val]){
this.inputs = typeList[val].inputs;
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)
})
}
}
},
computed: {
groupedTypes() {
return Object.keys(classList).map((key) => {
var tempList = [];
Object.keys(typeList).forEach((key2) => {
if(typeList[key2].class == key){
tempList.push({label: typeList[key2].name, value: key2})
}
})
return {label: classList[key], types: tempList}
})
}
},
mounted() {
this.typeOption = this.groupedTypes;
if(this.action == 'edit') {
this.selectedType = true;
}
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].inputs;
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: {
selectType(type) {
this.set.type = type;
this.selectedType = true;
},
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(){
if(data.msg.indexOf('自动部署账户')>0){
window.location.href = '/cert/deployaccount';
}else{
window.location.href = '/cert/certaccount';
}
});
}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}
{extend name="common/layout" /}
{block name="title"}{$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;
}
/* 账户类型卡片样式 */
.account-type-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-bottom: 20px;
}
.account-type-category {
width: 100%;
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.account-type-card {
width: calc(25% - 15px);
min-width: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
background: #fff;
height: 100px;
overflow: hidden;
}
.account-type-card:hover {
border-color: #409EFF;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.account-type-card .icon {
width: 30px;
margin: 11px 8px;
float: left;
}
.account-type-card .content {
margin-left: 38px;
}
.account-type-card .title {
font-size: 14px;
font-weight: bold;
margin-bottom: 3px;
color: #333;
}
.account-type-card .desc {
font-size: 12px;
color: #999;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 768px) {
.account-type-card {
width: calc(50% - 15px);
}
}
@media (max-width: 480px) {
.account-type-card {
width: 100%;
height: 78px;
}
.account-type-card .desc {
-webkit-line-clamp: 1;
}
}
</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}{$title}</h3></div>
<div class="panel-body">
<!-- 账户类型选择视图 -->
<div id="account-type-view" v-if="!selectedType">
<div v-for="(category, classId) in groupedTypes" :key="classId">
<div class="account-type-category">{{ category.label }}</div>
<div class="account-type-container">
<div class="account-type-card" v-for="type in category.types" :key="type.value" @click="selectType(type.value)">
<img class="icon" :src="'/static/images/' + typeList[type.value].icon" :alt="type.label">
<div class="content">
<div class="title">{{ type.label }}</div>
<div class="desc">{{ typeList[type.value].desc || ''}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 表单视图 -->
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="accountform" v-if="selectedType">
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>账户类型</label>
<div class="col-sm-6">
<div class="form-control-static">
{{ typeList[set.type].name }}
<a href="javascript:;" @click="selectedType = false" class="pull-right btn btn-default" v-if="action=='add'">重新选择</a>
</div>
<input type="hidden" name="type" v-model="set.type">
</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">&times;</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-3.1.1.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var info = {$info|json_encode|raw};
var typeList = {$typeList|json_encode|raw};
var classList = {$classList|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
selectedType: false,
set: {
deploy: '{$deploy}',
id: '',
type: '',
name: '',
config : '',
remark: '',
},
inputs: {},
config: {},
typeList: typeList,
classList: classList,
note: '',
typeOption: [],
},
watch: {
'set.type': function(val){
if(this.action == 'add' && val && typeList[val]){
this.inputs = typeList[val].inputs;
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)
})
}
}
},
computed: {
groupedTypes() {
return Object.keys(classList).map((key) => {
var tempList = [];
Object.keys(typeList).forEach((key2) => {
if(typeList[key2].class == key){
tempList.push({label: typeList[key2].name, value: key2})
}
})
return {label: classList[key], types: tempList}
})
}
},
mounted() {
this.typeOption = this.groupedTypes;
if(this.action == 'edit') {
this.selectedType = true;
}
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].inputs;
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: {
selectType(type) {
this.set.type = type;
this.selectedType = true;
},
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(){
if(data.msg.indexOf('自动部署账户')>0){
window.location.href = '/cert/deployaccount';
}else{
window.location.href = '/cert/certaccount';
}
});
}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}

View File

@ -1,93 +1,93 @@
{extend name="common/layout" /}
{block name="title"}SSL证书账户管理{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="账户名称或备注">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新SSL证书账户列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="/cert/account/add?deploy=0" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/account/data?deploy=0',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '所属平台',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+value;
}
},
{
field: 'name',
title: '账户名称'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/cert/account/edit?deploy=0&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="/cert/certorder?aid='+row.id+'" class="btn btn-default btn-xs">订单</a>';
return html;
}
},
]
})
})
function delItem(id){
layer.confirm('确定要删除此账户吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/account/del', {id: id, deploy: 0}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
});
}
</script>
{extend name="common/layout" /}
{block name="title"}SSL证书账户管理{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="账户名称或备注">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新SSL证书账户列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="/cert/account/add?deploy=0" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/account/data?deploy=0',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '所属平台',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+value;
}
},
{
field: 'name',
title: '账户名称'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/cert/account/edit?deploy=0&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="/cert/certorder?aid='+row.id+'" class="btn btn-default btn-xs">订单</a>';
return html;
}
},
]
})
})
function delItem(id){
layer.confirm('确定要删除此账户吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/account/del', {id: id, deploy: 0}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
});
}
</script>
{/block}

View File

@ -1,472 +1,472 @@
{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="">
<input type="hidden" name="aid" 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>
</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) {
if(value){
return '<span title="'+row.aremark+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+value+'('+row.aid+')</span>';
}
return '手动续期';
}
},
{
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: 'action',
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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
if(row.aid > 0){
html += '<a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-warning btn-xs"><i class="fa fa-refresh"></i> 续签</a>&nbsp;&nbsp;';
}
}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>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-repeat"></i> 重试</a>&nbsp;&nbsp;';
}
html += '<a href="/cert/order/edit?id='+row.id+'" class="btn btn-primary btn-xs"><i class="fa fa-edit"></i> 修改</a>&nbsp;&nbsp;';
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>';
if(row.aid > 0){
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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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 : {act: 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>
{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="">
<input type="hidden" name="aid" 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>
</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="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/FileSaver-2.0.5.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) {
if(value){
return '<span title="'+row.aremark+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+value+'('+row.aid+')</span>';
}
return '手动续期';
}
},
{
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: 'action',
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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
}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>&nbsp;&nbsp;';
if(row.aid > 0){
html += '<a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-warning btn-xs"><i class="fa fa-refresh"></i> 续签</a>&nbsp;&nbsp;';
}
}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>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-repeat"></i> 重试</a>&nbsp;&nbsp;';
}
html += '<a href="/cert/order/edit?id='+row.id+'" class="btn btn-primary btn-xs"><i class="fa fa-edit"></i> 修改</a>&nbsp;&nbsp;';
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>';
if(row.aid > 0){
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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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 : {act: 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}

View File

@ -1,112 +1,112 @@
{extend name="common/layout" /}
{block name="title"}SSL证书计划任务{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动续签设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">到期前续签天数</label>
<div class="col-sm-9"><input type="text" name="cert_renewdays" value="{:config_get('cert_renewdays', '7')}" class="form-control" placeholder="证书到期前多少天自动续签"/></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"/>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<li>提示:只有已开启自动续签的证书,并添加<a href="/system/cronset">计划任务</a>,才会自动续签。</li>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动部署设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">部署任务运行时段(小时)</label>
<div class="col-sm-9"><div class="input-group"><select class="form-control" name="deploy_hour_start" default="{:config_get('deploy_hour_start', '0')}">{for start="0" end="24"}<option value="{$i}">{$i}</option>{/for}</select><span class="input-group-addon"></span><select class="form-control" name="deploy_hour_end" default="{:config_get('deploy_hour_end', '23')}">{for start="0" end="24"}<option value="{$i}">{$i}</option>{/for}</select></div></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"/>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">通知设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">邮件通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_mail" default="{:config_get('cert_notice_mail')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">微信公众号通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_wxtpl" default="{:config_get('cert_notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Telegram机器人通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_tgbot" default="{:config_get('cert_notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">群机器人Webhook</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_webhook" default="{:config_get('cert_notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></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"/></div>
</div>
</form>
</div>
<div class="panel-footer">
<li>开启后,将在证书签发成功/失败,自动部署任务成功/失败时发送通知。只有计划任务执行会发送通知,控制台手动执行的不会发送。</li>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
</script>
{extend name="common/layout" /}
{block name="title"}SSL证书计划任务{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动续签设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">到期前续签天数</label>
<div class="col-sm-9"><input type="text" name="cert_renewdays" value="{:config_get('cert_renewdays', '7')}" class="form-control" placeholder="证书到期前多少天自动续签"/></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"/>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<li>提示:只有已开启自动续签的证书,并添加<a href="/system/cronset">计划任务</a>,才会自动续签。</li>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动部署设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">部署任务运行时段(小时)</label>
<div class="col-sm-9"><div class="input-group"><select class="form-control" name="deploy_hour_start" default="{:config_get('deploy_hour_start', '0')}">{for start="0" end="24"}<option value="{$i}">{$i}</option>{/for}</select><span class="input-group-addon"></span><select class="form-control" name="deploy_hour_end" default="{:config_get('deploy_hour_end', '23')}">{for start="0" end="24"}<option value="{$i}">{$i}</option>{/for}</select></div></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"/>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">通知设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">邮件通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_mail" default="{:config_get('cert_notice_mail')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">微信公众号通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_wxtpl" default="{:config_get('cert_notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Telegram机器人通知</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_tgbot" default="{:config_get('cert_notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">群机器人Webhook</label>
<div class="col-sm-9"><select class="form-control" name="cert_notice_webhook" default="{:config_get('cert_notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option><option value="2">开启(仅失败时)</option></select></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"/></div>
</div>
</form>
</div>
<div class="panel-footer">
<li>开启后,将在证书签发成功/失败,自动部署任务成功/失败时发送通知。只有计划任务执行会发送通知,控制台手动执行的不会发送。</li>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
</script>
{/block}

View File

@ -1,245 +1,245 @@
{extend name="common/layout" /}
{block name="title"}CMAME代理记录管理{/block}
{block name="main"}
<style>
.copy-btn{color:#52c41a;cursor:pointer;margin-right: 5px;}
.copy-btn:hover{color:#85ef79;}
.btn-refresh{margin-left:5px;font-size:10px;background-color:#6896cf}
tbody tr>td:nth-child(3){word-break:break-all;max-width:180px;}
tbody tr>td:nth-child(4){word-break:break-all;max-width:260px;}
</style>
<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">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">添加CNAME代理</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 no-padding-right">被代理域名</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="domain" onchange="changeDomain(this)" placeholder="需要申请SSL证书但未在本系统添加的域名" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">CNAME记录值</label>
<div class="col-sm-9"><div class="input-group">
<input type="text" name="rr" placeholder="自定义主机记录" class="form-control" required><span class="input-group-addon">.</span>
<select name="did" class="form-control" required>
{foreach $domains as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
</select></div></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="panel panel-default"><div class="panel-body"><p>CNAME代理可以让未在本系统添加的域名自动申请SSL证书支持所有DNS服务商。</p><p>仅支持基于ACME的证书类型不支持腾讯云等云厂商SSL证书。</p></div></div>
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="被代理域名">
</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>
</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}clipboard.js/1.7.1/clipboard.min.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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/cname/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'id',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'domain',
title: '被代理域名'
},
{
field: 'host',
title: '主机记录',
formatter: function(value, row, index) {
return value + '<a href="javascript:;" data-clipboard-text="'+value+'" class="copy-btn pull-right"><i class="fa fa-copy"></i></a>';
}
},
{
field: 'record',
title: 'CNAME记录值',
formatter: function(value, row, index) {
return value + '<a href="javascript:;" data-clipboard-text="'+value+'" class="copy-btn pull-right"><i class="fa fa-copy"></i></a>';
}
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
var html = '';
if(value == 1) {
html += '<span class="label label-success">已验证</span>';
} else {
html += '<span class="label label-warning">未验证</span>';
}
html += '<a href="javascript:checkItem('+row.id+')" title="立即验证" class="btn btn-primary btn-xs btn-refresh"><i class="fa fa-refresh"></i></a>';
return html;
}
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:editframe('+row.id+')" class="btn btn-primary btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
return html;
}
},
],
onLoadSuccess: function(data){
var clipboard = new Clipboard('.copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功!', {icon: 1, time: 600});
});
clipboard.on('error', function (e) {
layer.msg('复制失败', {icon: 2});
});
},
})
})
function addframe(){
$("#modal-store").modal('show');
$("#modal-title").html("添加CNAME代理");
$("#form-store input[name=action]").val("add");
$("#form-store input[name=id]").val('');
$("#form-store input[name=domain]").val('');
$("#form-store input[name=domain]").prop('readonly', false);
$("#form-store input[name=rr]").val('');
var defaultDid = getCookie('cname_did');
if(defaultDid){
$("#form-store select[name=did]").val(defaultDid);
}
}
function editframe(id){
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
$("#modal-store").modal('show');
$("#modal-title").html("修改CNAME代理");
$("#form-store input[name=action]").val("edit");
$("#form-store input[name=id]").val(id);
$("#form-store input[name=domain]").val(row.domain);
$("#form-store input[name=domain]").prop('readonly', true);
$("#form-store input[name=rr]").val(row.rr);
$("#form-store select[name=did]").val(row.did);
}
function changeDomain(obj){
var domain = $(obj).val();
if(domain == '') {
$("#form-store input[name=rr]").val('');
return;
}
var rr = domain.replace(/\./g, '-') + '.cname';
$("#form-store input[name=rr]").val(rr);
}
function save(){
if($("#form-store input[name=domain]").val()=='' || $("#form-store input[name=rr]").val()==''){
layer.alert('请确保各项不能为空!');return false;
}
var act = $("#form-store input[name=action]").val();
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/cert/cname/'+act,
data : $("#form-store").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
setCookie('cname_did', $("#form-store select[name=did]").val(), 2562000);
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){
layer.confirm('确定要删除此CNAME代理记录吗', {
btn: ['确定','取消']
}, function(){
$.post('/cert/cname/del', {id: id}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
});
}
function checkItem(id){
var ii = layer.load(2);
$.post('/cert/cname/check', {id: id}, function(data){
layer.close(ii);
if(data.code == 0) {
if(data.status == 1){
layer.alert('验证已通过!', {icon: 6});
}else{
layer.alert('验证未通过请按要求添加CNAME解析', {icon: 5});
}
searchRefresh();
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
}
</script>
{extend name="common/layout" /}
{block name="title"}CMAME代理记录管理{/block}
{block name="main"}
<style>
.copy-btn{color:#52c41a;cursor:pointer;margin-right: 5px;}
.copy-btn:hover{color:#85ef79;}
.btn-refresh{margin-left:5px;font-size:10px;background-color:#6896cf}
tbody tr>td:nth-child(3){word-break:break-all;max-width:180px;}
tbody tr>td:nth-child(4){word-break:break-all;max-width:260px;}
</style>
<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">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">添加CNAME代理</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 no-padding-right">被代理域名</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="domain" onchange="changeDomain(this)" placeholder="需要申请SSL证书但未在本系统添加的域名" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">CNAME记录值</label>
<div class="col-sm-9"><div class="input-group">
<input type="text" name="rr" placeholder="自定义主机记录" class="form-control" required><span class="input-group-addon">.</span>
<select name="did" class="form-control" required>
{foreach $domains as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
</select></div></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="panel panel-default"><div class="panel-body"><p>CNAME代理可以让未在本系统添加的域名自动申请SSL证书支持所有DNS服务商。</p><p>仅支持基于ACME的证书类型不支持腾讯云等云厂商SSL证书。</p></div></div>
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="被代理域名">
</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>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/clipboard-1.7.1.min.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/cname/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'id',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'domain',
title: '被代理域名'
},
{
field: 'host',
title: '主机记录',
formatter: function(value, row, index) {
return value + '<a href="javascript:;" data-clipboard-text="'+value+'" class="copy-btn pull-right"><i class="fa fa-copy"></i></a>';
}
},
{
field: 'record',
title: 'CNAME记录值',
formatter: function(value, row, index) {
return value + '<a href="javascript:;" data-clipboard-text="'+value+'" class="copy-btn pull-right"><i class="fa fa-copy"></i></a>';
}
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
var html = '';
if(value == 1) {
html += '<span class="label label-success">已验证</span>';
} else {
html += '<span class="label label-warning">未验证</span>';
}
html += '<a href="javascript:checkItem('+row.id+')" title="立即验证" class="btn btn-primary btn-xs btn-refresh"><i class="fa fa-refresh"></i></a>';
return html;
}
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:editframe('+row.id+')" class="btn btn-primary btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
return html;
}
},
],
onLoadSuccess: function(data){
var clipboard = new Clipboard('.copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功!', {icon: 1, time: 600});
});
clipboard.on('error', function (e) {
layer.msg('复制失败', {icon: 2});
});
},
})
})
function addframe(){
$("#modal-store").modal('show');
$("#modal-title").html("添加CNAME代理");
$("#form-store input[name=action]").val("add");
$("#form-store input[name=id]").val('');
$("#form-store input[name=domain]").val('');
$("#form-store input[name=domain]").prop('readonly', false);
$("#form-store input[name=rr]").val('');
var defaultDid = getCookie('cname_did');
if(defaultDid){
$("#form-store select[name=did]").val(defaultDid);
}
}
function editframe(id){
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
$("#modal-store").modal('show');
$("#modal-title").html("修改CNAME代理");
$("#form-store input[name=action]").val("edit");
$("#form-store input[name=id]").val(id);
$("#form-store input[name=domain]").val(row.domain);
$("#form-store input[name=domain]").prop('readonly', true);
$("#form-store input[name=rr]").val(row.rr);
$("#form-store select[name=did]").val(row.did);
}
function changeDomain(obj){
var domain = $(obj).val();
if(domain == '') {
$("#form-store input[name=rr]").val('');
return;
}
var rr = domain.replace(/\./g, '-') + '.cname';
$("#form-store input[name=rr]").val(rr);
}
function save(){
if($("#form-store input[name=domain]").val()=='' || $("#form-store input[name=rr]").val()==''){
layer.alert('请确保各项不能为空!');return false;
}
var act = $("#form-store input[name=action]").val();
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/cert/cname/'+act,
data : $("#form-store").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
setCookie('cname_did', $("#form-store select[name=did]").val(), 2562000);
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){
layer.confirm('确定要删除此CNAME代理记录吗', {
btn: ['确定','取消']
}, function(){
$.post('/cert/cname/del', {id: id}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
});
}
function checkItem(id){
var ii = layer.load(2);
$.post('/cert/cname/check', {id: id}, function(data){
layer.close(ii);
if(data.code == 0) {
if(data.status == 1){
layer.alert('验证已通过!', {icon: 6});
}else{
layer.alert('验证未通过请按要求添加CNAME解析', {icon: 5});
}
searchRefresh();
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
}
</script>
{/block}

View File

@ -1,255 +1,255 @@
{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="/cert/deploytask" 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="aid" v-model="set.aid" class="form-control" required :disabled="action=='edit'">
<option value="">--选择自动部署账户--</option>
{foreach $accounts as $k=>$v}
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
{/foreach}
</select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>关联SSL证书</label>
<div class="col-sm-6"><select name="oid" v-model="set.oid" class="form-control select2" placeholder="选择要部署的SSL证书">
{foreach $orders as $k=>$v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</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" rows="5" 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">&times;</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="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/select2.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/i18n/zh-CN.min.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: '',
aid: '',
oid: '',
config : '',
remark: '',
type: '',
},
inputs: {},
config: {},
typeList: typeList,
note: '',
},
watch: {
'set.aid': function(val){
this.set.type = $('option:selected', 'select[name=aid]').data('type');
},
'set.type': function(val){
if(this.action == 'add' && val && typeList[val]){
this.inputs = typeList[val].taskinputs;
this.note = typeList[val].tasknote;
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].taskinputs;
this.note = typeList[this.set.type].tasknote;
$.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)
})
}
var that = this;
this.$nextTick(function () {
$('[data-toggle="tooltip"]').tooltip();
$('select[name=oid]').select2({placeholder: '选择要部署的SSL证书'}).on('select2:select', function(e){
that.set.oid = e.params.data.id
});
if(document.referrer.indexOf('&oid=') > 0){
var oid = document.referrer.split('&oid=')[1].split('&')[0];
if(oid){
$('select[name=oid]').val(oid).trigger('change');
that.set.oid = oid;
}
}
})
},
methods: {
submit(){
var that=this;
if(this.set.aid == ''){
layer.msg('请选择自动部署账户', {icon: 2, time:900});
return false;
}
if(this.set.oid == ''){
layer.msg('请选择要部署的SSL证书', {icon: 2, time:900});
return false;
}
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);
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/cert/deploytask?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/cert/deploytask';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
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>
{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="/cert/deploytask" 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="aid" v-model="set.aid" class="form-control" required :disabled="action=='edit'">
<option value="">--选择自动部署账户--</option>
{foreach $accounts as $k=>$v}
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
{/foreach}
</select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>关联SSL证书</label>
<div class="col-sm-6"><select name="oid" v-model="set.oid" class="form-control select2" placeholder="选择要部署的SSL证书">
{foreach $orders as $k=>$v}
<option value="{$k}">{$v.name}</option>
{/foreach}
</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" rows="5" 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">&times;</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-3.1.1.js"></script>
<script src="/static/js/select2-4.0.13.min.js"></script>
<script src="/static/js/select2-i18n-zh-CN-4.0.13.min.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: '',
aid: '',
oid: '',
config : '',
remark: '',
type: '',
},
inputs: {},
config: {},
typeList: typeList,
note: '',
},
watch: {
'set.aid': function(val){
this.set.type = $('option:selected', 'select[name=aid]').data('type');
},
'set.type': function(val){
if(this.action == 'add' && val && typeList[val]){
this.inputs = typeList[val].taskinputs;
this.note = typeList[val].tasknote;
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].taskinputs;
this.note = typeList[this.set.type].tasknote;
$.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)
})
}
var that = this;
this.$nextTick(function () {
$('[data-toggle="tooltip"]').tooltip();
$('select[name=oid]').select2({placeholder: '选择要部署的SSL证书'}).on('select2:select', function(e){
that.set.oid = e.params.data.id
});
if(document.referrer.indexOf('&oid=') > 0){
var oid = document.referrer.split('&oid=')[1].split('&')[0];
if(oid){
$('select[name=oid]').val(oid).trigger('change');
that.set.oid = oid;
}
}
})
},
methods: {
submit(){
var that=this;
if(this.set.aid == ''){
layer.msg('请选择自动部署账户', {icon: 2, time:900});
return false;
}
if(this.set.oid == ''){
layer.msg('请选择要部署的SSL证书', {icon: 2, time:900});
return false;
}
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);
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/cert/deploytask?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/cert/deploytask';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
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}

View File

@ -1,93 +1,93 @@
{extend name="common/layout" /}
{block name="title"}自动部署任务账户管理{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="账户名称或备注">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新SSL证书账户列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="/cert/account/add?deploy=1" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/account/data?deploy=1',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '账户类型',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+value;
}
},
{
field: 'name',
title: '账户名称'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/cert/account/edit?deploy=1&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="/cert/deploytask?aid='+row.id+'" class="btn btn-default btn-xs">任务</a>';
return html;
}
},
]
})
})
function delItem(id){
layer.confirm('确定要删除此账户吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/account/del', {id: id, deploy: 1}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
});
}
</script>
{extend name="common/layout" /}
{block name="title"}自动部署任务账户管理{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="账户名称或备注">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新SSL证书账户列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="/cert/account/add?deploy=1" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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/account/data?deploy=1',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '账户类型',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.icon+'" class="type-logo"></img>'+value;
}
},
{
field: 'name',
title: '账户名称'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/cert/account/edit?deploy=1&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="/cert/deploytask?aid='+row.id+'" class="btn btn-default btn-xs">任务</a>';
return html;
}
},
]
})
})
function delItem(id){
layer.confirm('确定要删除此账户吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/account/del', {id: id, deploy: 1}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
});
}
</script>
{/block}

View File

@ -1,356 +1,356 @@
{extend name="common/layout" /}
{block name="title"}SSL证书自动部署任务{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(4){max-width:180px;}
.tips{cursor:pointer;}
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="aid" value="">
<input type="hidden" name="oid" 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">
<div class="form-group">
<input type="text" class="form-control" name="remark" 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="-1">处理失败</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>
<a href="/cert/deploy/add" 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="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><li><a href="javascript:operation('cert')">修改关联证书</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/deploy/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) {
if(!value) return '已被删除'
return '<span title="'+row.aname+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+(row.aremark?row.aremark:value+'('+row.aid+')')+'</span>';
}
},
{
field: 'domains',
title: '关联SSL证书',
formatter: function(value, row, index) {
var html = '<a href="/cert/certorder?id='+row.oid+'" target="_blank">ID:'+row.oid+''+row.certtypename+'</a><br/><span class="text-muted">';
html += value.length > 3 ? value.slice(0, 3).join('、') + ' 等'+value.length+'个域名' : value.join('、');
html += '</span>';
return html;
}
},
{
field: 'remark',
title: '备注'
},
{
field: 'active',
title: '任务开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'lasttime',
title: '上次执行时间',
formatter: function(value, row, index) {
return value ? value : '暂未执行'
}
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
if(value == 1) {
return '<span class="label label-success">已完成</span>';
} else if(value == 0) {
if(row.islock == 1) return '<span class="label" style="background-color: #3e76fb;">正在处理</span>';
else return '<span class="label label-info">待处理</span>';
} else {
return '<span class="label label-danger">处理失败</span>'+(row.error?' <span onclick="showmsg(\''+row.error+'\')" class="tips" title="失败原因"><i class="fa fa-info-circle"></i></span>':'');
}
}
},
{
field: 'action',
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>&nbsp;&nbsp;';
}else if(row.status == 1) {
html += '<a href="javascript:reDoOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-play-circle"></i> 重新执行</a>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-repeat"></i> 重试</a>&nbsp;&nbsp;';
}
html += '<a href="/cert/deploy/edit?id='+row.id+'" class="btn btn-primary btn-xs"><i class="fa fa-edit"></i> 修改</a>&nbsp;&nbsp;';
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 < 0){
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 setActive(id, active){
$.post('/cert/deploy/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id,status){
layer.confirm('确定要删除此自动部署任务吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/deploy/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('正在执行SSL证书部署...', {icon: 16,shade: 0.1,time: 0});
$.ajax({
type: "POST",
url: "/cert/deploy/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/deploy/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 reDoOrder(id){
layer.confirm('是否确定重新部署该证书?', {
btn: ['确定','取消'], title: '重新执行', icon: 0
}, function(){
doOrder(id, 1);
});
}
var intverval;
function showLog(processid){
if(processid == '' || processid == 'null'){
layer.msg('暂无日志', {time: 600});
return;
}
$.post('/cert/deploy/show_log', {processid: processid}, function(data){
if(data.code == 0) {
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/deploy/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;
}else if(action == 'cert'){
return batch_set_cert(ids);
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/cert/deploy/operation',
data : {act: 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});
}
}
});
}
function batch_set_cert(ids){
layer.prompt({title: '填写证书ID', value: '', formType: 0}, function(text, index){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/cert/deploy/operation',
data : {act: 'cert', ids: ids, certid: text},
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>
{extend name="common/layout" /}
{block name="title"}SSL证书自动部署任务{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(4){max-width:180px;}
.tips{cursor:pointer;}
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="aid" value="">
<input type="hidden" name="oid" 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">
<div class="form-group">
<input type="text" class="form-control" name="remark" 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="-1">处理失败</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>
<a href="/cert/deploy/add" 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="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><li><a href="javascript:operation('cert')">修改关联证书</a></li></ul>
</div>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/FileSaver-2.0.5.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/deploy/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) {
if(!value) return '已被删除'
return '<span title="'+row.aname+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+(row.aremark?row.aremark:value+'('+row.aid+')')+'</span>';
}
},
{
field: 'domains',
title: '关联SSL证书',
formatter: function(value, row, index) {
var html = '<a href="/cert/certorder?id='+row.oid+'" target="_blank">ID:'+row.oid+''+row.certtypename+'</a><br/><span class="text-muted">';
html += value.length > 3 ? value.slice(0, 3).join('、') + ' 等'+value.length+'个域名' : value.join('、');
html += '</span>';
return html;
}
},
{
field: 'remark',
title: '备注'
},
{
field: 'active',
title: '任务开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'lasttime',
title: '上次执行时间',
formatter: function(value, row, index) {
return value ? value : '暂未执行'
}
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
if(value == 1) {
return '<span class="label label-success">已完成</span>';
} else if(value == 0) {
if(row.islock == 1) return '<span class="label" style="background-color: #3e76fb;">正在处理</span>';
else return '<span class="label label-info">待处理</span>';
} else {
return '<span class="label label-danger">处理失败</span>'+(row.error?' <span onclick="showmsg(\''+row.error+'\')" class="tips" title="失败原因"><i class="fa fa-info-circle"></i></span>':'');
}
}
},
{
field: 'action',
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>&nbsp;&nbsp;';
}else if(row.status == 1) {
html += '<a href="javascript:reDoOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-play-circle"></i> 重新执行</a>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-repeat"></i> 重试</a>&nbsp;&nbsp;';
}
html += '<a href="/cert/deploy/edit?id='+row.id+'" class="btn btn-primary btn-xs"><i class="fa fa-edit"></i> 修改</a>&nbsp;&nbsp;';
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 < 0){
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 setActive(id, active){
$.post('/cert/deploy/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id,status){
layer.confirm('确定要删除此自动部署任务吗?', {
btn: ['确定','取消']
}, function(){
$.post('/cert/deploy/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('正在执行SSL证书部署...', {icon: 16,shade: 0.1,time: 0});
$.ajax({
type: "POST",
url: "/cert/deploy/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/deploy/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 reDoOrder(id){
layer.confirm('是否确定重新部署该证书?', {
btn: ['确定','取消'], title: '重新执行', icon: 0
}, function(){
doOrder(id, 1);
});
}
var intverval;
function showLog(processid){
if(processid == '' || processid == 'null'){
layer.msg('暂无日志', {time: 600});
return;
}
$.post('/cert/deploy/show_log', {processid: processid}, function(data){
if(data.code == 0) {
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/deploy/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;
}else if(action == 'cert'){
return batch_set_cert(ids);
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/cert/deploy/operation',
data : {act: 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});
}
}
});
}
function batch_set_cert(ids){
layer.prompt({title: '填写证书ID', value: '', formType: 0}, function(text, index){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/cert/deploy/operation',
data : {act: 'cert', ids: ids, certid: text},
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}

View File

@ -1,195 +1,195 @@
{extend name="common/layout" /}
{block name="title"}SSL证书订单{/block}
{block name="main"}
<style>
.tips{color: #f6a838; padding-left: 5px;}
.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="/cert/certorder" 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}SSL证书订单</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>证书账户</label>
<div class="col-sm-6"><select name="aid" v-model="set.aid" class="form-control" required>
<option value="">--选择证书账户--</option>
{foreach $accounts as $k=>$v}
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
{/foreach}
<option value="-1" data-type="">手动续期</option>
</select></div>
</div>
<div class="form-group" v-show="set.aid==-1">
<label class="col-sm-3 control-label no-padding-right" is-required>证书内容</label>
<div class="col-sm-6">
<div class="input-group">
<textarea name="fullchain" v-model="set.fullchain" class="form-control" rows="5" placeholder="输入PEM格式证书链" required></textarea>
<a class="btn btn-default input-group-addon" @click="upload('fullchain')" title="上传证书文件"><i class="fa fa-upload"></i></a>
</div>
</div>
</div>
<div class="form-group" v-show="set.aid==-1">
<label class="col-sm-3 control-label no-padding-right" is-required>私钥内容</label>
<div class="col-sm-6">
<div class="input-group">
<textarea name="privatekey" v-model="set.privatekey" class="form-control" rows="5" placeholder="输入PEM格式私钥" required></textarea>
<a class="btn btn-default input-group-addon" @click="upload('privatekey')" title="上传私钥文件"><i class="fa fa-upload"></i></a>
</div>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>签名算法</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="item in keytypeList">
<input type="radio" name="keytype" :value="item" v-model="set.keytype"> {{item}}
</label>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>密钥长度</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="item in keysizeList">
<input type="radio" name="keysize" :value="item.value" v-model="set.keysize"> {{item.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>绑定域名</label>
<div class="col-sm-6">
<textarea name="domains" v-model="domains" class="form-control" rows="5" placeholder="请输入域名,一行一个" required></textarea>
</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>
<div class="panel panel-default" v-show="set.aid!=-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:添加或修改订单信息,点击提交后,不会立即执行签发,只能通过计划任务或列表手动点击来执行</p><p>证书签发之前确保该主域名下没有CAA类型记录避免证书验证失败。</p></div></div>
<div class="panel panel-default" v-show="set.aid==-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:选择手动续期,到达设置的续期天数,只会发送消息通知。</p></div></div>
</form>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
type: '',
domains: '',
set: {
id: '',
aid: '',
fullchain: '',
privatekey: '',
keytype: '',
keysize: '',
domains: [],
},
keytypeList: [
'RSA',
'ECC'
],
keysizeMap: [
{label:'2048 bit',value:'2048',type:'RSA'},
{label:'3072 bit',value:'3072',type:'RSA'},
{label:'P-256',value:'256',type:'ECC'},
{label:'P-384',value:'384',type:'ECC'},
],
keysizeList: [],
},
watch: {
'set.aid': function(val){
this.type = $('option:selected', 'select[name=aid]').data('type');
},
'set.keytype': function(val){
this.keysizeList = this.keysizeMap.filter((item) => {
return item.type == val;
})
if(!this.keysizeList.filter((item) => {return item.value == this.set.keysize}).length)
this.set.keysize = this.keysizeList[0].value;
},
domains: function(val){
this.set.domains = val.split("\n").filter((item) => {
return item.trim() != '';
});
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
this.domains = info.domains.join("\n");
}else{
this.set.keytype = 'RSA';
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/cert/certorder?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/cert/certorder';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
upload(name){
//读取上传文件并填充到表单
var file = document.createElement('input');
file.type = 'file';
file.accept = '.pem,.crt,.key';
file.style.display = 'none';
file.onchange = function(){
var reader = new FileReader();
reader.onload = function(e){
this.set[name] = e.target.result;
}.bind(this);
reader.readAsText(file.files[0]);
}.bind(this);
document.body.appendChild(file);
file.click();
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}SSL证书订单{/block}
{block name="main"}
<style>
.tips{color: #f6a838; padding-left: 5px;}
.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="/cert/certorder" 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}SSL证书订单</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>证书账户</label>
<div class="col-sm-6"><select name="aid" v-model="set.aid" class="form-control" required>
<option value="">--选择证书账户--</option>
{foreach $accounts as $k=>$v}
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
{/foreach}
<option value="-1" data-type="">手动续期</option>
</select></div>
</div>
<div class="form-group" v-show="set.aid==-1">
<label class="col-sm-3 control-label no-padding-right" is-required>证书内容</label>
<div class="col-sm-6">
<div class="input-group">
<textarea name="fullchain" v-model="set.fullchain" class="form-control" rows="5" placeholder="输入PEM格式证书链" required></textarea>
<a class="btn btn-default input-group-addon" @click="upload('fullchain')" title="上传证书文件"><i class="fa fa-upload"></i></a>
</div>
</div>
</div>
<div class="form-group" v-show="set.aid==-1">
<label class="col-sm-3 control-label no-padding-right" is-required>私钥内容</label>
<div class="col-sm-6">
<div class="input-group">
<textarea name="privatekey" v-model="set.privatekey" class="form-control" rows="5" placeholder="输入PEM格式私钥" required></textarea>
<a class="btn btn-default input-group-addon" @click="upload('privatekey')" title="上传私钥文件"><i class="fa fa-upload"></i></a>
</div>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>签名算法</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="item in keytypeList">
<input type="radio" name="keytype" :value="item" v-model="set.keytype"> {{item}}
</label>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>密钥长度</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="item in keysizeList">
<input type="radio" name="keysize" :value="item.value" v-model="set.keysize"> {{item.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.aid!=-1">
<label class="col-sm-3 control-label no-padding-right" is-required>绑定域名</label>
<div class="col-sm-6">
<textarea name="domains" v-model="domains" class="form-control" rows="5" placeholder="请输入域名,一行一个" required></textarea>
</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>
<div class="panel panel-default" v-show="set.aid!=-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:添加或修改订单信息,点击提交后,不会立即执行签发,只能通过计划任务或列表手动点击来执行</p><p>证书签发之前确保该主域名下没有CAA类型记录避免证书验证失败。</p></div></div>
<div class="panel panel-default" v-show="set.aid==-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:选择手动续期,到达设置的续期天数,只会发送消息通知。</p></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-3.1.1.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
type: '',
domains: '',
set: {
id: '',
aid: '',
fullchain: '',
privatekey: '',
keytype: '',
keysize: '',
domains: [],
},
keytypeList: [
'RSA',
'ECC'
],
keysizeMap: [
{label:'2048 bit',value:'2048',type:'RSA'},
{label:'3072 bit',value:'3072',type:'RSA'},
{label:'P-256',value:'256',type:'ECC'},
{label:'P-384',value:'384',type:'ECC'},
],
keysizeList: [],
},
watch: {
'set.aid': function(val){
this.type = $('option:selected', 'select[name=aid]').data('type');
},
'set.keytype': function(val){
this.keysizeList = this.keysizeMap.filter((item) => {
return item.type == val;
})
if(!this.keysizeList.filter((item) => {return item.value == this.set.keysize}).length)
this.set.keysize = this.keysizeList[0].value;
},
domains: function(val){
this.set.domains = val.split("\n").filter((item) => {
return item.trim() != '';
});
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
this.domains = info.domains.join("\n");
}else{
this.set.keytype = 'RSA';
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/cert/certorder?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/cert/certorder';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
upload(name){
//读取上传文件并填充到表单
var file = document.createElement('input');
file.type = 'file';
file.accept = '.pem,.crt,.key';
file.style.display = 'none';
file.onchange = function(){
var reader = new FileReader();
reader.onload = function(e){
this.set[name] = e.target.result;
}.bind(this);
reader.readAsText(file.files[0]);
}.bind(this);
document.body.appendChild(file);
file.click();
}
},
});
</script>
{/block}

View File

@ -1,257 +1,257 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>{block name="title"}标题{/block}</title>
<link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}select2/4.0.13/css/select2.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css?v=2" rel="stylesheet"/>
{if file_exists(public_path() . 'static/css/custom.css')}
<link href="/static/css/custom.css" rel="stylesheet">
{/if}
<script src="{$cdnpublic}jquery/3.6.4/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
.type-logo{width: 18px;margin-top: -2px;padding-right: 4px;}
</style>
</head>
<body class="hold-transition {$skin} sidebar-mini">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="javascript:;" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><i class="fa fa-cube"></i></span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"></span>聚合DNS管理系统
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="visible-xs-inline">菜单</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li>
<a href="/" title="平台首页"><i class="fa fa-home fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="fullscreen" title="全屏"><i class="fa fa-arrows-alt fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="control-sidebar" title="更换皮肤"><i class="fa fa-wrench fa-fw"></i></a>
</li>
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/static/images/user.png" class="user-image" alt="User Image">
<span class="hidden-xs">{$user.username}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
<p>
{$user.username}
<small>{$user.regtime}</small>
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
{if request()->user['type'] eq 'user'}<div class="pull-left">
<a href="/setpwd" class="btn btn-primary"><i class="fa fa-lock"></i> 修改密码</a>
</div>{/if}
<div class="pull-right">
<a href="/logout" class="btn btn-danger"><i class="fa fa-sign-out"></i> 退出登录</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{$user.username}</p>
<i class="fa fa-circle text-success"></i> Online
</div>
</div>
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">功能导航</li>
{if request()->user['type'] eq 'user'}<li class="{:checkIfActive('index')}">
<a href="/"><i class="fa fa-home fa-fw"></i> <span>后台首页</span></a>
</li>{/if}
<li class="{:checkIfActive('domain,record,record_log,record_batch_add,domain_add,weight,record_batch_add2,record_batch_edit2,expire_notice')}">
<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')}">
<a href="/account"><i class="fa fa-lock fa-fw"></i> <span>域名账户</span></a>
</li>
<li class="treeview {:checkIfActive('overview,task,taskinfo,taskform')}">
<a href="javascript:;">
<i class="fa fa-heartbeat fa-fw"></i>
<span>容灾切换</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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="{:checkIfActive('stask,staskform')}">
<a href="/schedule/stask"><i class="fa fa-calendar fa-fw"></i> <span>定时切换</span></a>
</li>
<li class="treeview {:checkIfActive('certaccount,account_form,certorder,order_form,order_import,deployaccount,deploytask,deploy_form,certset,cname')}">
<a href="javascript:;">
<i class="fa fa-expeditedssl fa-fw"></i>
<span>SSL证书</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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('opipset,opiplist,opipform')}">
<a href="javascript:;">
<i class="fa fa-globe fa-fw"></i>
<span>CF优选IP</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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('cronset,loginset,noticeset,proxyset')}">
<a href="javascript:;">
<i class="fa fa-cogs fa-fw"></i>
<span>系统设置</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li class="{:checkIfActive('cronset')}"><a href="/system/cronset"><i class="fa fa-circle-o"></i> 计划任务</a></li>
<li class="{:checkIfActive('loginset')}"><a href="/system/loginset"><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>
<li class="{:checkIfActive('user')}">
<a href="/user"><i class="fa fa-user fa-fw"></i> <span>用户管理</span></a>
</li>{/if}
<li class="{:checkIfActive('log')}">
<a href="/log"><i class="fa fa-list fa-fw"></i> <span>操作日志</span></a>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
{block name="main"}主内容{/block}
</section>
<!-- /.content -->
</div>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark" style="display: none;">
<!-- Tab panes -->
<div class="tab-content">
<!-- Home tab content -->
<div class="tab-pane active">
<h4 class="control-sidebar-heading">皮肤</h4>
<ul class="list-unstyled clearfix skin-list">
<li><a href="javascript:;" data-skin="skin-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #4e73df;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black</p></li>
<li><a href="javascript:;" data-skin="skin-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #605ca8;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Purple</p></li>
<li><a href="javascript:;" data-skin="skin-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-green-active"></span><span class="bg-green" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Green</p></li>
<li><a href="javascript:;" data-skin="skin-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-red-active"></span><span class="bg-red" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Red</p></li>
<li><a href="javascript:;" data-skin="skin-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-yellow-active"></span><span class="bg-yellow" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-blue-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #4e73df;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Blue Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #000;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Black Light</p></li>
<li><a href="javascript:;" data-skin="skin-purple-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #605ca8;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Purple Light</p></li>
<li><a href="javascript:;" data-skin="skin-green-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-green"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Green Light</p></li>
<li><a href="javascript:;" data-skin="skin-red-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-red"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Red Light</p></li>
<li><a href="javascript:;" data-skin="skin-yellow-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-yellow"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Yellow Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #4e73df;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #605ca8;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Purple</p></li>
<li><a href="javascript:;" data-skin="skin-black-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-green"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Green</p></li>
<li><a href="javascript:;" data-skin="skin-black-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-red"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Red</p></li>
<li><a href="javascript:;" data-skin="skin-black-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-yellow"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-black-pink" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #f5549f;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Pink</p></li>
</ul>
</div>
<!-- /.tab-pane -->
</div>
</aside>
<!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed
immediately after the control sidebar -->
<div class="control-sidebar-bg"></div>
</div>
<!-- ./wrapper -->
<script src="{$cdnpublic}twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="{$cdnpublic}fastclick/1.0.6/fastclick.min.js"></script>
<script src="/static/js/app.js"></script>
<script>
document.addEventListener('pointerdown', function(e) {
if (e.target.closest('table')) {
return;
}
startX = e.clientX;
// 如果触摸开始于屏幕左边缘的10%区域内
if (startX < window.innerWidth * 0.1) {
e.preventDefault(); // 尝试阻止默认行为
}
}, false);
</script>
{block name="script"}{/block}
</body>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>{block name="title"}标题{/block}</title>
<link href="/static/css/bootstrap-3.4.1.min.css" rel="stylesheet"/>
<link href="/static/css/font-awesome-4.7.0.min.css" rel="stylesheet"/>
<link href="/static/css/select2-4.0.13.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css?v=2" rel="stylesheet"/>
{if file_exists(public_path() . 'static/css/custom.css')}
<link href="/static/css/custom.css" rel="stylesheet">
{/if}
<script src="/static/js/jquery-3.6.4.min.js"></script>
<!--[if lt IE 9]>
<script src="/static/js/html5shiv-3.7.3.min.js"></script>
<script src="/static/js/respond-1.4.2.min.js"></script>
<![endif]-->
<style>
.type-logo{width: 18px;margin-top: -2px;padding-right: 4px;}
</style>
</head>
<body class="hold-transition {$skin} sidebar-mini">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="javascript:;" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><i class="fa fa-cube"></i></span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"></span>聚合DNS管理系统
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="visible-xs-inline">菜单</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li>
<a href="/" title="平台首页"><i class="fa fa-home fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="fullscreen" title="全屏"><i class="fa fa-arrows-alt fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="control-sidebar" title="更换皮肤"><i class="fa fa-wrench fa-fw"></i></a>
</li>
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/static/images/user.png" class="user-image" alt="User Image">
<span class="hidden-xs">{$user.username}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
<p>
{$user.username}
<small>{$user.regtime}</small>
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
{if request()->user['type'] eq 'user'}<div class="pull-left">
<a href="/setpwd" class="btn btn-primary"><i class="fa fa-lock"></i> 修改密码</a>
</div>{/if}
<div class="pull-right">
<a href="/logout" class="btn btn-danger"><i class="fa fa-sign-out"></i> 退出登录</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{$user.username}</p>
<i class="fa fa-circle text-success"></i> Online
</div>
</div>
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">功能导航</li>
{if request()->user['type'] eq 'user'}<li class="{:checkIfActive('index')}">
<a href="/"><i class="fa fa-home fa-fw"></i> <span>后台首页</span></a>
</li>{/if}
<li class="{:checkIfActive('domain,record,record_log,record_batch_add,domain_add,weight,record_batch_add2,record_batch_edit2,expire_notice')}">
<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')}">
<a href="/account"><i class="fa fa-lock fa-fw"></i> <span>域名账户</span></a>
</li>
<li class="treeview {:checkIfActive('overview,task,taskinfo,taskform')}">
<a href="javascript:;">
<i class="fa fa-heartbeat fa-fw"></i>
<span>容灾切换</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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="{:checkIfActive('stask,staskform')}">
<a href="/schedule/stask"><i class="fa fa-calendar fa-fw"></i> <span>定时切换</span></a>
</li>
<li class="treeview {:checkIfActive('certaccount,account_form,certorder,order_form,order_import,deployaccount,deploytask,deploy_form,certset,cname')}">
<a href="javascript:;">
<i class="fa fa-expeditedssl fa-fw"></i>
<span>SSL证书</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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('opipset,opiplist,opipform')}">
<a href="javascript:;">
<i class="fa fa-globe fa-fw"></i>
<span>CF优选IP</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<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('cronset,loginset,noticeset,proxyset')}">
<a href="javascript:;">
<i class="fa fa-cogs fa-fw"></i>
<span>系统设置</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li class="{:checkIfActive('cronset')}"><a href="/system/cronset"><i class="fa fa-circle-o"></i> 计划任务</a></li>
<li class="{:checkIfActive('loginset')}"><a href="/system/loginset"><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>
<li class="{:checkIfActive('user')}">
<a href="/user"><i class="fa fa-user fa-fw"></i> <span>用户管理</span></a>
</li>{/if}
<li class="{:checkIfActive('log')}">
<a href="/log"><i class="fa fa-list fa-fw"></i> <span>操作日志</span></a>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
{block name="main"}主内容{/block}
</section>
<!-- /.content -->
</div>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark" style="display: none;">
<!-- Tab panes -->
<div class="tab-content">
<!-- Home tab content -->
<div class="tab-pane active">
<h4 class="control-sidebar-heading">皮肤</h4>
<ul class="list-unstyled clearfix skin-list">
<li><a href="javascript:;" data-skin="skin-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #4e73df;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black</p></li>
<li><a href="javascript:;" data-skin="skin-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #605ca8;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Purple</p></li>
<li><a href="javascript:;" data-skin="skin-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-green-active"></span><span class="bg-green" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Green</p></li>
<li><a href="javascript:;" data-skin="skin-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-red-active"></span><span class="bg-red" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Red</p></li>
<li><a href="javascript:;" data-skin="skin-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-yellow-active"></span><span class="bg-yellow" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-blue-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #4e73df;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Blue Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #000;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Black Light</p></li>
<li><a href="javascript:;" data-skin="skin-purple-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #605ca8;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Purple Light</p></li>
<li><a href="javascript:;" data-skin="skin-green-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-green"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Green Light</p></li>
<li><a href="javascript:;" data-skin="skin-red-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-red"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Red Light</p></li>
<li><a href="javascript:;" data-skin="skin-yellow-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-yellow"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Yellow Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #4e73df;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #605ca8;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Purple</p></li>
<li><a href="javascript:;" data-skin="skin-black-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-green"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Green</p></li>
<li><a href="javascript:;" data-skin="skin-black-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-red"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Red</p></li>
<li><a href="javascript:;" data-skin="skin-black-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-yellow"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-black-pink" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #f5549f;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Pink</p></li>
</ul>
</div>
<!-- /.tab-pane -->
</div>
</aside>
<!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed
immediately after the control sidebar -->
<div class="control-sidebar-bg"></div>
</div>
<!-- ./wrapper -->
<script src="/static/js/bootstrap-3.4.1.min.js"></script>
<script src="/static/js/fastclick-1.0.6.min.js"></script>
<script src="/static/js/app.js"></script>
<script>
document.addEventListener('pointerdown', function(e) {
if (e.target.closest('table')) {
return;
}
startX = e.clientX;
// 如果触摸开始于屏幕左边缘的10%区域内
if (startX < window.innerWidth * 0.1) {
e.preventDefault(); // 尝试阻止默认行为
}
}, false);
</script>
{block name="script"}{/block}
</body>
</html>

View File

@ -1,212 +1,212 @@
{extend name="common/layout" /}
{block name="title"}容灾切换运行概览{/block}
{block name="main"}
<style>
.info-box-content{padding:18px 10px}
.hbox{display:table;width:100%;height:100%;border-spacing:0;table-layout:fixed;border:1px solid #edf1f2}
.hbox .col{display:table-cell;float:none;height:100%;vertical-align:top;border:1px solid #edf1f2;padding-top:18px;padding-bottom:18px;color:#98a6ad}
.hbox .col .fa{display:block;padding-bottom:3px}
.hbox .col span{font-size:14px}
.hbox .col:hover{background-color:#f9f9f9;color:#6e7173}
</style>
<div class="modal" id="modal-clean">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">数据清理</h4>
</div>
<div class="modal-body">
<form id="form-clean" onsubmit="return false;">
<div class="form-group">
<label>清理多少天前的切换记录</label>
<input type="number" class="form-control" name="days" value="30">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-info" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" onclick="submitClean()">确定</button>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-notice">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">通知设置</h4>
</div>
<div class="modal-body">
<form id="form-notice" onsubmit="return false;" class="form-horizontal">
<div class="form-group">
<label class="col-sm-4 control-label">邮件通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_mail" default="{:config_get('notice_mail')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">微信公众号通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_wxtpl" default="{:config_get('notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">Telegram机器人通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_tgbot" default="{:config_get('notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">群机器人Webhook</label>
<div class="col-sm-8"><select class="form-control" name="notice_webhook" default="{:config_get('notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-info" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="submitNotice()">确定</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-aqua"><i class="fa fa-certificate"></i></span>
<div class="info-box-content">
<span class="info-box-text">运行状态</span>
<span class="info-box-number">{$info.run_state==1?'<font color="green">正在运行</font>':'<font color="red">已停止</font>'}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-heart"></i></span>
<div class="info-box-content">
<span class="info-box-text">今日运行次数</span>
<span class="info-box-number">{$info.run_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix visible-sm-block"></div>
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-bandcamp"></i></span>
<div class="info-box-content">
<span class="info-box-text">24H告警次数</span>
<span class="info-box-number">{$info.fail_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-gratipay"></i></span>
<div class="info-box-content">
<span class="info-box-text">24H切换次数</span>
<span class="info-box-number">{$info.switch_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">运行概览</h3></div>
<div class="panel-body">
<li class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>上次运行时间:</b> {$info.run_time}</li>
<li class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>当前时间:</b> {:date('Y-m-d H:i:s')}</li>
<li class="list-group-item"><span class="fa fa-th-large"></span> <b>Swoole组件</b> {$info.swoole|raw}</li>
{if $info.run_error}<li class="list-group-item"><span class="fa fa-times-circle"></span> <b>上次运行错误信息:</b> {$info.run_error}</li>{/if}
<div class="hbox text-center text-sm">
<a href="/dmonitor/task" class="col">
<i class="fa fa-list-alt fa-2x"></i>
<span>切换策略</span>
</a>
<a href="javascript:noticeset()" class="col">
<i class="fa fa-bullhorn fa-2x"></i>
<span>通知设置</span>
</a>
<a href="javascript:clean()" class="col">
<i class="fa fa-trash fa-2x"></i>
<span>数据清理</span>
</a>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">操作说明</h3></div>
<div class="panel-body">
<p>1、php需要安装swoole组件</p>
<p>2、在命令行执行以下命令启动进程</p>
<p><code>cd {:app()->getRootPath()} && php think dmtask</code></p>
<p>3、也可以使用进程守护管理器添加守护进程。<br/>运行目录:<code>{:app()->getRootPath()}</code><br/>启动命令:<code>php think dmtask</code></p>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function clean(){
$('#modal-clean').modal('show');
}
function noticeset(){
$('#modal-notice').modal('show');
}
function submitClean(){
var days = $('#form-clean input[name=days]').val();
if(days < 1){
layer.alert('清理天数不能小于1', {icon: 2});
return;
}
$.post('/dmonitor/clean', {days: days}, function(res){
if(res.code == 0){
layer.msg(res.msg, {icon: 1});
$('#modal-clean').modal('hide');
}else{
layer.alert(res.msg, {icon: 2});
}
});
}
function submitNotice(){
$.post('/system/set', $("#form-notice").serialize(), function(res){
if(res.code == 0){
layer.alert('设置保存成功!<br/>重启检测进程或容器后生效', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}容灾切换运行概览{/block}
{block name="main"}
<style>
.info-box-content{padding:18px 10px}
.hbox{display:table;width:100%;height:100%;border-spacing:0;table-layout:fixed;border:1px solid #edf1f2}
.hbox .col{display:table-cell;float:none;height:100%;vertical-align:top;border:1px solid #edf1f2;padding-top:18px;padding-bottom:18px;color:#98a6ad}
.hbox .col .fa{display:block;padding-bottom:3px}
.hbox .col span{font-size:14px}
.hbox .col:hover{background-color:#f9f9f9;color:#6e7173}
</style>
<div class="modal" id="modal-clean">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">数据清理</h4>
</div>
<div class="modal-body">
<form id="form-clean" onsubmit="return false;">
<div class="form-group">
<label>清理多少天前的切换记录</label>
<input type="number" class="form-control" name="days" value="30">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-info" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" onclick="submitClean()">确定</button>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-notice">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">通知设置</h4>
</div>
<div class="modal-body">
<form id="form-notice" onsubmit="return false;" class="form-horizontal">
<div class="form-group">
<label class="col-sm-4 control-label">邮件通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_mail" default="{:config_get('notice_mail')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">微信公众号通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_wxtpl" default="{:config_get('notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">Telegram机器人通知</label>
<div class="col-sm-8"><select class="form-control" name="notice_tgbot" default="{:config_get('notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">群机器人Webhook</label>
<div class="col-sm-8"><select class="form-control" name="notice_webhook" default="{:config_get('notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-info" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="submitNotice()">确定</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-aqua"><i class="fa fa-certificate"></i></span>
<div class="info-box-content">
<span class="info-box-text">运行状态</span>
<span class="info-box-number">{$info.run_state==1?'<font color="green">正在运行</font>':'<font color="red">已停止</font>'}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-heart"></i></span>
<div class="info-box-content">
<span class="info-box-text">今日运行次数</span>
<span class="info-box-number">{$info.run_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix visible-sm-block"></div>
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-bandcamp"></i></span>
<div class="info-box-content">
<span class="info-box-text">24H告警次数</span>
<span class="info-box-number">{$info.fail_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-gratipay"></i></span>
<div class="info-box-content">
<span class="info-box-text">24H切换次数</span>
<span class="info-box-number">{$info.switch_count}</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">运行概览</h3></div>
<div class="panel-body">
<li class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>上次运行时间:</b> {$info.run_time}</li>
<li class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>当前时间:</b> {:date('Y-m-d H:i:s')}</li>
<li class="list-group-item"><span class="fa fa-th-large"></span> <b>Swoole组件</b> {$info.swoole|raw}</li>
{if $info.run_error}<li class="list-group-item"><span class="fa fa-times-circle"></span> <b>上次运行错误信息:</b> {$info.run_error}</li>{/if}
<div class="hbox text-center text-sm">
<a href="/dmonitor/task" class="col">
<i class="fa fa-list-alt fa-2x"></i>
<span>切换策略</span>
</a>
<a href="javascript:noticeset()" class="col">
<i class="fa fa-bullhorn fa-2x"></i>
<span>通知设置</span>
</a>
<a href="javascript:clean()" class="col">
<i class="fa fa-trash fa-2x"></i>
<span>数据清理</span>
</a>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">操作说明</h3></div>
<div class="panel-body">
<p>1、php需要安装swoole组件</p>
<p>2、在命令行执行以下命令启动进程</p>
<p><code>cd {:app()->getRootPath()} && php think dmtask</code></p>
<p>3、也可以使用进程守护管理器添加守护进程。<br/>运行目录:<code>{:app()->getRootPath()}</code><br/>启动命令:<code>php think dmtask</code></p>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function clean(){
$('#modal-clean').modal('show');
}
function noticeset(){
$('#modal-notice').modal('show');
}
function submitClean(){
var days = $('#form-clean input[name=days]').val();
if(days < 1){
layer.alert('清理天数不能小于1', {icon: 2});
return;
}
$.post('/dmonitor/clean', {days: days}, function(res){
if(res.code == 0){
layer.msg(res.msg, {icon: 1});
$('#modal-clean').modal('hide');
}else{
layer.alert(res.msg, {icon: 2});
}
});
}
function submitNotice(){
$.post('/system/set', $("#form-notice").serialize(), function(res){
if(res.code == 0){
layer.alert('设置保存成功!<br/>重启检测进程或容器后生效', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
}
</script>
{/block}

View File

@ -1,233 +1,233 @@
{extend name="common/layout" /}
{block name="title"}容灾切换策略{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
tbody tr>td:nth-child(4){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="3">解析记录</option><option value="4">备用解析记录</option><option value="2">解析记录ID</option><option value="5">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="status" class="form-control"><option value="">健康状况</option><option value="0">正常</option><option value="1">异常</option></select>
</div>
</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="/dmonitor/task/add" 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="javascript:operation('open')">开启运行</a></li><li><a href="javascript:operation('close')">停止运行</a></li><li><a href="javascript:operation('retry')">立即重试</a></li><li><a href="javascript:operation('delete')">删除</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/dmonitor/task/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: '',
checkbox: true
},
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'main_value',
title: '解析记录',
formatter: function(value, row, index) {
return value;
}
},
{
field: 'type',
title: '切换设置',
formatter: function(value, row, index) {
if(value == 1) {
return '暂停解析';
} else if(value == 2) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="备用:'+row.backup_value+'" class="tips">切换备用</span>';
} else if(value == 3) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="同域名正常数量<='+row.cycle+'" class="tips">条件开启</span>';
} else {
return '无操作';
}
}
},
{
field: 'checktype',
title: '检测协议',
formatter: function(value, row, index) {
if(row.type <= 2){
if(value == 1) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.tcpport+'端口" class="tips">TCP</span>';
} else if(value == 2) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.checkurl+'" class="tips">HTTP(S)</span>';
} else {
return 'PING';
}
} else {
return '无';
}
}
},
{
field: 'frequency',
title: '检测间隔',
formatter: function(value, row, index) {
return value + '秒';
}
},
{
field: 'status',
title: '健康状况',
formatter: function(value, row, index) {
if(value == 0) {
return '<span class="label label-success">正常</span>';
} else {
return '<span class="label label-danger">异常</span>';
}
}
},
{
field: 'active',
title: '运行开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'checktimestr',
title: '上次检测时间'
},
{
field: 'addtimestr',
title: '添加时间',
visible: false
},
{
field: 'remark',
title: '备注',
visible: false
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/dmonitor/task/info/'+row.id+'" class="btn btn-info btn-xs">切换日志</a>&nbsp;&nbsp;';
html += '<a href="/dmonitor/task/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?subdomain='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/dmonitor/task/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此切换策略吗?', {
btn: ['确定','取消']
}, function(){
$.post('/dmonitor/task/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 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;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/dmonitor/task/operation',
data : {act: 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>
{extend name="common/layout" /}
{block name="title"}容灾切换策略{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
tbody tr>td:nth-child(4){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="3">解析记录</option><option value="4">备用解析记录</option><option value="2">解析记录ID</option><option value="5">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="status" class="form-control"><option value="">健康状况</option><option value="0">正常</option><option value="1">异常</option></select>
</div>
</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="/dmonitor/task/add" 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="javascript:operation('open')">开启运行</a></li><li><a href="javascript:operation('close')">停止运行</a></li><li><a href="javascript:operation('retry')">立即重试</a></li><li><a href="javascript:operation('delete')">删除</a></li></ul>
</div>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/dmonitor/task/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: '',
checkbox: true
},
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'main_value',
title: '解析记录',
formatter: function(value, row, index) {
return value;
}
},
{
field: 'type',
title: '切换设置',
formatter: function(value, row, index) {
if(value == 1) {
return '暂停解析';
} else if(value == 2) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="备用:'+row.backup_value+'" class="tips">切换备用</span>';
} else if(value == 3) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="同域名正常数量<='+row.cycle+'" class="tips">条件开启</span>';
} else {
return '无操作';
}
}
},
{
field: 'checktype',
title: '检测协议',
formatter: function(value, row, index) {
if(row.type <= 2){
if(value == 1) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.tcpport+'端口" class="tips">TCP</span>';
} else if(value == 2) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.checkurl+'" class="tips">HTTP(S)</span>';
} else {
return 'PING';
}
} else {
return '无';
}
}
},
{
field: 'frequency',
title: '检测间隔',
formatter: function(value, row, index) {
return value + '秒';
}
},
{
field: 'status',
title: '健康状况',
formatter: function(value, row, index) {
if(value == 0) {
return '<span class="label label-success">正常</span>';
} else {
return '<span class="label label-danger">异常</span>';
}
}
},
{
field: 'active',
title: '运行开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'checktimestr',
title: '上次检测时间'
},
{
field: 'addtimestr',
title: '添加时间',
visible: false
},
{
field: 'remark',
title: '备注',
visible: false
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/dmonitor/task/info/'+row.id+'" class="btn btn-info btn-xs">切换日志</a>&nbsp;&nbsp;';
html += '<a href="/dmonitor/task/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?subdomain='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/dmonitor/task/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此切换策略吗?', {
btn: ['确定','取消']
}, function(){
$.post('/dmonitor/task/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 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;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/dmonitor/task/operation',
data : {act: 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}

View File

@ -1,297 +1,297 @@
{extend name="common/layout" /}
{block name="title"}容灾切换策略{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.control-label[is-required]:before {
content: "*";
color: #f56c6c;
margin-right: 4px;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/dmonitor/task" 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="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<select name="did" v-model="set.did" class="form-control" required>
<option value="">--主域名--</option>
<option v-for="option in domainList" :value="option.id">{{option.name}}</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>解析记录</label>
<div class="col-sm-6"><div class="input-group">
<select name="recordid" v-model="set.recordid" id="recordid" class="form-control" required>
<option v-for="option in recordList" :value="option.RecordId">{{option.Value}} (线路:{{option.LineName}})</option>
</select>
<div class="input-group-btn">
<button type="button" @click="getRecordList" class="btn btn-info">点击获取</button>
</div>
</div></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>切换设置</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="option in typeList">
<input type="radio" name="type" :value="option.value" v-model="set.type" :disabled="option.disabled"> {{option.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.type==2">
<label class="col-sm-3 control-label no-padding-right" is-required>备用解析记录</label>
<div class="col-sm-6">
<input type="text" name="backup_value" v-model="set.backup_value" placeholder="支持填写IP或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" is-required>检测协议</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="option in checktypeList">
<input type="radio" name="checktype" :value="option.value" v-model="set.checktype" :disabled="option.disabled"> {{option.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype<2">
<label class="col-sm-3 control-label no-padding-right">指定检测IP</label>
<div class="col-sm-6">
<input type="text" name="checkip" v-model="set.checkurl" placeholder="留空默认为解析记录值IP" class="form-control" data-bv-ip="true">
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==1">
<label class="col-sm-3 control-label no-padding-right" is-required>TCP检测端口</label>
<div class="col-sm-6">
<input type="text" name="tcpport" v-model="set.tcpport" placeholder="填写TCP端口号" class="form-control" data-bv-integer="true" min="1" max="65535" required>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==2">
<label class="col-sm-3 control-label no-padding-right" is-required>检测URL地址</label>
<div class="col-sm-6">
<input type="text" name="checkurl" v-model="set.checkurl" placeholder="填写以http(s)://开头的完整地址http状态码须为2xx/3xx" class="form-control" data-bv-uri="true" required>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==2">
<label class="col-sm-3 control-label no-padding-right">使用代理请求</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="proxy" value="0" v-model="set.proxy">
</label>
<label class="radio-inline">
<input type="radio" name="proxy" value="1" v-model="set.proxy">
</label>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype>0">
<label class="col-sm-3 control-label no-padding-right" is-required>最大超时时间</label>
<div class="col-sm-3">
<div class="input-group">
<input type="text" name="timeout" v-model="set.timeout" placeholder="填写请求最大超时时间" class="form-control" data-bv-integer="true" min="1" required>
<span class="input-group-addon"></span>
</div>
</div>
</div>
<div class="form-group" v-show="set.type==3">
<label class="col-sm-3 control-label no-padding-right" is-required>同域名正常数量<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="与暂停解析配合使用,当同域名正常记录数量&lt;=几条时开启解析"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-3">
<input type="text" name="cycle" v-model="set.cycle" placeholder="同域名正常记录数量&lt;=几条时开启解析" class="form-control" data-bv-integer="true" min="0" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>检测间隔</label>
<div class="col-sm-3">
<div class="input-group">
<input type="text" name="frequency" v-model="set.frequency" placeholder="每次检测的间隔时间" class="form-control" data-bv-integer="true" min="1" required>
<span class="input-group-addon"></span>
</div>
</div>
</div>
<div class="form-group" v-show="set.type<=2">
<label class="col-sm-3 control-label no-padding-right" is-required>确认次数</label>
<div class="col-sm-3">
<input type="text" name="cycle" v-model="set.cycle" placeholder="连续失败几次后进行切换" class="form-control" data-bv-integer="true" min="1" required>
</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">
<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="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<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',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
recordid: '',
recordinfo: '',
main_value: '',
type: 1,
backup_value: '',
checktype: 1,
tcpport: 80,
checkurl: '',
frequency: 5,
timeout: 2,
cycle: 3,
proxy: 0,
cdn: 0,
},
dnstype: null,
domainList: domainList,
recordList: [],
typeList: [
{value:0, label:'无操作'},
{value:1, label:'暂停解析'},
{value:2, label:'切换备用解析'},
{value:3, label:'条件开启解析'},
],
checktypeList: [
{value:0, label:'PING', disabled: support_ping=='0'},
{value:1, label:'TCP'},
{value:2, label:'HTTP(S)'},
]
},
watch: {
'set.recordid': function(val){
if(val == '') return;
var record = this.recordList.find(item => item.RecordId == val);
if(record){
this.set.recordinfo = JSON.stringify({Line:record.Line, LineName:record.LineName, TTL:record.TTL});
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() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
var recordinfo = JSON.parse(this.set.recordinfo);
this.recordList = [{RecordId:this.set.recordid, Value:this.set.main_value, Line:recordinfo.Line, LineName:recordinfo.LineName, TTL:recordinfo.TTL}];
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
getRecordList(){
var that = this;
if(this.set.did == ''){
layer.msg('请先选择域名', {time: 800});return;
}
if(this.set.rr == ''){
layer.msg('主机记录不能为空', {time: 800});return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/record/list',
data : {id:this.set.did, rr:this.set.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.msg('成功获取到'+data.data.length+'条解析记录', {icon:1, time:800});
that.recordList = data.data;
if(that.set.recordid){
var record = that.recordList.find(item => item.RecordId == that.set.recordid);
if(record){
that.set.recordinfo = JSON.stringify({Line:record.Line, LineName:record.LineName, TTL:record.TTL});
if(typeof record.Value == 'object') that.set.main_value = record.Value[0];
else that.set.main_value = record.Value;
}
}
}else{
layer.alert(data.msg, {icon:2});
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('task?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/dmonitor/task';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}容灾切换策略{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.control-label[is-required]:before {
content: "*";
color: #f56c6c;
margin-right: 4px;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/dmonitor/task" 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="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<select name="did" v-model="set.did" class="form-control" required>
<option value="">--主域名--</option>
<option v-for="option in domainList" :value="option.id">{{option.name}}</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>解析记录</label>
<div class="col-sm-6"><div class="input-group">
<select name="recordid" v-model="set.recordid" id="recordid" class="form-control" required>
<option v-for="option in recordList" :value="option.RecordId">{{option.Value}} (线路:{{option.LineName}})</option>
</select>
<div class="input-group-btn">
<button type="button" @click="getRecordList" class="btn btn-info">点击获取</button>
</div>
</div></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>切换设置</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="option in typeList">
<input type="radio" name="type" :value="option.value" v-model="set.type" :disabled="option.disabled"> {{option.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.type==2">
<label class="col-sm-3 control-label no-padding-right" is-required>备用解析记录</label>
<div class="col-sm-6">
<input type="text" name="backup_value" v-model="set.backup_value" placeholder="支持填写IP或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" is-required>检测协议</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="option in checktypeList">
<input type="radio" name="checktype" :value="option.value" v-model="set.checktype" :disabled="option.disabled"> {{option.label}}
</label>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype<2">
<label class="col-sm-3 control-label no-padding-right">指定检测IP</label>
<div class="col-sm-6">
<input type="text" name="checkip" v-model="set.checkurl" placeholder="留空默认为解析记录值IP" class="form-control" data-bv-ip="true">
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==1">
<label class="col-sm-3 control-label no-padding-right" is-required>TCP检测端口</label>
<div class="col-sm-6">
<input type="text" name="tcpport" v-model="set.tcpport" placeholder="填写TCP端口号" class="form-control" data-bv-integer="true" min="1" max="65535" required>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==2">
<label class="col-sm-3 control-label no-padding-right" is-required>检测URL地址</label>
<div class="col-sm-6">
<input type="text" name="checkurl" v-model="set.checkurl" placeholder="填写以http(s)://开头的完整地址http状态码须为2xx/3xx" class="form-control" data-bv-uri="true" required>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype==2">
<label class="col-sm-3 control-label no-padding-right">使用代理请求</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="proxy" value="0" v-model="set.proxy">
</label>
<label class="radio-inline">
<input type="radio" name="proxy" value="1" v-model="set.proxy">
</label>
</div>
</div>
<div class="form-group" v-show="set.type<=2&&set.checktype>0">
<label class="col-sm-3 control-label no-padding-right" is-required>最大超时时间</label>
<div class="col-sm-3">
<div class="input-group">
<input type="text" name="timeout" v-model="set.timeout" placeholder="填写请求最大超时时间" class="form-control" data-bv-integer="true" min="1" required>
<span class="input-group-addon"></span>
</div>
</div>
</div>
<div class="form-group" v-show="set.type==3">
<label class="col-sm-3 control-label no-padding-right" is-required>同域名正常数量<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="与暂停解析配合使用,当同域名正常记录数量&lt;=几条时开启解析"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-3">
<input type="text" name="cycle" v-model="set.cycle" placeholder="同域名正常记录数量&lt;=几条时开启解析" class="form-control" data-bv-integer="true" min="0" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>检测间隔</label>
<div class="col-sm-3">
<div class="input-group">
<input type="text" name="frequency" v-model="set.frequency" placeholder="每次检测的间隔时间" class="form-control" data-bv-integer="true" min="1" required>
<span class="input-group-addon"></span>
</div>
</div>
</div>
<div class="form-group" v-show="set.type<=2">
<label class="col-sm-3 control-label no-padding-right" is-required>确认次数</label>
<div class="col-sm-3">
<input type="text" name="cycle" v-model="set.cycle" placeholder="连续失败几次后进行切换" class="form-control" data-bv-integer="true" min="1" required>
</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">
<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-3.1.1.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<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',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
recordid: '',
recordinfo: '',
main_value: '',
type: 1,
backup_value: '',
checktype: 1,
tcpport: 80,
checkurl: '',
frequency: 5,
timeout: 2,
cycle: 3,
proxy: 0,
cdn: 0,
},
dnstype: null,
domainList: domainList,
recordList: [],
typeList: [
{value:0, label:'无操作'},
{value:1, label:'暂停解析'},
{value:2, label:'切换备用解析'},
{value:3, label:'条件开启解析'},
],
checktypeList: [
{value:0, label:'PING', disabled: support_ping=='0'},
{value:1, label:'TCP'},
{value:2, label:'HTTP(S)'},
]
},
watch: {
'set.recordid': function(val){
if(val == '') return;
var record = this.recordList.find(item => item.RecordId == val);
if(record){
this.set.recordinfo = JSON.stringify({Line:record.Line, LineName:record.LineName, TTL:record.TTL});
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() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
var recordinfo = JSON.parse(this.set.recordinfo);
this.recordList = [{RecordId:this.set.recordid, Value:this.set.main_value, Line:recordinfo.Line, LineName:recordinfo.LineName, TTL:recordinfo.TTL}];
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
getRecordList(){
var that = this;
if(this.set.did == ''){
layer.msg('请先选择域名', {time: 800});return;
}
if(this.set.rr == ''){
layer.msg('主机记录不能为空', {time: 800});return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/record/list',
data : {id:this.set.did, rr:this.set.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.msg('成功获取到'+data.data.length+'条解析记录', {icon:1, time:800});
that.recordList = data.data;
if(that.set.recordid){
var record = that.recordList.find(item => item.RecordId == that.set.recordid);
if(record){
that.set.recordinfo = JSON.stringify({Line:record.Line, LineName:record.LineName, TTL:record.TTL});
if(typeof record.Value == 'object') that.set.main_value = record.Value[0];
else that.set.main_value = record.Value;
}
}
}else{
layer.alert(data.msg, {icon:2});
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('task?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/dmonitor/task';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{/block}

View File

@ -1,73 +1,73 @@
{extend name="common/layout" /}
{block name="title"}切换记录{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(4){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="action" class="form-control"><option value="0">操作类型</option><option value="1">发生异常</option><option value="2">恢复正常</option></select>
</div>
</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>
&nbsp;&nbsp;24H告警次数<strong>{$info.fail_count}</strong>&nbsp;&nbsp;切换次数:<strong>{$info.switch_count}</strong>
</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="/static/js/custom.js"></script>
<script>
var action_name = {$info.action_name|json_encode|raw};
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/dmonitor/task/log/data/{$info.id}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'action',
title: '操作类型',
formatter: function(value, row, index) {
return action_name[value];
}
},
{
field: 'date',
title: '时间'
},
{
field: 'errmsg',
title: '异常原因'
}
],
})
})
</script>
{extend name="common/layout" /}
{block name="title"}切换记录{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(4){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="action" class="form-control"><option value="0">操作类型</option><option value="1">发生异常</option><option value="2">恢复正常</option></select>
</div>
</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>
&nbsp;&nbsp;24H告警次数<strong>{$info.fail_count}</strong>&nbsp;&nbsp;切换次数:<strong>{$info.switch_count}</strong>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
var action_name = {$info.action_name|json_encode|raw};
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/dmonitor/task/log/data/{$info.id}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'action',
title: '操作类型',
formatter: function(value, row, index) {
return action_name[value];
}
},
{
field: 'date',
title: '时间'
},
{
field: 'errmsg',
title: '异常原因'
}
],
})
})
</script>
{/block}

View File

@ -1,248 +1,248 @@
{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">&times;</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">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="AccessKey或备注">
</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>
</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="/static/js/custom.js"></script>
<script>
var dnsconfig = {$dnsconfig|json_encode|raw};
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/account/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '所属平台',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+value;
}
},
{
field: 'ak',
title: 'AccessKey'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
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>';
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(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/account/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
</script>
{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">&times;</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">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="AccessKey或备注">
</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>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<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;
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: '/account/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '所属平台',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+value;
}
},
{
field: 'ak',
title: 'AccessKey'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
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>';
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(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/account/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
</script>
{/block}

View File

@ -1,138 +1,138 @@
{extend name="common/layout" /}
{block name="title"}批量添加解析 - {$domainName}{/block}
{block name="main"}
<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="/record/{$domainId}" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加解析 - {$domainName}</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right">主机记录&记录值</label>
<div class="col-sm-6">
<textarea name="record" placeholder="主机记录和记录值用空格隔开,一行一个" class="form-control" rows="8" required></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control">
<option value="">A / CNAME / AAAA 自动识别</option>
<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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
{if $dnsconfig.redirect}<option value="REDIRECT_URL">显性URL</option>
<option value="FORWARD_URL">隐性URL</option>{/if}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6" id="line_list">
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" value="10">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" value="600" placeholder="指解析结果在DNS服务器中的缓存时间" required min="{$minTTL}">
</div>
</div>
<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>
</form>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/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;
$(document).ready(function(){
$("select[name=type]").change(function(){
if($(this).val() == 'MX'){
$("#mx_type").show();
}else{
$("#mx_type").hide();
}
});
$("#form-store").bootstrapValidator();
initLine();
});
function initLine(option, elem){
option = option || '';
elem = elem || 'line_list';
$("#"+elem).empty();
$.each(recordLine, function(index, item){
if(item.parent == null){
option += '<option value="'+item.id+'">'+item.name+'</option>';
}
})
$("#"+elem).append('<select name="line" class="form-control" onchange="changeLine(this, \''+elem+'\')">'+option+'</select>');
}
function changeLine(obj, elem){
var line = $(obj).val();
var flag = false;
$("#"+elem).children().each(function(index, elem){
if(flag) $(elem).remove()
if(obj == elem){
flag = true;
}
})
if($(obj).find("option:selected").text() == '子集线路(非必填)') return;
var tempLine = recordLine.filter((x) => x.parent == line)
if(tempLine.length > 0){
var option = '<option value="'+line+'">子集线路(非必填)</option>';
$.each(tempLine, function(index, item){
option += '<option value="'+item.id+'">'+item.name+'</option>';
})
$("#"+elem).append('<select name="line" class="form-control" onchange="changeLine(this, \''+elem+'\')">'+option+'</select>');
}
}
function save(){
$("#form-store").data("bootstrapValidator").validate();
if(!$("#form-store").data("bootstrapValidator").isValid()){
return;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/batchadd/{$domainId}',
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(){
if(document.referrer.indexOf('/record?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/record/{$domainId}';
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}批量添加解析 - {$domainName}{/block}
{block name="main"}
<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="/record/{$domainId}" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加解析 - {$domainName}</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right">主机记录&记录值</label>
<div class="col-sm-6">
<textarea name="record" placeholder="主机记录和记录值用空格隔开,一行一个" class="form-control" rows="8" required></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control">
<option value="">A / CNAME / AAAA 自动识别</option>
<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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
{if $dnsconfig.redirect}<option value="REDIRECT_URL">显性URL</option>
<option value="FORWARD_URL">隐性URL</option>{/if}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6" id="line_list">
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" value="10">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" value="600" placeholder="指解析结果在DNS服务器中的缓存时间" required min="{$minTTL}">
</div>
</div>
<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>
</form>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.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;
$(document).ready(function(){
$("select[name=type]").change(function(){
if($(this).val() == 'MX'){
$("#mx_type").show();
}else{
$("#mx_type").hide();
}
});
$("#form-store").bootstrapValidator();
initLine();
});
function initLine(option, elem){
option = option || '';
elem = elem || 'line_list';
$("#"+elem).empty();
$.each(recordLine, function(index, item){
if(item.parent == null){
option += '<option value="'+item.id+'">'+item.name+'</option>';
}
})
$("#"+elem).append('<select name="line" class="form-control" onchange="changeLine(this, \''+elem+'\')">'+option+'</select>');
}
function changeLine(obj, elem){
var line = $(obj).val();
var flag = false;
$("#"+elem).children().each(function(index, elem){
if(flag) $(elem).remove()
if(obj == elem){
flag = true;
}
})
if($(obj).find("option:selected").text() == '子集线路(非必填)') return;
var tempLine = recordLine.filter((x) => x.parent == line)
if(tempLine.length > 0){
var option = '<option value="'+line+'">子集线路(非必填)</option>';
$.each(tempLine, function(index, item){
option += '<option value="'+item.id+'">'+item.name+'</option>';
})
$("#"+elem).append('<select name="line" class="form-control" onchange="changeLine(this, \''+elem+'\')">'+option+'</select>');
}
}
function save(){
$("#form-store").data("bootstrapValidator").validate();
if(!$("#form-store").data("bootstrapValidator").isValid()){
return;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/batchadd/{$domainId}',
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(){
if(document.referrer.indexOf('/record?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/record/{$domainId}';
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
</script>
{/block}

View File

@ -1,176 +1,176 @@
{extend name="common/layout" /}
{block name="title"}批量添加解析{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){min-width:300px;word-break:break-all;}
</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="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加解析</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right">主机记录&记录值</label>
<div class="col-sm-6">
<textarea name="record" v-model="set.record" placeholder="主机记录和记录值用空格隔开,一行一个" class="form-control" rows="8" required></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control" v-model="set.type">
<option value="">A / CNAME / AAAA 自动识别</option>
<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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6">
<select name="line" class="form-control" disabled><option value="default">默认</option></select>
</div>
</div>
<div class="form-group" v-if="existCF">
<label class="col-sm-3 control-label no-padding-right">开启反代</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="proxy" value="0" v-model="set.proxy">
</label>
<label class="radio-inline">
<input type="radio" name="proxy" value="1" v-model="set.proxy">仅Cloudflare域名
</label>
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" v-model="set.mx">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" v-model="set.ttl" placeholder="指解析结果在DNS服务器中的缓存时间" required>
</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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">解析记录添加结果</h3></div>
<div class="panel-body">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>ID</th>
<th>域名</th>
<th>添加结果</th>
</tr>
</thead>
<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 v-html="item.result"></td>
</tr>
</tbody>
</table>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
new Vue({
el: '#app',
data: {
domainList: [],
set: {
record: '',
type: '',
mx: 10,
ttl: 600,
proxy: 0,
},
existCF: false,
},
watch: {
'set.type': function(val){
if(val == 'MX'){
$("#mx_type").show();
}else{
$("#mx_type").hide();
}
}
},
mounted() {
this.domainList = JSON.parse(sessionStorage.getItem('domains')) || [];
if(this.domainList.length == 0){
layer.alert('请先选中要添加解析的域名', {icon: 2}, function(){
window.location.href = '/domain';
});
}
for(var i=0; i<this.domainList.length; i++){
this.$set(this.domainList[i], 'result', '<span class="text-muted">待添加</span>');
}
this.existCF = this.domainList.some(item => item.type === 'cloudflare');
},
methods: {
async save(id){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
url: '/record/batchadd/'+id,
data: that.set,
dataType: 'json',
success: function(data) {
resolve(data);
},
error: function() {
reject('服务器错误');
}
});
});
},
async submit(){
if(this.set.record == ''){
layer.alert('请填写主机记录和记录值', {icon: 2});
return;
}
if(this.set.ttl == ''){
layer.alert('请填写TTL', {icon: 2});
return;
}
if(this.set.type == 'MX' && this.set.mx == ''){
layer.alert('请填写MX优先级', {icon: 2});
return;
}
var ii = layer.load(2);
for(var i=0; i<this.domainList.length; i++){
this.domainList[i].result = '<span class="text-yellow"><i class="fa fa-refresh fa-spin fa-fw"></i> 正在添加</span>';
var res = await this.save(this.domainList[i].id);
if(res.code == 0){
this.domainList[i].result = '<span class="text-green">'+res.msg+'</span>';
}else{
this.domainList[i].result = '<span class="text-red">'+res.msg+'</span>';
}
}
layer.close(ii);
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}批量添加解析{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){min-width:300px;word-break:break-all;}
</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="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加解析</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right">主机记录&记录值</label>
<div class="col-sm-6">
<textarea name="record" v-model="set.record" placeholder="主机记录和记录值用空格隔开,一行一个" class="form-control" rows="8" required></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control" v-model="set.type">
<option value="">A / CNAME / AAAA 自动识别</option>
<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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6">
<select name="line" class="form-control" disabled><option value="default">默认</option></select>
</div>
</div>
<div class="form-group" v-if="existCF">
<label class="col-sm-3 control-label no-padding-right">开启反代</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="proxy" value="0" v-model="set.proxy">
</label>
<label class="radio-inline">
<input type="radio" name="proxy" value="1" v-model="set.proxy">仅Cloudflare域名
</label>
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" v-model="set.mx">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" v-model="set.ttl" placeholder="指解析结果在DNS服务器中的缓存时间" required>
</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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">解析记录添加结果</h3></div>
<div class="panel-body">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>ID</th>
<th>域名</th>
<th>添加结果</th>
</tr>
</thead>
<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 v-html="item.result"></td>
</tr>
</tbody>
</table>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/vue-2.7.16.min.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script>
new Vue({
el: '#app',
data: {
domainList: [],
set: {
record: '',
type: '',
mx: 10,
ttl: 600,
proxy: 0,
},
existCF: false,
},
watch: {
'set.type': function(val){
if(val == 'MX'){
$("#mx_type").show();
}else{
$("#mx_type").hide();
}
}
},
mounted() {
this.domainList = JSON.parse(sessionStorage.getItem('domains')) || [];
if(this.domainList.length == 0){
layer.alert('请先选中要添加解析的域名', {icon: 2}, function(){
window.location.href = '/domain';
});
}
for(var i=0; i<this.domainList.length; i++){
this.$set(this.domainList[i], 'result', '<span class="text-muted">待添加</span>');
}
this.existCF = this.domainList.some(item => item.type === 'cloudflare');
},
methods: {
async save(id){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
url: '/record/batchadd/'+id,
data: that.set,
dataType: 'json',
success: function(data) {
resolve(data);
},
error: function() {
reject('服务器错误');
}
});
});
},
async submit(){
if(this.set.record == ''){
layer.alert('请填写主机记录和记录值', {icon: 2});
return;
}
if(this.set.ttl == ''){
layer.alert('请填写TTL', {icon: 2});
return;
}
if(this.set.type == 'MX' && this.set.mx == ''){
layer.alert('请填写MX优先级', {icon: 2});
return;
}
var ii = layer.load(2);
for(var i=0; i<this.domainList.length; i++){
this.domainList[i].result = '<span class="text-yellow"><i class="fa fa-refresh fa-spin fa-fw"></i> 正在添加</span>';
var res = await this.save(this.domainList[i].id);
if(res.code == 0){
this.domainList[i].result = '<span class="text-green">'+res.msg+'</span>';
}else{
this.domainList[i].result = '<span class="text-red">'+res.msg+'</span>';
}
}
layer.close(ii);
}
},
});
</script>
{/block}

View File

@ -1,157 +1,157 @@
{extend name="common/layout" /}
{block name="title"}批量修改解析{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){min-width:300px;word-break:break-all;}
</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="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量修改解析</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<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="name" v-model="set.name" placeholder="填写已有的主机记录" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control" v-model="set.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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6">
<select name="line" class="form-control" disabled><option value="default">默认</option></select>
</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" class="form-control" name="value" v-model="set.value" placeholder="输入新的记录值" required>
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" v-model="set.mx" placeholder="留空则不修改">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" v-model="set.ttl" placeholder="留空则不修改">
</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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">解析记录修改结果</h3></div>
<div class="panel-body">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>ID</th>
<th>域名</th>
<th>修改结果</th>
</tr>
</thead>
<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 v-html="item.result"></td>
</tr>
</tbody>
</table>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
new Vue({
el: '#app',
data: {
domainList: [],
set: {
id: '',
name: '',
type: 'A',
value: '',
mx: '',
ttl: '',
}
},
mounted() {
this.domainList = JSON.parse(sessionStorage.getItem('domains')) || [];
if(this.domainList.length == 0){
layer.alert('请先选中要修改解析的域名', {icon: 2}, function(){
window.location.href = '/domain';
});
}
for(var i=0; i<this.domainList.length; i++){
this.$set(this.domainList[i], 'result', '<span class="text-muted">待修改</span>');
}
},
methods: {
async save(id){
var that = this;
return new Promise((resolve, reject) => {
that.set.id = id;
$.ajax({
type: "POST",
url: '/record/batchedit',
data: that.set,
dataType: 'json',
success: function(data) {
resolve(data);
},
error: function() {
reject('服务器错误');
}
});
});
},
async submit(){
if(this.set.name == ''){
layer.alert('请填写主机记录', {icon: 2});
return;
}
if(this.set.value == ''){
layer.alert('请填写记录值', {icon: 2});
return;
}
var ii = layer.load(2);
for(var i=0; i<this.domainList.length; i++){
this.domainList[i].result = '<span class="text-yellow"><i class="fa fa-refresh fa-spin fa-fw"></i> 正在修改</span>';
var res = await this.save(this.domainList[i].id);
if(res.code == 0){
this.domainList[i].result = '<span class="text-green">'+res.msg+'</span>';
}else{
this.domainList[i].result = '<span class="text-red">'+res.msg+'</span>';
}
}
layer.close(ii);
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}批量修改解析{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){min-width:300px;word-break:break-all;}
</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="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量修改解析</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<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="name" v-model="set.name" placeholder="填写已有的主机记录" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录类型</label>
<div class="col-sm-6">
<select name="type" class="form-control" v-model="set.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="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路类型</label>
<div class="col-sm-6">
<select name="line" class="form-control" disabled><option value="default">默认</option></select>
</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" class="form-control" name="value" v-model="set.value" placeholder="输入新的记录值" required>
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mx" v-model="set.mx" placeholder="留空则不修改">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="ttl" v-model="set.ttl" placeholder="留空则不修改">
</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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">解析记录修改结果</h3></div>
<div class="panel-body">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>ID</th>
<th>域名</th>
<th>修改结果</th>
</tr>
</thead>
<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 v-html="item.result"></td>
</tr>
</tbody>
</table>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/vue-2.7.16.min.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script>
new Vue({
el: '#app',
data: {
domainList: [],
set: {
id: '',
name: '',
type: 'A',
value: '',
mx: '',
ttl: '',
}
},
mounted() {
this.domainList = JSON.parse(sessionStorage.getItem('domains')) || [];
if(this.domainList.length == 0){
layer.alert('请先选中要修改解析的域名', {icon: 2}, function(){
window.location.href = '/domain';
});
}
for(var i=0; i<this.domainList.length; i++){
this.$set(this.domainList[i], 'result', '<span class="text-muted">待修改</span>');
}
},
methods: {
async save(id){
var that = this;
return new Promise((resolve, reject) => {
that.set.id = id;
$.ajax({
type: "POST",
url: '/record/batchedit',
data: that.set,
dataType: 'json',
success: function(data) {
resolve(data);
},
error: function() {
reject('服务器错误');
}
});
});
},
async submit(){
if(this.set.name == ''){
layer.alert('请填写主机记录', {icon: 2});
return;
}
if(this.set.value == ''){
layer.alert('请填写记录值', {icon: 2});
return;
}
var ii = layer.load(2);
for(var i=0; i<this.domainList.length; i++){
this.domainList[i].result = '<span class="text-yellow"><i class="fa fa-refresh fa-spin fa-fw"></i> 正在修改</span>';
var res = await this.save(this.domainList[i].id);
if(res.code == 0){
this.domainList[i].result = '<span class="text-green">'+res.msg+'</span>';
}else{
this.domainList[i].result = '<span class="text-red">'+res.msg+'</span>';
}
}
layer.close(ii);
}
},
});
</script>
{/block}

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +1,158 @@
{extend name="common/layout" /}
{block name="title"}批量添加域名{/block}
{block name="main"}
<div class="row" id="app">
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title"><a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加域名</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 control-label">域名账户</label>
<div class="col-sm-9">
<div class="input-group">
<select name="aid" class="form-control" v-model="aid">
{foreach $accounts as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
</select>
<div class="input-group-btn">
<button type="button" @click="getDomainList" class="btn btn-info">获取域名</button>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">选择域名</label>
<div class="col-sm-9">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th><input type="checkbox" v-model="checkall"></th>
<th>域名</th>
</tr>
</thead>
<tbody>
<tr v-for="item in domainList">
<td><input type="checkbox" name="domain[]" :value="item.DomainId" v-model="item.checked" :disabled="item.disabled"></td>
<td><span :title="item.DomainId">{{item.Domain}}</span><font color="#888" v-if="item.disabled"> (已添加)</font></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9"><button type="button" class="btn btn-primary" @click="submit">确定添加</button></div>
</div>
</form>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
new Vue({
el: '#app',
data: {
aid: '',
domainList: [],
page: 1,
pagesize: 10,
checkall: false,
},
watch: {
aid: function(val){
this.domainList = [];
},
checkall: function(val){
this.domainList.forEach(function(item){
item.checked = val&&!item.disabled;
});
}
},
mounted() {
this.aid = '{$accounts|@key}';
},
methods: {
async getDomainListPaged(){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
url: "/domain/list",
data: {aid: that.aid, page: that.page, pagesize: that.pagesize},
dataType: 'json',
success: function(data) {
if(data.code == 0){
resolve(data.data);
}else{
reject(data.msg);
}
}
});
});
},
async getDomainList(){
this.domainList = [];
while(true){
try{
layer.msg('正在获取第'+this.page+'页域名', {icon: 16, shade: 0.01});
var data = await this.getDomainListPaged();
if(data.total == 0 || data.list.length == 0){
layer.closeAll();
break;
}
this.domainList = this.domainList.concat(data.list);
if(this.domainList.length >= data.total){
layer.closeAll();
break;
}
this.page++;
}catch(e){
layer.alert(e, {icon: 2});
break;
}
}
},
submit(){
var domains = [];
this.domainList.forEach(function(item){
if(item.checked && !item.disabled){
domains.push({name: item.Domain, id: item.DomainId, recordcount:item.RecordCount});
}
});
if(this.aid == ''){
layer.alert('请选择域名账户');
return;
}
if(domains.length == 0){
layer.alert('请选择要添加的域名');
return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "/domain/op/act/batchadd",
data: {aid: this.aid, domains: domains},
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
window.location.href = '/domain';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}批量添加域名{/block}
{block name="main"}
<div class="row" id="app">
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title"><a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>批量添加域名</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="form-store">
<div class="form-group">
<label class="col-sm-3 control-label">域名账户</label>
<div class="col-sm-9">
<div class="input-group">
<select name="aid" class="form-control" v-model="aid">
{foreach $accounts as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
</select>
<div class="input-group-btn">
<button type="button" @click="getDomainList" class="btn btn-info">获取域名</button>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">选择域名</label>
<div class="col-sm-9">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th><input type="checkbox" v-model="checkall"></th>
<th>域名</th>
</tr>
</thead>
<tbody>
<tr v-for="item in domainList">
<td><input type="checkbox" name="domain[]" :value="item.DomainId" v-model="item.checked" :disabled="item.disabled"></td>
<td><span :title="item.DomainId">{{item.Domain}}</span><font color="#888" v-if="item.disabled"> (已添加)</font></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9"><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-3.1.1.js"></script>
<script>
new Vue({
el: '#app',
data: {
aid: '',
domainList: [],
page: 1,
pagesize: 10,
checkall: false,
},
watch: {
aid: function(val){
this.domainList = [];
},
checkall: function(val){
this.domainList.forEach(function(item){
item.checked = val&&!item.disabled;
});
}
},
mounted() {
this.aid = '{$accounts|@key}';
},
methods: {
async getDomainListPaged(){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
url: "/domain/list",
data: {aid: that.aid, page: that.page, pagesize: that.pagesize},
dataType: 'json',
success: function(data) {
if(data.code == 0){
resolve(data.data);
}else{
reject(data.msg);
}
}
});
});
},
async getDomainList(){
this.domainList = [];
while(true){
try{
layer.msg('正在获取第'+this.page+'页域名', {icon: 16, shade: 0.01});
var data = await this.getDomainListPaged();
if(data.total == 0 || data.list.length == 0){
layer.closeAll();
break;
}
this.domainList = this.domainList.concat(data.list);
if(this.domainList.length >= data.total){
layer.closeAll();
break;
}
this.page++;
}catch(e){
layer.alert(e, {icon: 2});
break;
}
}
},
submit(){
var domains = [];
this.domainList.forEach(function(item){
if(item.checked && !item.disabled){
domains.push({name: item.Domain, id: item.DomainId, recordcount:item.RecordCount});
}
});
if(this.aid == ''){
layer.alert('请选择域名账户');
return;
}
if(domains.length == 0){
layer.alert('请选择要添加的域名');
return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "/domain/op/act/batchadd",
data: {aid: this.aid, domains: domains},
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
window.location.href = '/domain';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{/block}

View File

@ -1,81 +1,81 @@
{extend name="common/layout" /}
{block name="title"}域名到期提醒设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title"><a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>域名到期提醒设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">到期提醒天数</label>
<div class="col-sm-9"><input type="text" name="expire_noticedays" value="{:config_get('expire_noticedays')}" class="form-control" placeholder="留空则不开启到期提醒"/><font color="green">域名到期前多少天发送通知可填写多个天数用英文逗号隔开。例如填写7,14则在域名到期前7天与14天分别发送通知。</font></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">邮件通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_mail" default="{:config_get('expire_notice_mail')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">微信公众号通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_wxtpl" default="{:config_get('expire_notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Telegram机器人通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_tgbot" default="{:config_get('expire_notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">群机器人Webhook</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_webhook" default="{:config_get('expire_notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option></select></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"/>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<p>需添加<a href="/system/cronset">计划任务</a>,支持域名到期提醒+域名列表到期时间自动刷新。</p>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
</script>
{extend name="common/layout" /}
{block name="title"}域名到期提醒设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title"><a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>域名到期提醒设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">到期提醒天数</label>
<div class="col-sm-9"><input type="text" name="expire_noticedays" value="{:config_get('expire_noticedays')}" class="form-control" placeholder="留空则不开启到期提醒"/><font color="green">域名到期前多少天发送通知可填写多个天数用英文逗号隔开。例如填写7,14则在域名到期前7天与14天分别发送通知。</font></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">邮件通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_mail" default="{:config_get('expire_notice_mail')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">微信公众号通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_wxtpl" default="{:config_get('expire_notice_wxtpl')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Telegram机器人通知</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_tgbot" default="{:config_get('expire_notice_tgbot')}"><option value="0">关闭</option><option value="1">开启</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">群机器人Webhook</label>
<div class="col-sm-9"><select class="form-control" name="expire_notice_webhook" default="{:config_get('expire_notice_webhook')}"><option value="0">关闭</option><option value="1">开启</option></select></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"/>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<p>需添加<a href="/system/cronset">计划任务</a>,支持域名到期提醒+域名列表到期时间自动刷新。</p>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
</script>
{/block}

View File

@ -1,47 +1,47 @@
{extend name="common/layout" /}
{block name="title"}域名日志{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/record/log/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'time',
title: '操作时间'
},
{
field: 'data',
title: '操作行为'
}
],
})
})
</script>
{extend name="common/layout" /}
{block name="title"}域名日志{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/record/log/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'time',
title: '操作时间'
},
{
field: 'data',
title: '操作行为'
}
],
})
})
</script>
{/block}

File diff suppressed because it is too large Load Diff

View File

@ -1,252 +1,252 @@
{extend name="common/layout" /}
{block name="title"}权重配置 - {$domainName}{/block}
{block name="main"}
<style>
.table-bordered>tbody>tr>td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;vertical-align:middle;}
</style>
<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">&times;</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" onsubmit="return save(this)">
<input type="hidden" name="id"/>
<input type="hidden" name="subdomain"/>
<input type="hidden" name="type"/>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">选择线路</label>
<div class="col-sm-9">
<select name="line" id="line" class="form-control" onchange="changeLine()"></select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路权重开关</label>
<div class="col-sm-9">
<div class="material-switch" style="padding-top:7px"><input id="weight-switch" name="status" type="checkbox" value="1" checked onchange="changeOpen()"/><label for="weight-switch" class="label-primary"></label></div>
</div>
</div>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr><th>主机记录</th><th>记录类型</th><th>记录值</th><th>权重</th></tr>
</thead>
<tbody id="weight-list">
<tr><td colspan="4" class="text-center">正在加载...</td></tr>
</tbody>
</table>
<span class="text-muted">提示权重范围为数字0-100</span>
<div class="form-group">
<div class="col-sm-12 text-right">
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<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="/record/{$domainId}" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>权重配置 - {$domainName}</h3></div>
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="keyword" placeholder="子域名">
</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>
</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="/static/js/custom.js"></script>
<script>
var dnsconfig = {$dnsconfig|json_encode|raw};
var recordLine = {$recordLine|json_encode|raw};
var domainId = {$domainId};
var weightList = [];
var lineList = [];
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/record/weight/data/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'id',
columns: [
{
field: 'SubDomain',
title: '子域名'
},
{
field: 'Type',
title: '记录类型'
},
{
field: 'RecordCount',
title: '记录数量'
},
{
field: 'Open',
title: '权重配置状态',
formatter: function(value, row, index) {
if(value == 1){
return '<font color="green"><i class="fa fa-check-circle"></i>已开启</font>';
}else{
return '<font color="#b5bbc8"><i class="fa fa-pause-circle"></i>已关闭</font>';
}
}
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
var html = '';
if(row.Open == 1){
if(row.Type == 'CNAME'){
html += '<a class="btn btn-warning btn-xs" title="CNAME类型解析默认必须开权重模式每次解析应答只返回一条解析结果记录值" disabled>关闭权重</a>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:setWeightStatus(\''+row.SubDomain+'\', \'0\')" class="btn btn-warning btn-xs">关闭权重</a>&nbsp;&nbsp;';
}
}else{
html += '<a href="javascript:setWeightStatus(\''+row.SubDomain+'\', \'1\')" class="btn btn-success btn-xs">开启权重</a>&nbsp;&nbsp;';
}
html += '<a href="javascript:editframe('+row.id+')" class="btn btn-primary btn-xs">设置权重</a>';
return html;
}
},
],
})
})
function editframe(id){
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/list',
data : {id: domainId, rr: row.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store").modal('show');
$("#modal-title").html("设置权重 - "+row.SubDomain);
$("#form-store input[name=id]").val(id);
$("#form-store input[name=subdomain]").val(row.SubDomain);
$("#form-store input[name=type]").val(row.Type);
if(row.Type == 'CNAME') $("#weight-switch").prop("disabled", true);
else $("#weight-switch").prop("disabled", false);
lineList = [];
$.each(recordLine, function(i, item){
if(data.data.find(x => x.Line == item.id)){
item.open = row.Open;
if(row.LineAlgorithms && row.LineAlgorithms.LineAlgorithm.length > 0){
var tmpLine = row.LineAlgorithms.LineAlgorithm.find(x => x.Line == item.id);
if(tmpLine) item.open = tmpLine.Open;
}
lineList.push(item);
}
});
$("#line").empty();
$.each(lineList, function(i, item){
$("#line").append('<option value="'+item.id+'">'+item.name+'</option>');
});
weightList = data.data;
changeLine();
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function changeLine(){
var line = $("#line").val();
$("#weight-switch").prop("checked", lineList.find(x => x.id == line).open);
$("#weight-list").empty();
$.each(weightList, function(i, item){
if(item.Line == line){
$("#weight-list").append('<tr><td>'+item.Name+'</td><td>'+item.Type+'</td><td>'+item.Value+'</td><td><input type="number" class="form-control" name="weight['+item.RecordId+']" value="'+item.Weight+'" style="width:80px;" min="0" max="100"/></td></tr>');
}
});
changeOpen();
}
function changeOpen(){
if($("#weight-switch").is(':checked')){
$("#weight-list input[name^='weight']").prop("disabled", false);
}else{
$("#weight-list input[name^='weight']").prop("disabled", true);
}
}
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 : '/record/weight/{$domainId}/act/update',
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})
}
}
});
return false;
}
function setWeightStatus(subdomain, status){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/weight/{$domainId}/act/status',
data : {subdomain: subdomain, status: status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}权重配置 - {$domainName}{/block}
{block name="main"}
<style>
.table-bordered>tbody>tr>td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;vertical-align:middle;}
</style>
<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">&times;</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" onsubmit="return save(this)">
<input type="hidden" name="id"/>
<input type="hidden" name="subdomain"/>
<input type="hidden" name="type"/>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">选择线路</label>
<div class="col-sm-9">
<select name="line" id="line" class="form-control" onchange="changeLine()"></select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">线路权重开关</label>
<div class="col-sm-9">
<div class="material-switch" style="padding-top:7px"><input id="weight-switch" name="status" type="checkbox" value="1" checked onchange="changeOpen()"/><label for="weight-switch" class="label-primary"></label></div>
</div>
</div>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr><th>主机记录</th><th>记录类型</th><th>记录值</th><th>权重</th></tr>
</thead>
<tbody id="weight-list">
<tr><td colspan="4" class="text-center">正在加载...</td></tr>
</tbody>
</table>
<span class="text-muted">提示权重范围为数字0-100</span>
<div class="form-group">
<div class="col-sm-12 text-right">
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<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="/record/{$domainId}" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>权重配置 - {$domainName}</h3></div>
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="keyword" placeholder="子域名">
</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>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<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};
var recordLine = {$recordLine|json_encode|raw};
var domainId = {$domainId};
var weightList = [];
var lineList = [];
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/record/weight/data/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'id',
columns: [
{
field: 'SubDomain',
title: '子域名'
},
{
field: 'Type',
title: '记录类型'
},
{
field: 'RecordCount',
title: '记录数量'
},
{
field: 'Open',
title: '权重配置状态',
formatter: function(value, row, index) {
if(value == 1){
return '<font color="green"><i class="fa fa-check-circle"></i>已开启</font>';
}else{
return '<font color="#b5bbc8"><i class="fa fa-pause-circle"></i>已关闭</font>';
}
}
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
var html = '';
if(row.Open == 1){
if(row.Type == 'CNAME'){
html += '<a class="btn btn-warning btn-xs" title="CNAME类型解析默认必须开权重模式每次解析应答只返回一条解析结果记录值" disabled>关闭权重</a>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:setWeightStatus(\''+row.SubDomain+'\', \'0\')" class="btn btn-warning btn-xs">关闭权重</a>&nbsp;&nbsp;';
}
}else{
html += '<a href="javascript:setWeightStatus(\''+row.SubDomain+'\', \'1\')" class="btn btn-success btn-xs">开启权重</a>&nbsp;&nbsp;';
}
html += '<a href="javascript:editframe('+row.id+')" class="btn btn-primary btn-xs">设置权重</a>';
return html;
}
},
],
})
})
function editframe(id){
var row = $("#listTable").bootstrapTable('getRowByUniqueId', id);
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/list',
data : {id: domainId, rr: row.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store").modal('show');
$("#modal-title").html("设置权重 - "+row.SubDomain);
$("#form-store input[name=id]").val(id);
$("#form-store input[name=subdomain]").val(row.SubDomain);
$("#form-store input[name=type]").val(row.Type);
if(row.Type == 'CNAME') $("#weight-switch").prop("disabled", true);
else $("#weight-switch").prop("disabled", false);
lineList = [];
$.each(recordLine, function(i, item){
if(data.data.find(x => x.Line == item.id)){
item.open = row.Open;
if(row.LineAlgorithms && row.LineAlgorithms.LineAlgorithm.length > 0){
var tmpLine = row.LineAlgorithms.LineAlgorithm.find(x => x.Line == item.id);
if(tmpLine) item.open = tmpLine.Open;
}
lineList.push(item);
}
});
$("#line").empty();
$.each(lineList, function(i, item){
$("#line").append('<option value="'+item.id+'">'+item.name+'</option>');
});
weightList = data.data;
changeLine();
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function changeLine(){
var line = $("#line").val();
$("#weight-switch").prop("checked", lineList.find(x => x.id == line).open);
$("#weight-list").empty();
$.each(weightList, function(i, item){
if(item.Line == line){
$("#weight-list").append('<tr><td>'+item.Name+'</td><td>'+item.Type+'</td><td>'+item.Value+'</td><td><input type="number" class="form-control" name="weight['+item.RecordId+']" value="'+item.Weight+'" style="width:80px;" min="0" max="100"/></td></tr>');
}
});
changeOpen();
}
function changeOpen(){
if($("#weight-switch").is(':checked')){
$("#weight-list input[name^='weight']").prop("disabled", false);
}else{
$("#weight-list input[name^='weight']").prop("disabled", true);
}
}
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 : '/record/weight/{$domainId}/act/update',
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})
}
}
});
return false;
}
function setWeightStatus(subdomain, status){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/weight/{$domainId}/act/status',
data : {subdomain: subdomain, status: status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}
</script>
{/block}

View File

@ -1,240 +1,240 @@
{extend name="common/layout" /}
{block name="title"}聚合DNS管理系统{/block}
{block name="main"}
<style>
.table>tbody>tr>td{white-space: normal;}
.query-title {
background-color:#f5fafe;
word-break: keep-all;
}
.query-result{
word-break: break-all;
}
</style>
<div class="row">
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-aqua">
<div class="inner">
<h3 id="count1">0</h3>
<p>域名数量</p>
</div>
<div class="icon">
<i class="fa fa-list-ul"></i>
</div>
<a href="/domain" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-green">
<div class="inner">
<h3 id="count2">0</h3>
<p>容灾切换策略</p>
</div>
<div class="icon">
<i class="fa fa-heartbeat"></i>
</div>
<a href="/dmonitor/task" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-yellow">
<div class="inner">
<h3 id="count3">0</h3>
<p>SSL证书订单</p>
</div>
<div class="icon">
<i class="fa fa-expeditedssl"></i>
</div>
<a href="/cert/certorder" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-red">
<div class="inner">
<h3 id="count4">0</h3>
<p>SSL部署任务</p>
</div>
<div class="icon">
<i class="fa fa-connectdevelop"></i>
</div>
<a href="/cert/deploytask" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-md-12">
<div id="browser-notice"></div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<i class="fa fa-heartbeat fa-fw"></i>
<h3 class="box-title">容灾切换概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li><a href="/dmonitor/overview">运行状态<b><span class="pull-right" id="dmonitor_state"></span></b></a></li>
<li><a href="/dmonitor/task">切换策略(已开启)<span class="pull-right badge bg-blue" id="dmonitor_active">0</span></a>
</li>
<li><a href="/dmonitor/task?status=0">健康状况正常<span class="pull-right badge bg-green" id="dmonitor_status_0">0</span></a></li>
<li><a href="/dmonitor/task?status=1">健康状况异常<span class="pull-right badge bg-red" id="dmonitor_status_1">0</span></a></li>
</ul>
</div>
<div class="box box-info">
<div class="box-header with-border">
<i class="fa fa-globe fa-fw"></i>
<h3 class="box-title">CF优选IP概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li><a href="/optimizeip/opiplist">任务数量(已开启)<span class="pull-right badge bg-blue" id="optimizeip_active">0</span></a>
</li>
<li><a href="/optimizeip/opiplist?status=1">更新成功<span class="pull-right badge bg-green" id="optimizeip_status_1">0</span></a></li>
<li><a href="/optimizeip/opiplist?status=2">更新失败<span class="pull-right badge bg-red" id="optimizeip_status_2">0</span></a></li>
</ul>
</div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-success">
<div class="box-header with-border">
<i class="fa fa-globe fa-fw"></i>
<h3 class="box-title">SSL证书概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li class="text-center"><a href="/cert/certorder"><b>SSL证书订单</b></a></li>
<li><a href="/cert/certorder?status=3">已签发<span class="pull-right badge bg-green" id="certorder_status_3">0</span></a>
</li>
<li><a href="/cert/certorder?status=5">签发失败<span class="pull-right badge bg-red" id="certorder_status_5">0</span></a></li>
<li><a href="/cert/certorder?status=6">即将过期<span class="pull-right badge bg-orange" id="certorder_status_6">0</span></a></li>
<li><a href="/cert/certorder?status=7">已过期<span class="pull-right badge bg-gray" id="certorder_status_7">0</span></a></li>
<li class="text-center"><a href="/cert/deploytask"><b>自动部署任务</b></a></li>
<li><a href="/cert/deploytask?status=0">待处理<span class="pull-right badge bg-aqua" id="certdeploy_status_0">0</span></a></li>
<li><a href="/cert/deploytask?status=1">部署成功<span class="pull-right badge bg-green" id="certdeploy_status_1">0</span></a></li>
<li><a href="/cert/deploytask?status=-1">部署失败<span class="pull-right badge bg-red" id="certdeploy_status_2">0</span></a></li>
</ul>
</div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<i class="fa fa-cloud"></i>
<h3 class="box-title">服务器信息</h3>
</div>
<table class="table table-bordered">
<tbody>
<tr>
<td class="query-title">框架版本</td>
<td class="query-result">{$info.framework_version}</td>
</tr>
<tr>
<td class="query-title">PHP版本</td>
<td class="query-result">{$info.php_version}</td>
</tr>
<tr>
<td class="query-title">数据库版本</td>
<td class="query-result">{$info.mysql_version}</td>
</tr>
<tr>
<td class="query-title">Web服务器</td>
<td class="query-result">{$info.software}</td>
</tr>
<tr>
<td class="query-title">服务器时间</td>
<td class="query-result">{$info.date}</td>
</tr>
</tbody>
</table>
</div>
<div class="box box-default">
<div class="box-header with-border">
<i class="fa fa-volume-up"></i>
<h3 class="box-title">版本信息</h3>
</div>
<ul class="list-group text-dark" id="checkupdate"></ul>
</div>
<button class="btn btn-default btn-block" onclick="cleancache()"><i class="fa fa-trash"></i>清理缓存</button><br/>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
$(document).ready(function(){
$.ajax({
type : "POST",
url : "/",
data : {do: 'stat'},
dataType : 'json',
success : function(data) {
$('#count1').html(data.domains);
$('#count2').html(data.tasks);
$('#count3').html(data.certs);
$('#count4').html(data.deploys);
$('#dmonitor_state').html(data.dmonitor_state==1?'<font color="green">正在运行</font>':'<font color="red">已停止</font>');
$('#dmonitor_active').html(data.dmonitor_active);
$('#dmonitor_status_0').html(data.dmonitor_status_0);
$('#dmonitor_status_1').html(data.dmonitor_status_1);
$('#optimizeip_active').html(data.optimizeip_active);
$('#optimizeip_status_1').html(data.optimizeip_status_1);
$('#optimizeip_status_2').html(data.optimizeip_status_2);
$('#certorder_status_3').html(data.certorder_status_3);
$('#certorder_status_5').html(data.certorder_status_5);
$('#certorder_status_6').html(data.certorder_status_6);
$('#certorder_status_7').html(data.certorder_status_7);
$('#certdeploy_status_0').html(data.certdeploy_status_0);
$('#certdeploy_status_1').html(data.certdeploy_status_1);
$('#certdeploy_status_2').html(data.certdeploy_status_2);
$('.badge').each(function() {
if ($(this).text().trim() === '0') {
$(this).css('opacity', '0.4');
}
});
$.ajax({
url: '{$checkupdate}',
type: 'get',
dataType: 'jsonp',
jsonpCallback: 'callback'
}).done(function(data){
$("#checkupdate").html(data.msg);
})
}
})
})
function cleancache(){
var ii = layer.load(2);
$.ajax({
type : 'GET',
url : '/cleancache',
dataType : 'json',
success : function(data) {
layer.close(ii);
layer.msg('清理缓存成功', {icon: 1});
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
<script>
function speedModeNotice(){
var ua = window.navigator.userAgent;
if(ua.indexOf('Windows NT')>-1 && ua.indexOf('Trident/')>-1){
var html = "<div class=\"panel panel-default\"><div class=\"panel-body\">当前浏览器是兼容模式,为确保后台功能正常使用,请切换到<b style='color:#51b72f'>极速模式</b><br>操作方法点击浏览器地址栏右侧的IE符号<b style='color:#51b72f;'><i class='fa fa-internet-explorer fa-fw'></i></b>→选择“<b style='color:#51b72f;'><i class='fa fa-flash fa-fw'></i></b><b style='color:#51b72f;'>极速模式</b></div></div>";
$("#browser-notice").html(html)
}else if(window.location.protocol.indexOf("https")==-1){
var html = "<div class=\"panel panel-default\"><div class=\"panel-body\"><b style='color:#CC3022;'><i class='fa fa-info-circle fa-fw'></i></b>当前正在使用HTTP访问可能存在被窃取敏感信息风险请使用HTTPS访问</div></div>";
$("#browser-notice").html(html)
}
}
speedModeNotice();
</script>
{extend name="common/layout" /}
{block name="title"}聚合DNS管理系统{/block}
{block name="main"}
<style>
.table>tbody>tr>td{white-space: normal;}
.query-title {
background-color:#f5fafe;
word-break: keep-all;
}
.query-result{
word-break: break-all;
}
</style>
<div class="row">
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-aqua">
<div class="inner">
<h3 id="count1">0</h3>
<p>域名数量</p>
</div>
<div class="icon">
<i class="fa fa-list-ul"></i>
</div>
<a href="/domain" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-green">
<div class="inner">
<h3 id="count2">0</h3>
<p>容灾切换策略</p>
</div>
<div class="icon">
<i class="fa fa-heartbeat"></i>
</div>
<a href="/dmonitor/task" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-yellow">
<div class="inner">
<h3 id="count3">0</h3>
<p>SSL证书订单</p>
</div>
<div class="icon">
<i class="fa fa-expeditedssl"></i>
</div>
<a href="/cert/certorder" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-red">
<div class="inner">
<h3 id="count4">0</h3>
<p>SSL部署任务</p>
</div>
<div class="icon">
<i class="fa fa-connectdevelop"></i>
</div>
<a href="/cert/deploytask" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-md-12">
<div id="browser-notice"></div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<i class="fa fa-heartbeat fa-fw"></i>
<h3 class="box-title">容灾切换概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li><a href="/dmonitor/overview">运行状态<b><span class="pull-right" id="dmonitor_state"></span></b></a></li>
<li><a href="/dmonitor/task">切换策略(已开启)<span class="pull-right badge bg-blue" id="dmonitor_active">0</span></a>
</li>
<li><a href="/dmonitor/task?status=0">健康状况正常<span class="pull-right badge bg-green" id="dmonitor_status_0">0</span></a></li>
<li><a href="/dmonitor/task?status=1">健康状况异常<span class="pull-right badge bg-red" id="dmonitor_status_1">0</span></a></li>
</ul>
</div>
<div class="box box-info">
<div class="box-header with-border">
<i class="fa fa-globe fa-fw"></i>
<h3 class="box-title">CF优选IP概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li><a href="/optimizeip/opiplist">任务数量(已开启)<span class="pull-right badge bg-blue" id="optimizeip_active">0</span></a>
</li>
<li><a href="/optimizeip/opiplist?status=1">更新成功<span class="pull-right badge bg-green" id="optimizeip_status_1">0</span></a></li>
<li><a href="/optimizeip/opiplist?status=2">更新失败<span class="pull-right badge bg-red" id="optimizeip_status_2">0</span></a></li>
</ul>
</div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-success">
<div class="box-header with-border">
<i class="fa fa-globe fa-fw"></i>
<h3 class="box-title">SSL证书概览</h3>
</div>
<ul class="nav nav-pills nav-stacked">
<li class="text-center"><a href="/cert/certorder"><b>SSL证书订单</b></a></li>
<li><a href="/cert/certorder?status=3">已签发<span class="pull-right badge bg-green" id="certorder_status_3">0</span></a>
</li>
<li><a href="/cert/certorder?status=5">签发失败<span class="pull-right badge bg-red" id="certorder_status_5">0</span></a></li>
<li><a href="/cert/certorder?status=6">即将过期<span class="pull-right badge bg-orange" id="certorder_status_6">0</span></a></li>
<li><a href="/cert/certorder?status=7">已过期<span class="pull-right badge bg-gray" id="certorder_status_7">0</span></a></li>
<li class="text-center"><a href="/cert/deploytask"><b>自动部署任务</b></a></li>
<li><a href="/cert/deploytask?status=0">待处理<span class="pull-right badge bg-aqua" id="certdeploy_status_0">0</span></a></li>
<li><a href="/cert/deploytask?status=1">部署成功<span class="pull-right badge bg-green" id="certdeploy_status_1">0</span></a></li>
<li><a href="/cert/deploytask?status=-1">部署失败<span class="pull-right badge bg-red" id="certdeploy_status_2">0</span></a></li>
</ul>
</div>
</div>
<div class="col-md-4 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<i class="fa fa-cloud"></i>
<h3 class="box-title">服务器信息</h3>
</div>
<table class="table table-bordered">
<tbody>
<tr>
<td class="query-title">框架版本</td>
<td class="query-result">{$info.framework_version}</td>
</tr>
<tr>
<td class="query-title">PHP版本</td>
<td class="query-result">{$info.php_version}</td>
</tr>
<tr>
<td class="query-title">数据库版本</td>
<td class="query-result">{$info.mysql_version}</td>
</tr>
<tr>
<td class="query-title">Web服务器</td>
<td class="query-result">{$info.software}</td>
</tr>
<tr>
<td class="query-title">服务器时间</td>
<td class="query-result">{$info.date}</td>
</tr>
</tbody>
</table>
</div>
<div class="box box-default">
<div class="box-header with-border">
<i class="fa fa-volume-up"></i>
<h3 class="box-title">版本信息</h3>
</div>
<ul class="list-group text-dark" id="checkupdate"></ul>
</div>
<button class="btn btn-default btn-block" onclick="cleancache()"><i class="fa fa-trash"></i>清理缓存</button><br/>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
$(document).ready(function(){
$.ajax({
type : "POST",
url : "/",
data : {do: 'stat'},
dataType : 'json',
success : function(data) {
$('#count1').html(data.domains);
$('#count2').html(data.tasks);
$('#count3').html(data.certs);
$('#count4').html(data.deploys);
$('#dmonitor_state').html(data.dmonitor_state==1?'<font color="green">正在运行</font>':'<font color="red">已停止</font>');
$('#dmonitor_active').html(data.dmonitor_active);
$('#dmonitor_status_0').html(data.dmonitor_status_0);
$('#dmonitor_status_1').html(data.dmonitor_status_1);
$('#optimizeip_active').html(data.optimizeip_active);
$('#optimizeip_status_1').html(data.optimizeip_status_1);
$('#optimizeip_status_2').html(data.optimizeip_status_2);
$('#certorder_status_3').html(data.certorder_status_3);
$('#certorder_status_5').html(data.certorder_status_5);
$('#certorder_status_6').html(data.certorder_status_6);
$('#certorder_status_7').html(data.certorder_status_7);
$('#certdeploy_status_0').html(data.certdeploy_status_0);
$('#certdeploy_status_1').html(data.certdeploy_status_1);
$('#certdeploy_status_2').html(data.certdeploy_status_2);
$('.badge').each(function() {
if ($(this).text().trim() === '0') {
$(this).css('opacity', '0.4');
}
});
$.ajax({
url: '{$checkupdate}',
type: 'get',
dataType: 'jsonp',
jsonpCallback: 'callback'
}).done(function(data){
$("#checkupdate").html(data.msg);
})
}
})
})
function cleancache(){
var ii = layer.load(2);
$.ajax({
type : 'GET',
url : '/cleancache',
dataType : 'json',
success : function(data) {
layer.close(ii);
layer.msg('清理缓存成功', {icon: 1});
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
<script>
function speedModeNotice(){
var ua = window.navigator.userAgent;
if(ua.indexOf('Windows NT')>-1 && ua.indexOf('Trident/')>-1){
var html = "<div class=\"panel panel-default\"><div class=\"panel-body\">当前浏览器是兼容模式,为确保后台功能正常使用,请切换到<b style='color:#51b72f'>极速模式</b><br>操作方法点击浏览器地址栏右侧的IE符号<b style='color:#51b72f;'><i class='fa fa-internet-explorer fa-fw'></i></b>→选择“<b style='color:#51b72f;'><i class='fa fa-flash fa-fw'></i></b><b style='color:#51b72f;'>极速模式</b></div></div>";
$("#browser-notice").html(html)
}else if(window.location.protocol.indexOf("https")==-1){
var html = "<div class=\"panel panel-default\"><div class=\"panel-body\"><b style='color:#CC3022;'><i class='fa fa-info-circle fa-fw'></i></b>当前正在使用HTTP访问可能存在被窃取敏感信息风险请使用HTTPS访问</div></div>";
$("#browser-notice").html(html)
}
}
speedModeNotice();
</script>
{/block}

View File

@ -1,187 +1,187 @@
{extend name="common/layout" /}
{block name="title"}修改密码{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">修改密码</h3></div>
<div class="panel-body">
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
<div class="form-group">
<label>旧密码:</label>
<input type="password" name="oldpwd" value="" class="form-control" placeholder="请输入当前的密码" required/>
</div>
<div class="form-group">
<label>新密码:</label>
<input type="password" name="newpwd" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group">
<label>重输密码:</label>
<input type="password" name="newpwd2" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="确定" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">TOTP二次验证</h3></div>
<div class="panel-body">
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
<div class="form-group">
<div class="input-group">
{if $user.totp_open == 1}
<input type="text" name="totp_status" value="已开启" style="color:green" class="form-control" readonly/>
<div class="input-group-btn"><button type="button" class="btn btn-info" onclick="open_totp()">重置</button></div>
<div class="input-group-btn"><button type="button" class="btn btn-danger" onclick="close_totp()">关闭</button></div>
{else}
<input type="text" name="totp_status" value="未开启" style="color:blue" class="form-control" readonly/>
<div class="input-group-btn"><button type="button" class="btn btn-info" onclick="open_totp()">开启</button></div>
{/if}
</div>
</div>
</form>
</div>
<div class="panel-footer">
<p><span class="glyphicon glyphicon-info-sign"></span> 开启后登录时需使用支持TOTP的认证软件进行二次验证提高账号安全性。开启前需确保服务器时间正确。</p>
<p>支持TOTP的认证软件<a href="https://sj.qq.com/appdetail/com.tencent.authenticator" target="_blank" rel="noreferrer">腾讯身份验证器</a><a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank" rel="noreferrer">谷歌身份验证器</a><a href="https://www.microsoft.com/zh-cn/security/mobile-authenticator-app" target="_blank" rel="noreferrer">微软身份验证器</a><a href="https://github.com/freeotp" target="_blank" rel="noreferrer">FreeOTP</a></p>
</div>
</div>
<div class="modal" id="modal-totp" data-backdrop="static" data-keyboard="false" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">TOTP绑定</h4>
</div>
<div class="modal-body text-center">
<p>使用支持TOTP的认证软件扫描以下二维码</p>
<div class="qr-image mt-4" id="qrcode"></div>
<p><a href="javascript:;" data-clipboard-text="" id="copy-btn">复制密钥</a></p>
<form id="form-totp" style="text-align: left;" onsubmit="return bind_totp()">
<div class="form-group mt-4">
<div class="input-group"><input type="number" class="form-control input-lg" name="code" id="code" value="" placeholder="填写动态口令" autocomplete="off" required><div class="input-group-btn"><input type="submit" name="submit" value="完成绑定" class="btn btn-success btn-lg btn-block"/></div></div>
</div>
</form>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script src="{$cdnpublic}clipboard.js/1.7.1/clipboard.min.js"></script>
<script>
var commonData = {secret:null,qrcode:null};
function saveAccount(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/setpwd',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('密码修改成功!请重新登录。', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function open_totp(){
if(!commonData.qrcode || !commonData.secret){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/generate', {}, function(res){
layer.close(ii);
if(res.code == 0){
commonData.secret = res.data.secret;
commonData.qrcode = res.data.qrcode;
$('#qrcode').qrcode({
text: commonData.qrcode,
width: 150,
height: 150,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
$("#copy-btn").attr('data-clipboard-text', commonData.secret);
$('#modal-totp').modal('show');
$("#code").focus();
}else{
layer.alert(res.msg, {icon: 2});
}
});
}else{
$('#modal-totp').modal('show');
$("#code").focus();
}
}
function bind_totp(){
var code = $("#code").val();
if(code.length != 6){
layer.msg('动态口令格式错误', {icon: 2});
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/bind', {secret:commonData.secret, code:code}, function(res){
layer.close(ii);
if(res.code == 0){
layer.alert('TOTP绑定成功', {icon: 1}, function(){
window.location.reload();
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
return false;
}
function close_totp(){
layer.confirm('确定要关闭TOTP二次验证吗', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/close', {}, function(res){
layer.close(ii);
if(res.code == 0){
layer.alert('TOTP已关闭', {icon: 1}, function(){
window.location.reload();
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
});
}
$(document).ready(function(){
var clipboard = new Clipboard('#copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功!', {icon: 1, time: 600});
});
clipboard.on('error', function (e) {
layer.msg('复制失败', {icon: 2});
});
$("#code").keyup(function(){
var code = $(this).val();
if(code.length == 6){
$("#form-totp").submit();
}
});
});
</script>
{extend name="common/layout" /}
{block name="title"}修改密码{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">修改密码</h3></div>
<div class="panel-body">
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
<div class="form-group">
<label>旧密码:</label>
<input type="password" name="oldpwd" value="" class="form-control" placeholder="请输入当前的密码" required/>
</div>
<div class="form-group">
<label>新密码:</label>
<input type="password" name="newpwd" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group">
<label>重输密码:</label>
<input type="password" name="newpwd2" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="确定" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">TOTP二次验证</h3></div>
<div class="panel-body">
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
<div class="form-group">
<div class="input-group">
{if $user.totp_open == 1}
<input type="text" name="totp_status" value="已开启" style="color:green" class="form-control" readonly/>
<div class="input-group-btn"><button type="button" class="btn btn-info" onclick="open_totp()">重置</button></div>
<div class="input-group-btn"><button type="button" class="btn btn-danger" onclick="close_totp()">关闭</button></div>
{else}
<input type="text" name="totp_status" value="未开启" style="color:blue" class="form-control" readonly/>
<div class="input-group-btn"><button type="button" class="btn btn-info" onclick="open_totp()">开启</button></div>
{/if}
</div>
</div>
</form>
</div>
<div class="panel-footer">
<p><span class="glyphicon glyphicon-info-sign"></span> 开启后登录时需使用支持TOTP的认证软件进行二次验证提高账号安全性。开启前需确保服务器时间正确。</p>
<p>支持TOTP的认证软件<a href="https://sj.qq.com/appdetail/com.tencent.authenticator" target="_blank" rel="noreferrer">腾讯身份验证器</a><a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank" rel="noreferrer">谷歌身份验证器</a><a href="https://www.microsoft.com/zh-cn/security/mobile-authenticator-app" target="_blank" rel="noreferrer">微软身份验证器</a><a href="https://github.com/freeotp" target="_blank" rel="noreferrer">FreeOTP</a></p>
</div>
</div>
<div class="modal" id="modal-totp" data-backdrop="static" data-keyboard="false" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">TOTP绑定</h4>
</div>
<div class="modal-body text-center">
<p>使用支持TOTP的认证软件扫描以下二维码</p>
<div class="qr-image mt-4" id="qrcode"></div>
<p><a href="javascript:;" data-clipboard-text="" id="copy-btn">复制密钥</a></p>
<form id="form-totp" style="text-align: left;" onsubmit="return bind_totp()">
<div class="form-group mt-4">
<div class="input-group"><input type="number" class="form-control input-lg" name="code" id="code" value="" placeholder="填写动态口令" autocomplete="off" required><div class="input-group-btn"><input type="submit" name="submit" value="完成绑定" class="btn btn-success btn-lg btn-block"/></div></div>
</div>
</form>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/jquery-qrcode-1.0.min.js"></script>
<script src="/static/js/clipboard-1.7.1.min.js"></script>
<script>
var commonData = {secret:null,qrcode:null};
function saveAccount(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/setpwd',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('密码修改成功!请重新登录。', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function open_totp(){
if(!commonData.qrcode || !commonData.secret){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/generate', {}, function(res){
layer.close(ii);
if(res.code == 0){
commonData.secret = res.data.secret;
commonData.qrcode = res.data.qrcode;
$('#qrcode').qrcode({
text: commonData.qrcode,
width: 150,
height: 150,
foreground: "#000000",
background: "#ffffff",
typeNumber: -1
});
$("#copy-btn").attr('data-clipboard-text', commonData.secret);
$('#modal-totp').modal('show');
$("#code").focus();
}else{
layer.alert(res.msg, {icon: 2});
}
});
}else{
$('#modal-totp').modal('show');
$("#code").focus();
}
}
function bind_totp(){
var code = $("#code").val();
if(code.length != 6){
layer.msg('动态口令格式错误', {icon: 2});
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/bind', {secret:commonData.secret, code:code}, function(res){
layer.close(ii);
if(res.code == 0){
layer.alert('TOTP绑定成功', {icon: 1}, function(){
window.location.reload();
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
return false;
}
function close_totp(){
layer.confirm('确定要关闭TOTP二次验证吗', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.post('/totp/close', {}, function(res){
layer.close(ii);
if(res.code == 0){
layer.alert('TOTP已关闭', {icon: 1}, function(){
window.location.reload();
});
}else{
layer.alert(res.msg, {icon: 2});
}
});
});
}
$(document).ready(function(){
var clipboard = new Clipboard('#copy-btn');
clipboard.on('success', function (e) {
layer.msg('复制成功!', {icon: 1, time: 600});
});
clipboard.on('error', function (e) {
layer.msg('复制失败', {icon: 2});
});
$("#code").keyup(function(){
var code = $(this).val();
if(code.length == 6){
$("#form-totp").submit();
}
});
});
</script>
{/block}

View File

@ -1,188 +1,188 @@
{extend name="common/layout" /}
{block name="title"}优选IP任务{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/optimizeip/opiplist" 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}优选IP任务</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="taskform">
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<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}
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="不支持对CloudFlare里的域名添加优选必须使用其他DNS服务商。" data-original-title="说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">CDN服务商</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="(value,key) in cdntypeList">
<input type="radio" name="cdntype" :value="key" v-model="set.cdn_type"> {{value}}
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">解析线路类型</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="type" value="0" v-model="set.type"> 电信/联通/移动线路<span class="tips" title="用于已在CF添加需优化三网访问速度的域名" data-toggle="tooltip" data-placement="bottom" data-original-title=""><i class="fa fa-question-circle"></i></span>
</label>
<label class="radio-inline">
<input type="radio" name="type" value="1" v-model="set.type"> 默认/联通/移动线路<span class="tips" title="将电信优选IP解析到默认线路用于给其他域名提供CNAME服务" data-toggle="tooltip" data-placement="bottom" data-original-title=""><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">解析IP类型<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="同时开启IPv6&IPv4将会请求2次接口消耗双倍积分"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<label class="checkbox-inline" v-for="option in iptypeList">
<input type="checkbox" name="ip_type" :value="option.value" v-model="set.ip_type_select" required> {{option.label}}
</label><br/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">每线路解析数量<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="数量不要超过当前域名套餐允许的最大数量,否则会添加解析失败"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<input type="text" name="recordnum" v-model="set.recordnum" placeholder="填写每线路解析数量" class="form-control" data-bv-integer="true" min="1" max="5" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="TTL不要低于当前域名套餐允许的最小值否则会添加解析失败"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<input type="text" name="ttl" v-model="set.ttl" placeholder="填写TTL" class="form-control" data-bv-integer="true" min="1" max="3600" required>
</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="set.type==0">
<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">&times;</span></button>
<strong>提示:</strong>所选域名需保留一个默认线路的解析记录,指向{{cdntypeList[set.cdn_type]}}提供的CNAME地址其他线路的解析记录需要删除添加任务后将自动为当前域名添加电信/联通/移动线路的解析记录。
</div>
</div>
</div>
<div class="form-group" v-show="set.type==1">
<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">&times;</span></button>
<strong>提示:</strong>所选域名需删除全部解析记录,添加任务后将自动为当前域名添加解析记录。
</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="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
type: 0,
cdn_type: 1,
ip_type_select: ['v4'],
ip_type: 'v4',
recordnum: 2,
ttl: 600,
},
iptypeList: [
{value:'v4', label:'IPv4(A记录)'},
{value:'v6', label:'IPv6(AAAA记录)'},
],
cdntypeList: {
1:'CloudFlare',
2:"CloudFront",
4:'EdgeOne'
},
},
watch: {
'set.ip_type_select': function(val){
this.set.ip_type = val.join(',');
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
this.set.ip_type_select = this.set.ip_type.split(',')
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover()
},
methods: {
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/opiplist?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/optimizeip/opiplist';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}优选IP任务{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/optimizeip/opiplist" 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}优选IP任务</h3></div>
<div class="panel-body">
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="taskform">
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<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}
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="不支持对CloudFlare里的域名添加优选必须使用其他DNS服务商。" data-original-title="说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">CDN服务商</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="(value,key) in cdntypeList">
<input type="radio" name="cdntype" :value="key" v-model="set.cdn_type"> {{value}}
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">解析线路类型</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="type" value="0" v-model="set.type"> 电信/联通/移动线路<span class="tips" title="用于已在CF添加需优化三网访问速度的域名" data-toggle="tooltip" data-placement="bottom" data-original-title=""><i class="fa fa-question-circle"></i></span>
</label>
<label class="radio-inline">
<input type="radio" name="type" value="1" v-model="set.type"> 默认/联通/移动线路<span class="tips" title="将电信优选IP解析到默认线路用于给其他域名提供CNAME服务" data-toggle="tooltip" data-placement="bottom" data-original-title=""><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">解析IP类型<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="同时开启IPv6&IPv4将会请求2次接口消耗双倍积分"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<label class="checkbox-inline" v-for="option in iptypeList">
<input type="checkbox" name="ip_type" :value="option.value" v-model="set.ip_type_select" required> {{option.label}}
</label><br/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">每线路解析数量<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="数量不要超过当前域名套餐允许的最大数量,否则会添加解析失败"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<input type="text" name="recordnum" v-model="set.recordnum" placeholder="填写每线路解析数量" class="form-control" data-bv-integer="true" min="1" max="5" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="TTL不要低于当前域名套餐允许的最小值否则会添加解析失败"><i class="fa fa-question-circle"></i></span></label>
<div class="col-sm-6">
<input type="text" name="ttl" v-model="set.ttl" placeholder="填写TTL" class="form-control" data-bv-integer="true" min="1" max="3600" required>
</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="set.type==0">
<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">&times;</span></button>
<strong>提示:</strong>所选域名需保留一个默认线路的解析记录,指向{{cdntypeList[set.cdn_type]}}提供的CNAME地址其他线路的解析记录需要删除添加任务后将自动为当前域名添加电信/联通/移动线路的解析记录。
</div>
</div>
</div>
<div class="form-group" v-show="set.type==1">
<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">&times;</span></button>
<strong>提示:</strong>所选域名需删除全部解析记录,添加任务后将自动为当前域名添加解析记录。
</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-3.1.1.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
type: 0,
cdn_type: 1,
ip_type_select: ['v4'],
ip_type: 'v4',
recordnum: 2,
ttl: 600,
},
iptypeList: [
{value:'v4', label:'IPv4(A记录)'},
{value:'v6', label:'IPv6(AAAA记录)'},
],
cdntypeList: {
1:'CloudFlare',
2:"CloudFront",
4:'EdgeOne'
},
},
watch: {
'set.ip_type_select': function(val){
this.set.ip_type = val.join(',');
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
this.set.ip_type_select = this.set.ip_type.split(',')
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover()
},
methods: {
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('/opiplist?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/optimizeip/opiplist';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{/block}

View File

@ -1,189 +1,189 @@
{extend name="common/layout" /}
{block name="title"}CF优选IP任务管理{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(2){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="2">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="status" class="form-control"><option value="">更新结果</option><option value="1">成功</option><option value="2">失败</option></select>
</div>
</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="/optimizeip/opipform/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/optimizeip/opiplist/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'cdn_type',
title: 'CDN运营商',
formatter: function(value, row, index) {
if(value == 1){
return 'CloudFlare';
}else if(value == 2){
return 'CloudFront';
}else if(value == 3){
return 'Gcore';
}else if(value == 4){
return 'EdgeOne';
}else{
return '未知';
}
}
},
{
field: 'recordnum',
title: '解析数量',
formatter: function(value, row, index) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="TTL'+row.ttl+'" class="tips">'+value+'</span>';
}
},
{
field: 'ip_type',
title: '解析IP类型',
formatter: function(value, row, index) {
var value = value.split(',')
value.forEach((element, index) => {
if(element == 'v4') value[index] = 'IPv4'
else if(element == 'v6') value[index] = 'IPv6'
});
return value.join(',');
}
},
{
field: 'active',
title: '任务开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'updatetime',
title: '上次更新时间',
formatter: function(value, row, index) {
return value ? value : '无';
}
},
{
field: 'status',
title: '上次更新结果',
formatter: function(value, row, index) {
if(value == 1) {
return '<span class="label label-success">成功</span>';
} else if(value == 2) {
return '<span class="label label-danger">失败</span> <span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.errmsg+'" class="tips"><i class="fa fa-info-circle"></i></span>';
} else {
return '<span class="label label-warning">未运行</span>';
}
}
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:runTask(\''+row.id+'\')" class="btn btn-success btn-xs">手动更新</a>&nbsp;&nbsp;';
html += '<a href="/optimizeip/opipform/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?keyword='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/optimizeip/opipform/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此任务吗?', {
btn: ['确定','取消']
}, function(){
$.post('/optimizeip/opipform/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 runTask(id){
var ii = layer.msg('正在更新中...', {icon: 16,shade: 0.1,time: 0});
$.post('/optimizeip/opipform/run', {id: id}, function(data){
layer.close(ii);
if(data.code == 0) {
layer.alert(data.msg, {icon: 1});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
}
</script>
{extend name="common/layout" /}
{block name="title"}CF优选IP任务管理{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(2){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="2">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="status" class="form-control"><option value="">更新结果</option><option value="1">成功</option><option value="2">失败</option></select>
</div>
</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="/optimizeip/opipform/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/optimizeip/opiplist/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'cdn_type',
title: 'CDN运营商',
formatter: function(value, row, index) {
if(value == 1){
return 'CloudFlare';
}else if(value == 2){
return 'CloudFront';
}else if(value == 3){
return 'Gcore';
}else if(value == 4){
return 'EdgeOne';
}else{
return '未知';
}
}
},
{
field: 'recordnum',
title: '解析数量',
formatter: function(value, row, index) {
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="TTL'+row.ttl+'" class="tips">'+value+'</span>';
}
},
{
field: 'ip_type',
title: '解析IP类型',
formatter: function(value, row, index) {
var value = value.split(',')
value.forEach((element, index) => {
if(element == 'v4') value[index] = 'IPv4'
else if(element == 'v6') value[index] = 'IPv6'
});
return value.join(',');
}
},
{
field: 'active',
title: '任务开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'updatetime',
title: '上次更新时间',
formatter: function(value, row, index) {
return value ? value : '无';
}
},
{
field: 'status',
title: '上次更新结果',
formatter: function(value, row, index) {
if(value == 1) {
return '<span class="label label-success">成功</span>';
} else if(value == 2) {
return '<span class="label label-danger">失败</span> <span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+row.errmsg+'" class="tips"><i class="fa fa-info-circle"></i></span>';
} else {
return '<span class="label label-warning">未运行</span>';
}
}
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:runTask(\''+row.id+'\')" class="btn btn-success btn-xs">手动更新</a>&nbsp;&nbsp;';
html += '<a href="/optimizeip/opipform/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?keyword='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/optimizeip/opipform/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
$('#listTable').bootstrapTable('refresh');
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此任务吗?', {
btn: ['确定','取消']
}, function(){
$.post('/optimizeip/opipform/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 runTask(id){
var ii = layer.msg('正在更新中...', {icon: 16,shade: 0.1,time: 0});
$.post('/optimizeip/opipform/run', {id: id}, function(data){
layer.close(ii);
if(data.code == 0) {
layer.alert(data.msg, {icon: 1});
$('#listTable').bootstrapTable('refresh');
} else {
layer.alert(data.msg, {icon: 2});
}
}, 'json');
}
</script>
{/block}

View File

@ -1,121 +1,121 @@
{extend name="common/layout" /}
{block name="title"}CF优选IP设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-md-6">
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">功能简介</h3></div>
<div class="panel-body">
<p>由于CloudFlare官方IP是泛播路由同一个IP在不同地区不同运营商所链接的机房是不同的速度或延迟也会有区别。目前网上也有很多CF优选CNAME服务然而公共的CNAME可能无法满足稳定性和安全性的需要。</p>
<p>本功能可以获取CloudFlare最新的优选IP地址分为电信/联通/移动线路),并自动更新到域名解析记录。</p>
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">使用说明</h3></div>
<div class="panel-body">
<p><li>不支持对CloudFlare里的域名添加优选必须使用其他DNS服务商。需开通Cloudflare for SaaS且域名使用CNAME的方式解析到CloudFlare。</li></p>
<p><li>数据接口:<a href="https://www.wetest.vip/" target="_blank" rel="noreferrer">wetest.vip</a> 数据接口支持CloudFlare、CloudFront、EdgeOne<a href="https://stock.hostmonit.com/" target="_blank" rel="noreferrer">HostMonit</a> 只支持CloudFlare。</li></p>
<p><li>接口密钥默认o1zrmHAF为免费KEY可永久免费使用。</li></p>
<p><li>自动更新:可查看<a href="/system/cronset">计划任务设置</a></p>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">数据接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">数据接口</label>
<div class="col-sm-9"><select class="form-control" name="optimize_ip_api" default="{:config_get('optimize_ip_api')}"><option value="0">wetest.vip</option><option value="1">HostMonit</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">接口密钥</label>
<div class="col-sm-9"><input type="text" name="optimize_ip_key" value="{:config_get('optimize_ip_key', 'o1zrmHAF')}" class="form-control"/></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"/>
<a href="javascript:queryapi()" class="btn btn-default btn-block">查询积分</a>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动更新设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">自动更新时间间隔(分钟)</label>
<div class="col-sm-9"><input type="text" name="optimize_ip_min" value="{:config_get('optimize_ip_min', '30')}" class="form-control" placeholder="单位:分钟"/></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"/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function queryapi(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/optimizeip/queryapi',
data : $("form").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}CF优选IP设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-md-6">
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">功能简介</h3></div>
<div class="panel-body">
<p>由于CloudFlare官方IP是泛播路由同一个IP在不同地区不同运营商所链接的机房是不同的速度或延迟也会有区别。目前网上也有很多CF优选CNAME服务然而公共的CNAME可能无法满足稳定性和安全性的需要。</p>
<p>本功能可以获取CloudFlare最新的优选IP地址分为电信/联通/移动线路),并自动更新到域名解析记录。</p>
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">使用说明</h3></div>
<div class="panel-body">
<p><li>不支持对CloudFlare里的域名添加优选必须使用其他DNS服务商。需开通Cloudflare for SaaS且域名使用CNAME的方式解析到CloudFlare。</li></p>
<p><li>数据接口:<a href="https://www.wetest.vip/" target="_blank" rel="noreferrer">wetest.vip</a> 数据接口支持CloudFlare、CloudFront、EdgeOne<a href="https://stock.hostmonit.com/" target="_blank" rel="noreferrer">HostMonit</a> 只支持CloudFlare。</li></p>
<p><li>接口密钥默认o1zrmHAF为免费KEY可永久免费使用。</li></p>
<p><li>自动更新:可查看<a href="/system/cronset">计划任务设置</a></p>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">数据接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">数据接口</label>
<div class="col-sm-9"><select class="form-control" name="optimize_ip_api" default="{:config_get('optimize_ip_api')}"><option value="0">wetest.vip</option><option value="1">HostMonit</option></select></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">接口密钥</label>
<div class="col-sm-9"><input type="text" name="optimize_ip_key" value="{:config_get('optimize_ip_key', 'o1zrmHAF')}" class="form-control"/></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"/>
<a href="javascript:queryapi()" class="btn btn-default btn-block">查询积分</a>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">自动更新设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">自动更新时间间隔(分钟)</label>
<div class="col-sm-9"><input type="text" name="optimize_ip_min" value="{:config_get('optimize_ip_min', '30')}" class="form-control" placeholder="单位:分钟"/></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"/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function queryapi(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/optimizeip/queryapi',
data : $("form").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{/block}

View File

@ -1,216 +1,216 @@
{extend name="common/layout" /}
{block name="title"}定时切换策略{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
tbody tr>td:nth-child(5){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="3">备用解析记录</option><option value="2">解析记录ID</option><option value="4">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="stype" class="form-control"><option value="">执行方式</option><option value="0">单次执行</option><option value="1">周期执行</option></select>
</div>
</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="/schedule/stask/add" 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="javascript:operation('open')">开启运行</a></li><li><a href="javascript:operation('close')">停止运行</a></li><li><a href="javascript:operation('delete')">删除</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/schedule/stask/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: '',
checkbox: true
},
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'type',
title: '时间设置',
formatter: function(value, row, index) {
if(value == 1){
var text = '<span class="label bg-purple">周期执行</span> ';
if(row.cycle == 1) {
weekday = ['日', '一', '二', '三', '四', '五', '六'];
text += '每周'+weekday[row.switchdate]+' ';
} else if(row.cycle == 2) {
text += '每月'+row.switchdate+'日 ';
} else {
text += '每天 ';
}
return text + row.switchtime;
}else{
return '<span class="label bg-aqua">单次执行</span> '+row.switchtime.replace('T', ' ');
}
}
},
{
field: 'switchtype',
title: '切换设置',
formatter: function(value, row, index) {
if(value == 1) {
return '启用解析';
} else if(value == 2) {
return '暂停解析';
} else if(value == 3) {
return '删除解析';
} else {
return '修改解析['+row.value+']';
}
}
},
{
field: 'active',
title: '运行开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'updatetimestr',
title: '上次切换时间'
},
{
field: 'nexttimestr',
title: '下次切换时间',
visible: false
},
{
field: 'addtimestr',
title: '添加时间',
visible: false
},
{
field: 'remark',
title: '备注'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var domain = row.rr + '.' + row.domain;
var html = '<a href="/log?uid=0&domain='+domain+'" class="btn btn-info btn-xs">切换日志</a>&nbsp;&nbsp;';
html += '<a href="/schedule/stask/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?subdomain='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/schedule/stask/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此切换策略吗?', {
btn: ['确定','取消']
}, function(){
$.post('/schedule/stask/del', {id: id}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, '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;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/schedule/stask/operation',
data : {act: 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>
{extend name="common/layout" /}
{block name="title"}定时切换策略{/block}
{block name="main"}
<style>
tbody tr>td:nth-child(3){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:180px;}
tbody tr>td:nth-child(5){overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:200px;}
</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">
<div class="form-group">
<label>搜索</label>
<div class="form-group">
<select name="type" class="form-control"><option value="1">域名</option><option value="3">备用解析记录</option><option value="2">解析记录ID</option><option value="4">备注</option></select>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="kw" placeholder="">
</div>
<div class="form-group">
<div class="form-group">
<select name="stype" class="form-control"><option value="">执行方式</option><option value="0">单次执行</option><option value="1">周期执行</option></select>
</div>
</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="/schedule/stask/add" 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="javascript:operation('open')">开启运行</a></li><li><a href="javascript:operation('close')">停止运行</a></li><li><a href="javascript:operation('delete')">删除</a></li></ul>
</div>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/schedule/stask/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: '',
checkbox: true
},
{
field: 'id',
title: 'ID'
},
{
field: 'rr',
title: '域名',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right">' + value + '.' + row.domain + '</span>';
}
},
{
field: 'type',
title: '时间设置',
formatter: function(value, row, index) {
if(value == 1){
var text = '<span class="label bg-purple">周期执行</span> ';
if(row.cycle == 1) {
weekday = ['日', '一', '二', '三', '四', '五', '六'];
text += '每周'+weekday[row.switchdate]+' ';
} else if(row.cycle == 2) {
text += '每月'+row.switchdate+'日 ';
} else {
text += '每天 ';
}
return text + row.switchtime;
}else{
return '<span class="label bg-aqua">单次执行</span> '+row.switchtime.replace('T', ' ');
}
}
},
{
field: 'switchtype',
title: '切换设置',
formatter: function(value, row, index) {
if(value == 1) {
return '启用解析';
} else if(value == 2) {
return '暂停解析';
} else if(value == 3) {
return '删除解析';
} else {
return '修改解析['+row.value+']';
}
}
},
{
field: 'active',
title: '运行开关',
formatter: function(value, row, index) {
if(value == 1){
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" checked onchange="setActive('+row.id+',0)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}else{
return '<div class="material-switch"><input id="active'+row.id+'" type="checkbox" onchange="setActive('+row.id+',1)"/><label for="active'+row.id+'" class="label-primary"></label></div>';
}
}
},
{
field: 'updatetimestr',
title: '上次切换时间'
},
{
field: 'nexttimestr',
title: '下次切换时间',
visible: false
},
{
field: 'addtimestr',
title: '添加时间',
visible: false
},
{
field: 'remark',
title: '备注'
},
{
field: 'action',
title: '操作',
formatter: function(value, row, index) {
var domain = row.rr + '.' + row.domain;
var html = '<a href="/log?uid=0&domain='+domain+'" class="btn btn-info btn-xs">切换日志</a>&nbsp;&nbsp;';
html += '<a href="/schedule/stask/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
html += '<a href="/record/'+row.did+'?subdomain='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a>&nbsp;&nbsp;';
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
})
function setActive(id, active){
$.post('/schedule/stask/setactive', {id: id, active: active}, function(data){
if(data.code == 0) {
layer.msg('修改成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'json');
}
function delItem(id){
layer.confirm('确定要删除此切换策略吗?', {
btn: ['确定','取消']
}, function(){
$.post('/schedule/stask/del', {id: id}, function(data){
if(data.code == 0) {
layer.msg('删除成功', {icon: 1, time:800});
searchRefresh();
} else {
layer.msg(data.msg, {icon: 2});
}
}, '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;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/schedule/stask/operation',
data : {act: 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}

View File

@ -1,269 +1,269 @@
{extend name="common/layout" /}
{block name="title"}定时切换策略{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.control-label[is-required]:before {
content: "*";
color: #f56c6c;
margin-right: 4px;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/schedule/stask" 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="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<select name="did" v-model="set.did" class="form-control" required>
<option value="">--主域名--</option>
<option v-for="option in domainList" :value="option.id">{{option.name}}</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>解析记录</label>
<div class="col-sm-6"><div class="input-group">
<select name="recordid" v-model="set.recordid" id="recordid" class="form-control" required>
<option v-for="option in recordList" :value="option.RecordId">{{option.Value}} (线路:{{option.LineName}})</option>
</select>
<div class="input-group-btn">
<button type="button" @click="getRecordList" class="btn btn-info">点击获取</button>
</div>
</div></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>执行方式</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="type" value="0" v-model="set.type"> 单次执行
</label>
<label class="radio-inline">
<input type="radio" name="type" value="1" v-model="set.type"> 周期执行
</label>
</div>
</div>
<div class="form-group" v-show="set.type==0">
<label class="col-sm-3 control-label no-padding-right" is-required>时间设置</label>
<div class="col-sm-6">
<input type="datetime-local" name="switchtime" v-model="set.switchtime" class="form-control" required>
</div>
</div>
<div class="form-group" v-show="set.type==1">
<label class="col-sm-3 control-label no-padding-right" is-required>时间设置</label>
<div class="col-sm-6">
<div class="input-group">
<select name="cycle" v-model="set.cycle" class="form-control" required>
<option value="0">每天</option>
<option value="1">每周</option>
<option value="2">每月</option>
</select>
<span class="input-group-addon" v-show="set.cycle!=0"></span>
<select name="switchdate" v-model="set.switchdate" class="form-control" required v-show="set.cycle==1">
<option value="0"></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
<option value="6"></option>
</select>
<input type="number" name="switchdate" v-model="set.switchdate" class="form-control" required min="1" max="31" v-show="set.cycle==2" placeholder="日期1~31">
<span class="input-group-addon"></span>
<input type="time" name="switchtime" v-model="set.switchtime" class="form-control" required>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>切换设置</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="switchtype" value="0" v-model="set.switchtype"> 修改解析
</label>
<label class="radio-inline">
<input type="radio" name="switchtype" value="1" v-model="set.switchtype"> 启用解析
</label>
<label class="radio-inline">
<input type="radio" name="switchtype" value="2" v-model="set.switchtype"> 暂停解析
</label>
<label class="radio-inline" v-show="set.type==0">
<input type="radio" name="switchtype" value="3" v-model="set.switchtype"> 删除解析
</label>
</div>
</div>
<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>
</div>
</div>
<div class="form-group" v-show="set.switchtype==0&&dnstype=='cloudflare'">
<label class="col-sm-3 control-label no-padding-right" is-required>线路</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="line" value="" v-model="set.line"> 不修改
</label>
<label class="radio-inline">
<input type="radio" name="line" value="0" v-model="set.line"> 改为仅DNS模式
</label>
<label class="radio-inline">
<input type="radio" name="line" value="1" v-model="set.line"> 改为代理模式
</label>
</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">
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" @click="submit">提交</button></div>
</div>
</form>
</div>
<div class="panel-footer">
<p>添加定时切换策略后,还需要配置好<a href="/system/cronset">计划任务</a>,才能自动切换。</p>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}vue/2.7.16/vue.min.js"></script>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
var domainList = {$domains|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
recordid: '',
recordinfo: '',
type: 0,
cycle: 0,
switchtype: 0,
switchdate: '',
switchtime: '',
value: '',
line: '',
},
dnstype: null,
domainList: domainList,
recordList: [],
},
watch: {
'set.recordid': function(val){
if(val == '') return;
var record = this.recordList.find(item => item.RecordId == val);
if(record){
this.set.recordinfo = JSON.stringify({Value:record.Value, Line:record.Line, LineName:record.LineName, TTL:record.TTL});
}
},
'set.did': function(val){
if(val == '') return;
this.dnstype = this.domainList.find(item => item.id == val).type;
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
var recordinfo = JSON.parse(this.set.recordinfo);
this.recordList = [{RecordId:this.set.recordid, Value:recordinfo.Value, Line:recordinfo.Line, LineName:recordinfo.LineName, TTL:recordinfo.TTL}];
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
getRecordList(){
var that = this;
if(this.set.did == ''){
layer.msg('请先选择域名', {time: 800});return;
}
if(this.set.rr == ''){
layer.msg('主机记录不能为空', {time: 800});return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/record/list',
data : {id:this.set.did, rr:this.set.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.msg('成功获取到'+data.data.length+'条解析记录', {icon:1, time:800});
that.recordList = data.data;
if(that.set.recordid){
var record = that.recordList.find(item => item.RecordId == that.set.recordid);
if(record){
that.set.recordinfo = JSON.stringify({Value:record.Value, Line:record.Line, LineName:record.LineName, TTL:record.TTL});
}
}
}else{
layer.alert(data.msg, {icon:2});
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('task?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/dmonitor/task';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{extend name="common/layout" /}
{block name="title"}定时切换策略{/block}
{block name="main"}
<style>
.dselect::before{
content: '.';
position: absolute;
left: 0;
}
.control-label[is-required]:before {
content: "*";
color: #f56c6c;
margin-right: 4px;
}
.tips{color: #f6a838; padding-left: 5px;}
</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="/schedule/stask" 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="taskform">
<div class="form-group">
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>域名选择</label>
<div class="col-sm-6">
<div class="input-group">
<input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required>
<span class="input-group-addon">.</span>
<select name="did" v-model="set.did" class="form-control" required>
<option value="">--主域名--</option>
<option v-for="option in domainList" :value="option.id">{{option.name}}</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>解析记录</label>
<div class="col-sm-6"><div class="input-group">
<select name="recordid" v-model="set.recordid" id="recordid" class="form-control" required>
<option v-for="option in recordList" :value="option.RecordId">{{option.Value}} (线路:{{option.LineName}})</option>
</select>
<div class="input-group-btn">
<button type="button" @click="getRecordList" class="btn btn-info">点击获取</button>
</div>
</div></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>执行方式</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="type" value="0" v-model="set.type"> 单次执行
</label>
<label class="radio-inline">
<input type="radio" name="type" value="1" v-model="set.type"> 周期执行
</label>
</div>
</div>
<div class="form-group" v-show="set.type==0">
<label class="col-sm-3 control-label no-padding-right" is-required>时间设置</label>
<div class="col-sm-6">
<input type="datetime-local" name="switchtime" v-model="set.switchtime" class="form-control" required>
</div>
</div>
<div class="form-group" v-show="set.type==1">
<label class="col-sm-3 control-label no-padding-right" is-required>时间设置</label>
<div class="col-sm-6">
<div class="input-group">
<select name="cycle" v-model="set.cycle" class="form-control" required>
<option value="0">每天</option>
<option value="1">每周</option>
<option value="2">每月</option>
</select>
<span class="input-group-addon" v-show="set.cycle!=0"></span>
<select name="switchdate" v-model="set.switchdate" class="form-control" required v-show="set.cycle==1">
<option value="0"></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
<option value="6"></option>
</select>
<input type="number" name="switchdate" v-model="set.switchdate" class="form-control" required min="1" max="31" v-show="set.cycle==2" placeholder="日期1~31">
<span class="input-group-addon"></span>
<input type="time" name="switchtime" v-model="set.switchtime" class="form-control" required>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" is-required>切换设置</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="switchtype" value="0" v-model="set.switchtype"> 修改解析
</label>
<label class="radio-inline">
<input type="radio" name="switchtype" value="1" v-model="set.switchtype"> 启用解析
</label>
<label class="radio-inline">
<input type="radio" name="switchtype" value="2" v-model="set.switchtype"> 暂停解析
</label>
<label class="radio-inline" v-show="set.type==0">
<input type="radio" name="switchtype" value="3" v-model="set.switchtype"> 删除解析
</label>
</div>
</div>
<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>
</div>
</div>
<div class="form-group" v-show="set.switchtype==0&&dnstype=='cloudflare'">
<label class="col-sm-3 control-label no-padding-right" is-required>线路</label>
<div class="col-sm-6">
<label class="radio-inline">
<input type="radio" name="line" value="" v-model="set.line"> 不修改
</label>
<label class="radio-inline">
<input type="radio" name="line" value="0" v-model="set.line"> 改为仅DNS模式
</label>
<label class="radio-inline">
<input type="radio" name="line" value="1" v-model="set.line"> 改为代理模式
</label>
</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">
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" @click="submit">提交</button></div>
</div>
</form>
</div>
<div class="panel-footer">
<p>添加定时切换策略后,还需要配置好<a href="/system/cronset">计划任务</a>,才能自动切换。</p>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/vue-2.7.16.min.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script>
var action = '{$action}';
var info = {$info|json_encode|raw};
var domainList = {$domains|json_encode|raw};
new Vue({
el: '#app',
data: {
action: '{$action}',
set: {
id: '',
remark: '',
rr: '',
did: '',
recordid: '',
recordinfo: '',
type: 0,
cycle: 0,
switchtype: 0,
switchdate: '',
switchtime: '',
value: '',
line: '',
},
dnstype: null,
domainList: domainList,
recordList: [],
},
watch: {
'set.recordid': function(val){
if(val == '') return;
var record = this.recordList.find(item => item.RecordId == val);
if(record){
this.set.recordinfo = JSON.stringify({Value:record.Value, Line:record.Line, LineName:record.LineName, TTL:record.TTL});
}
},
'set.did': function(val){
if(val == '') return;
this.dnstype = this.domainList.find(item => item.id == val).type;
}
},
mounted() {
if(this.action == 'edit'){
Object.keys(info).forEach((key) => {
this.$set(this.set, key, info[key])
})
var recordinfo = JSON.parse(this.set.recordinfo);
this.recordList = [{RecordId:this.set.recordid, Value:recordinfo.Value, Line:recordinfo.Line, LineName:recordinfo.LineName, TTL:recordinfo.TTL}];
}
$("#taskform").bootstrapValidator({
live: 'submitted',
});
$('[data-toggle="tooltip"]').tooltip();
},
methods: {
getRecordList(){
var that = this;
if(this.set.did == ''){
layer.msg('请先选择域名', {time: 800});return;
}
if(this.set.rr == ''){
layer.msg('主机记录不能为空', {time: 800});return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/record/list',
data : {id:this.set.did, rr:this.set.rr},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.msg('成功获取到'+data.data.length+'条解析记录', {icon:1, time:800});
that.recordList = data.data;
if(that.set.recordid){
var record = that.recordList.find(item => item.RecordId == that.set.recordid);
if(record){
that.set.recordinfo = JSON.stringify({Value:record.Value, Line:record.Line, LineName:record.LineName, TTL:record.TTL});
}
}
}else{
layer.alert(data.msg, {icon:2});
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
},
submit(){
var that=this;
$("#taskform").data("bootstrapValidator").validate();
if(!$("#taskform").data("bootstrapValidator").isValid()){
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type: "POST",
url: "",
data: this.set,
dataType: 'json',
success: function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1}, function(){
if(document.referrer.indexOf('task?') > 0)
window.location.href = document.referrer;
else
window.location.href = '/dmonitor/task';
});
}else{
layer.alert(data.msg, {icon: 2});
}
},
error: function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
},
});
</script>
{/block}

View File

@ -80,7 +80,7 @@
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {

View File

@ -18,7 +18,7 @@
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {

View File

@ -1,252 +1,252 @@
{extend name="common/layout" /}
{block name="title"}通知设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">发信邮箱设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">发信模式</label>
<div class="col-sm-9"><select class="form-control" name="mail_type" default="{:config_get('mail_type')}"><option value="0">SMTP发信</option><option value="1">搜狐Sendcloud</option><option value="2">阿里云邮件推送</option></select></div>
</div>
<div id="frame_set1">
<div class="form-group">
<label class="col-sm-3 control-label">SMTP服务器</label>
<div class="col-sm-9"><input type="text" name="mail_smtp" value="{:config_get('mail_smtp')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">SMTP端口</label>
<div class="col-sm-9"><input type="text" name="mail_port" value="{:config_get('mail_port')}" 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="mail_name" value="{:config_get('mail_name')}" 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="mail_pwd" value="{:config_get('mail_pwd')}" class="form-control"/></div>
</div>
</div>
<div id="frame_set2">
<div class="form-group">
<label class="col-sm-3 control-label">API_USER</label>
<div class="col-sm-9"><input type="text" name="mail_apiuser" value="{:config_get('mail_apiuser')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">API_KEY</label>
<div class="col-sm-9"><input type="text" name="mail_apikey" value="{:config_get('mail_apikey')}" 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="mail_name2" value="{:config_get('mail_name')}" class="form-control"/></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">收信邮箱</label>
<div class="col-sm-9"><input type="text" name="mail_recv" value="{:config_get('mail_recv')}" class="form-control" placeholder="不填默认为发信邮箱"/></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"/>
<a href="javascript:mailtest()" class="btn btn-default btn-block">发送测试邮件</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<span class="glyphicon glyphicon-info-sign"></span>
使用普通模式发信时建议使用QQ邮箱SMTP服务器smtp.qq.com端口465或587密码是QQ邮箱设置界面生成的<a href="https://service.mail.qq.com/detail/0/75" target="_blank" rel="noreferrer">授权码</a><br/>阿里云邮件推送:<a href="https://www.aliyun.com/product/directmail" target="_blank" rel="noreferrer">点此进入</a><a href="https://usercenter.console.aliyun.com/#/manage/ak" target="_blank" rel="noreferrer">获取AK/SK</a>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">微信公众号消息接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">appToken</label>
<div class="col-sm-9"><input type="text" name="wechat_apptoken" value="{:config_get('wechat_apptoken')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">用户UID</label>
<div class="col-sm-9"><input type="text" name="wechat_appuid" value="{:config_get('wechat_appuid')}" class="form-control"/></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"/></div>
</div>
</form>
</div>
<div class="panel-footer">
<b>WxPusher</b><a href="https://wxpusher.zjiecode.com/admin/" target="_blank" rel="noopener noreferrer">点此进入</a> ,注册并且创建应用 -> 将appToken填写到上方输入框 -> 扫码关注应用 -> 在用户列表查看自己的UID填写到上方输入框<br/>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">Telegram机器人接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">Token</label>
<div class="col-sm-9"><input type="text" name="tgbot_token" value="{:config_get('tgbot_token')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Chat Id</label>
<div class="col-sm-9"><input type="text" name="tgbot_chatid" value="{:config_get('tgbot_chatid')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">使用代理服务器</label>
<div class="col-sm-9"><select class="form-control" name="tgbot_proxy" default="{:config_get('tgbot_proxy')}"><option value="0"></option><option value="1"></option><option value="2">自定义反代URL</option></select></div>
</div>
<div class="form-group" id="tgbot_url_div" style="display:none;">
<label class="col-sm-3 control-label">自定义反代URL</label>
<div class="col-sm-9"><input type="text" name="tgbot_url" value="{:config_get('tgbot_url')}" class="form-control" placeholder="默认为https://api.telegram.org"/></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"/>
<a href="javascript:tgbottest()" class="btn btn-default btn-block">发送测试消息</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<a href="https://t.me/BotFather" target="_blank" rel="noopener noreferrer">@BotFather</a>对话,使用/newbot命令创建一个新的机器人根据提示输入机器人的名称和用户名可得到Token或使用/mybots命令查看已创建的机器人<a href="https://t.me/getmyid_bot" target="_blank" rel="noopener noreferrer">@getmyid_bot</a>对话可得到Chat Id<br/>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">群机器人Webhook</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<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">
<div class="col-sm-offset-3 col-sm-9">
<input type="submit" name="submit" value="保存" class="btn btn-primary btn-block"/>
<a href="javascript:webhooktest()" class="btn btn-default btn-block">发送测试消息</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
仅支持填写企业微信、钉钉、飞书群机器人的Webhook地址
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
$("select[name='mail_type']").change(function(){
if($(this).val() == 0){
$("#frame_set1").show();
$("#frame_set2").hide();
}else{
$("#frame_set1").hide();
$("#frame_set2").show();
}
});
$("select[name='tgbot_proxy']").change(function(){
if($(this).val() == 2){
$("#tgbot_url_div").show();
}else{
$("#tgbot_url_div").hide();
}
});
$("select[name='mail_type']").change();
$("select[name='tgbot_proxy']").change();
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!<br/>如有使用容灾切换,重启检测进程后生效', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function mailtest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/mailtest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
function tgbottest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/tgbottest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
function webhooktest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/webhooktest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}通知设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">发信邮箱设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">发信模式</label>
<div class="col-sm-9"><select class="form-control" name="mail_type" default="{:config_get('mail_type')}"><option value="0">SMTP发信</option><option value="1">搜狐Sendcloud</option><option value="2">阿里云邮件推送</option></select></div>
</div>
<div id="frame_set1">
<div class="form-group">
<label class="col-sm-3 control-label">SMTP服务器</label>
<div class="col-sm-9"><input type="text" name="mail_smtp" value="{:config_get('mail_smtp')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">SMTP端口</label>
<div class="col-sm-9"><input type="text" name="mail_port" value="{:config_get('mail_port')}" 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="mail_name" value="{:config_get('mail_name')}" 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="mail_pwd" value="{:config_get('mail_pwd')}" class="form-control"/></div>
</div>
</div>
<div id="frame_set2">
<div class="form-group">
<label class="col-sm-3 control-label">API_USER</label>
<div class="col-sm-9"><input type="text" name="mail_apiuser" value="{:config_get('mail_apiuser')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">API_KEY</label>
<div class="col-sm-9"><input type="text" name="mail_apikey" value="{:config_get('mail_apikey')}" 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="mail_name2" value="{:config_get('mail_name')}" class="form-control"/></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">收信邮箱</label>
<div class="col-sm-9"><input type="text" name="mail_recv" value="{:config_get('mail_recv')}" class="form-control" placeholder="不填默认为发信邮箱"/></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"/>
<a href="javascript:mailtest()" class="btn btn-default btn-block">发送测试邮件</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<span class="glyphicon glyphicon-info-sign"></span>
使用普通模式发信时建议使用QQ邮箱SMTP服务器smtp.qq.com端口465或587密码是QQ邮箱设置界面生成的<a href="https://service.mail.qq.com/detail/0/75" target="_blank" rel="noreferrer">授权码</a><br/>阿里云邮件推送:<a href="https://www.aliyun.com/product/directmail" target="_blank" rel="noreferrer">点此进入</a><a href="https://usercenter.console.aliyun.com/#/manage/ak" target="_blank" rel="noreferrer">获取AK/SK</a>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">微信公众号消息接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">appToken</label>
<div class="col-sm-9"><input type="text" name="wechat_apptoken" value="{:config_get('wechat_apptoken')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">用户UID</label>
<div class="col-sm-9"><input type="text" name="wechat_appuid" value="{:config_get('wechat_appuid')}" class="form-control"/></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"/></div>
</div>
</form>
</div>
<div class="panel-footer">
<b>WxPusher</b><a href="https://wxpusher.zjiecode.com/admin/" target="_blank" rel="noopener noreferrer">点此进入</a> ,注册并且创建应用 -> 将appToken填写到上方输入框 -> 扫码关注应用 -> 在用户列表查看自己的UID填写到上方输入框<br/>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">Telegram机器人接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">Token</label>
<div class="col-sm-9"><input type="text" name="tgbot_token" value="{:config_get('tgbot_token')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Chat Id</label>
<div class="col-sm-9"><input type="text" name="tgbot_chatid" value="{:config_get('tgbot_chatid')}" class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">使用代理服务器</label>
<div class="col-sm-9"><select class="form-control" name="tgbot_proxy" default="{:config_get('tgbot_proxy')}"><option value="0"></option><option value="1"></option><option value="2">自定义反代URL</option></select></div>
</div>
<div class="form-group" id="tgbot_url_div" style="display:none;">
<label class="col-sm-3 control-label">自定义反代URL</label>
<div class="col-sm-9"><input type="text" name="tgbot_url" value="{:config_get('tgbot_url')}" class="form-control" placeholder="默认为https://api.telegram.org"/></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"/>
<a href="javascript:tgbottest()" class="btn btn-default btn-block">发送测试消息</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
<a href="https://t.me/BotFather" target="_blank" rel="noopener noreferrer">@BotFather</a>对话,使用/newbot命令创建一个新的机器人根据提示输入机器人的名称和用户名可得到Token或使用/mybots命令查看已创建的机器人<a href="https://t.me/getmyid_bot" target="_blank" rel="noopener noreferrer">@getmyid_bot</a>对话可得到Chat Id<br/>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">群机器人Webhook</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<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">
<div class="col-sm-offset-3 col-sm-9">
<input type="submit" name="submit" value="保存" class="btn btn-primary btn-block"/>
<a href="javascript:webhooktest()" class="btn btn-default btn-block">发送测试消息</a>
</div>
</div>
</form>
</div>
<div class="panel-footer">
仅支持填写企业微信、钉钉、飞书群机器人的Webhook地址
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
$("select[name='mail_type']").change(function(){
if($(this).val() == 0){
$("#frame_set1").show();
$("#frame_set2").hide();
}else{
$("#frame_set1").hide();
$("#frame_set2").show();
}
});
$("select[name='tgbot_proxy']").change(function(){
if($(this).val() == 2){
$("#tgbot_url_div").show();
}else{
$("#tgbot_url_div").hide();
}
});
$("select[name='mail_type']").change();
$("select[name='tgbot_proxy']").change();
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!<br/>如有使用容灾切换,重启检测进程后生效', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function mailtest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/mailtest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
function tgbottest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/tgbottest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
function webhooktest(){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'GET',
url : '/system/webhooktest',
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg, {icon: 1});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{/block}

View File

@ -1,114 +1,114 @@
{extend name="common/layout" /}
{block name="title"}代理设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">代理服务器设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">代理IP</label>
<div class="col-sm-9"><input type="text" name="proxy_server" value="{:config_get('proxy_server')}" class="form-control"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理端口</label>
<div class="col-sm-9"><input type="text" name="proxy_port" value="{:config_get('proxy_port')}" class="form-control"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理账号</label>
<div class="col-sm-9"><input type="text" name="proxy_user" value="{:config_get('proxy_user')}" class="form-control" placeholder="没有请留空"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理密码</label>
<div class="col-sm-9"><input type="text" name="proxy_pwd" value="{:config_get('proxy_pwd')}" class="form-control" placeholder="没有请留空"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理协议</label>
<div class="col-sm-9"><select class="form-control" name="proxy_type" default="{:config_get('proxy_type')}">
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
<option value="sock4">SOCK4</option>
<option value="sock5">SOCK5</option>
<option value="sock5h">SOCK5H</option>
</select></div>
</div><br/>
<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"/><br/>
<a href="javascript:proxytest()" class="btn btn-default btn-block">测试连通性</a></div>
</div>
</form>
</div>
<div class="panel-footer">
为保证代理稳定性建议在本机安装Xray入站代理使用普通http协议仅监听本地IP出站代理使用加密协议连接其他服务器。
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function proxytest(){
var proxy_server = $("input[name='proxy_server']").val();
var proxy_port = $("input[name='proxy_port']").val();
var proxy_user = $("input[name='proxy_user']").val();
var proxy_pwd = $("input[name='proxy_pwd']").val();
var proxy_type = $("select[name='proxy_type']").val();
if(proxy_server=='' || proxy_port==''){
layer.alert('代理服务器和端口不能为空!');
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/proxytest',
data : {proxy_server:proxy_server, proxy_port:proxy_port, proxy_user:proxy_user, proxy_pwd:proxy_pwd, proxy_type:proxy_type},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('连通性测试成功!', {icon: 1})
}else{
layer.alert('连通性测试失败:'+data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{extend name="common/layout" /}
{block name="title"}代理设置{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">代理服务器设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
<div class="form-group">
<label class="col-sm-3 control-label">代理IP</label>
<div class="col-sm-9"><input type="text" name="proxy_server" value="{:config_get('proxy_server')}" class="form-control"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理端口</label>
<div class="col-sm-9"><input type="text" name="proxy_port" value="{:config_get('proxy_port')}" class="form-control"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理账号</label>
<div class="col-sm-9"><input type="text" name="proxy_user" value="{:config_get('proxy_user')}" class="form-control" placeholder="没有请留空"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理密码</label>
<div class="col-sm-9"><input type="text" name="proxy_pwd" value="{:config_get('proxy_pwd')}" class="form-control" placeholder="没有请留空"/></div>
</div><br/>
<div class="form-group">
<label class="col-sm-3 control-label">代理协议</label>
<div class="col-sm-9"><select class="form-control" name="proxy_type" default="{:config_get('proxy_type')}">
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
<option value="sock4">SOCK4</option>
<option value="sock5">SOCK5</option>
<option value="sock5h">SOCK5H</option>
</select></div>
</div><br/>
<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"/><br/>
<a href="javascript:proxytest()" class="btn btn-default btn-block">测试连通性</a></div>
</div>
</form>
</div>
<div class="panel-footer">
为保证代理稳定性建议在本机安装Xray入站代理使用普通http协议仅监听本地IP出站代理使用加密协议连接其他服务器。
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script>
var items = $("select[default]");
for (i = 0; i < items.length; i++) {
$(items[i]).val($(items[i]).attr("default")||0);
}
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/set',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('设置保存成功!', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function proxytest(){
var proxy_server = $("input[name='proxy_server']").val();
var proxy_port = $("input[name='proxy_port']").val();
var proxy_user = $("input[name='proxy_user']").val();
var proxy_pwd = $("input[name='proxy_pwd']").val();
var proxy_type = $("select[name='proxy_type']").val();
if(proxy_server=='' || proxy_port==''){
layer.alert('代理服务器和端口不能为空!');
return false;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/system/proxytest',
data : {proxy_server:proxy_server, proxy_port:proxy_port, proxy_user:proxy_user, proxy_pwd:proxy_pwd, proxy_type:proxy_type},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('连通性测试成功!', {icon: 1})
}else{
layer.alert('连通性测试失败:'+data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
{/block}

View File

@ -1,77 +1,77 @@
{extend name="common/layout" /}
{block name="title"}操作日志{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
{if request()->user['level'] eq 2}<input type="text" class="form-control" name="uid" placeholder="UID">{/if}
<input type="text" class="form-control" name="domain" placeholder="域名">
<input type="text" class="form-control" name="kw" placeholder="操作类型/操作详情">
</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>
</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="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/log/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'uid',
title: 'UID',
formatter: function(value, row, index) {
return value>0?'<a href="/user?kw='+value+'" target="_blank">'+value+'</a>':'管理员';
}
},
{
field: 'domain',
title: '域名'
},
{
field: 'action',
title: '操作类型'
},
{
field: 'data',
title: '操作详情'
},
{
field: 'addtime',
title: '时间'
}
],
})
})
</script>
{extend name="common/layout" /}
{block name="title"}操作日志{/block}
{block name="main"}
<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">
<div class="form-group">
<label>搜索</label>
{if request()->user['level'] eq 2}<input type="text" class="form-control" name="uid" placeholder="UID">{/if}
<input type="text" class="form-control" name="domain" placeholder="域名">
<input type="text" class="form-control" name="kw" placeholder="操作类型/操作详情">
</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>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/log/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'uid',
title: 'UID',
formatter: function(value, row, index) {
return value>0?'<a href="/user?kw='+value+'" target="_blank">'+value+'</a>':'管理员';
}
},
{
field: 'domain',
title: '域名'
},
{
field: 'action',
title: '操作类型'
},
{
field: 'data',
title: '操作详情'
},
{
field: 'addtime',
title: '时间'
}
],
})
})
</script>
{/block}

View File

@ -1,338 +1,338 @@
{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">&times;</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 no-padding-right">用户名</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="username" required>
</div>
</div>
<div class="form-group" style="display:none" id="password_input">
<label class="col-sm-3 control-label no-padding-right">密码</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="password" autocomplete="off">
<a class="input-group-addon" id="create_password" href="javascript:">随机生成</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">API接口</label>
<div class="col-sm-9">
<select name="is_api" class="form-control">
<option value="0">关闭</option>
<option value="1">开启</option>
</select>
</div>
</div>
<div class="form-group" style="display:none" id="apikey_input">
<label class="col-sm-3 control-label no-padding-right">API接口密钥</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="apikey" autocomplete="off" readonly>
<a class="input-group-addon" id="create_apikey" href="javascript:">生成密钥</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">用户等级</label>
<div class="col-sm-9">
<div class="input-group">
<select name="level" class="form-control">
<option value="1">普通用户</option>
<option value="2">管理员</option>
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="普通用户只能管理指定的域名解析,管理员拥有和你相同的权限" data-original-title="用户权限说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group" style="display:none" id="permission_input">
<label class="col-sm-3 control-label">域名权限</label>
<div class="col-sm-9">
<select class="form-control select2" id="permission" name="permission[]" multiple="multiple" placeholder="留空">
{foreach $domains as $v}
<option value="{$v}">{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group" style="display:none" id="repwd_input">
<label class="col-sm-3 control-label no-padding-right">重置密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="repwd" autocomplete="off" 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">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="UID或用户名">
</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>
</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}select2/4.0.13/js/select2.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/i18n/zh-CN.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/user/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'UID'
},
{
field: 'username',
title: '用户名'
},
{
field: 'level',
title: '用户等级',
formatter: function(value, row, index) {
switch(value){
case 1: return '<font color="blue">普通用户</font>';break;
case 2: return '<font color="orange">管理员</font>';break;
}
}
},
{
field: 'is_api',
title: 'API接口',
formatter: function(value, row, index) {
switch(value){
case 0: return '<font color="grey">关闭</font>';break;
case 1: return '<font color="green">开启</font>';break;
}
}
},
{
field: 'regtime',
title: '添加时间'
},
{
field: 'lasttime',
title: '上次登录时间'
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
switch(value){
case 0: return '<a href="javascript:setStatus('+row.id+',1)"><font color=red><i class="fa fa-times-circle"></i>封禁</font></a>';break;
case 1: return '<a href="javascript:setStatus('+row.id+',0)"><font color=green><i class="fa fa-check-circle"></i>正常</font></a>';break;
}
}
},
{
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>';
return html;
}
},
],
})
})
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=username]").val('');
$("#form-store input[name=password]").val('');
$("#form-store select[name=is_api]").val(0);
$("#form-store select[name=level]").val(1);
$("#password_input").show();
$("#repwd_input").hide();
$("#create_apikey").click();
$('#permission').val(null).trigger("change");
$("select[name=is_api]").change();
$("select[name=level]").change();
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/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 input[name=username]").val(data.data.username);
$("#form-store select[name=is_api]").val(data.data.is_api);
$("#form-store input[name=apikey]").val(data.data.apikey);
$("#form-store select[name=level]").val(data.data.level);
$("#form-store input[name=repwd]").val('');
$("#password_input").hide();
$("#repwd_input").show();
$('#permission').val(null).trigger("change");
if(data.data.permission != null && data.data.permission.length > 0){
$('#permission').val(data.data.permission).trigger('change');
}
$("select[name=is_api]").change();
$("select[name=level]").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 : '/user/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 setStatus(id,status) {
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/set',
data : {id:id, status:status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
searchRefresh();
}else{
layer.msg(data.msg, {icon:2, time:1500});
}
}
});
}
function delItem(id) {
var confirmobj = layer.confirm('确定要删除此用户吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
var CreatePassword = function (len)
{
var str = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
var pass = '';
for (var i = 0; i < len; i++ )
pass += str.charAt(Math.floor( Math.random() * str.length));
return pass;
}
$(document).ready(function(){
$("select[name=is_api]").change(function(){
if($(this).val() == 1){
$("#apikey_input").show();
}else{
$("#apikey_input").hide();
}
});
$("select[name=level]").change(function(){
if($(this).val() == 2){
$("#permission_input").hide();
}else{
$("#permission_input").show();
$('#permission').select2({placeholder: '请选择该用户可管理解析的域名'});
}
});
$("#create_password").click(function(){
$("input[name='password']").val(CreatePassword(12));
});
$("#create_apikey").click(function(){
$("input[name='apikey']").val(CreatePassword(16));
});
$('[data-toggle="popover"]').popover()
})
</script>
{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">&times;</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 no-padding-right">用户名</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="username" required>
</div>
</div>
<div class="form-group" style="display:none" id="password_input">
<label class="col-sm-3 control-label no-padding-right">密码</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="password" autocomplete="off">
<a class="input-group-addon" id="create_password" href="javascript:">随机生成</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">API接口</label>
<div class="col-sm-9">
<select name="is_api" class="form-control">
<option value="0">关闭</option>
<option value="1">开启</option>
</select>
</div>
</div>
<div class="form-group" style="display:none" id="apikey_input">
<label class="col-sm-3 control-label no-padding-right">API接口密钥</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="apikey" autocomplete="off" readonly>
<a class="input-group-addon" id="create_apikey" href="javascript:">生成密钥</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">用户等级</label>
<div class="col-sm-9">
<div class="input-group">
<select name="level" class="form-control">
<option value="1">普通用户</option>
<option value="2">管理员</option>
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="普通用户只能管理指定的域名解析,管理员拥有和你相同的权限" data-original-title="用户权限说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group" style="display:none" id="permission_input">
<label class="col-sm-3 control-label">域名权限</label>
<div class="col-sm-9">
<select class="form-control select2" id="permission" name="permission[]" multiple="multiple" placeholder="留空">
{foreach $domains as $v}
<option value="{$v}">{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group" style="display:none" id="repwd_input">
<label class="col-sm-3 control-label no-padding-right">重置密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="repwd" autocomplete="off" 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">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="UID或用户名">
</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>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="/static/js/layer-3.1.1.js"></script>
<script src="/static/js/bootstrap-table-1.21.4.min.js"></script>
<script src="/static/js/bootstrap-table-page-jump-to-1.21.4.min.js"></script>
<script src="/static/js/select2-4.0.13.min.js"></script>
<script src="/static/js/select2-i18n-zh-CN-4.0.13.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
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: '/user/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'UID'
},
{
field: 'username',
title: '用户名'
},
{
field: 'level',
title: '用户等级',
formatter: function(value, row, index) {
switch(value){
case 1: return '<font color="blue">普通用户</font>';break;
case 2: return '<font color="orange">管理员</font>';break;
}
}
},
{
field: 'is_api',
title: 'API接口',
formatter: function(value, row, index) {
switch(value){
case 0: return '<font color="grey">关闭</font>';break;
case 1: return '<font color="green">开启</font>';break;
}
}
},
{
field: 'regtime',
title: '添加时间'
},
{
field: 'lasttime',
title: '上次登录时间'
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
switch(value){
case 0: return '<a href="javascript:setStatus('+row.id+',1)"><font color=red><i class="fa fa-times-circle"></i>封禁</font></a>';break;
case 1: return '<a href="javascript:setStatus('+row.id+',0)"><font color=green><i class="fa fa-check-circle"></i>正常</font></a>';break;
}
}
},
{
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>';
return html;
}
},
],
})
})
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=username]").val('');
$("#form-store input[name=password]").val('');
$("#form-store select[name=is_api]").val(0);
$("#form-store select[name=level]").val(1);
$("#password_input").show();
$("#repwd_input").hide();
$("#create_apikey").click();
$('#permission').val(null).trigger("change");
$("select[name=is_api]").change();
$("select[name=level]").change();
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/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 input[name=username]").val(data.data.username);
$("#form-store select[name=is_api]").val(data.data.is_api);
$("#form-store input[name=apikey]").val(data.data.apikey);
$("#form-store select[name=level]").val(data.data.level);
$("#form-store input[name=repwd]").val('');
$("#password_input").hide();
$("#repwd_input").show();
$('#permission').val(null).trigger("change");
if(data.data.permission != null && data.data.permission.length > 0){
$('#permission').val(data.data.permission).trigger('change');
}
$("select[name=is_api]").change();
$("select[name=level]").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 : '/user/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 setStatus(id,status) {
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/set',
data : {id:id, status:status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
searchRefresh();
}else{
layer.msg(data.msg, {icon:2, time:1500});
}
}
});
}
function delItem(id) {
var confirmobj = layer.confirm('确定要删除此用户吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchRefresh();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
var CreatePassword = function (len)
{
var str = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
var pass = '';
for (var i = 0; i < len; i++ )
pass += str.charAt(Math.floor( Math.random() * str.length));
return pass;
}
$(document).ready(function(){
$("select[name=is_api]").change(function(){
if($(this).val() == 1){
$("#apikey_input").show();
}else{
$("#apikey_input").hide();
}
});
$("select[name=level]").change(function(){
if($(this).val() == 2){
$("#permission_input").hide();
}else{
$("#permission_input").show();
$('#permission').select2({placeholder: '请选择该用户可管理解析的域名'});
}
});
$("#create_password").click(function(){
$("input[name='password']").val(CreatePassword(12));
});
$("#create_apikey").click(function(){
$("input[name='apikey']").val(CreatePassword(16));
});
$('[data-toggle="popover"]').popover()
})
</script>
{/block}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});
//# sourceMappingURL=FileSaver.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
/**
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);

2
public/static/js/jquery-3.6.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,28 @@
(function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.data=a}function o(a,c){this.typeNumber=a;this.errorCorrectLevel=c;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[]}function q(a,c){if(void 0==a.length)throw Error(a.length+"/"+c);for(var d=0;d<a.length&&0==a[d];)d++;this.num=Array(a.length-d+c);for(var b=0;b<a.length-d;b++)this.num[b]=a[b+d]}function p(a,c){this.totalCount=a;this.dataCount=c}function t(){this.buffer=[];this.length=0}u.prototype={getLength:function(){return this.data.length},
write:function(a){for(var c=0;c<this.data.length;c++)a.put(this.data.charCodeAt(c),8)}};o.prototype={addData:function(a){this.dataList.push(new u(a));this.dataCache=null},isDark:function(a,c){if(0>a||this.moduleCount<=a||0>c||this.moduleCount<=c)throw Error(a+","+c);return this.modules[a][c]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){for(var a=1,a=1;40>a;a++){for(var c=p.getRSBlocks(a,this.errorCorrectLevel),d=new t,b=0,e=0;e<c.length;e++)b+=c[e].dataCount;
for(e=0;e<this.dataList.length;e++)c=this.dataList[e],d.put(c.mode,4),d.put(c.getLength(),j.getLengthInBits(c.mode,a)),c.write(d);if(d.getLengthInBits()<=8*b)break}this.typeNumber=a}this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17;this.modules=Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=Array(this.moduleCount);for(var b=0;b<this.moduleCount;b++)this.modules[d][b]=null}this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-
7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(a,c);7<=this.typeNumber&&this.setupTypeNumber(a);null==this.dataCache&&(this.dataCache=o.createData(this.typeNumber,this.errorCorrectLevel,this.dataList));this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,c){for(var d=-1;7>=d;d++)if(!(-1>=a+d||this.moduleCount<=a+d))for(var b=-1;7>=b;b++)-1>=c+b||this.moduleCount<=c+b||(this.modules[a+d][c+b]=
0<=d&&6>=d&&(0==b||6==b)||0<=b&&6>=b&&(0==d||6==d)||2<=d&&4>=d&&2<=b&&4>=b?!0:!1)},getBestMaskPattern:function(){for(var a=0,c=0,d=0;8>d;d++){this.makeImpl(!0,d);var b=j.getLostPoint(this);if(0==d||a>b)a=b,c=d}return c},createMovieClip:function(a,c,d){a=a.createEmptyMovieClip(c,d);this.make();for(c=0;c<this.modules.length;c++)for(var d=1*c,b=0;b<this.modules[c].length;b++){var e=1*b;this.modules[c][b]&&(a.beginFill(0,100),a.moveTo(e,d),a.lineTo(e+1,d),a.lineTo(e+1,d+1),a.lineTo(e,d+1),a.endFill())}return a},
setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(a=8;a<this.moduleCount-8;a++)null==this.modules[6][a]&&(this.modules[6][a]=0==a%2)},setupPositionAdjustPattern:function(){for(var a=j.getPatternPosition(this.typeNumber),c=0;c<a.length;c++)for(var d=0;d<a.length;d++){var b=a[c],e=a[d];if(null==this.modules[b][e])for(var f=-2;2>=f;f++)for(var i=-2;2>=i;i++)this.modules[b+f][e+i]=-2==f||2==f||-2==i||2==i||0==f&&0==i?!0:!1}},setupTypeNumber:function(a){for(var c=
j.getBCHTypeNumber(this.typeNumber),d=0;18>d;d++){var b=!a&&1==(c>>d&1);this.modules[Math.floor(d/3)][d%3+this.moduleCount-8-3]=b}for(d=0;18>d;d++)b=!a&&1==(c>>d&1),this.modules[d%3+this.moduleCount-8-3][Math.floor(d/3)]=b},setupTypeInfo:function(a,c){for(var d=j.getBCHTypeInfo(this.errorCorrectLevel<<3|c),b=0;15>b;b++){var e=!a&&1==(d>>b&1);6>b?this.modules[b][8]=e:8>b?this.modules[b+1][8]=e:this.modules[this.moduleCount-15+b][8]=e}for(b=0;15>b;b++)e=!a&&1==(d>>b&1),8>b?this.modules[8][this.moduleCount-
b-1]=e:9>b?this.modules[8][15-b-1+1]=e:this.modules[8][15-b-1]=e;this.modules[this.moduleCount-8][8]=!a},mapData:function(a,c){for(var d=-1,b=this.moduleCount-1,e=7,f=0,i=this.moduleCount-1;0<i;i-=2)for(6==i&&i--;;){for(var g=0;2>g;g++)if(null==this.modules[b][i-g]){var n=!1;f<a.length&&(n=1==(a[f]>>>e&1));j.getMask(c,b,i-g)&&(n=!n);this.modules[b][i-g]=n;e--; -1==e&&(f++,e=7)}b+=d;if(0>b||this.moduleCount<=b){b-=d;d=-d;break}}}};o.PAD0=236;o.PAD1=17;o.createData=function(a,c,d){for(var c=p.getRSBlocks(a,
c),b=new t,e=0;e<d.length;e++){var f=d[e];b.put(f.mode,4);b.put(f.getLength(),j.getLengthInBits(f.mode,a));f.write(b)}for(e=a=0;e<c.length;e++)a+=c[e].dataCount;if(b.getLengthInBits()>8*a)throw Error("code length overflow. ("+b.getLengthInBits()+">"+8*a+")");for(b.getLengthInBits()+4<=8*a&&b.put(0,4);0!=b.getLengthInBits()%8;)b.putBit(!1);for(;!(b.getLengthInBits()>=8*a);){b.put(o.PAD0,8);if(b.getLengthInBits()>=8*a)break;b.put(o.PAD1,8)}return o.createBytes(b,c)};o.createBytes=function(a,c){for(var d=
0,b=0,e=0,f=Array(c.length),i=Array(c.length),g=0;g<c.length;g++){var n=c[g].dataCount,h=c[g].totalCount-n,b=Math.max(b,n),e=Math.max(e,h);f[g]=Array(n);for(var k=0;k<f[g].length;k++)f[g][k]=255&a.buffer[k+d];d+=n;k=j.getErrorCorrectPolynomial(h);n=(new q(f[g],k.getLength()-1)).mod(k);i[g]=Array(k.getLength()-1);for(k=0;k<i[g].length;k++)h=k+n.getLength()-i[g].length,i[g][k]=0<=h?n.get(h):0}for(k=g=0;k<c.length;k++)g+=c[k].totalCount;d=Array(g);for(k=n=0;k<b;k++)for(g=0;g<c.length;g++)k<f[g].length&&
(d[n++]=f[g][k]);for(k=0;k<e;k++)for(g=0;g<c.length;g++)k<i[g].length&&(d[n++]=i[g][k]);return d};s=4;for(var j={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,
78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var c=a<<10;0<=j.getBCHDigit(c)-j.getBCHDigit(j.G15);)c^=j.G15<<j.getBCHDigit(c)-j.getBCHDigit(j.G15);return(a<<10|c)^j.G15_MASK},getBCHTypeNumber:function(a){for(var c=a<<12;0<=j.getBCHDigit(c)-
j.getBCHDigit(j.G18);)c^=j.G18<<j.getBCHDigit(c)-j.getBCHDigit(j.G18);return a<<12|c},getBCHDigit:function(a){for(var c=0;0!=a;)c++,a>>>=1;return c},getPatternPosition:function(a){return j.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,c,d){switch(a){case 0:return 0==(c+d)%2;case 1:return 0==c%2;case 2:return 0==d%3;case 3:return 0==(c+d)%3;case 4:return 0==(Math.floor(c/2)+Math.floor(d/3))%2;case 5:return 0==c*d%2+c*d%3;case 6:return 0==(c*d%2+c*d%3)%2;case 7:return 0==(c*d%3+(c+d)%2)%2;default:throw Error("bad maskPattern:"+
a);}},getErrorCorrectPolynomial:function(a){for(var c=new q([1],0),d=0;d<a;d++)c=c.multiply(new q([1,l.gexp(d)],0));return c},getLengthInBits:function(a,c){if(1<=c&&10>c)switch(a){case 1:return 10;case 2:return 9;case s:return 8;case 8:return 8;default:throw Error("mode:"+a);}else if(27>c)switch(a){case 1:return 12;case 2:return 11;case s:return 16;case 8:return 10;default:throw Error("mode:"+a);}else if(41>c)switch(a){case 1:return 14;case 2:return 13;case s:return 16;case 8:return 12;default:throw Error("mode:"+
a);}else throw Error("type:"+c);},getLostPoint:function(a){for(var c=a.getModuleCount(),d=0,b=0;b<c;b++)for(var e=0;e<c;e++){for(var f=0,i=a.isDark(b,e),g=-1;1>=g;g++)if(!(0>b+g||c<=b+g))for(var h=-1;1>=h;h++)0>e+h||c<=e+h||0==g&&0==h||i==a.isDark(b+g,e+h)&&f++;5<f&&(d+=3+f-5)}for(b=0;b<c-1;b++)for(e=0;e<c-1;e++)if(f=0,a.isDark(b,e)&&f++,a.isDark(b+1,e)&&f++,a.isDark(b,e+1)&&f++,a.isDark(b+1,e+1)&&f++,0==f||4==f)d+=3;for(b=0;b<c;b++)for(e=0;e<c-6;e++)a.isDark(b,e)&&!a.isDark(b,e+1)&&a.isDark(b,e+
2)&&a.isDark(b,e+3)&&a.isDark(b,e+4)&&!a.isDark(b,e+5)&&a.isDark(b,e+6)&&(d+=40);for(e=0;e<c;e++)for(b=0;b<c-6;b++)a.isDark(b,e)&&!a.isDark(b+1,e)&&a.isDark(b+2,e)&&a.isDark(b+3,e)&&a.isDark(b+4,e)&&!a.isDark(b+5,e)&&a.isDark(b+6,e)&&(d+=40);for(e=f=0;e<c;e++)for(b=0;b<c;b++)a.isDark(b,e)&&f++;a=Math.abs(100*f/c/c-50)/5;return d+10*a}},l={glog:function(a){if(1>a)throw Error("glog("+a+")");return l.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;256<=a;)a-=255;return l.EXP_TABLE[a]},EXP_TABLE:Array(256),
LOG_TABLE:Array(256)},m=0;8>m;m++)l.EXP_TABLE[m]=1<<m;for(m=8;256>m;m++)l.EXP_TABLE[m]=l.EXP_TABLE[m-4]^l.EXP_TABLE[m-5]^l.EXP_TABLE[m-6]^l.EXP_TABLE[m-8];for(m=0;255>m;m++)l.LOG_TABLE[l.EXP_TABLE[m]]=m;q.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var c=Array(this.getLength()+a.getLength()-1),d=0;d<this.getLength();d++)for(var b=0;b<a.getLength();b++)c[d+b]^=l.gexp(l.glog(this.get(d))+l.glog(a.get(b)));return new q(c,0)},mod:function(a){if(0>
this.getLength()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b<this.getLength();b++)d[b]=this.get(b);for(b=0;b<a.getLength();b++)d[b]^=l.gexp(l.glog(a.get(b))+c);return(new q(d,0)).mod(a)}};p.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],
[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,
116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,
43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,
3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,
55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,
45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];p.getRSBlocks=function(a,c){var d=p.getRsBlockTable(a,c);if(void 0==d)throw Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+c);for(var b=d.length/3,e=[],f=0;f<b;f++)for(var h=d[3*f+0],g=d[3*f+1],j=d[3*f+2],l=0;l<h;l++)e.push(new p(g,j));return e};p.getRsBlockTable=function(a,c){switch(c){case 1:return p.RS_BLOCK_TABLE[4*(a-1)+0];case 0:return p.RS_BLOCK_TABLE[4*(a-1)+1];case 3:return p.RS_BLOCK_TABLE[4*
(a-1)+2];case 2:return p.RS_BLOCK_TABLE[4*(a-1)+3]}};t.prototype={get:function(a){return 1==(this.buffer[Math.floor(a/8)]>>>7-a%8&1)},put:function(a,c){for(var d=0;d<c;d++)this.putBit(1==(a>>>c-d-1&1))},getLengthInBits:function(){return this.length},putBit:function(a){var c=Math.floor(this.length/8);this.buffer.length<=c&&this.buffer.push(0);a&&(this.buffer[c]|=128>>>this.length%8);this.length++}};"string"===typeof h&&(h={text:h});h=r.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,
correctLevel:2,background:"#ffffff",foreground:"#000000"},h);return this.each(function(){var a;if("canvas"==h.render){a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();var c=document.createElement("canvas");c.width=h.width;c.height=h.height;for(var d=c.getContext("2d"),b=h.width/a.getModuleCount(),e=h.height/a.getModuleCount(),f=0;f<a.getModuleCount();f++)for(var i=0;i<a.getModuleCount();i++){d.fillStyle=a.isDark(f,i)?h.foreground:h.background;var g=Math.ceil((i+1)*b)-Math.floor(i*b),
j=Math.ceil((f+1)*b)-Math.floor(f*b);d.fillRect(Math.round(i*b),Math.round(f*e),g,j)}}else{a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();c=r("<table></table>").css("width",h.width+"px").css("height",h.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",h.background);d=h.width/a.getModuleCount();b=h.height/a.getModuleCount();for(e=0;e<a.getModuleCount();e++){f=r("<tr></tr>").css("height",b+"px").appendTo(c);for(i=0;i<a.getModuleCount();i++)r("<td></td>").css("width",
d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,131 @@
//! moment.js locale configuration
//! locale : Chinese (China) [zh-cn]
//! author : suupic : https://github.com/suupic
//! author : Zeno Zeng : https://github.com/zenozeng
//! author : uu109 : https://github.com/uu109
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
&& typeof require === 'function' ? factory(require('../moment')) :
typeof define === 'function' && define.amd ? define(['../moment'], factory) :
factory(global.moment)
}(this, (function (moment) { 'use strict';
//! moment.js locale configuration
var zhCn = moment.defineLocale('zh-cn', {
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split(
'_'
),
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split(
'_'
),
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
longDateFormat: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日Ah点mm分',
LLLL: 'YYYY年M月D日ddddAh点mm分',
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm',
},
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
meridiemHour: function (hour, meridiem) {
if (hour === 12) {
hour = 0;
}
if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') {
return hour;
} else if (meridiem === '下午' || meridiem === '晚上') {
return hour + 12;
} else {
// '中午'
return hour >= 11 ? hour : hour + 12;
}
},
meridiem: function (hour, minute, isLower) {
var hm = hour * 100 + minute;
if (hm < 600) {
return '凌晨';
} else if (hm < 900) {
return '早上';
} else if (hm < 1130) {
return '上午';
} else if (hm < 1230) {
return '中午';
} else if (hm < 1800) {
return '下午';
} else {
return '晚上';
}
},
calendar: {
sameDay: '[今天]LT',
nextDay: '[明天]LT',
nextWeek: function (now) {
if (now.week() !== this.week()) {
return '[下]dddLT';
} else {
return '[本]dddLT';
}
},
lastDay: '[昨天]LT',
lastWeek: function (now) {
if (this.week() !== now.week()) {
return '[上]dddLT';
} else {
return '[本]dddLT';
}
},
sameElse: 'L',
},
dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/,
ordinal: function (number, period) {
switch (period) {
case 'd':
case 'D':
case 'DDD':
return number + '日';
case 'M':
return number + '月';
case 'w':
case 'W':
return number + '周';
default:
return number;
}
},
relativeTime: {
future: '%s后',
past: '%s前',
s: '几秒',
ss: '%d 秒',
m: '1 分钟',
mm: '%d 分钟',
h: '1 小时',
hh: '%d 小时',
d: '1 天',
dd: '%d 天',
w: '1 周',
ww: '%d 周',
M: '1 个月',
MM: '%d 个月',
y: '1 年',
yy: '%d 年',
},
week: {
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
dow: 1, // Monday is the first day of the week.
doy: 4, // The week that contains Jan 4th is the first week of the year.
},
});
return zhCn;
})));

1
public/static/js/moment-2.29.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
public/static/js/respond-1.4.2.min.js vendored Normal file
View File

@ -0,0 +1,5 @@
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
* */
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(){var n;jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd&&(n=jQuery.fn.select2.amd),n.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(n){return"请删除"+(n.input.length-n.maximum)+"个字符"},inputTooShort:function(n){return"请再输入至少"+(n.minimum-n.input.length)+"个字符"},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(n){return"最多只能选择"+n.maximum+"个项目"},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"},removeAllItems:function(){return"删除所有项目"}}}),n.define,n.require}();

11
public/static/js/vue-2.7.16.min.js vendored Normal file

File diff suppressed because one or more lines are too long