优化自建DNS交互

This commit is contained in:
刘祥超
2021-08-10 10:47:12 +08:00
parent 4d092f329b
commit 6da4949c98
17 changed files with 686 additions and 147 deletions

View File

@@ -1,5 +1,5 @@
<first-menu>
<menu-item href="/ns">所有域名</menu-item>
<menu-item href="/ns/domains">所有域名</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/ns/domains/domain?domainId=' + domain.id" code="index">详情<span class="grey small">{{domain.name}}</span></menu-item>
<menu-item :href="'/ns/domains/records?domainId=' + domain.id" code="record">记录({{domain.countRecords}})</menu-item>

View File

@@ -1,5 +1,5 @@
<first-menu>
<menu-item href="/ns" code="index">域名列表</menu-item>
<menu-item href="/ns/domains" code="index">域名列表</menu-item>
<menu-item href="/ns/domains/create" code="create">添加域名</menu-item>
</first-menu>
<div class="margin"></div>

View File

@@ -1,6 +1,5 @@
{$layout}
{$template "../menu"}
{$template "menu"}
<div v-if="countClusters == 0">
<not-found-box>

View File

@@ -1,3 +1,3 @@
Tea.context(function () {
this.success = NotifySuccess("保存成功", "/ns")
this.success = NotifySuccess("保存成功", "/ns/domains")
})

View File

@@ -0,0 +1,61 @@
{$layout}
{$template "menu"}
<div v-if="countClusters == 0">
<not-found-box>
暂时还没有集群,请先 <a href="/ns/clusters/create">创建集群</a>
</not-found-box>
</div>
<div v-if="countClusters > 0">
<form class="ui form" method="get" action="/ns/domains">
<div class="ui fields inline">
<div class="ui field">
<ns-cluster-selector :v-cluster-id="clusterId"></ns-cluster-selector>
</div>
<div class="ui field">
<ns-user-selector :v-user-id="userId"></ns-user-selector>
</div>
<div class="ui field">
<input type="text" name="keyword" v-model="keyword" placeholder="域名..."/>
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
</div>
</div>
</form>
<div v-if="domains.length == 0">
<div class="margin"></div>
<p class="comment">暂时还没有域名。</p>
</div>
<!-- 域名列表 -->
<table class="ui table selectable celled" v-if="domains.length > 0">
<thead>
<tr>
<th>域名</th>
<th>集群</th>
<th>用户</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="domain in domains">
<td><keyword :v-word="keyword">{{domain.name}}</keyword></td>
<td>
{{domain.cluster.name}}<link-icon :href="'/ns/clusters/cluster?clusterId=' + domain.cluster.id"></link-icon>
</td>
<td>
<span v-if="domain.user != null">
{{domain.user.fullname}} ({{domain.user.username}})
</span>
<span v-else class="disabled">-</span>
</td>
<td><label-on :v-is-on="domain.isOn"></label-on></td>
<td>
<a :href="'/ns/domains/domain?domainId=' + domain.id">详情</a> &nbsp; <a href="" @click.prevent="deleteDomain(domain.id)">删除</a>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,14 @@
Tea.context(function () {
this.deleteDomain = function (domainId) {
let that = this
teaweb.confirm("确定要删除此域名吗?", function () {
that.$post("/ns/domains/delete")
.params({
domainId: domainId
})
.success(function () {
teaweb.reload()
})
})
}
})

View File

@@ -0,0 +1,33 @@
.ui.message .icon {
position: absolute;
right: 1em;
top: 1.8em;
}
.grid {
margin-top: 2em !important;
margin-left: 2em !important;
}
.grid .column {
margin-bottom: 2em;
border-right: 1px #eee solid;
}
.grid .column div.value {
margin-top: 1.5em;
}
.grid .column div.value span {
font-size: 2em;
margin-right: 0.2em;
}
.grid .column.no-border {
border-right: 0;
}
.grid h4 a {
display: none;
}
.grid .column:hover a {
display: inline;
}
.chart-box {
height: 20em;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG,QACF;EACC,kBAAA;EACA,UAAA;EACA,UAAA;;AAIF;EACC,0BAAA;EACA,2BAAA;;AAFD,KAIC;EACC,kBAAA;EACA,4BAAA;;AANF,KAIC,QAIC,IAAG;EACF,iBAAA;;AATH,KAIC,QAIC,IAAG,MAGF;EACC,cAAA;EACA,mBAAA;;AAbJ,KAkBC,QAAO;EACN,eAAA;;AAnBF,KAsBC,GACC;EACC,aAAA;;AAxBH,KA4BC,QAAO,MACN;EACC,eAAA;;AAKH;EACC,YAAA","file":"index.css"}

View File

@@ -1,61 +1,60 @@
{$layout}
{$template "menu"}
{$template "/echarts"}
<div v-if="countClusters == 0">
<not-found-box>
暂时还没有集群,请先 <a href="/ns/clusters/create">创建集群</a>
</not-found-box>
<div class="ui four columns grid">
<div class="ui column">
<h4>域名<link-icon href="/ns/domains"></link-icon></h4>
<div class="value"><span>{{board.countDomains}}</span></div>
</div>
<div class="ui column">
<h4>记录<link-icon href="/ns/domains"></link-icon></h4>
<div class="value"><span>{{board.countRecords}}</span></div>
</div>
<div class="ui column">
<h4>集群<link-icon href="/ns/clusters"></link-icon></h4>
<div class="value"><span>{{board.countClusters}}</span></div>
</div>
<div class="ui column no-border">
<h4>节点<link-icon href="/ns/clusters"></link-icon></h4>
<div class="value"><span>{{board.countNodes}}</span>
<span v-if="board.countOfflineNodes > 0" class="red" style="font-size: 1em">{{board.countOfflineNodes}}离线</span>
<span v-else style="font-size: 1em"></span>
</div>
</div>
</div>
<div v-if="countClusters > 0">
<form class="ui form" method="get" action="/ns">
<div class="ui fields inline">
<div class="ui field">
<ns-cluster-selector :v-cluster-id="clusterId"></ns-cluster-selector>
</div>
<div class="ui field">
<ns-user-selector :v-user-id="userId"></ns-user-selector>
</div>
<div class="ui field">
<input type="text" name="keyword" v-model="keyword" placeholder="域名..."/>
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
</div>
</div>
</form>
<!-- 流量统计 -->
<div class="ui menu tabular">
<a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时流量趋势</a>
<a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天流量趋势</a>
</div>
<div v-if="domains.length == 0">
<div class="margin"></div>
<p class="comment">暂时还没有域名。</p>
</div>
<!-- 域名列表 -->
<table class="ui table selectable celled" v-if="domains.length > 0">
<thead>
<tr>
<th>域名</th>
<th>集群</th>
<th>用户</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="domain in domains">
<td><keyword :v-word="keyword">{{domain.name}}</keyword></td>
<td>
{{domain.cluster.name}}<link-icon :href="'/ns/clusters/cluster?clusterId=' + domain.cluster.id"></link-icon>
</td>
<td>
<span v-if="domain.user != null">
{{domain.user.fullname}} ({{domain.user.username}})
</span>
<span v-else class="disabled">-</span>
</td>
<td><label-on :v-is-on="domain.isOn"></label-on></td>
<td>
<a :href="'/ns/domains/domain?domainId=' + domain.id">详情</a> &nbsp; <a href="" @click.prevent="deleteDomain(domain.id)">删除</a>
</td>
</tr>
</table>
</div>
<!-- 按小时统计流量 -->
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
<!-- 按日统计流量 -->
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
<div class="ui divider"></div>
<!-- 域名排行 -->
<h4>域名访问排行 <span>24小时</span></h4>
<div class="chart-box" id="top-domains-chart"></div>
<div class="ui divider"></div>
<!-- 节点排行 -->
<h4>节点访问排行 <span>24小时</span></h4>
<div class="chart-box" id="top-nodes-chart"></div>
<div class="ui divider"></div>
<!-- 系统信息 -->
<div class="ui divider"></div>
<div class="ui menu tabular">
<a href="" class="item" :class="{active: nodeStatusTab == 'cpu'}" @click.prevent="selectNodeStatusTab('cpu')">DNS节点CPU</a>
<a href="" class="item" :class="{active: nodeStatusTab == 'memory'}" @click.prevent="selectNodeStatusTab('memory')">DNS节点内存</a>
<a href="" class="item" :class="{active: nodeStatusTab == 'load'}" @click.prevent="selectNodeStatusTab('load')">DNS节点负载</a>
</div>
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>

View File

@@ -1,14 +1,246 @@
Tea.context(function () {
this.deleteDomain = function (domainId) {
this.$delay(function () {
this.reloadHourlyTrafficChart()
this.reloadTopDomainsChart()
this.reloadTopNodesChart()
this.reloadCPUChart()
})
/**
* 流量统计
*/
this.trafficTab = "hourly"
this.selectTrafficTab = function (tab) {
this.trafficTab = tab
if (tab == "hourly") {
this.$delay(function () {
this.reloadHourlyTrafficChart()
})
} else if (tab == "daily") {
this.$delay(function () {
this.reloadDailyTrafficChart()
})
}
}
this.reloadHourlyTrafficChart = function () {
let stats = this.hourlyStats
this.reloadTrafficChart("hourly-traffic-chart", "流量统计", stats, function (args) {
return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes)
})
}
this.reloadDailyTrafficChart = function () {
let stats = this.dailyStats
this.reloadTrafficChart("daily-traffic-chart", "流量统计", stats, function (args) {
return stats[args.dataIndex].day + " 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes)
})
}
this.reloadTrafficChart = function (chartId, name, stats, tooltipFunc) {
let chartBox = document.getElementById(chartId)
if (chartBox == null) {
return
}
let axis = teaweb.bytesAxis(stats, function (v) {
return v.bytes
})
let chart = teaweb.initChart(chartBox)
let option = {
xAxis: {
data: stats.map(function (v) {
if (v.hour != null) {
return v.hour
}
return v.day
})
},
yAxis: {
axisLabel: {
formatter: function (value) {
return value + axis.unit
}
}
},
tooltip: {
show: true,
trigger: "item",
formatter: tooltipFunc
},
grid: {
left: 50,
top: 40,
right: 20,
bottom: 20
},
series: [
{
name: "流量",
type: "line",
data: stats.map(function (v) {
return v.bytes / axis.divider
}),
itemStyle: {
color: "#9DD3E8"
},
areaStyle: {
color: "#9DD3E8"
},
smooth: true
}
],
animation: true
}
chart.setOption(option)
chart.resize()
}
// 域名排行
this.reloadTopDomainsChart = function () {
let that = this
teaweb.confirm("确定要删除此域名吗?", function () {
that.$post("/ns/domains/delete")
.params({
domainId: domainId
})
.success(function () {
teaweb.reload()
})
let axis = teaweb.countAxis(this.topDomainStats, function (v) {
return v.countRequests
})
teaweb.renderBarChart({
id: "top-domains-chart",
name: "域名",
values: this.topDomainStats,
x: function (v) {
return v.domainName
},
tooltip: function (args, stats) {
return stats[args.dataIndex].domainName + "<br/>请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "<br/>流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes)
},
value: function (v) {
return v.countRequests / axis.divider;
},
axis: axis,
click: function (args, stats) {
window.location = "/ns/domains/domain?domainId=" + stats[args.dataIndex].domainId
}
})
}
// 节点排行
this.reloadTopNodesChart = function () {
let that = this
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
return v.countRequests
})
teaweb.renderBarChart({
id: "top-nodes-chart",
name: "节点",
values: this.topNodeStats,
x: function (v) {
return v.nodeName
},
tooltip: function (args, stats) {
return stats[args.dataIndex].nodeName + "<br/>请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "<br/>流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes)
},
value: function (v) {
return v.countRequests / axis.divider;
},
axis: axis,
click: function (args, stats) {
window.location = "/ns/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + stats[args.dataIndex].clusterId
}
})
}
/**
* 系统信息
*/
this.nodeStatusTab = "cpu"
this.selectNodeStatusTab = function (tab) {
this.nodeStatusTab = tab
this.$delay(function () {
switch (tab) {
case "cpu":
this.reloadCPUChart()
break
case "memory":
this.reloadMemoryChart()
break
case "load":
this.reloadLoadChart()
break
}
})
}
this.reloadCPUChart = function () {
let axis = {unit: "%", divider: 1}
teaweb.renderLineChart({
id: "cpu-chart",
name: "CPU",
values: this.cpuValues,
x: function (v) {
return v.time
},
tooltip: function (args, stats) {
return stats[args.dataIndex].time + "" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%"
},
value: function (v) {
return v.value * 100;
},
axis: axis,
max: 100
})
}
this.reloadMemoryChart = function () {
let axis = {unit: "%", divider: 1}
teaweb.renderLineChart({
id: "memory-chart",
name: "内存",
values: this.memoryValues,
x: function (v) {
return v.time
},
tooltip: function (args, stats) {
return stats[args.dataIndex].time + "" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%"
},
value: function (v) {
return v.value * 100;
},
axis: axis,
max: 100
})
}
this.reloadLoadChart = function () {
let axis = {unit: "", divider: 1}
let max = this.loadValues.$map(function (k, v) {
return v.value
}).$max()
if (max < 10) {
max = 10
} else if (max < 20) {
max = 20
} else if (max < 100) {
max = 100
} else {
max = null
}
teaweb.renderLineChart({
id: "load-chart",
name: "负载",
values: this.loadValues,
x: function (v) {
return v.time
},
tooltip: function (args, stats) {
return stats[args.dataIndex].time + "" + (Math.ceil(stats[args.dataIndex].value * 100) / 100)
},
value: function (v) {
return v.value;
},
axis: axis,
max: max
})
}
})

View File

@@ -0,0 +1,46 @@
.ui.message {
.icon {
position: absolute;
right: 1em;
top: 1.8em;
}
}
.grid {
margin-top: 2em !important;
margin-left: 2em !important;
.column {
margin-bottom: 2em;
border-right: 1px #eee solid;
div.value {
margin-top: 1.5em;
span {
font-size: 2em;
margin-right: 0.2em;
}
}
}
.column.no-border {
border-right: 0;
}
h4 {
a {
display: none;
}
}
.column:hover {
a {
display: inline;
}
}
}
.chart-box {
height: 20em;
}