Compare commits

..

11 Commits

Author SHA1 Message Date
刘祥超
c3df181dcc 版本号改为0.6.4.2 2023-03-16 08:55:12 +08:00
刘祥超
699ea47ac5 更新components.js 2023-03-16 08:55:01 +08:00
刘祥超
fba69f3109 节点列表增加连接数列 2023-03-15 17:57:32 +08:00
刘祥超
063583dda1 调整节点任务和DNS同步任务弹窗高度 2023-03-15 16:24:14 +08:00
刘祥超
a31ca5cfb6 提高消息弹窗高度 2023-03-15 15:48:35 +08:00
刘祥超
c0a13305ad 管理系统增加两种背景风格 2023-03-15 15:35:11 +08:00
刘祥超
9d8cbf87dc URL跳转之域名跳转增加跳转后域名校验 2023-03-15 15:07:55 +08:00
刘祥超
009f7da26b 优化看板中的统计图表 2023-03-15 14:39:45 +08:00
刘祥超
f1d0359dc4 优化代码 2023-03-13 16:24:37 +08:00
刘祥超
473baa8c5b 版本号改为0.6.5 2023-03-13 16:16:26 +08:00
刘祥超
1a5dda6c72 Update Dockerfile 2023-03-13 12:12:44 +08:00
22 changed files with 336 additions and 76 deletions

View File

@@ -1,7 +1,7 @@
FROM alpine:latest
LABEL maintainer="iwind.liu@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 0.6.4
ENV VERSION 0.6.4.1
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"

View File

@@ -21,9 +21,9 @@ type APIConfig struct {
// LoadAPIConfig 加载API配置
func LoadAPIConfig() (*APIConfig, error) {
// 候选文件
localFile := Tea.ConfigFile("api.yaml")
isFromLocal := false
paths := []string{localFile}
var localFile = Tea.ConfigFile("api.yaml")
var isFromLocal = false
var paths = []string{localFile}
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
@@ -45,7 +45,7 @@ func LoadAPIConfig() (*APIConfig, error) {
return nil, err
}
config := &APIConfig{}
var config = &APIConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err

View File

@@ -1,9 +1,9 @@
package teaconst
const (
Version = "0.6.4.1"
Version = "0.6.4.2"
APINodeVersion = "0.6.4.1"
APINodeVersion = "0.6.4.2"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -32,11 +32,12 @@ func (this *NodesAction) RunGet(params struct {
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
ConnectionsOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
@@ -44,7 +45,7 @@ func (this *NodesAction) RunGet(params struct {
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
@@ -112,6 +113,10 @@ func (this *NodesAction) RunGet(params struct {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
} else if params.ConnectionsOrder == "asc" {
req.ConnectionsAsc = true
} else if params.ConnectionsOrder == "desc" {
req.ConnectionsDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
@@ -121,8 +126,8 @@ func (this *NodesAction) RunGet(params struct {
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
var isSynced = false
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -211,16 +216,17 @@ func (this *NodesAction) RunGet(params struct {
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -33,11 +33,12 @@ func (this *NodesAction) RunGet(params struct {
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
ConnectionsOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
@@ -46,7 +47,7 @@ func (this *NodesAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["clusterId"] = params.ClusterId
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
@@ -114,6 +115,10 @@ func (this *NodesAction) RunGet(params struct {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
} else if params.ConnectionsOrder == "asc" {
req.ConnectionsAsc = true
} else if params.ConnectionsOrder == "desc" {
req.ConnectionsDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
@@ -123,8 +128,8 @@ func (this *NodesAction) RunGet(params struct {
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
var isSynced = false
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -213,16 +218,17 @@ func (this *NodesAction) RunGet(params struct {
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -3,6 +3,7 @@ package redirects
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
@@ -134,6 +135,23 @@ func (this *CreatePopupAction) RunPost(params struct {
this.FailField("domainAfter", "请输入跳转后域名")
return
}
// 检查用户输入的是否为域名
if !domainutils.ValidateDomainFormat(params.DomainAfter) {
// 是否为URL
u, err := url.Parse(params.DomainAfter)
if err == nil {
if len(u.Host) == 0 {
this.FailField("domainAfter", "跳转后域名输入不正确")
return
}
params.DomainAfter = u.Host
} else {
this.FailField("domainAfter", "跳转后域名输入不正确")
return
}
}
config.DomainAfter = params.DomainAfter
config.DomainAfterScheme = params.DomainAfterScheme
case serverconfigs.HTTPHostRedirectTypePort:

View File

@@ -15,7 +15,7 @@ type ThemeAction struct {
func (this *ThemeAction) RunPost(params struct{}) {
theme := configloaders.FindAdminTheme(this.AdminId())
var themes = []string{"theme1", "theme2", "theme3", "theme4", "theme5"}
var themes = []string{"theme1", "theme2", "theme3", "theme4", "theme5", "theme6", "theme7"}
var nextTheme = "theme1"
if len(theme) == 0 {
nextTheme = "theme2"

View File

@@ -5282,7 +5282,9 @@ example2.com
<option value="10">[每页]</option><option value="10" selected="selected">10条</option><option value="20">20条</option><option value="30">30条</option><option value="40">40条</option><option value="50">50条</option><option value="60">60条</option><option value="70">70条</option><option value="80">80条</option><option value="90">90条</option><option value="100">100条</option>
</select>`}),Vue.component("second-menu",{template:' \t\t<div class="second-menu"> \t\t\t<div class="ui menu text blue small">\t\t\t\t<slot></slot>\t\t\t</div> \t\t\t<div class="ui divider"></div> \t\t</div>'}),Vue.component("loading-message",{template:`<div class="ui message loading">
<div class="ui active inline loader small"></div> &nbsp; <slot></slot>
</div>`}),Vue.component("more-options-angle",{data:function(){return{isVisible:!1}},methods:{show:function(){this.isVisible=!this.isVisible,this.$emit("change",this.isVisible)}},template:'<a href="" @click.prevent="show()"><span v-if="!isVisible">更多选项</span><span v-if="isVisible">收起选项</span><i class="icon angle" :class="{down:!isVisible, up:isVisible}"></i></a>'}),Vue.component("inner-menu-item",{props:["href","active","code"],data:function(){var e,t=this.active;return void 0===t&&(e="",t=(e=void 0!==window.TEA.ACTION.data.firstMenuItem?window.TEA.ACTION.data.firstMenuItem:e)==this.code),{vHref:null==this.href?"":this.href,vActive:t}},template:'\t\t<a :href="vHref" class="item right" style="color:#4183c4" :class="{active:vActive}">[<slot></slot>]</a> \t\t'}),Vue.component("health-check-config-box",{props:["v-health-check-config","v-check-domain-url"],data:function(){let t=this.vHealthCheckConfig,i="http",n="",s="/",o="";if(null==t){t={isOn:!1,url:"",interval:{count:60,unit:"second"},statusCodes:[200],timeout:{count:10,unit:"second"},countTries:3,tryDelay:{count:100,unit:"ms"},autoDown:!0,countUp:1,countDown:3,userAgent:"",onlyBasicRequest:!0,accessLogIsOn:!0};let e=this;setTimeout(function(){e.changeURL()},500)}else{try{let e=new URL(t.url);i=e.protocol.substring(0,e.protocol.length-1);var a=(o="%24%7Bhost%7D"==(o=e.host)?"${host}":o).indexOf(":");0<a&&(o=o.substring(0,a)),n=e.port,s=e.pathname,0<e.search.length&&(s+=e.search)}catch(e){}null==t.statusCodes&&(t.statusCodes=[200]),null==t.interval&&(t.interval={count:60,unit:"second"}),null==t.timeout&&(t.timeout={count:10,unit:"second"}),null==t.tryDelay&&(t.tryDelay={count:100,unit:"ms"}),(null==t.countUp||t.countUp<1)&&(t.countUp=1),(null==t.countDown||t.countDown<1)&&(t.countDown=3)}return{healthCheck:t,advancedVisible:!1,urlProtocol:i,urlHost:o,urlPort:n,urlRequestURI:s,urlIsEditing:0==t.url.length,hostErr:""}},watch:{urlRequestURI:function(){0<this.urlRequestURI.length&&"/"!=this.urlRequestURI[0]&&(this.urlRequestURI="/"+this.urlRequestURI),this.changeURL()},urlPort:function(e){let t=parseInt(e);isNaN(t)?this.urlPort="":this.urlPort=t.toString(),this.changeURL()},urlProtocol:function(){this.changeURL()},urlHost:function(){this.changeURL(),this.hostErr=""},"healthCheck.countTries":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countTries=0:this.healthCheck.countTries=e},"healthCheck.countUp":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countUp=0:this.healthCheck.countUp=e},"healthCheck.countDown":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countDown=0:this.healthCheck.countDown=e}},methods:{showAdvanced:function(){this.advancedVisible=!this.advancedVisible},changeURL:function(){let e=this.urlHost;0==e.length&&(e="${host}"),this.healthCheck.url=this.urlProtocol+"://"+e+(0<this.urlPort.length?":"+this.urlPort:"")+this.urlRequestURI},changeStatus:function(e){this.healthCheck.statusCodes=e.$map(function(e,t){t=parseInt(t);return isNaN(t)?0:t})},onChangeURLHost:function(){var e=this.vCheckDomainUrl;if(null!=e&&0!=e.length){let t=this;Tea.action(e).params({host:this.urlHost}).success(function(e){e.data.isOk?t.hostErr="":t.hostErr="在当前集群中找不到此域名,可能会影响健康检查结果。"}).post()}},editURL:function(){this.urlIsEditing=!this.urlIsEditing}},template:`<div>
</div>`}),Vue.component("more-options-angle",{data:function(){return{isVisible:!1}},methods:{show:function(){this.isVisible=!this.isVisible,this.$emit("change",this.isVisible)}},template:'<a href="" @click.prevent="show()"><span v-if="!isVisible">更多选项</span><span v-if="isVisible">收起选项</span><i class="icon angle" :class="{down:!isVisible, up:isVisible}"></i></a>'}),Vue.component("inner-menu-item",{props:["href","active","code"],data:function(){var e,t=this.active;return void 0===t&&(e="",t=(e=void 0!==window.TEA.ACTION.data.firstMenuItem?window.TEA.ACTION.data.firstMenuItem:e)==this.code),{vHref:null==this.href?"":this.href,vActive:t}},template:'\t\t<a :href="vHref" class="item right" style="color:#4183c4" :class="{active:vActive}">[<slot></slot>]</a> \t\t'}),Vue.component("columns-grid",{props:[],mounted:function(){this.columns=this.calculateColumns();let e=this;window.addEventListener("resize",function(){e.columns=e.calculateColumns()})},data:function(){return{columns:"four"}},methods:{calculateColumns:function(){var e=window.innerWidth;let i=Math.floor(e/250);0==i&&(i=1);var n=this.$el.getElementsByClassName("column");if(0!=n.length){e=n.length;i>e&&(i=e);for(let t=0;t<n.length;t++){let e=n[t];e.className=e.className.replace("with-border",""),t%i!=i-1&&t!=n.length-1||(e.className+=" with-border")}switch(i){case 1:return"one";case 2:return"two";case 3:return"three";case 4:return"four";case 5:return"five";case 6:return"six";case 7:return"seven";case 8:return"eight";case 9:return"nine";default:return"ten"}}}},template:`<div :class="'ui ' + columns + ' columns grid counter-chart'">
<slot></slot>
</div>`}),Vue.component("health-check-config-box",{props:["v-health-check-config","v-check-domain-url"],data:function(){let t=this.vHealthCheckConfig,i="http",n="",s="/",o="";if(null==t){t={isOn:!1,url:"",interval:{count:60,unit:"second"},statusCodes:[200],timeout:{count:10,unit:"second"},countTries:3,tryDelay:{count:100,unit:"ms"},autoDown:!0,countUp:1,countDown:3,userAgent:"",onlyBasicRequest:!0,accessLogIsOn:!0};let e=this;setTimeout(function(){e.changeURL()},500)}else{try{let e=new URL(t.url);i=e.protocol.substring(0,e.protocol.length-1);var a=(o="%24%7Bhost%7D"==(o=e.host)?"${host}":o).indexOf(":");0<a&&(o=o.substring(0,a)),n=e.port,s=e.pathname,0<e.search.length&&(s+=e.search)}catch(e){}null==t.statusCodes&&(t.statusCodes=[200]),null==t.interval&&(t.interval={count:60,unit:"second"}),null==t.timeout&&(t.timeout={count:10,unit:"second"}),null==t.tryDelay&&(t.tryDelay={count:100,unit:"ms"}),(null==t.countUp||t.countUp<1)&&(t.countUp=1),(null==t.countDown||t.countDown<1)&&(t.countDown=3)}return{healthCheck:t,advancedVisible:!1,urlProtocol:i,urlHost:o,urlPort:n,urlRequestURI:s,urlIsEditing:0==t.url.length,hostErr:""}},watch:{urlRequestURI:function(){0<this.urlRequestURI.length&&"/"!=this.urlRequestURI[0]&&(this.urlRequestURI="/"+this.urlRequestURI),this.changeURL()},urlPort:function(e){let t=parseInt(e);isNaN(t)?this.urlPort="":this.urlPort=t.toString(),this.changeURL()},urlProtocol:function(){this.changeURL()},urlHost:function(){this.changeURL(),this.hostErr=""},"healthCheck.countTries":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countTries=0:this.healthCheck.countTries=e},"healthCheck.countUp":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countUp=0:this.healthCheck.countUp=e},"healthCheck.countDown":function(e){e=parseInt(e);isNaN(e)?this.healthCheck.countDown=0:this.healthCheck.countDown=e}},methods:{showAdvanced:function(){this.advancedVisible=!this.advancedVisible},changeURL:function(){let e=this.urlHost;0==e.length&&(e="${host}"),this.healthCheck.url=this.urlProtocol+"://"+e+(0<this.urlPort.length?":"+this.urlPort:"")+this.urlRequestURI},changeStatus:function(e){this.healthCheck.statusCodes=e.$map(function(e,t){t=parseInt(t);return isNaN(t)?0:t})},onChangeURLHost:function(){var e=this.vCheckDomainUrl;if(null!=e&&0!=e.length){let t=this;Tea.action(e).params({host:this.urlHost}).success(function(e){e.data.isOk?t.hostErr="":t.hostErr="在当前集群中找不到此域名,可能会影响健康检查结果。"}).post()}},editURL:function(){this.urlIsEditing=!this.urlIsEditing}},template:`<div>
<input type="hidden" name="healthCheckJSON" :value="JSON.stringify(healthCheck)"/>
<table class="ui table definition selectable">
<tbody>

View File

@@ -15654,6 +15654,78 @@ Vue.component("inner-menu-item", {
'
});
Vue.component("columns-grid", {
props: [],
mounted: function () {
this.columns = this.calculateColumns()
let that = this
window.addEventListener("resize", function () {
that.columns = that.calculateColumns()
})
},
data: function () {
return {
columns: "four"
}
},
methods: {
calculateColumns: function () {
let w = window.innerWidth
let columns = Math.floor(w / 250)
if (columns == 0) {
columns = 1
}
let columnElements = this.$el.getElementsByClassName("column")
if (columnElements.length == 0) {
return
}
let maxColumns = columnElements.length
if (columns > maxColumns) {
columns = maxColumns
}
// 添加右侧边框
for (let index = 0; index < columnElements.length; index++) {
let el = columnElements[index]
el.className = el.className.replace("with-border", "")
if (index % columns == columns - 1 || index == columnElements.length - 1 /** 最后一个 **/) {
el.className += " with-border"
}
}
switch (columns) {
case 1:
return "one"
case 2:
return "two"
case 3:
return "three"
case 4:
return "four"
case 5:
return "five"
case 6:
return "six"
case 7:
return "seven"
case 8:
return "eight"
case 9:
return "nine"
case 10:
return "ten"
default:
return "ten"
}
}
},
template: `<div :class="'ui ' + columns + ' columns grid counter-chart'">
<slot></slot>
</div>`
})
Vue.component("health-check-config-box", {
props: ["v-health-check-config", "v-check-domain-url"],
data: function () {

View File

@@ -0,0 +1,71 @@
Vue.component("columns-grid", {
props: [],
mounted: function () {
this.columns = this.calculateColumns()
let that = this
window.addEventListener("resize", function () {
that.columns = that.calculateColumns()
})
},
data: function () {
return {
columns: "four"
}
},
methods: {
calculateColumns: function () {
let w = window.innerWidth
let columns = Math.floor(w / 250)
if (columns == 0) {
columns = 1
}
let columnElements = this.$el.getElementsByClassName("column")
if (columnElements.length == 0) {
return
}
let maxColumns = columnElements.length
if (columns > maxColumns) {
columns = maxColumns
}
// 添加右侧边框
for (let index = 0; index < columnElements.length; index++) {
let el = columnElements[index]
el.className = el.className.replace("with-border", "")
if (index % columns == columns - 1 || index == columnElements.length - 1 /** 最后一个 **/) {
el.className += " with-border"
}
}
switch (columns) {
case 1:
return "one"
case 2:
return "two"
case 3:
return "three"
case 4:
return "four"
case 5:
return "five"
case 6:
return "six"
case 7:
return "seven"
case 8:
return "eight"
case 9:
return "nine"
case 10:
return "ten"
default:
return "ten"
}
}
},
template: `<div :class="'ui ' + columns + ' columns grid counter-chart'">
<slot></slot>
</div>`
})

View File

@@ -1,5 +1,5 @@
.grid.counter-chart {
margin-top: 1.5em !important;
margin-top: 1em !important;
margin-left: 0.4em !important;
}
.grid.counter-chart .column {
@@ -22,14 +22,19 @@
border-right: 1px rgba(0, 0, 0, 0.1) solid;
}
.grid.counter-chart h4 {
color: grey;
position: relative;
font-size: 1em;
text-align: left;
}
.grid.counter-chart h4 a {
position: absolute;
right: 0.1em;
font-size: 1.26em;
display: none;
}
.grid.counter-chart .column:hover {
background: rgba(0, 0, 0, 0.1) !important;
background: rgba(0, 0, 0, 0.03) !important;
}
.grid.counter-chart .column:hover a {
display: inline;

View File

@@ -1 +1 @@
{"version":3,"sources":["@grids.less"],"names":[],"mappings":"AAAA,KAAK;EACJ,iBAAA;EACA,kBAAA;;AAFD,KAAK,cAIJ;EACC,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,kBAAA;EACA,oCAAA;EACA,eAAA;;AAVF,KAAK,cAIJ,QAQC,IAAG;EACF,iBAAA;EACA,mBAAA;;AAdH,KAAK,cAIJ,QAQC,IAAG,MAIF;EACC,gBAAA;EACA,mBAAA;;AAlBJ,KAAK,cAuBJ,QAAO;EACN,0CAAA;;AAxBF,KAAK,cA2BJ;EAKC,cAAA;EACA,gBAAA;;AAjCF,KAAK,cA2BJ,GACC;EACC,aAAA;;AA7BH,KAAK,cAoCJ,QAAO;EACN,8BAAA;;AArCF,KAAK,cAoCJ,QAAO,MAGN;EACC,eAAA","file":"@grids.css"}
{"version":3,"sources":["@grids.less"],"names":[],"mappings":"AAAA,KAAK;EACJ,0BAAA;EACA,kBAAA;;AAFD,KAAK,cAIJ;EACC,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,kBAAA;EACA,oCAAA;EACA,eAAA;;AAVF,KAAK,cAIJ,QAQC,IAAG;EACF,iBAAA;EACA,mBAAA;;AAdH,KAAK,cAIJ,QAQC,IAAG,MAIF;EACC,gBAAA;EACA,mBAAA;;AAlBJ,KAAK,cAuBJ,QAAO;EACN,0CAAA;;AAxBF,KAAK,cA2BJ;EACC,WAAA;EACA,kBAAA;EASA,cAAA;EACA,gBAAA;;AAvCF,KAAK,cA2BJ,GAIC;EACC,kBAAA;EACA,YAAA;EACA,iBAAA;EACA,aAAA;;AAnCH,KAAK,cA0CJ,QAAO;EACN,+BAAA;;AA3CF,KAAK,cA0CJ,QAAO,MAGN;EACC,eAAA","file":"@grids.css"}

View File

@@ -405,6 +405,12 @@ body.expanded .main {
.top-nav.theme5 {
background: #1C7947 !important;
}
.top-nav.theme6 {
background: #1D365D !important;
}
.top-nav.theme7 {
background: black !important;
}
.top-nav::-webkit-scrollbar {
height: 2px;
}
@@ -563,6 +569,18 @@ body.expanded .main {
.main-menu.theme5 .menu {
background: #1C7947 !important;
}
.main-menu.theme6 {
background: #1D365D !important;
}
.main-menu.theme6 .menu {
background: #1D365D !important;
}
.main-menu.theme7 {
background: black !important;
}
.main-menu.theme7 .menu {
background: black !important;
}
.main-menu::-webkit-scrollbar {
width: 2px;
}

File diff suppressed because one or more lines are too long

View File

@@ -82,7 +82,7 @@ Tea.context(function () {
this.showMessages = function () {
teaweb.popup("/messages", {
height: "24em",
height: "28em",
width: "50em"
})
}
@@ -138,7 +138,7 @@ Tea.context(function () {
this.showNodeTasks = function () {
teaweb.popup("/clusters/tasks/listPopup", {
height: "24em",
height: "28em",
width: "54em"
})
}
@@ -177,7 +177,7 @@ Tea.context(function () {
this.showDNSTasks = function () {
teaweb.popup("/dns/tasks/listPopup", {
height: "24em",
height: "28em",
width: "54em"
})
}

View File

@@ -338,11 +338,18 @@ body.expanded .main {
background: #1C7947 !important;
}
.top-nav.theme6 {
background: #1D365D !important;
}
.top-nav.theme7 {
background: black !important;
}
.top-nav::-webkit-scrollbar {
height: 2px;
}
/** 顶部菜单 **/
.top-secondary-menu {
position: fixed;
@@ -544,6 +551,22 @@ body.expanded .main {
}
}
.main-menu.theme6 {
background: #1D365D !important;
.menu {
background: #1D365D !important;
}
}
.main-menu.theme7 {
background: black !important;
.menu {
background: black !important;
}
}
.main-menu::-webkit-scrollbar {
width: 2px;
}

View File

@@ -64,10 +64,11 @@
<th>节点名称</th>
<th>IP</th>
<th class="width10">DNS线路</th>
<th class="width5 center">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
<th class="width5 center">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
<th class="center" style="width: 7em">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="center" style="width: 7em">负载<sort-arrow name="loadOrder"></sort-arrow></th>
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">连接数<sort-arrow name="connectionsOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">负载<sort-arrow name="loadOrder"></sort-arrow></th>
<th class="two wide center">状态</th>
<th class="two op">操作</th>
</tr>
@@ -117,20 +118,24 @@
</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">
<span v-if="node.status.isActive" :class="{red:node.status.cpuUsage > 0.50}">{{node.status.cpuUsageText}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">
<span v-if="node.status.isActive" :class="{red:node.status.memUsage > 0.80}">{{node.status.memUsageText}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">
<span v-if="node.status.isActive && node.status.trafficOutBytes > 0">{{teaweb.formatBits(node.status.trafficOutBytes * 8/60)}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="node.status.isActive">{{node.status.load1m}}</span>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">
<span v-if="node.status.isActive && node.status.countConnections > 0">{{node.status.countConnections}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">
<span v-if="node.status.isActive && node.status.load1m > 0">{{node.status.load1m}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">

View File

@@ -1,6 +1,20 @@
Tea.context(function () {
this.teaweb = teaweb
// 显示的统计项
this.windowWidth = window.innerWidth
this.miniWidth = 760
this.columnWidth1 = 800
this.columnWidth2 = 900
this.columnWidth3 = 1000
this.columnWidth4 = 1100
this.columnWidth5 = 1200
let that = this
window.addEventListener("resize", function () {
that.windowWidth = window.innerWidth
})
this.deleteNode = function (nodeId) {
teaweb.confirm("确定要从当前集群中删除这个节点吗?", function () {
this.$post("/nodes/delete")

View File

@@ -58,10 +58,11 @@
<th>节点名称</th>
<th>IP</th>
<th class="width10">DNS线路</th>
<th class="width5 center">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
<th class="width5 center">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
<th class="center" style="width: 7em">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="center" style="width: 7em">负载<sort-arrow name="loadOrder"></sort-arrow></th>
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">连接数<sort-arrow name="connectionsOrder"></sort-arrow></th>
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">负载<sort-arrow name="loadOrder"></sort-arrow></th>
<th class="two wide center">状态</th>
<th class="one op">操作</th>
</tr>
@@ -111,20 +112,24 @@
</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="node.status.isActive" :class="{red:node.status.cpuUsage > 0.50}">{{node.status.cpuUsageText}}%</span>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">
<span v-if="node.status.isActive" :class="{red:node.status.cpuUsage > 0.50}">{{node.status.cpuUsageText}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="node.status.isActive" :class="{red:node.status.memUsage > 0.80}">{{node.status.memUsageText}}%</span>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">
<span v-if="node.status.isActive" :class="{red:node.status.memUsage > 0.80}">{{node.status.memUsageText}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">
<span v-if="node.status.isActive && node.status.trafficOutBytes > 0">{{teaweb.formatBits(node.status.trafficOutBytes * 8/60)}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="node.status.isActive">{{node.status.load1m}}<span class="grey small"></span></span>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">
<span v-if="node.status.isActive && node.status.countConnections > 0">{{node.status.countConnections}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">
<span v-if="node.status.isActive && node.status.load1m > 0">{{node.status.load1m}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">

View File

@@ -1,6 +1,20 @@
Tea.context(function () {
this.teaweb = teaweb
// 显示的统计项
this.windowWidth = window.innerWidth
this.miniWidth = 760
this.columnWidth1 = 800
this.columnWidth2 = 900
this.columnWidth3 = 1000
this.columnWidth4 = 1100
this.columnWidth5 = 1200
let that = this
window.addEventListener("resize", function () {
that.windowWidth = window.innerWidth
})
this.deleteNode = function (nodeId) {
teaweb.confirm("确定要删除这个节点吗?", function () {
this.$post("/cluster/nodes/delete")

View File

@@ -51,7 +51,7 @@
</div>
<!-- 统计图表 -->
<div class="ui three columns grid counter-chart" v-if="!isLoading">
<columns-grid v-if="!isLoading">
<div class="ui column">
<h4>集群<link-icon href="/clusters" v-if="dashboard.canGoNodes"></link-icon></h4>
<div class="value"><span>{{dashboard.countNodeClusters}}</span></div>
@@ -85,7 +85,7 @@
<h4>今日流量</h4>
<div class="value"><span>{{todayTraffic}}</span>{{todayTrafficUnit}}</div>
</div>
</div>
</columns-grid>
<div class="ui divider" v-show="!isLoading"></div>

View File

@@ -90,7 +90,8 @@
<tr>
<td class="color-border">跳转后域名 *</td>
<td>
<input type="text" name="domainAfter" maxlength="100" v-model="redirect.domainAfter"/>
<input type="text" name="domainAfter" maxlength="100" v-model="redirect.domainAfter" placeholder="比如example.com"/>
<p class="comment">这里填写的是网址中的域名部分,不需要添加<code-label>https://</code-label>部分。</p>
</td>
</tr>
<tr>