mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2024-11-15 19:22:22 +08:00
1.2
This commit is contained in:
parent
d43ac1404a
commit
923cdc5e90
76
app/command/Dmtask.php
Normal file
76
app/command/Dmtask.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use Exception;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
use app\lib\TaskRunner;
|
||||
|
||||
class Dmtask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('dmtask')
|
||||
->setDescription('容灾切换任务');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
config_set('run_error', '');
|
||||
if(!extension_loaded('swoole')){
|
||||
$output->writeln('[Error] 未安装Swoole扩展');
|
||||
config_set('run_error', '未安装Swoole扩展');
|
||||
return;
|
||||
}
|
||||
try{
|
||||
$output->writeln('进程启动成功.');
|
||||
$this->runtask();
|
||||
}catch(Exception $e){
|
||||
$output->writeln('[Error] '.$e->getMessage());
|
||||
config_set('run_error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function runtask(){
|
||||
\Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
|
||||
\Co\run(function() {
|
||||
$date = date("Ymd");
|
||||
$count = config_get('run_count', null, true) ?? 0;
|
||||
while(true){
|
||||
sleep(1);
|
||||
if($date != date("Ymd")){
|
||||
$count = 0;
|
||||
$date = date("Ymd");
|
||||
}
|
||||
|
||||
$rows = Db::name('dmtask')->where('checknexttime', '<=', time())->where('active', 1)->order('id', 'ASC')->select();
|
||||
foreach($rows as $row){
|
||||
\go(function () use($row) {
|
||||
try{
|
||||
(new TaskRunner())->execute($row);
|
||||
} catch (\Swoole\ExitException $e) {
|
||||
echo $e->getStatus()."\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->__toString()."\n";
|
||||
}
|
||||
});
|
||||
Db::name('dmtask')->where('id', $row['id'])->update([
|
||||
'checktime' => time(),
|
||||
'checknexttime' => time() + $row['frequency']
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
|
||||
config_set('run_time', date("Y-m-d H:i:s"));
|
||||
config_set('run_count', $count);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -201,10 +201,53 @@ function checkPermission($type, $domain = null){
|
|||
function getAdminSkin(){
|
||||
$skin = cookie('admin_skin');
|
||||
if(empty($skin)){
|
||||
$skin = cache('admin_skin');
|
||||
$skin = config_get('admin_skin');
|
||||
}
|
||||
if(empty($skin)){
|
||||
$skin = 'skin-black-blue';
|
||||
}
|
||||
return $skin;
|
||||
}
|
||||
|
||||
function config_get($key, $default = null, $force = false)
|
||||
{
|
||||
if ($force) {
|
||||
$value = Db::name('config')->where('key', $key)->value('value');
|
||||
} else {
|
||||
$value = config('sys.'.$key);
|
||||
}
|
||||
return $value ?: $default;
|
||||
}
|
||||
|
||||
function config_set($key, $value)
|
||||
{
|
||||
$res = Db::name('config')->replace()->insert(['key'=>$key, 'value'=>$value]);
|
||||
return $res!==false;
|
||||
}
|
||||
|
||||
function getMillisecond()
|
||||
{
|
||||
list($s1, $s2) = explode(' ', microtime());
|
||||
return (int)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
|
||||
}
|
||||
|
||||
function getDnsType($value){
|
||||
if(filter_var($value, FILTER_VALIDATE_IP))return 'A';
|
||||
else return 'CNAME';
|
||||
}
|
||||
|
||||
function convert_second($s){
|
||||
$m = floor($s/60);
|
||||
if($m == 0){
|
||||
return $s.'秒';
|
||||
}else{
|
||||
$s = $s%60;
|
||||
$h = floor($m/60);
|
||||
if($h == 0){
|
||||
return $m.'分钟'.$s.'秒';
|
||||
}else{
|
||||
$m = $m%60;
|
||||
return $h.'小时'.$m.'分钟'.$s.'秒';
|
||||
}
|
||||
}
|
||||
}
|
261
app/controller/Dmonitor.php
Normal file
261
app/controller/Dmonitor.php
Normal file
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use app\lib\DnsHelper;
|
||||
|
||||
class Dmonitor extends BaseController
|
||||
{
|
||||
public function overview()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$switch_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$run_state = config_get('run_time') ? (time()-strtotime(config_get('run_time')) > 10 ? 0 : 1) : 0;
|
||||
View::assign('info', [
|
||||
'run_count' => config_get('run_count', null, true) ?? 0,
|
||||
'run_time' => config_get('run_time', null, true) ?? '无',
|
||||
'run_state' => $run_state,
|
||||
'run_error' => config_get('run_error', null, true),
|
||||
'switch_count' => $switch_count,
|
||||
'fail_count' => $fail_count,
|
||||
'swoole' => extension_loaded('swoole') ? '<font color="green">已安装</font>' : '<font color="red">未安装</font>',
|
||||
]);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task_data(){
|
||||
if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]);
|
||||
$type = input('post.type/d', 1);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('dmtask')->alias('A')->join('domain B','A.did = B.id');
|
||||
if(!empty($kw)){
|
||||
if($type == 1){
|
||||
$select->whereLike('rr|B.name', '%'.$kw.'%');
|
||||
}elseif($type == 2){
|
||||
$select->where('recordid', $kw);
|
||||
}elseif($type == 3){
|
||||
$select->where('main_value', $kw);
|
||||
}elseif($type == 4){
|
||||
$select->where('backup_value', $kw);
|
||||
}elseif($type == 5){
|
||||
$select->whereLike('remark', '%'.$kw.'%');
|
||||
}
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('A.id','desc')->limit($offset, $limit)->field('A.*,B.name domain')->select()->toArray();
|
||||
|
||||
foreach($list as &$row){
|
||||
$row['checktimestr'] = date('Y-m-d H:i:s', $row['checktime']);
|
||||
}
|
||||
|
||||
return json(['total'=>$total, 'rows'=>$list]);
|
||||
}
|
||||
|
||||
public function taskform()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$action = input('param.action');
|
||||
if(request()->isPost()){
|
||||
if($action == 'add'){
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
'addtime' => time(),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if(empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])){
|
||||
return json(['code'=>-1, 'msg'=>'必填项不能为空']);
|
||||
}
|
||||
if($task['checktype'] > 0 && $task['timeout'] > $task['frequency']){
|
||||
return json(['code'=>-1, 'msg'=>'为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if($task['type'] == 2 && $task['backup_value'] == $task['main_value']){
|
||||
return json(['code'=>-1, 'msg'=>'主备地址不能相同']);
|
||||
}
|
||||
if(Db::name('dmtask')->where('recordid', $task['recordid'])->find()){
|
||||
return json(['code'=>-1, 'msg'=>'当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->insert($task);
|
||||
return json(['code'=>0, 'msg'=>'添加成功']);
|
||||
}elseif($action == 'edit'){
|
||||
$id = input('post.id/d');
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
];
|
||||
|
||||
if(empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])){
|
||||
return json(['code'=>-1, 'msg'=>'必填项不能为空']);
|
||||
}
|
||||
if($task['checktype'] > 0 && $task['timeout'] > $task['frequency']){
|
||||
return json(['code'=>-1, 'msg'=>'为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if($task['type'] == 2 && $task['backup_value'] == $task['main_value']){
|
||||
return json(['code'=>-1, 'msg'=>'主备地址不能相同']);
|
||||
}
|
||||
if(Db::name('dmtask')->where('recordid', $task['recordid'])->where('id', '<>', $id)->find()){
|
||||
return json(['code'=>-1, 'msg'=>'当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->where('id', $id)->update($task);
|
||||
return json(['code'=>0, 'msg'=>'修改成功']);
|
||||
}elseif($action == 'setactive'){
|
||||
$id = input('post.id/d');
|
||||
$active = input('post.active/d');
|
||||
Db::name('dmtask')->where('id', $id)->update(['active'=>$active]);
|
||||
return json(['code'=>0, 'msg'=>'设置成功']);
|
||||
}elseif($action == 'del'){
|
||||
$id = input('post.id/d');
|
||||
Db::name('dmtask')->where('id', $id)->delete();
|
||||
Db::name('dmlog')->where('taskid', $id)->delete();
|
||||
return json(['code'=>0, 'msg'=>'删除成功']);
|
||||
}else{
|
||||
return json(['code'=>-1, 'msg'=>'参数错误']);
|
||||
}
|
||||
}
|
||||
$task = null;
|
||||
if($action == 'edit'){
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('dmtask')->where('id', $id)->find();
|
||||
if(empty($task)) return $this->alert('error', '任务不存在');
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
foreach(Db::name('domain')->select() as $row){
|
||||
$domains[$row['id']] = $row['name'];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
|
||||
View::assign('info', $task);
|
||||
View::assign('action', $action);
|
||||
View::assign('support_ping', function_exists('exec')?'1':'0');
|
||||
return View::fetch('taskform');
|
||||
}
|
||||
|
||||
public function taskinfo()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('param.id/d');
|
||||
$task = Db::name('dmtask')->where('id', $id)->find();
|
||||
if(empty($task)) return $this->alert('error', '任务不存在');
|
||||
|
||||
$switch_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s",strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$task['switch_count'] = $switch_count;
|
||||
$task['fail_count'] = $fail_count;
|
||||
if($task['type'] == 2){
|
||||
$task['action_name'] = ['未知', '<font color="red">切换备用解析记录</font>', '<font color="green">恢复主解析记录</font>'];
|
||||
}else{
|
||||
$task['action_name'] = ['未知', '<font color="red">暂停解析</font>', '<font color="green">启用解析</font>'];
|
||||
}
|
||||
View::assign('info', $task);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function tasklog_data(){
|
||||
if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]);
|
||||
$taskid = input('param.id/d');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
$action = input('post.action/d', 0);
|
||||
|
||||
$select = Db::name('dmlog')->where('taskid', $taskid);
|
||||
if($action > 0){
|
||||
$select->where('action', $action);
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('id','desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total'=>$total, 'rows'=>$list]);
|
||||
}
|
||||
|
||||
public function noticeset()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if(request()->isPost()){
|
||||
$params = input('post.');
|
||||
if(isset($params['mail_type']) && isset($params['mail_name2']) && $params['mail_type'] > 0){
|
||||
$params['mail_name'] = $params['mail_name2'];
|
||||
unset($params['mail_name2']);
|
||||
}
|
||||
foreach ($params as $key=>$value){
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
}
|
||||
return json(['code'=>0, 'msg'=>'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function mailtest()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$mail_name = config_get('mail_recv')?config_get('mail_recv'):config_get('mail_name');
|
||||
if(empty($mail_name)) return json(['code'=>-1, 'msg'=>'您还未设置邮箱!']);
|
||||
$result = \app\lib\MsgNotice::send_mail($mail_name,'邮件发送测试。','这是一封测试邮件!<br/><br/>来自:'.request()->root(true));
|
||||
if($result === true){
|
||||
return json(['code'=>0, 'msg'=>'邮件发送成功!']);
|
||||
}else{
|
||||
return json(['code'=>-1, 'msg'=>'邮件发送失败!'.$result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if(request()->isPost()){
|
||||
$days = input('post.days/d');
|
||||
if(!$days || $days < 0) return json(['code'=>-1, 'msg'=>'参数错误']);
|
||||
Db::execute("DELETE FROM `".config('database.connections.mysql.prefix')."dmlog` WHERE `date`<'".date("Y-m-d H:i:s",strtotime("-".$days." days"))."'");
|
||||
Db::execute("OPTIMIZE TABLE `".config('database.connections.mysql.prefix')."dmlog`");
|
||||
return json(['code'=>0, 'msg'=>'清理成功']);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$run_state = config_get('run_time', null, true) ? (time()-strtotime(config_get('run_time')) > 10 ? 0 : 1) : 0;
|
||||
return $run_state == 1 ? 'ok' : 'error';
|
||||
}
|
||||
}
|
|
@ -219,6 +219,7 @@ class Domain extends BaseController
|
|||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('post.id/d');
|
||||
Db::name('domain')->where('id', $id)->delete();
|
||||
Db::name('dmtask')->where('did', $id)->delete();
|
||||
return json(['code'=>0]);
|
||||
}
|
||||
return json(['code'=>-3]);
|
||||
|
@ -345,13 +346,35 @@ class Domain extends BaseController
|
|||
|
||||
$recordLine = cache('record_line_'.$id);
|
||||
|
||||
$list = [];
|
||||
foreach($domainRecords['list'] as $row){
|
||||
foreach($domainRecords['list'] as &$row){
|
||||
$row['LineName'] = isset($recordLine[$row['Line']]) ? $recordLine[$row['Line']]['name'] : $row['Line'];
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return json(['total'=>$domainRecords['total'], 'rows'=>$list]);
|
||||
return json(['total'=>$domainRecords['total'], 'rows'=>$domainRecords['list']]);
|
||||
}
|
||||
|
||||
public function record_list(){
|
||||
if(!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('post.id/d');
|
||||
$rr = input('post.rr', null, 'trim');
|
||||
|
||||
$drow = Db::name('domain')->where('id', $id)->find();
|
||||
if(!$drow){
|
||||
return json(['code'=>-1, 'msg'=>'域名不存在']);
|
||||
}
|
||||
if(!checkPermission(0, $drow['name'])) return json(['code'=>-1, 'msg'=>'无权限']);
|
||||
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
$domainRecords = $dns->getSubDomainRecords($rr, 1, 100);
|
||||
if(!$domainRecords) return json(['code'=>-1, 'msg'=>'获取记录列表失败,'.$dns->getError()]);
|
||||
|
||||
list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
|
||||
|
||||
foreach($domainRecords['list'] as &$row){
|
||||
$row['LineName'] = isset($recordLine[$row['Line']]) ? $recordLine[$row['Line']]['name'] : $row['Line'];
|
||||
}
|
||||
|
||||
return json(['code'=>0, 'data'=>$domainRecords['list']]);
|
||||
}
|
||||
|
||||
public function record_add(){
|
||||
|
|
|
@ -32,6 +32,12 @@ class Index extends BaseController
|
|||
return json(['code'=>-3]);
|
||||
}
|
||||
|
||||
if(config('app.dbversion') && config_get('version') != config('app.dbversion')){
|
||||
$this->db_update();
|
||||
config_set('version', config('app.dbversion'));
|
||||
Cache::clear();
|
||||
}
|
||||
|
||||
$tmp = 'version()';
|
||||
$mysqlVersion = Db::query("select version()")[0][$tmp];
|
||||
$info = [
|
||||
|
@ -47,13 +53,26 @@ class Index extends BaseController
|
|||
return view();
|
||||
}
|
||||
|
||||
private function db_update(){
|
||||
$sqls=file_get_contents(app()->getAppPath().'sql/update.sql');
|
||||
$mysql_prefix = env('database.prefix', 'dnsmgr_');
|
||||
$sqls=explode(';', $sqls);
|
||||
foreach ($sqls as $value) {
|
||||
$value=trim($value);
|
||||
if(empty($value))continue;
|
||||
$value = str_replace('dnsmgr_',$mysql_prefix,$value);
|
||||
Db::execute($value);
|
||||
}
|
||||
}
|
||||
|
||||
public function changeskin(){
|
||||
$skin = input('post.skin');
|
||||
if(request()->user['level'] == 2){
|
||||
if(cookie('admin_skin')){
|
||||
cookie('admin_skin', null);
|
||||
}
|
||||
cache('admin_skin', $skin);
|
||||
config_set('admin_skin', $skin);
|
||||
Cache::clear();
|
||||
}else{
|
||||
cookie('admin_skin', $skin);
|
||||
}
|
||||
|
|
76
app/lib/CheckUtils.php
Normal file
76
app/lib/CheckUtils.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
class CheckUtils
|
||||
{
|
||||
public static function curl($url, $timeout)
|
||||
{
|
||||
$status = true;
|
||||
$errmsg = null;
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$status = false;
|
||||
$errmsg = curl_error($ch);
|
||||
}
|
||||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if($status && ($httpcode < 200 || $httpcode >= 400)){
|
||||
$status = false;
|
||||
$errmsg = 'http_code='.$httpcode;
|
||||
}
|
||||
$usetime = round(curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000);
|
||||
curl_close($ch);
|
||||
return ['status'=>$status, 'errmsg'=>$errmsg, 'usetime'=>$usetime];
|
||||
}
|
||||
|
||||
public static function tcp($target, $port, $timeout){
|
||||
if(!filter_var($target,FILTER_VALIDATE_IP) && checkDomain($target)){
|
||||
$target = gethostbyname($target);
|
||||
if(!$target)return ['status'=>false, 'error'=>'DNS resolve failed', 'usetime'=>0];
|
||||
}
|
||||
$starttime = getMillisecond();
|
||||
$fp = @fsockopen($target, $port, $errCode, $errStr, $timeout);
|
||||
if ($fp) {
|
||||
$status = true;
|
||||
fclose($fp);
|
||||
} else {
|
||||
$status = false;
|
||||
}
|
||||
$endtime = getMillisecond();
|
||||
$usetime = $endtime-$starttime;
|
||||
return ['status'=>$status, 'errmsg'=>$errStr, 'usetime'=>$usetime];
|
||||
}
|
||||
|
||||
public static function ping($target){
|
||||
if(!function_exists('exec'))return ['status'=>false, 'error'=>'exec函数不可用', 'usetime'=>0];
|
||||
if(!filter_var($target,FILTER_VALIDATE_IP) && checkDomain($target)){
|
||||
$target = gethostbyname($target);
|
||||
if(!$target)return ['status'=>false, 'error'=>'DNS resolve failed', 'usetime'=>0];
|
||||
}
|
||||
if(!filter_var($target,FILTER_VALIDATE_IP)){
|
||||
return ['status'=>false, 'error'=>'Invalid IP address', 'usetime'=>0];
|
||||
}
|
||||
$timeout = 1;
|
||||
exec('ping -c 1 -w '.$timeout.' '.$target.'', $output, $return_var);
|
||||
$usetime = !empty($output[1]) ? round(getSubstr($output[1], 'time=', ' ms')) : 0;
|
||||
$errmsg = null;
|
||||
if($return_var !== 0){
|
||||
$usetime = $usetime == 0 ? $timeout*1000 : $usetime;
|
||||
$errmsg = 'ping timeout';
|
||||
}
|
||||
return ['status'=>$return_var===0, 'errmsg'=>$errmsg, 'usetime'=>$usetime];
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ class DnsHelper
|
|||
'sk' => 'API密码'
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
],
|
||||
|
@ -99,4 +99,17 @@ class DnsHelper
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getModel2($config)
|
||||
{
|
||||
$dnstype = $config['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if(class_exists($class)){
|
||||
$config['domain'] = $config['name'];
|
||||
$config['domainid'] = $config['thirdid'];
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
105
app/lib/MsgNotice.php
Normal file
105
app/lib/MsgNotice.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
class MsgNotice
|
||||
{
|
||||
private static $sitename = '聚合DNS管理系统';
|
||||
|
||||
public static function send($action, $task, $result)
|
||||
{
|
||||
if($action == 1){
|
||||
$mail_title = 'DNS容灾切换-发生告警通知';
|
||||
$mail_content = '尊敬的系统管理员,您好:<br/>您的域名 <b>'.$task['domain'].'</b> 的 <b>'.$task['main_value'].'</b> 记录发生了异常';
|
||||
if($task['type'] == 2){
|
||||
$mail_content .= ',已自动切换为备用解析记录 '.$task['backup_value'].' ';
|
||||
}elseif($task['type'] == 1){
|
||||
$mail_content .= ',已自动暂停解析';
|
||||
}else{
|
||||
$mail_content .= ',请及时处理';
|
||||
}
|
||||
if(!empty($result['errmsg'])){
|
||||
$mail_content .= '。<br/>异常信息:'.$result['errmsg'];
|
||||
}
|
||||
}else{
|
||||
$mail_title = 'DNS容灾切换-恢复正常通知';
|
||||
$mail_content = '尊敬的系统管理员,您好:<br/>您的域名 <b>'.$task['domain'].'</b> 的 <b>'.$task['main_value'].'</b> 记录已恢复正常';
|
||||
if($task['type'] == 2){
|
||||
$mail_content .= ',已自动切换回当前解析记录';
|
||||
}elseif($task['type'] == 1){
|
||||
$mail_content .= ',已自动开启解析';
|
||||
}
|
||||
$lasttime = convert_second(time() - $task['switchtime']);
|
||||
$mail_content .= '。<br/>异常持续时间:'.$lasttime;
|
||||
}
|
||||
if(!empty($task['remark'])) $mail_title .= '('.$task['remark'].')';
|
||||
if(!empty($task['remark'])) $mail_content .= '<br/>备注:'.$task['remark'];
|
||||
$mail_content .= '<br/>'.self::$sitename.'<br/>'.date('Y-m-d H:i:s');
|
||||
|
||||
if(config_get('notice_mail') == 1){
|
||||
$mail_name = config_get('mail_recv')?config_get('mail_recv'):config_get('mail_name');
|
||||
self::send_mail($mail_name, $mail_title, $mail_content);
|
||||
}
|
||||
if(config_get('notice_wxtpl') == 1){
|
||||
$mail_content = str_replace(['<br/>', '<b>', '</b>'], ["\n\n", '**', '**'], $mail_content);
|
||||
self::send_wechat_tplmsg($mail_title, $mail_content);
|
||||
}
|
||||
}
|
||||
|
||||
public static function send_mail($to, $sub, $msg){
|
||||
$mail_type = config_get('mail_type');
|
||||
if($mail_type == 1){
|
||||
$mail = new \app\lib\mail\Sendcloud(config_get('mail_apiuser'), config_get('mail_apikey'));
|
||||
return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename);
|
||||
}elseif($mail_type == 2){
|
||||
$mail = new \app\lib\mail\Aliyun(config_get('mail_apiuser'), config_get('mail_apikey'));
|
||||
return $mail->send($to, $sub, $msg, config_get('mail_name'), self::$sitename);
|
||||
}else{
|
||||
$mail_name = config_get('mail_name');
|
||||
$mail_port = intval(config_get('mail_port'));
|
||||
$mail_smtp = config_get('mail_smtp');
|
||||
$mail_pwd = config_get('mail_pwd');
|
||||
if(!$mail_name || !$mail_port || !$mail_smtp || !$mail_pwd)return false;
|
||||
$mail = new \app\lib\mail\PHPMailer\PHPMailer(true);
|
||||
try{
|
||||
$mail->SMTPDebug = 0;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Timeout = 5;
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $mail_smtp;
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = $mail_name;
|
||||
$mail->Password = $mail_pwd;
|
||||
if($mail_port == 587) $mail->SMTPSecure = 'tls';
|
||||
else if($mail_port >= 465) $mail->SMTPSecure = 'ssl';
|
||||
else $mail->SMTPAutoTLS = false;
|
||||
$mail->Port = $mail_port;
|
||||
$mail->setFrom($mail_name, self::$sitename);
|
||||
$mail->addAddress($to);
|
||||
$mail->addReplyTo($mail_name, self::$sitename);
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $sub;
|
||||
$mail->Body = $msg;
|
||||
$mail->send();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $mail->ErrorInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function send_wechat_tplmsg($title, $content){
|
||||
$wechat_apptoken = config_get('wechat_apptoken');
|
||||
$wechat_appuid = config_get('wechat_appuid');
|
||||
if(!$wechat_apptoken||!$wechat_appuid)return false;
|
||||
$url = 'https://wxpusher.zjiecode.com/api/send/message';
|
||||
$post = ['appToken'=>$wechat_apptoken, 'content'=>$content, 'summary'=>$title, 'contentType'=>3, 'uids'=>[$wechat_appuid]];
|
||||
$result = get_curl($url, json_encode($post),0,0,0,0,0,['Content-Type: application/json; charset=UTF-8']);
|
||||
$arr = json_decode($result, true);
|
||||
if(isset($arr['success']) && $arr['success']==true){
|
||||
return true;
|
||||
}else{
|
||||
return $arr['msg'];
|
||||
}
|
||||
}
|
||||
}
|
17
app/lib/NewDb.php
Normal file
17
app/lib/NewDb.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
namespace app\lib;
|
||||
|
||||
use think\Facade;
|
||||
|
||||
class NewDb extends Facade
|
||||
{
|
||||
/**
|
||||
* 获取当前Facade对应类名(或者已经绑定的容器对象标识)
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return 'app\lib\NewDbManager';
|
||||
}
|
||||
}
|
25
app/lib/NewDbManager.php
Normal file
25
app/lib/NewDbManager.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use think\db\ConnectionInterface;
|
||||
|
||||
class NewDbManager extends \think\Db
|
||||
{
|
||||
/**
|
||||
* 创建数据库连接实例
|
||||
* @access protected
|
||||
* @param string|null $name 连接标识
|
||||
* @param bool $force 强制重新连接
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function instance(string $name = null, bool $force = false): ConnectionInterface
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getConfig('default', 'mysql');
|
||||
}
|
||||
|
||||
return $this->createConnection($name);
|
||||
}
|
||||
}
|
111
app/lib/TaskRunner.php
Normal file
111
app/lib/TaskRunner.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
namespace app\lib;
|
||||
|
||||
use app\lib\NewDb;
|
||||
use app\lib\CheckUtils;
|
||||
use app\lib\DnsHelper;
|
||||
use app\lib\MsgNotice;
|
||||
|
||||
class TaskRunner
|
||||
{
|
||||
private $conn;
|
||||
|
||||
private function db()
|
||||
{
|
||||
if(!$this->conn){
|
||||
$this->conn = NewDb::connect();
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
private function closeDb()
|
||||
{
|
||||
if($this->conn){
|
||||
$this->conn->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function execute($row)
|
||||
{
|
||||
if($row['checktype'] == 2){
|
||||
$result = CheckUtils::curl($row['checkurl'], $row['timeout']);
|
||||
}else if($row['checktype'] == 1){
|
||||
$result = CheckUtils::tcp($row['main_value'], $row['tcpport'], $row['timeout']);
|
||||
}else{
|
||||
$result = CheckUtils::ping($row['main_value']);
|
||||
}
|
||||
|
||||
$action = 0;
|
||||
if($result['status'] && $row['status']==1){
|
||||
if($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']){
|
||||
$this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>0, 'errcount'=>0, 'switchtime'=>time()]);
|
||||
$action = 2;
|
||||
}else{
|
||||
$this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update();
|
||||
}
|
||||
}elseif(!$result['status'] && $row['status']==0){
|
||||
if($row['cycle'] <= 1 || $row['errcount'] >= $row['cycle']){
|
||||
$this->db()->name('dmtask')->where('id', $row['id'])->update(['status'=>1, 'errcount'=>0, 'switchtime'=>time()]);
|
||||
$action = 1;
|
||||
}else{
|
||||
$this->db()->name('dmtask')->where('id', $row['id'])->inc('errcount')->update();
|
||||
}
|
||||
}elseif($row['errcount'] > 0){
|
||||
$this->db()->name('dmtask')->where('id', $row['id'])->update(['errcount'=>0]);
|
||||
}
|
||||
|
||||
if($action > 0){
|
||||
$drow = $this->db()->name('domain')->alias('A')->join('account B','A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find();
|
||||
if(!$drow){
|
||||
echo '域名不存在(ID:'.$row['did'].')'."\n";
|
||||
$this->closeDb();
|
||||
return;
|
||||
}
|
||||
$row['domain'] = $row['rr'] . '.' . $drow['name'];
|
||||
}
|
||||
if($action == 1){
|
||||
if($row['type'] == 2){
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$recordinfo = json_decode($row['recordinfo'], true);
|
||||
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['backup_value']), $row['backup_value'], $recordinfo['Line'], $recordinfo['TTL']);
|
||||
if(!$res){
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
}elseif($row['type'] == 1){
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$res = $dns->setDomainRecordStatus($row['recordid'], '0');
|
||||
if(!$res){
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '暂停解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
}
|
||||
}elseif($action == 2){
|
||||
if($row['type'] == 2){
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$recordinfo = json_decode($row['recordinfo'], true);
|
||||
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['main_value']), $row['main_value'], $recordinfo['Line'], $recordinfo['TTL']);
|
||||
if(!$res){
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
}elseif($row['type'] == 1){
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$res = $dns->setDomainRecordStatus($row['recordid'], '1');
|
||||
if(!$res){
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '启用解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->closeDb();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db()->name('dmlog')->insert([
|
||||
'taskid' => $row['id'],
|
||||
'action' => $action,
|
||||
'errmsg' => $result['status'] ? null : $result['errmsg'],
|
||||
'date' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
$this->closeDb();
|
||||
|
||||
MsgNotice::send($action, $row, $result);
|
||||
}
|
||||
}
|
|
@ -86,7 +86,7 @@ class aliyun implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$param = ['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $SubDomain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Type' => $Type, 'Line' => $Line];
|
||||
$param = ['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $SubDomain . '.' . $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Type' => $Type, 'Line' => $Line];
|
||||
$data = $this->request($param, true);
|
||||
if($data){
|
||||
$list = [];
|
||||
|
|
|
@ -80,11 +80,8 @@ class baidu implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '')$SubDomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
|
|
@ -57,10 +57,11 @@ class cloudflare implements DnsInterface {
|
|||
if($data){
|
||||
$list = [];
|
||||
foreach($data['result'] as $row){
|
||||
$name = $row['zone_name'] == $row['name'] ? '@' : str_replace('.'.$row['zone_name'], '', $row['name']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $row['zone_name'],
|
||||
'Name' => str_replace('.'.$row['zone_name'], '', $row['name']),
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['content'],
|
||||
'Line' => $row['proxied'] ? '1' : '0',
|
||||
|
@ -79,17 +80,16 @@ class cloudflare implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '@')$SubDomain=$this->domain;
|
||||
else $SubDomain .= '.'.$this->domain;
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId){
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
if($data){
|
||||
$name = $data['result']['zone_name'] == $data['result']['name'] ? '@' : str_replace('.'.$data['result']['zone_name'], '', $data['result']['name']);
|
||||
return [
|
||||
'RecordId' => $data['result']['id'],
|
||||
'Domain' => $data['result']['zone_name'],
|
||||
|
|
|
@ -89,11 +89,8 @@ class dnsla implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '')$SubDomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
|
|
@ -90,11 +90,8 @@ class dnspod implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '')$SubDomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
|
|
@ -7,8 +7,6 @@ class huawei implements DnsInterface {
|
|||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "dns.myhuaweicloud.com";
|
||||
private $service = "dnspod";
|
||||
private $version = "2021-03-23";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
|
@ -55,7 +53,7 @@ class huawei implements DnsInterface {
|
|||
$offset = ($PageNumber-1)*$PageSize;
|
||||
$query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'status' => $Status, 'offset' => $offset, 'limit' => $PageSize];
|
||||
if(!isNullOrEmpty(($SubDomain))){
|
||||
$param['name'] = $SubDomain;
|
||||
$query['name'] = $SubDomain;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query);
|
||||
if($data){
|
||||
|
@ -84,11 +82,8 @@ class huawei implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '')$SubDomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
|
|
@ -3,6 +3,9 @@ namespace app\lib\dns;
|
|||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
/**
|
||||
* @see http://apipost.west.cn/
|
||||
*/
|
||||
class west implements DnsInterface {
|
||||
private $username;
|
||||
private $api_password;
|
||||
|
@ -30,8 +33,8 @@ class west implements DnsInterface {
|
|||
|
||||
//获取域名列表
|
||||
public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
|
||||
$param = ['page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord];
|
||||
$data = $this->execute('/domain/?act=getdomains', $param);
|
||||
$param = ['act' => 'getdomains', 'page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if($data){
|
||||
$list = [];
|
||||
foreach($data['items'] as $row){
|
||||
|
@ -48,23 +51,23 @@ class west implements DnsInterface {
|
|||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
|
||||
$param = ['act' => 'dnsrec.list', 'domain' => $this->domain, 'record_type' => $Type, 'record_line' => $Line, 'hostname' => $KeyWord, 'pageno' => $PageNumber, 'limit' => $PageSize];
|
||||
$param = ['act' => 'getdnsrecord', 'domain' => $this->domain, 'type' => $Type, 'line' => $Line, 'host' => $KeyWord, 'pageno' => $PageNumber, 'limit' => $PageSize];
|
||||
if(!isNullOrEmpty(($SubDomain))){
|
||||
$param['hostname'] = $SubDomain;
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
$data = $this->execute2('/domain/dns/', $param);
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if($data){
|
||||
$list = [];
|
||||
foreach($data['items'] as $row){
|
||||
$list[] = [
|
||||
'RecordId' => $row['record_id'],
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['hostname'],
|
||||
'Type' => $row['record_type'],
|
||||
'Value' => $row['record_value'],
|
||||
'Line' => $row['record_line'],
|
||||
'TTL' => $row['record_ttl'],
|
||||
'MX' => $row['record_mx'],
|
||||
'Name' => $row['item'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => $row['level'],
|
||||
'Status' => $row['pause'] == 1 ? '0' : '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
|
@ -78,11 +81,8 @@ class west implements DnsInterface {
|
|||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
|
||||
$domain_arr = explode('.', $SubDomain);
|
||||
$domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
|
||||
$subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
|
||||
if($subdomain == '')$subdomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
|
||||
if($SubDomain == '')$SubDomain='@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
@ -92,15 +92,15 @@ class west implements DnsInterface {
|
|||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
|
||||
$param = ['act' => 'dnsrec.add', 'domain' => $this->domain, 'hostname' => $Name, 'record_type' => $this->convertType($Type), 'record_value' => $Value, 'record_level' => $MX, 'record_ttl' => intval($TTL), 'record_line' => $Line];
|
||||
$data = $this->execute2('/domain/dns/', $param);
|
||||
return is_array($data) ? $data['record_id'] : false;
|
||||
$param = ['act' => 'adddnsrecord', 'domain' => $this->domain, 'host' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
|
||||
$param = ['act' => 'dnsrec.modify', 'domain' => $this->domain, 'record_id' => $RecordId, 'record_type' => $this->convertType($Type), 'record_value' => $Value, 'record_level' => $MX, 'record_ttl' => intval($TTL), 'record_line' => $Line];
|
||||
$data = $this->execute2('/domain/dns/', $param);
|
||||
$param = ['act' => 'moddnsrecord', 'domain' => $this->domain, 'id' => $RecordId, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
|
@ -111,14 +111,16 @@ class west implements DnsInterface {
|
|||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId){
|
||||
$param = ['act' => 'dnsrec.remove', 'domain' => $this->domain, 'record_id' => $RecordId];
|
||||
$data = $this->execute2('/domain/dns/', $param);
|
||||
$param = ['act' => 'deldnsrecord', 'domain' => $this->domain, 'id' => $RecordId];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status){
|
||||
return false;
|
||||
$param = ['act' => 'pause', 'domain' => $this->domain, 'id' => $RecordId, 'val' => $Status == '1' ? '0' : '1'];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
|
@ -162,26 +164,7 @@ class west implements DnsInterface {
|
|||
$arr=json_decode($response,true);
|
||||
if($arr){
|
||||
if($arr['result'] == 200){
|
||||
return $arr['data'];
|
||||
}else{
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function execute2($path, $params){
|
||||
$params['username'] = $this->username;
|
||||
$params['apikey'] = md5($this->api_password);
|
||||
$response = $this->curl($path, $params);
|
||||
$response = mb_convert_encoding($response, 'UTF-8', 'GBK');
|
||||
$arr=json_decode($response,true);
|
||||
if($arr){
|
||||
if($arr['code'] == 200){
|
||||
return $arr['body'];
|
||||
return isset($arr['data']) ? $arr['data'] : [];
|
||||
}else{
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
|
|
72
app/lib/mail/Aliyun.php
Normal file
72
app/lib/mail/Aliyun.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
namespace app\lib\mail;
|
||||
|
||||
class Aliyun
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
|
||||
function __construct($AccessKeyId, $AccessKeySecret)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
}
|
||||
private function aliyunSignature($parameters, $accessKeySecret, $method)
|
||||
{
|
||||
ksort($parameters);
|
||||
$canonicalizedQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
if($value === null) continue;
|
||||
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
|
||||
}
|
||||
$stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
|
||||
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
|
||||
|
||||
return $signature;
|
||||
}
|
||||
private function percentEncode($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
public function send($to, $sub, $msg, $from, $from_name)
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false;
|
||||
$url = 'https://dm.aliyuncs.com/';
|
||||
$data = array(
|
||||
'Action' => 'SingleSendMail',
|
||||
'AccountName' => $from,
|
||||
'ReplyToAddress' => 'false',
|
||||
'AddressType' => 1,
|
||||
'ToAddress' => $to,
|
||||
'FromAlias' => $from_name,
|
||||
'Subject' => $sub,
|
||||
'HtmlBody' => $msg,
|
||||
'Format' => 'JSON',
|
||||
'Version' => '2015-11-23',
|
||||
'AccessKeyId' => $this->AccessKeyId,
|
||||
'SignatureMethod' => 'HMAC-SHA1',
|
||||
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'SignatureVersion' => '1.0',
|
||||
'SignatureNonce' => random(8)
|
||||
);
|
||||
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||
$json = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
$arr = json_decode($json, true);
|
||||
if ($httpCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
return $arr['Message'];
|
||||
}
|
||||
}
|
||||
}
|
2
app/lib/mail/PHPMailer/Exception.php
Normal file
2
app/lib/mail/PHPMailer/Exception.php
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
namespace app\lib\mail\PHPMailer; class Exception extends \Exception { public function errorMessage() { return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n"; } }
|
2
app/lib/mail/PHPMailer/PHPMailer.php
Normal file
2
app/lib/mail/PHPMailer/PHPMailer.php
Normal file
File diff suppressed because one or more lines are too long
2
app/lib/mail/PHPMailer/SMTP.php
Normal file
2
app/lib/mail/PHPMailer/SMTP.php
Normal file
File diff suppressed because one or more lines are too long
37
app/lib/mail/Sendcloud.php
Normal file
37
app/lib/mail/Sendcloud.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
namespace app\lib\mail;
|
||||
|
||||
class Sendcloud {
|
||||
private $apiUser;
|
||||
private $apiKey;
|
||||
|
||||
function __construct($apiUser, $apiKey){
|
||||
$this->apiUser = $apiUser;
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
public function send($to, $sub, $msg, $from, $from_name){
|
||||
if(empty($this->apiUser)||empty($this->apiKey))return false;
|
||||
$url='http://api.sendcloud.net/apiv2/mail/send';
|
||||
$data=array(
|
||||
'apiUser' => $this->apiUser,
|
||||
'apiKey' => $this->apiKey,
|
||||
'from' => $from,
|
||||
'fromName' => $from_name,
|
||||
'to' => $to,
|
||||
'subject' => $sub,
|
||||
'html' => $msg);
|
||||
$ch=curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
$json=curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$arr=json_decode($json,true);
|
||||
if($arr['statusCode']==200){
|
||||
return true;
|
||||
}else{
|
||||
return implode("\n",$arr['message']);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ declare (strict_types = 1);
|
|||
|
||||
namespace app\middleware;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
|
||||
|
@ -27,7 +28,15 @@ class LoadConfig
|
|||
return $next($request);
|
||||
}
|
||||
}
|
||||
Config::set([], 'sys');
|
||||
|
||||
try{
|
||||
$res = Db::name('config')->cache('configs',0)->column('value','key');
|
||||
Config::set($res, 'sys');
|
||||
}catch(Exception $e){
|
||||
if(!strpos($e->getMessage(), 'doesn\'t exist')){
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$request->isApi = false;
|
||||
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
DROP TABLE IF EXISTS `dnsmgr_config`;
|
||||
CREATE TABLE `dnsmgr_config` (
|
||||
`key` varchar(32) NOT NULL,
|
||||
`value` TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT INTO `dnsmgr_config` VALUES ('version', '1003');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_mail', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_wxtpl', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('mail_smtp', 'smtp.qq.com');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('mail_port', '465');
|
||||
|
||||
DROP TABLE IF EXISTS `dnsmgr_account`;
|
||||
CREATE TABLE `dnsmgr_account` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
|
@ -61,3 +74,43 @@ CREATE TABLE `dnsmgr_log` (
|
|||
KEY `uid` (`uid`),
|
||||
KEY `domain` (`domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `dnsmgr_dmtask`;
|
||||
CREATE TABLE `dnsmgr_dmtask` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`did` int(11) unsigned NOT NULL,
|
||||
`rr` varchar(128) NOT NULL,
|
||||
`recordid` varchar(60) NOT NULL,
|
||||
`type` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`main_value` varchar(128) DEFAULT NULL,
|
||||
`backup_value` varchar(128) DEFAULT NULL,
|
||||
`checktype` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`checkurl` varchar(512) DEFAULT NULL,
|
||||
`tcpport` tinyint(5) DEFAULT NULL,
|
||||
`frequency` tinyint(5) NOT NULL,
|
||||
`cycle` tinyint(5) NOT NULL DEFAULT 3,
|
||||
`timeout` tinyint(5) NOT NULL DEFAULT 2,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
`addtime` int(11) NOT NULL DEFAULT 0,
|
||||
`checktime` int(11) NOT NULL DEFAULT 0,
|
||||
`checknexttime` int(11) NOT NULL DEFAULT 0,
|
||||
`switchtime` int(11) NOT NULL DEFAULT 0,
|
||||
`errcount` tinyint(5) NOT NULL DEFAULT 0,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`active` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`recordinfo` varchar(200) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `dnsmgr_dmlog`;
|
||||
CREATE TABLE `dnsmgr_dmlog` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`taskid` int(11) unsigned NOT NULL,
|
||||
`action` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`errmsg` varchar(100) DEFAULT NULL,
|
||||
`date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `taskid` (`taskid`),
|
||||
KEY `date` (`date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
43
app/sql/update.sql
Normal file
43
app/sql/update.sql
Normal file
|
@ -0,0 +1,43 @@
|
|||
CREATE TABLE IF NOT EXISTS `dnsmgr_config` (
|
||||
`key` varchar(32) NOT NULL,
|
||||
`value` TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dnsmgr_dmtask` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`did` int(11) unsigned NOT NULL,
|
||||
`rr` varchar(128) NOT NULL,
|
||||
`recordid` varchar(60) NOT NULL,
|
||||
`type` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`main_value` varchar(128) DEFAULT NULL,
|
||||
`backup_value` varchar(128) DEFAULT NULL,
|
||||
`checktype` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`checkurl` varchar(512) DEFAULT NULL,
|
||||
`tcpport` tinyint(5) DEFAULT NULL,
|
||||
`frequency` tinyint(5) NOT NULL,
|
||||
`cycle` tinyint(5) NOT NULL DEFAULT 3,
|
||||
`timeout` tinyint(5) NOT NULL DEFAULT 2,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
`addtime` int(11) NOT NULL DEFAULT 0,
|
||||
`checktime` int(11) NOT NULL DEFAULT 0,
|
||||
`checknexttime` int(11) NOT NULL DEFAULT 0,
|
||||
`switchtime` int(11) NOT NULL DEFAULT 0,
|
||||
`errcount` tinyint(5) NOT NULL DEFAULT 0,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`active` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`recordinfo` varchar(200) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dnsmgr_dmlog` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`taskid` int(11) unsigned NOT NULL,
|
||||
`action` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`errmsg` varchar(100) DEFAULT NULL,
|
||||
`date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `taskid` (`taskid`),
|
||||
KEY `date` (`date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
@ -106,7 +106,22 @@
|
|||
<li class="{:checkIfActive('domain,record,record_log')}">
|
||||
<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')}">
|
||||
{if request()->user['level'] eq 2}
|
||||
<li class="treeview {:checkIfActive('overview,task,noticeset,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><a href="/dmonitor/overview"><i class="fa fa-circle-o"></i> 运行概览</a></li>
|
||||
<li><a href="/dmonitor/task"><i class="fa fa-circle-o"></i> 切换策略</a></li>
|
||||
<li><a href="/dmonitor/noticeset"><i class="fa fa-circle-o"></i> 通知设置</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="{:checkIfActive('account')}">
|
||||
<a href="/account"><i class="fa fa-lock fa-fw"></i> <span>域名账户</span></a>
|
||||
</li>
|
||||
<li class="{:checkIfActive('user')}">
|
||||
|
|
170
app/view/dmonitor/noticeset.html
Normal file
170
app/view/dmonitor/noticeset.html
Normal file
|
@ -0,0 +1,170 @@
|
|||
{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="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-3 control-label">微信公众号通知</label>
|
||||
<div class="col-sm-9"><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">
|
||||
<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="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>
|
||||
</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='mail_type']").change();
|
||||
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 mailtest(){
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'GET',
|
||||
url : '/dmonitor/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('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
155
app/view/dmonitor/overview.html
Normal file
155
app/view/dmonitor/overview.html
Normal file
|
@ -0,0 +1,155 @@
|
|||
{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">×</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="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="/dmonitor/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、也可以使用进程守护管理器,添加守护进程,运行目录:{:app()->getRootPath()},启动命令:php think dmtask</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
{block name="script"}
|
||||
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
|
||||
<script>
|
||||
function clean(){
|
||||
$('#modal-clean').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});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
169
app/view/dmonitor/task.html
Normal file
169
app/view/dmonitor/task.html
Normal file
|
@ -0,0 +1,169 @@
|
|||
{extend name="common/layout" /}
|
||||
{block name="title"}容灾切换策略{/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="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>
|
||||
<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>
|
||||
</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.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="{$cdnpublic}bootstrap-table/1.20.2/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: 'id',
|
||||
title: 'ID'
|
||||
},
|
||||
{
|
||||
field: 'rr',
|
||||
title: '域名',
|
||||
formatter: function(value, row, index) {
|
||||
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right" title="Tooltip on 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 {
|
||||
return '无操作';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'frequency',
|
||||
title: '检测间隔',
|
||||
formatter: function(value, row, index) {
|
||||
var checktype = 'PING';
|
||||
if(row.checktype == 2){
|
||||
checktype = row.checkurl;
|
||||
}else if(row.checktype == 1){
|
||||
checktype = 'TCP('+row.tcpport+'端口)';
|
||||
}
|
||||
return '<span title="" data-toggle="tooltip" data-placement="bottom" data-original-title="'+checktype+'" class="tips">' + value + '秒</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
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) {
|
||||
var html = '';
|
||||
if(value == 1) {
|
||||
html += '<span class="btn btn-success btn-xs" onclick="setActive('+row.id+', 0)">开启</span>';
|
||||
} else {
|
||||
html += '<span class="btn btn-warning btn-xs" onclick="setActive('+row.id+', 1)">暂停</span>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'checktime',
|
||||
title: '上次检测时间',
|
||||
formatter: function(value, row, index) {
|
||||
return value > 0 ? row.checktimestr : '未运行';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: '',
|
||||
title: '操作',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '<a href="/dmonitor/task/info/'+row.id+'" class="btn btn-info btn-xs">切换日志</a> ';
|
||||
html += '<a href="/dmonitor/task/edit?id='+row.id+'" class="btn btn-primary btn-xs">修改</a> ';
|
||||
html += '<a href="/record/'+row.did+'?keyword='+row.rr+'" class="btn btn-default btn-xs" target="_blank">解析</a> ';
|
||||
html += '<a href="javascript:delItem(\''+row.id+'\')" class="btn btn-danger btn-xs">删除</a> ';
|
||||
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');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
242
app/view/dmonitor/taskform.html
Normal file
242
app/view/dmonitor/taskform.html
Normal file
|
@ -0,0 +1,242 @@
|
|||
{extend name="common/layout" /}
|
||||
{block name="title"}容灾切换策略{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
.dselect::before{
|
||||
content: '.';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
</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">域名选择</label>
|
||||
<div class="col-sm-3 col-xs-5"><input type="text" name="rr" v-model="set.rr" placeholder="主机记录" class="form-control" required></div>
|
||||
<div class="col-sm-3 col-xs-7 dselect"><select name="did" v-model="set.did" class="form-control" required>
|
||||
<option value="">--主域名--</option>
|
||||
{foreach $domains as $k=>$v}
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/foreach}
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">解析记录</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">切换设置</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">备用解析记录</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" name="backup_value" v-model="set.backup_value" placeholder="支持填写IPv4或CNAME地址" class="form-control" required>
|
||||
</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" 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.checktype==1">
|
||||
<label class="col-sm-3 control-label no-padding-right">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.checktype==2">
|
||||
<label class="col-sm-3 control-label no-padding-right">检测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.checktype>0">
|
||||
<label class="col-sm-3 control-label no-padding-right">最大超时时间</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">
|
||||
<label class="col-sm-3 control-label no-padding-right">检测间隔</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">
|
||||
<label class="col-sm-3 control-label no-padding-right">确认次数</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.6.14/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 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: 2,
|
||||
tcpport: 80,
|
||||
checkurl: '',
|
||||
frequency: 5,
|
||||
timeout: 2,
|
||||
cycle: 3,
|
||||
},
|
||||
recordList: [],
|
||||
typeList: [
|
||||
{value:0, label:'无操作'},
|
||||
{value:1, label:'暂停解析'},
|
||||
{value:2, 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;
|
||||
}
|
||||
}
|
||||
},
|
||||
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',
|
||||
});
|
||||
},
|
||||
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(){
|
||||
window.history.back();
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{/block}
|
73
app/view/dmonitor/taskinfo.html
Normal file
73
app/view/dmonitor/taskinfo.html
Normal file
|
@ -0,0 +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>
|
||||
24H告警次数:<strong>{$info.fail_count}</strong> 切换次数:<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.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="{$cdnpublic}bootstrap-table/1.20.2/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>
|
||||
{/block}
|
|
@ -28,7 +28,8 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.2",
|
||||
"topthink/think-trace":"^1.0"
|
||||
"topthink/think-trace":"^1.0",
|
||||
"swoole/ide-helper": "^5.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -30,5 +30,7 @@ return [
|
|||
// 显示错误信息
|
||||
'show_error_msg' => false,
|
||||
|
||||
'version' => '1001',
|
||||
'version' => '1003',
|
||||
|
||||
'dbversion' => '1003'
|
||||
];
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
return [
|
||||
// 指令定义
|
||||
'commands' => [
|
||||
'dmtask' => 'app\command\Dmtask',
|
||||
],
|
||||
];
|
||||
|
|
|
@ -51,7 +51,7 @@ return [
|
|||
// 是否严格检查字段是否存在
|
||||
'fields_strict' => true,
|
||||
// 是否需要断线重连
|
||||
'break_reconnect' => false,
|
||||
'break_reconnect' => true,
|
||||
// 监听SQL
|
||||
'trigger_sql' => env('app_debug', true),
|
||||
// 开启字段缓存
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
// [ 应用入口文件 ]
|
||||
namespace think;
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.4.0', '<')) {
|
||||
die('require PHP >= 7.4 !');
|
||||
}
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 执行HTTP应用并响应
|
||||
|
|
|
@ -23,6 +23,7 @@ Route::any('/login', 'auth/login')->middleware(\think\middleware\SessionInit::cl
|
|||
->middleware(\app\middleware\ViewOutput::class);
|
||||
Route::get('/logout', 'auth/logout');
|
||||
Route::any('/quicklogin', 'auth/quicklogin');
|
||||
Route::any('/dmtask/status', 'dmonitor/status');
|
||||
|
||||
Route::group(function () {
|
||||
Route::any('/', 'index/index');
|
||||
|
@ -54,8 +55,19 @@ Route::group(function () {
|
|||
Route::post('/record/remark/:id', 'domain/record_remark');
|
||||
Route::post('/record/batch/:id', 'domain/record_batch');
|
||||
Route::any('/record/log/:id', 'domain/record_log');
|
||||
Route::post('/record/list', 'domain/record_list');
|
||||
Route::get('/record/:id', 'domain/record');
|
||||
|
||||
Route::get('/dmonitor/overview', 'dmonitor/overview');
|
||||
Route::post('/dmonitor/task/data', 'dmonitor/task_data');
|
||||
Route::post('/dmonitor/task/log/data/:id', 'dmonitor/tasklog_data');
|
||||
Route::get('/dmonitor/task/info/:id', 'dmonitor/taskinfo');
|
||||
Route::any('/dmonitor/task/:action', 'dmonitor/taskform');
|
||||
Route::get('/dmonitor/task', 'dmonitor/task');
|
||||
Route::any('/dmonitor/noticeset', 'dmonitor/noticeset');
|
||||
Route::get('/dmonitor/mailtest', 'dmonitor/mailtest');
|
||||
Route::post('/dmonitor/clean', 'dmonitor/clean');
|
||||
|
||||
})->middleware(\app\middleware\CheckLogin::class)
|
||||
->middleware(\app\middleware\ViewOutput::class);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user