Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b7dc8eb96 | ||
|
|
7c0af45dd0 | ||
|
|
daea270132 | ||
|
|
a42e3ac4a6 | ||
|
|
c94c526b43 | ||
|
|
e531787773 | ||
|
|
0eda84a99a | ||
|
|
89b791448a | ||
|
|
2239328b2e | ||
|
|
e440a46080 | ||
|
|
4a0fec6e35 | ||
|
|
a9b46987bc | ||
|
|
6771afead5 | ||
|
|
553fca2981 | ||
|
|
8ef1e36b8b | ||
|
|
9e6c49b67c | ||
|
|
170632ddbc | ||
|
|
4a74488cac | ||
|
|
5fd6a08988 | ||
|
|
c86b91ead6 | ||
|
|
aeebb763c1 | ||
|
|
b05630e3e0 | ||
|
|
51905a0ee3 | ||
|
|
869ebd59d1 | ||
|
|
8a6aedca16 | ||
|
|
cf98c3453a | ||
|
|
1b6f234f39 | ||
|
|
26c7f0f566 | ||
|
|
617ad9b16e | ||
|
|
04a6792dbf | ||
|
|
f2bf8f2dbf | ||
|
|
3aba0abd24 | ||
|
|
b56ccc7cef | ||
|
|
aeebf55e60 | ||
|
|
9a9e0d25f3 | ||
|
|
6b3033e240 | ||
|
|
2859a59340 | ||
|
|
45367dd579 | ||
|
|
fee3fd743f | ||
|
|
ba4cfea47c | ||
|
|
5c11f07f8e | ||
|
|
ffac080611 |
@@ -1,7 +1,7 @@
|
||||
FROM --platform=linux/amd64 alpine:latest
|
||||
LABEL maintainer="goedge.cdn@gmail.com"
|
||||
ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 1.2.10
|
||||
ENV VERSION 1.3.0
|
||||
ENV ROOT_DIR /usr/local/goedge
|
||||
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "1.2.10"
|
||||
Version = "1.3.0"
|
||||
|
||||
APINodeVersion = "1.2.10"
|
||||
APINodeVersion = "1.3.0"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -63,7 +63,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
|
||||
buffer.Write(typesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 条件操作符
|
||||
@@ -73,7 +73,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
|
||||
buffer.Write(requestOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 请求变量
|
||||
@@ -83,7 +83,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_VARIABLES = ")
|
||||
buffer.Write(requestVariablesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 指标
|
||||
@@ -93,7 +93,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
|
||||
buffer.Write(metricHTTPKeysJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值项目
|
||||
@@ -103,7 +103,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
|
||||
buffer.Write(ipAddrThresholdItemsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值动作
|
||||
@@ -113,7 +113,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
|
||||
buffer.Write(ipAddrThresholdActionsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF操作符
|
||||
@@ -123,7 +123,17 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
|
||||
buffer.Write(wafOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF验证码类型
|
||||
captchaTypesJSON, err := json.Marshal(firewallconfigs.FindAllCaptchaTypes())
|
||||
if err != nil {
|
||||
logs.Println("ComponentsAction marshal captcha types failed: " + err.Error())
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_CAPTCHA_TYPES = ")
|
||||
buffer.Write(captchaTypesJSON)
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "node", "update")
|
||||
this.SecondMenu("threshold")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
// 列出所有阈值
|
||||
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
thresholdMaps := []maps.Map{}
|
||||
for _, threshold := range thresholdsResp.NodeThresholds {
|
||||
thresholdMaps = append(thresholdMaps, maps.Map{
|
||||
"id": threshold.Id,
|
||||
"itemName": nodeconfigs.FindNodeValueItemName(threshold.Item),
|
||||
"paramName": nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param),
|
||||
"operatorName": nodeconfigs.FindNodeValueOperatorName(threshold.Operator),
|
||||
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
|
||||
"sumMethodName": nodeconfigs.FindNodeValueSumMethodName(threshold.SumMethod),
|
||||
"duration": threshold.Duration,
|
||||
"durationUnitName": nodeconfigs.FindNodeValueDurationUnitName(threshold.DurationUnit),
|
||||
"isOn": threshold.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["thresholds"] = thresholdMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -14,7 +14,7 @@ type IndexAction struct {
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "setting", "")
|
||||
this.Nav("", "setting", "index")
|
||||
this.SecondMenu("dns")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type RecordsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *RecordsAction) Init() {
|
||||
this.Nav("", "setting", "records")
|
||||
this.SecondMenu("dns")
|
||||
}
|
||||
|
||||
func (this *RecordsAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
}) {
|
||||
// 集群信息
|
||||
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var cluster = clusterResp.NodeCluster
|
||||
if cluster == nil {
|
||||
this.NotFound("nodeCluster", params.ClusterId)
|
||||
return
|
||||
}
|
||||
this.Data["cluster"] = maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
}
|
||||
|
||||
// DNS信息
|
||||
dnsResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var defaultRoute = dnsResp.DefaultRoute
|
||||
var domainName = ""
|
||||
var dnsMap = maps.Map{
|
||||
"dnsName": dnsResp.Name,
|
||||
"domainId": 0,
|
||||
"domainName": "",
|
||||
"providerId": 0,
|
||||
"providerName": "",
|
||||
"providerTypeName": "",
|
||||
}
|
||||
if dnsResp.Domain != nil {
|
||||
domainName = dnsResp.Domain.Name
|
||||
dnsMap["domainId"] = dnsResp.Domain.Id
|
||||
dnsMap["domainName"] = dnsResp.Domain.Name
|
||||
}
|
||||
if dnsResp.Provider != nil {
|
||||
dnsMap["providerId"] = dnsResp.Provider.Id
|
||||
dnsMap["providerName"] = dnsResp.Provider.Name
|
||||
dnsMap["providerTypeName"] = dnsResp.Provider.TypeName
|
||||
}
|
||||
|
||||
if len(dnsResp.CnameRecords) > 0 {
|
||||
dnsMap["cnameRecords"] = dnsResp.CnameRecords
|
||||
} else {
|
||||
dnsMap["cnameRecords"] = []string{}
|
||||
}
|
||||
|
||||
this.Data["dnsInfo"] = dnsMap
|
||||
|
||||
// 未安装的节点
|
||||
notInstalledNodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: false,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var allNodes = notInstalledNodesResp.Nodes
|
||||
|
||||
// 节点DNS解析记录
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var installedNodeIdsMap = map[int64]bool{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
installedNodeIdsMap[node.Id] = true
|
||||
}
|
||||
|
||||
allNodes = append(allNodes, nodesResp.Nodes...)
|
||||
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range allNodes {
|
||||
var isInstalled = installedNodeIdsMap[node.Id]
|
||||
|
||||
if len(node.Routes) > 0 {
|
||||
for _, route := range node.Routes {
|
||||
// 检查是否已解析
|
||||
var isResolved = false
|
||||
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: cluster.DnsName,
|
||||
Type: recordType,
|
||||
Route: route.Code,
|
||||
Value: node.IpAddr,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": route.Name,
|
||||
"code": route.Code,
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
|
||||
"isOffline": node.IsOffline,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 默认线路
|
||||
var isResolved = false
|
||||
if isInstalled && len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: cluster.DnsName,
|
||||
Type: recordType,
|
||||
Route: defaultRoute,
|
||||
Value: node.IpAddr,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": "",
|
||||
"code": "",
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
|
||||
"isOffline": node.IsOffline,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["nodes"] = nodeMaps
|
||||
|
||||
// 代理服务解析记录
|
||||
serversResp, err := this.RPC().ServerRPC().FindAllEnabledServersDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledServersDNSWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverMaps = []maps.Map{}
|
||||
for _, server := range serversResp.Servers {
|
||||
// 检查是否已解析
|
||||
isResolved := false
|
||||
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(server.DnsName) > 0 && len(domainName) > 0 {
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: server.DnsName,
|
||||
Type: "CNAME",
|
||||
Value: cluster.DnsName + "." + domainName,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
|
||||
serverMaps = append(serverMaps, maps.Map{
|
||||
"id": server.Id,
|
||||
"name": server.Name,
|
||||
"dnsName": server.DnsName,
|
||||
"isResolved": isResolved,
|
||||
})
|
||||
}
|
||||
this.Data["servers"] = serverMaps
|
||||
|
||||
// 检查解析记录是否有变化
|
||||
checkChangesResp, err := this.RPC().NodeClusterRPC().CheckNodeClusterDNSChanges(this.AdminContext(), &pb.CheckNodeClusterDNSChangesRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["dnsHasChanges"] = checkChangesResp.IsChanged
|
||||
|
||||
// 需要解决的问题
|
||||
issuesResp, err := this.RPC().DNSRPC().FindAllDNSIssues(this.AdminContext(), &pb.FindAllDNSIssuesRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var issueMaps = []maps.Map{}
|
||||
for _, issue := range issuesResp.Issues {
|
||||
issueMaps = append(issueMaps, maps.Map{
|
||||
"target": issue.Target,
|
||||
"targetId": issue.TargetId,
|
||||
"type": issue.Type,
|
||||
"description": issue.Description,
|
||||
"params": issue.Params,
|
||||
})
|
||||
}
|
||||
this.Data["issues"] = issueMaps
|
||||
|
||||
// 当前正在执行的任务
|
||||
resp, err := this.RPC().DNSTaskRPC().FindAllDoingDNSTasks(this.AdminContext(), &pb.FindAllDoingDNSTasksRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var taskMaps = []maps.Map{}
|
||||
for _, task := range resp.DnsTasks {
|
||||
var clusterMap maps.Map = nil
|
||||
var nodeMap maps.Map = nil
|
||||
var serverMap maps.Map = nil
|
||||
var domainMap maps.Map = nil
|
||||
|
||||
if task.NodeCluster != nil {
|
||||
clusterMap = maps.Map{
|
||||
"id": task.NodeCluster.Id,
|
||||
"name": task.NodeCluster.Name,
|
||||
}
|
||||
}
|
||||
if task.Node != nil {
|
||||
nodeMap = maps.Map{
|
||||
"id": task.Node.Id,
|
||||
"name": task.Node.Name,
|
||||
}
|
||||
}
|
||||
if task.Server != nil {
|
||||
serverMap = maps.Map{
|
||||
"id": task.Server.Id,
|
||||
"name": task.Server.Name,
|
||||
}
|
||||
}
|
||||
if task.DnsDomain != nil {
|
||||
domainMap = maps.Map{
|
||||
"id": task.DnsDomain.Id,
|
||||
"name": task.DnsDomain.Name,
|
||||
}
|
||||
}
|
||||
|
||||
taskMaps = append(taskMaps, maps.Map{
|
||||
"id": task.Id,
|
||||
"type": task.Type,
|
||||
"isDone": task.IsDone,
|
||||
"isOk": task.IsOk,
|
||||
"error": task.Error,
|
||||
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", task.UpdatedAt),
|
||||
"cluster": clusterMap,
|
||||
"node": nodeMap,
|
||||
"server": serverMap,
|
||||
"domain": domainMap,
|
||||
})
|
||||
}
|
||||
this.Data["tasks"] = taskMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -40,6 +40,7 @@ func init() {
|
||||
// DNS
|
||||
Prefix("/clusters/cluster/settings/dns").
|
||||
GetPost("", new(dns.IndexAction)).
|
||||
Get("/records", new(dns.RecordsAction)).
|
||||
Post("/randomName", new(dns.RandomNameAction)).
|
||||
|
||||
// 系统服务设置
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
@@ -58,7 +57,7 @@ func CheckDiskPartitions(thresholdPercent float64) (path string, usage uint64, u
|
||||
|
||||
stat, _ := disk.Usage(p.Mountpoint)
|
||||
if stat != nil {
|
||||
if stat.Used < 2*uint64(sizes.G) {
|
||||
if stat.Used < (5<<30) || stat.Free > (100<<30) {
|
||||
continue
|
||||
}
|
||||
if stat.UsedPercent > thresholdPercent {
|
||||
|
||||
@@ -111,6 +111,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -111,6 +111,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -102,6 +102,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -20,7 +20,7 @@ func (this *CreatePopupAction) Init() {
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Data["bodyTypes"] = shared.FindAllBodyTypes()
|
||||
this.Data["bodyTypes"] = serverconfigs.FindAllHTTPPageBodyTypes()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -32,22 +32,33 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
URL string `alias:"url"`
|
||||
Body string
|
||||
|
||||
ExceptURLPatternsJSON []byte
|
||||
OnlyURLPatternsJSON []byte
|
||||
|
||||
NewStatus int
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// TODO 对状态码进行更多校验
|
||||
|
||||
params.Must.
|
||||
Field("status", params.Status).
|
||||
Require("请输入响应状态码")
|
||||
|
||||
if len(params.Status) != 3 {
|
||||
this.FailField("status", "状态码长度必须为3位")
|
||||
return
|
||||
}
|
||||
|
||||
switch params.BodyType {
|
||||
case shared.BodyTypeURL:
|
||||
case serverconfigs.HTTPPageBodyTypeURL:
|
||||
params.Must.
|
||||
Field("url", params.URL).
|
||||
Require("请输入要显示的URL").
|
||||
Match( `^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case shared.BodyTypeHTML:
|
||||
Match(`^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case serverconfigs.HTTPPageBodyTypeRedirectURL:
|
||||
params.Must.
|
||||
Field("url", params.URL).
|
||||
Require("请输入要跳转的URL").
|
||||
Match(`^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case serverconfigs.HTTPPageBodyTypeHTML:
|
||||
params.Must.
|
||||
Field("body", params.Body).
|
||||
Require("请输入要显示的HTML内容")
|
||||
@@ -58,12 +69,32 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
var exceptURLPatterns = []*shared.URLPattern{}
|
||||
if len(params.ExceptURLPatternsJSON) > 0 {
|
||||
err := json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var onlyURLPatterns = []*shared.URLPattern{}
|
||||
if len(params.OnlyURLPatternsJSON) > 0 {
|
||||
err := json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().HTTPPageRPC().CreateHTTPPage(this.AdminContext(), &pb.CreateHTTPPageRequest{
|
||||
StatusList: []string{params.Status},
|
||||
BodyType: params.BodyType,
|
||||
Url: params.URL,
|
||||
Body: params.Body,
|
||||
NewStatus: types.Int32(params.NewStatus),
|
||||
StatusList: []string{params.Status},
|
||||
BodyType: params.BodyType,
|
||||
Url: params.URL,
|
||||
Body: params.Body,
|
||||
NewStatus: types.Int32(params.NewStatus),
|
||||
ExceptURLPatternsJSON: params.ExceptURLPatternsJSON,
|
||||
OnlyURLPatternsJSON: params.OnlyURLPatternsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
@@ -77,7 +76,11 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// check url
|
||||
if page.BodyType == shared.BodyTypeURL && !urlReg.MatchString(page.URL) {
|
||||
if page.BodyType == serverconfigs.HTTPPageBodyTypeURL && !urlReg.MatchString(page.URL) {
|
||||
this.Fail("自定义页面中 '" + page.URL + "' 不是一个正确的URL,请进行修改")
|
||||
return
|
||||
}
|
||||
if page.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL && !urlReg.MatchString(page.URL) {
|
||||
this.Fail("自定义页面中 '" + page.URL + "' 不是一个正确的URL,请进行修改")
|
||||
return
|
||||
}
|
||||
@@ -99,7 +102,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
if shutdownConfig.BodyType == shared.BodyTypeURL {
|
||||
if shutdownConfig.BodyType == serverconfigs.HTTPPageBodyTypeURL {
|
||||
if len(shutdownConfig.URL) > 512 {
|
||||
this.Fail("临时关闭页面中URL过长,不能超过512字节")
|
||||
return
|
||||
@@ -109,7 +112,17 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Fail("临时关闭页面中 '" + shutdownConfig.URL + "' 不是一个正确的URL,请进行修改")
|
||||
return
|
||||
}
|
||||
} else if shutdownConfig.Body == shared.BodyTypeHTML {
|
||||
} else if shutdownConfig.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL {
|
||||
if len(shutdownConfig.URL) > 512 {
|
||||
this.Fail("临时关闭页面中URL过长,不能超过512字节")
|
||||
return
|
||||
}
|
||||
|
||||
if shutdownConfig.IsOn /** 只有启用的时候才校验 **/ && !urlReg.MatchString(shutdownConfig.URL) {
|
||||
this.Fail("临时关闭页面中 '" + shutdownConfig.URL + "' 不是一个正确的URL,请进行修改")
|
||||
return
|
||||
}
|
||||
} else if shutdownConfig.Body == serverconfigs.HTTPPageBodyTypeHTML {
|
||||
if len(shutdownConfig.Body) > 32*1024 {
|
||||
this.Fail("临时关闭页面中HTML内容长度不能超过32K")
|
||||
return
|
||||
|
||||
@@ -22,7 +22,7 @@ func (this *UpdatePopupAction) Init() {
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
PageId int64
|
||||
}) {
|
||||
this.Data["bodyTypes"] = shared.FindAllBodyTypes()
|
||||
this.Data["bodyTypes"] = serverconfigs.FindAllHTTPPageBodyTypes()
|
||||
|
||||
configResp, err := this.RPC().HTTPPageRPC().FindEnabledHTTPPageConfig(this.AdminContext(), &pb.FindEnabledHTTPPageConfigRequest{HttpPageId: params.PageId})
|
||||
if err != nil {
|
||||
@@ -30,12 +30,18 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
pageConfig := &serverconfigs.HTTPPageConfig{}
|
||||
var pageConfig = &serverconfigs.HTTPPageConfig{}
|
||||
err = json.Unmarshal(configResp.PageJSON, pageConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if pageConfig.ExceptURLPatterns == nil {
|
||||
pageConfig.ExceptURLPatterns = []*shared.URLPattern{}
|
||||
}
|
||||
if pageConfig.OnlyURLPatterns == nil {
|
||||
pageConfig.OnlyURLPatterns = []*shared.URLPattern{}
|
||||
}
|
||||
this.Data["pageConfig"] = pageConfig
|
||||
|
||||
this.Show()
|
||||
@@ -50,6 +56,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
URL string `alias:"url"`
|
||||
Body string
|
||||
|
||||
ExceptURLPatternsJSON []byte
|
||||
OnlyURLPatternsJSON []byte
|
||||
|
||||
NewStatus int
|
||||
|
||||
Must *actions.Must
|
||||
@@ -61,13 +70,23 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
Field("status", params.Status).
|
||||
Require("请输入响应状态码")
|
||||
|
||||
if len(params.Status) != 3 {
|
||||
this.FailField("status", "状态码长度必须为3位")
|
||||
return
|
||||
}
|
||||
|
||||
switch params.BodyType {
|
||||
case shared.BodyTypeURL:
|
||||
case serverconfigs.HTTPPageBodyTypeURL:
|
||||
params.Must.
|
||||
Field("url", params.URL).
|
||||
Require("请输入要显示的URL").
|
||||
Match( `^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case shared.BodyTypeHTML:
|
||||
Match(`^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case serverconfigs.HTTPPageBodyTypeRedirectURL:
|
||||
params.Must.
|
||||
Field("url", params.URL).
|
||||
Require("请输入要跳转的URL").
|
||||
Match(`^(?i)(http|https)://`, "请输入正确的URL")
|
||||
case serverconfigs.HTTPPageBodyTypeHTML:
|
||||
params.Must.
|
||||
Field("body", params.Body).
|
||||
Require("请输入要显示的HTML内容")
|
||||
@@ -78,13 +97,33 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
var exceptURLPatterns = []*shared.URLPattern{}
|
||||
if len(params.ExceptURLPatternsJSON) > 0 {
|
||||
err := json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var onlyURLPatterns = []*shared.URLPattern{}
|
||||
if len(params.OnlyURLPatternsJSON) > 0 {
|
||||
err := json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err := this.RPC().HTTPPageRPC().UpdateHTTPPage(this.AdminContext(), &pb.UpdateHTTPPageRequest{
|
||||
HttpPageId: params.PageId,
|
||||
StatusList: []string{params.Status},
|
||||
BodyType: params.BodyType,
|
||||
Url: params.URL,
|
||||
Body: params.Body,
|
||||
NewStatus: types.Int32(params.NewStatus),
|
||||
HttpPageId: params.PageId,
|
||||
StatusList: []string{params.Status},
|
||||
BodyType: params.BodyType,
|
||||
Url: params.URL,
|
||||
Body: params.Body,
|
||||
NewStatus: types.Int32(params.NewStatus),
|
||||
ExceptURLPatternsJSON: params.ExceptURLPatternsJSON,
|
||||
OnlyURLPatternsJSON: params.OnlyURLPatternsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -52,7 +52,11 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
PortAfter int
|
||||
PortAfterScheme string
|
||||
|
||||
Status int
|
||||
Status int
|
||||
|
||||
ExceptDomainsJSON []byte
|
||||
OnlyDomainsJSON []byte
|
||||
|
||||
CondsJSON []byte
|
||||
IsOn bool
|
||||
|
||||
@@ -186,6 +190,27 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
Field("status", params.Status).
|
||||
Gte(0, "请选择正确的跳转状态码")
|
||||
|
||||
// 域名
|
||||
if len(params.ExceptDomainsJSON) > 0 {
|
||||
var exceptDomains = []string{}
|
||||
err := json.Unmarshal(params.ExceptDomainsJSON, &exceptDomains)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
config.ExceptDomains = exceptDomains
|
||||
}
|
||||
|
||||
if len(params.OnlyDomainsJSON) > 0 {
|
||||
var onlyDomains = []string{}
|
||||
err := json.Unmarshal(params.OnlyDomainsJSON, &onlyDomains)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
config.OnlyDomains = onlyDomains
|
||||
}
|
||||
|
||||
// 校验匹配条件
|
||||
var conds *shared.HTTPRequestCondsConfig
|
||||
if len(params.CondsJSON) > 0 {
|
||||
|
||||
@@ -134,6 +134,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
RequestHostExcludingPort: reverseProxyConfig.RequestHostExcludingPort,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -42,6 +42,7 @@ func (this *CountriesAction) RunGet(params struct {
|
||||
this.NotFound("firewallPolicy", params.FirewallPolicyId)
|
||||
return
|
||||
}
|
||||
|
||||
var deniedCountryIds = []int64{}
|
||||
var allowedCountryIds = []int64{}
|
||||
var countryHTML = ""
|
||||
@@ -95,6 +96,24 @@ func (this *CountriesAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||
|
||||
// 获取当前服务所在集群的WAF设置
|
||||
clusterFirewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyWithServerId(this.AdminContext(), params.ServerId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if clusterFirewallPolicy != nil {
|
||||
this.Data["clusterFirewallPolicy"] = maps.Map{
|
||||
"id": clusterFirewallPolicy.Id,
|
||||
"name": clusterFirewallPolicy.Name,
|
||||
"isOn": clusterFirewallPolicy.IsOn,
|
||||
"mode": clusterFirewallPolicy.Mode,
|
||||
"modeInfo": firewallconfigs.FindFirewallMode(clusterFirewallPolicy.Mode),
|
||||
}
|
||||
} else {
|
||||
this.Data["clusterFirewallPolicy"] = nil
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,24 @@ func (this *ProvincesAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||
|
||||
// 获取当前服务所在集群的WAF设置
|
||||
clusterFirewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyWithServerId(this.AdminContext(), params.ServerId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if clusterFirewallPolicy != nil {
|
||||
this.Data["clusterFirewallPolicy"] = maps.Map{
|
||||
"id": clusterFirewallPolicy.Id,
|
||||
"name": clusterFirewallPolicy.Name,
|
||||
"isOn": clusterFirewallPolicy.IsOn,
|
||||
"mode": clusterFirewallPolicy.Mode,
|
||||
"modeInfo": firewallconfigs.FindFirewallMode(clusterFirewallPolicy.Mode),
|
||||
}
|
||||
} else {
|
||||
this.Data["clusterFirewallPolicy"] = nil
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
|
||||
@@ -408,7 +408,8 @@ func (this *MySQLInstaller) Download() (path string, err error) {
|
||||
|
||||
// check latest version
|
||||
this.log("checking mysql latest version ...")
|
||||
var latestVersion = "8.1.0" // default version
|
||||
var latestVersion = "8.2.0" // default version
|
||||
var majorVersion = "8.2"
|
||||
{
|
||||
req, err := http.NewRequest(http.MethodGet, "https://dev.mysql.com/downloads/mysql/", nil)
|
||||
if err != nil {
|
||||
@@ -434,6 +435,10 @@ func (this *MySQLInstaller) Download() (path string, err error) {
|
||||
var matches = reg.FindSubmatch(data)
|
||||
if len(matches) > 0 {
|
||||
latestVersion = string(matches[1])
|
||||
var matchPieces = strings.Split(latestVersion, ".")
|
||||
if len(matchPieces) >= 2 {
|
||||
majorVersion = strings.Join(matchPieces[:2], ".")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,7 +446,7 @@ func (this *MySQLInstaller) Download() (path string, err error) {
|
||||
|
||||
// download
|
||||
this.log("start downloading ...")
|
||||
var downloadURL = "https://cdn.mysql.com/Downloads/MySQL-8.1/mysql-" + latestVersion + "-linux-glibc2.17-x86_64-minimal.tar.xz"
|
||||
var downloadURL = "https://cdn.mysql.com/Downloads/MySQL-" + majorVersion + "/mysql-" + latestVersion + "-linux-glibc2.17-x86_64-minimal.tar.xz"
|
||||
|
||||
{
|
||||
this.log("downloading from url '" + downloadURL + "' ...")
|
||||
|
||||
@@ -71,7 +71,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
|
||||
buffer.Write(typesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 条件操作符
|
||||
@@ -81,7 +81,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
|
||||
buffer.Write(requestOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 请求变量
|
||||
@@ -91,7 +91,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_VARIABLES = ")
|
||||
buffer.Write(requestVariablesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 指标
|
||||
@@ -101,7 +101,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
|
||||
buffer.Write(metricHTTPKeysJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值项目
|
||||
@@ -111,7 +111,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
|
||||
buffer.Write(ipAddrThresholdItemsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值动作
|
||||
@@ -121,7 +121,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
|
||||
buffer.Write(ipAddrThresholdActionsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF操作符
|
||||
@@ -131,7 +131,17 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
|
||||
buffer.Write(wafOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF验证码类型
|
||||
captchaTypesJSON, err := json.Marshal(firewallconfigs.FindAllCaptchaTypes())
|
||||
if err != nil {
|
||||
logs.Println("ComponentsAction marshal captcha types failed: " + err.Error())
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_CAPTCHA_TYPES = ")
|
||||
buffer.Write(captchaTypesJSON)
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
componentsData = buffer.Bytes()
|
||||
|
||||
@@ -13,6 +13,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeUser)).
|
||||
Data("teaMenu", "users").
|
||||
Prefix("/users").
|
||||
Data("teaSubMenu", "users").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
|
||||
|
||||
@@ -147,6 +147,13 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
||||
"module": configloaders.AdminModuleCodeUser,
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Users),
|
||||
"icon": "users",
|
||||
"subItems": []maps.Map{
|
||||
{
|
||||
"name": langs.Message(langCode, codes.AdminMenu_UserList),
|
||||
"url": "/users",
|
||||
"code": "users",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"code": "admins",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
80
web/public/js/components/common/chart-columns-grid.js
Normal file
80
web/public/js/components/common/chart-columns-grid.js
Normal file
@@ -0,0 +1,80 @@
|
||||
Vue.component("chart-columns-grid", {
|
||||
props: [],
|
||||
mounted: function () {
|
||||
this.columns = this.calculateColumns()
|
||||
|
||||
let that = this
|
||||
window.addEventListener("resize", function () {
|
||||
that.columns = that.calculateColumns()
|
||||
})
|
||||
},
|
||||
updated: function () {
|
||||
let totalElements = this.$el.getElementsByClassName("column").length
|
||||
if (totalElements == this.totalElements) {
|
||||
return
|
||||
}
|
||||
this.totalElements = totalElements
|
||||
this.calculateColumns()
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
columns: "four",
|
||||
totalElements: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
calculateColumns: function () {
|
||||
let w = window.innerWidth
|
||||
let columns = Math.floor(w / 500)
|
||||
if (columns == 0) {
|
||||
columns = 1
|
||||
}
|
||||
|
||||
let columnElements = this.$el.getElementsByClassName("column")
|
||||
if (columnElements.length == 0) {
|
||||
return "one"
|
||||
}
|
||||
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 chart-grid'">
|
||||
<slot></slot>
|
||||
</div>`
|
||||
})
|
||||
@@ -5,6 +5,7 @@ Vue.component("url-patterns-box", {
|
||||
if (this.value != null) {
|
||||
patterns = this.value
|
||||
}
|
||||
|
||||
return {
|
||||
patterns: patterns,
|
||||
isAdding: false,
|
||||
@@ -12,7 +13,9 @@ Vue.component("url-patterns-box", {
|
||||
addingPattern: {"type": "wildcard", "pattern": ""},
|
||||
editingIndex: -1,
|
||||
|
||||
patternIsInvalid: false
|
||||
patternIsInvalid: false,
|
||||
|
||||
windowIsSmall: window.innerWidth < 600
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -105,7 +108,7 @@ Vue.component("url-patterns-box", {
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isAdding" style="margin-top: 0.5em">
|
||||
<div class="ui fields inline">
|
||||
<div :class="{'ui fields inline': !windowIsSmall}">
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown auto-width" v-model="addingPattern.type">
|
||||
<option value="wildcard">通配符</option>
|
||||
|
||||
@@ -34,6 +34,14 @@ Vue.component("traffic-map-box", {
|
||||
},
|
||||
methods: {
|
||||
render: function () {
|
||||
if (this.$el.offsetWidth < 300) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.render()
|
||||
}, 100)
|
||||
return
|
||||
}
|
||||
|
||||
this.chart = teaweb.initChart(document.getElementById("traffic-map-box"));
|
||||
let that = this
|
||||
this.chart.setOption({
|
||||
|
||||
@@ -64,7 +64,7 @@ Vue.component("message-row", {
|
||||
</tr>
|
||||
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
|
||||
<td>
|
||||
{{message.body}}
|
||||
<pre style="padding: 0; margin:0; word-break: break-all;">{{message.body}}</pre>
|
||||
|
||||
<!-- 健康检查 -->
|
||||
<div v-if="message.type == 'HealthCheckFailed'" style="margin-top: 0.8em">
|
||||
|
||||
@@ -338,6 +338,9 @@ Vue.component("http-firewall-actions-box", {
|
||||
this.editingIndex = index
|
||||
|
||||
this.actionCode = config.code
|
||||
this.action = this.actions.$find(function (k, v) {
|
||||
return v.code == config.code
|
||||
})
|
||||
|
||||
switch (config.code) {
|
||||
case "block":
|
||||
@@ -771,7 +774,7 @@ Vue.component("http-firewall-actions-box", {
|
||||
<input type="text" style="width: 5em" maxlength="9" v-model="captchaMaxFails" @keyup.enter="confirm()" @keypress.enter.prevent="1"/>
|
||||
<span class="ui label">次</span>
|
||||
</div>
|
||||
<p class="comment">允许用户失败尝试的最多次数,超过这个次数将被自动加入黑名单;如果为空或者为0表示默认。</p>
|
||||
<p class="comment"><span v-if="captchaMaxFails > 0 && captchaMaxFails < 5" class="red">建议填入一个不小于5的数字,以减少误判几率。</span>允许用户失败尝试的最多次数,超过这个次数将被自动加入黑名单;如果为空或者为0表示默认。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="actionCode == 'captcha'">
|
||||
|
||||
@@ -25,7 +25,8 @@ Vue.component("http-firewall-captcha-options-viewer", {
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
summary: ""
|
||||
summary: "",
|
||||
captchaTypes: window.WAF_CAPTCHA_TYPES
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -43,8 +44,18 @@ Vue.component("http-firewall-captcha-options-viewer", {
|
||||
if (this.options.failBlockScopeAll) {
|
||||
summaryList.push("全局封禁")
|
||||
}
|
||||
if (this.options.uiIsOn) {
|
||||
summaryList.push("定制UI")
|
||||
let that = this
|
||||
let typeDef = this.captchaTypes.$find(function (k, v) {
|
||||
return v.code == that.options.captchaType
|
||||
})
|
||||
if (typeDef != null) {
|
||||
summaryList.push("默认验证方式:" + typeDef.name)
|
||||
}
|
||||
|
||||
if (this.options.captchaType == "default") {
|
||||
if (this.options.uiIsOn) {
|
||||
summaryList.push("定制UI")
|
||||
}
|
||||
}
|
||||
if (summaryList.length == 0) {
|
||||
this.summary = "默认配置"
|
||||
|
||||
@@ -7,6 +7,7 @@ Vue.component("http-firewall-captcha-options", {
|
||||
let options = this.vCaptchaOptions
|
||||
if (options == null) {
|
||||
options = {
|
||||
captchaType: "default",
|
||||
countLetters: 0,
|
||||
life: 0,
|
||||
maxFails: 0,
|
||||
@@ -27,11 +28,17 @@ Vue.component("http-firewall-captcha-options", {
|
||||
if (options.countLetters <= 0) {
|
||||
options.countLetters = 6
|
||||
}
|
||||
|
||||
if (options.captchaType == null || options.captchaType.length == 0) {
|
||||
options.captchaType = "default"
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
isEditing: false,
|
||||
summary: "",
|
||||
uiBodyWarning: ""
|
||||
uiBodyWarning: "",
|
||||
captchaTypes: window.WAF_CAPTCHA_TYPES
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -73,6 +80,9 @@ Vue.component("http-firewall-captcha-options", {
|
||||
"options.failBlockScopeAll": function (v) {
|
||||
this.updateSummary()
|
||||
},
|
||||
"options.captchaType": function (v) {
|
||||
this.updateSummary()
|
||||
},
|
||||
"options.uiIsOn": function (v) {
|
||||
this.updateSummary()
|
||||
},
|
||||
@@ -102,9 +112,21 @@ Vue.component("http-firewall-captcha-options", {
|
||||
if (this.options.failBlockScopeAll) {
|
||||
summaryList.push("全局封禁")
|
||||
}
|
||||
if (this.options.uiIsOn) {
|
||||
summaryList.push("定制UI")
|
||||
|
||||
let that = this
|
||||
let typeDef = this.captchaTypes.$find(function (k, v) {
|
||||
return v.code == that.options.captchaType
|
||||
})
|
||||
if (typeDef != null) {
|
||||
summaryList.push("默认验证方式:" + typeDef.name)
|
||||
}
|
||||
|
||||
if (this.options.captchaType == "default") {
|
||||
if (this.options.uiIsOn) {
|
||||
summaryList.push("定制UI")
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryList.length == 0) {
|
||||
this.summary = "默认配置"
|
||||
} else {
|
||||
@@ -121,6 +143,15 @@ Vue.component("http-firewall-captcha-options", {
|
||||
<div v-show="isEditing" style="margin-top: 0.5em">
|
||||
<table class="ui table definition selectable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>默认验证方式</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="options.captchaType">
|
||||
<option v-for="captchaDef in captchaTypes" :value="captchaDef.code">{{captchaDef.name}}</option>
|
||||
</select>
|
||||
<p class="comment" v-for="captchaDef in captchaTypes" v-if="captchaDef.code == options.captchaType">{{captchaDef.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">有效时间</td>
|
||||
<td>
|
||||
@@ -138,7 +169,7 @@ Vue.component("http-firewall-captcha-options", {
|
||||
<input type="text" style="width: 5em" maxlength="9" v-model="options.maxFails" @keyup.enter="confirm()" @keypress.enter.prevent="1"/>
|
||||
<span class="ui label">次</span>
|
||||
</div>
|
||||
<p class="comment">允许用户失败尝试的最多次数,超过这个次数将被自动加入黑名单。如果为空或者为0,表示不限制。</p>
|
||||
<p class="comment"><span v-if="options.maxFails > 0 && options.maxFails < 5" class="red">建议填入一个不小于5的数字,以减少误判几率。</span>允许用户失败尝试的最多次数,超过这个次数将被自动加入黑名单。如果为空或者为0,表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -158,7 +189,8 @@ Vue.component("http-firewall-captcha-options", {
|
||||
<p class="comment">是否在失败时全局封禁,默认为只封禁对单个网站的访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<tr v-show="options.captchaType == 'default'">
|
||||
<td>验证码中数字个数</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="options.countLetters">
|
||||
@@ -166,12 +198,13 @@ Vue.component("http-firewall-captcha-options", {
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="options.captchaType == 'default'">
|
||||
<td class="color-border">定制UI</td>
|
||||
<td><checkbox v-model="options.uiIsOn"></checkbox></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
<tbody v-show="options.uiIsOn">
|
||||
<tbody v-show="options.uiIsOn && options.captchaType == 'default'">
|
||||
<tr>
|
||||
<td class="color-border">页面标题</td>
|
||||
<td>
|
||||
|
||||
@@ -7,14 +7,20 @@ Vue.component("http-firewall-config-box", {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
firewallPolicyId: 0,
|
||||
ignoreGlobalRules: false
|
||||
ignoreGlobalRules: false,
|
||||
defaultCaptchaType: "none"
|
||||
}
|
||||
}
|
||||
|
||||
if (firewall.defaultCaptchaType == null || firewall.defaultCaptchaType.length == 0) {
|
||||
firewall.defaultCaptchaType = "none"
|
||||
}
|
||||
|
||||
return {
|
||||
firewall: firewall,
|
||||
moreOptionsVisible: false,
|
||||
execGlobalRules: !firewall.ignoreGlobalRules
|
||||
execGlobalRules: !firewall.ignoreGlobalRules,
|
||||
captchaTypes: window.WAF_CAPTCHA_TYPES
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -46,7 +52,7 @@ Vue.component("http-firewall-config-box", {
|
||||
<prior-checkbox :v-config="firewall" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||
<tbody v-show="(!vIsLocation && !vIsGroup) || firewall.isPrior">
|
||||
<tr>
|
||||
<td class="title">启用WAF</td>
|
||||
<td class="title">启用Web防火墙</td>
|
||||
<td>
|
||||
<checkbox v-model="firewall.isOn"></checkbox>
|
||||
<p class="comment">选中后,表示启用当前网站的WAF功能。</p>
|
||||
@@ -55,6 +61,17 @@ Vue.component("http-firewall-config-box", {
|
||||
</tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible" v-show="firewall.isOn"></more-options-tbody>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>人机识别验证方式</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="firewall.defaultCaptchaType">
|
||||
<option value="none">默认</option>
|
||||
<option v-for="captchaType in captchaTypes" :value="captchaType.code">{{captchaType.name}}</option>
|
||||
</select>
|
||||
<p class="comment" v-if="firewall.defaultCaptchaType == 'none'">使用系统默认的设置。</p>
|
||||
<p class="comment" v-for="captchaType in captchaTypes" v-if="captchaType.code == firewall.defaultCaptchaType">{{captchaType.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用系统全局规则</td>
|
||||
<td>
|
||||
|
||||
@@ -93,6 +93,7 @@ Vue.component("http-header-policy-box", {
|
||||
},
|
||||
addSettingHeader: function (policyId) {
|
||||
teaweb.popup("/servers/server/settings/headers/createSetPopup?" + this.vParams + "&headerPolicyId=" + policyId + "&type=" + this.type, {
|
||||
height: "22em",
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
@@ -114,6 +115,7 @@ Vue.component("http-header-policy-box", {
|
||||
},
|
||||
updateSettingPopup: function (policyId, headerId) {
|
||||
teaweb.popup("/servers/server/settings/headers/updateSetPopup?" + this.vParams + "&headerPolicyId=" + policyId + "&headerId=" + headerId + "&type=" + this.type, {
|
||||
height: "22em",
|
||||
callback: function () {
|
||||
teaweb.successRefresh("保存成功")
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ Vue.component("http-host-redirect-box", {
|
||||
|
||||
teaweb.popup("/servers/server/settings/redirects/createPopup", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
height: "36em",
|
||||
callback: function (resp) {
|
||||
that.id++
|
||||
resp.data.redirect.id = that.id
|
||||
@@ -60,7 +60,7 @@ Vue.component("http-host-redirect-box", {
|
||||
|
||||
teaweb.popup("/servers/server/settings/redirects/createPopup", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
height: "36em",
|
||||
callback: function (resp) {
|
||||
resp.data.redirect.id = redirect.id
|
||||
Vue.set(that.redirects, index, resp.data.redirect)
|
||||
@@ -119,6 +119,8 @@ Vue.component("http-host-redirect-box", {
|
||||
<grey-label v-if="redirect.matchPrefix">匹配前缀</grey-label>
|
||||
<grey-label v-if="redirect.matchRegexp">正则匹配</grey-label>
|
||||
<grey-label v-if="!redirect.matchPrefix && !redirect.matchRegexp">精准匹配</grey-label>
|
||||
<grey-label v-if="redirect.exceptDomains != null && redirect.exceptDomains.length > 0" v-for="domain in redirect.exceptDomains">排除:{{domain}}</grey-label>
|
||||
<grey-label v-if="redirect.onlyDomains != null && redirect.onlyDomains.length > 0" v-for="domain in redirect.onlyDomains">仅限:{{domain}}</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="redirect.type == 'domain'">
|
||||
|
||||
@@ -48,29 +48,32 @@ Vue.component("http-pages-and-shutdown-box", {
|
||||
addPage: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/server/settings/pages/createPopup", {
|
||||
height: "26em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.pages.push(resp.data.page)
|
||||
that.notifyChange()
|
||||
}
|
||||
})
|
||||
},
|
||||
updatePage: function (pageIndex, pageId) {
|
||||
let that = this
|
||||
teaweb.popup("/servers/server/settings/pages/updatePopup?pageId=" + pageId, {
|
||||
height: "26em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
Vue.set(that.pages, pageIndex, resp.data.page)
|
||||
that.notifyChange()
|
||||
}
|
||||
})
|
||||
},
|
||||
removePage: function (pageIndex) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要移除此页面吗?", function () {
|
||||
teaweb.confirm("确定要删除此自定义页面吗?", function () {
|
||||
that.pages.$remove(pageIndex)
|
||||
that.notifyChange()
|
||||
})
|
||||
},
|
||||
addShutdownHTMLTemplate: function () {
|
||||
this.shutdownConfig.body = `<!DOCTYPE html>
|
||||
this.shutdownConfig.body = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
\t<title>升级中</title>
|
||||
@@ -89,80 +92,157 @@ Vue.component("http-pages-and-shutdown-box", {
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
},
|
||||
notifyChange: function () {
|
||||
let parent = this.$el.parentNode
|
||||
while (true) {
|
||||
if (parent == null) {
|
||||
break
|
||||
}
|
||||
if (parent.tagName == "FORM") {
|
||||
break
|
||||
}
|
||||
parent = parent.parentNode
|
||||
}
|
||||
if (parent != null) {
|
||||
setTimeout(function () {
|
||||
Tea.runActionOn(parent)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="pagesJSON" :value="JSON.stringify(pages)"/>
|
||||
<input type="hidden" name="shutdownJSON" :value="JSON.stringify(shutdownConfig)"/>
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td class="title">自定义页面</td>
|
||||
<td>
|
||||
<div v-if="pages.length > 0">
|
||||
<div class="ui label small basic" v-for="(page,index) in pages">
|
||||
{{page.status}} -> <span v-if="page.bodyType == 'url'">{{page.url}}</span><span v-if="page.bodyType == 'html'">[HTML内容]</span> <a href="" title="修改" @click.prevent="updatePage(index, page.id)"><i class="icon pencil small"></i></a> <a href="" title="删除" @click.prevent="removePage(index)"><i class="icon remove"></i></a>
|
||||
<h4 style="margin-bottom: 0.5em">自定义页面</h4>
|
||||
|
||||
<p class="comment" style="padding-top: 0; margin-top: 0">根据响应状态码返回一些自定义页面,比如404,500等错误页面。</p>
|
||||
|
||||
<div v-if="pages.length > 0">
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="two wide">响应状态码</th>
|
||||
<th>页面类型</th>
|
||||
<th class="two wide">新状态码</th>
|
||||
<th>例外URL</th>
|
||||
<th>限制URL</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="(page,index) in pages">
|
||||
<td>
|
||||
<a href="" @click.prevent="updatePage(index, page.id)">
|
||||
<span v-if="page.status != null && page.status.length == 1">{{page.status[0]}}</span>
|
||||
<span v-else>{{page.status}}</span>
|
||||
|
||||
<i class="icon expand small"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td style="word-break: break-all">
|
||||
<div v-if="page.bodyType == 'url'">
|
||||
{{page.url}}
|
||||
<div>
|
||||
<grey-label>读取URL</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="ui button small" type="button" @click.prevent="addPage()">+</button>
|
||||
</div>
|
||||
<p class="comment">根据响应状态码返回一些自定义页面,比如404,500等错误页面。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>临时关闭页面</td>
|
||||
<td>
|
||||
<div>
|
||||
<table class="ui table selectable definition">
|
||||
<prior-checkbox :v-config="shutdownConfig" v-if="vIsLocation"></prior-checkbox>
|
||||
<tbody v-show="!vIsLocation || shutdownConfig.isPrior">
|
||||
<tr>
|
||||
<td class="title">临时关闭网站</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="shutdownConfig.isOn" />
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,表示临时关闭当前网站,并显示自定义内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-show="(!vIsLocation || shutdownConfig.isPrior) && shutdownConfig.isOn">
|
||||
<tr>
|
||||
<td>显示内容类型 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="shutdownConfig.bodyType">
|
||||
<option value="html">HTML</option>
|
||||
<option value="url">读取URL</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="shutdownConfig.bodyType == 'url'">
|
||||
<td class="title">显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" v-model="shutdownConfig.url" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="shutdownConfig.bodyType == 'html'">
|
||||
<td>显示页面HTML *</td>
|
||||
<td>
|
||||
<textarea name="body" ref="shutdownHTMLBody" v-model="shutdownConfig.body"></textarea>
|
||||
<p class="comment"><a href="" @click.prevent="addShutdownHTMLTemplate">[使用模板]</a>。填写页面的HTML内容,支持请求变量。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>状态码</td>
|
||||
<td><input type="text" size="3" maxlength="3" name="shutdownStatus" style="width:5.2em" placeholder="状态码" v-model="shutdownStatus"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="comment">开启临时关闭页面时,所有请求都会直接显示此页面。可用于临时升级网站或者禁止用户访问某个网页。</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div v-if="page.bodyType == 'redirectURL'">
|
||||
{{page.url}}
|
||||
<div>
|
||||
<grey-label>跳转URL</grey-label>
|
||||
<grey-label v-if="page.newStatus > 0">{{page.newStatus}}</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="page.bodyType == 'html'">
|
||||
[HTML内容]
|
||||
<div>
|
||||
<grey-label v-if="page.newStatus > 0">{{page.newStatus}}</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="page.newStatus > 0">{{page.newStatus}}</span>
|
||||
<span v-else class="disabled">保持</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="page.exceptURLPatterns != null && page.exceptURLPatterns">
|
||||
<span v-for="urlPattern in page.exceptURLPatterns" class="ui basic label small">{{urlPattern.pattern}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="page.onlyURLPatterns != null && page.onlyURLPatterns">
|
||||
<span v-for="urlPattern in page.onlyURLPatterns" class="ui basic label small">{{urlPattern.pattern}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" title="修改" @click.prevent="updatePage(index, page.id)">修改</a>
|
||||
<a href="" title="删除" @click.prevent="removePage(index)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="margin-top: 1em">
|
||||
<button class="ui button small" type="button" @click.prevent="addPage()">+添加自定义页面</button>
|
||||
</div>
|
||||
|
||||
<h4 style="margin-top: 2em;">临时关闭页面</h4>
|
||||
<p class="comment" style="margin-top: 0; padding-top: 0">开启临时关闭页面时,所有请求都会直接显示此页面。可用于临时升级网站或者禁止用户访问某个网页。</p>
|
||||
<div>
|
||||
<table class="ui table selectable definition">
|
||||
<prior-checkbox :v-config="shutdownConfig" v-if="vIsLocation"></prior-checkbox>
|
||||
<tbody v-show="!vIsLocation || shutdownConfig.isPrior">
|
||||
<tr>
|
||||
<td class="title">启用临时关闭网站</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="shutdownConfig.isOn" />
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,表示临时关闭当前网站,并显示自定义内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-show="(!vIsLocation || shutdownConfig.isPrior) && shutdownConfig.isOn">
|
||||
<tr>
|
||||
<td>显示内容类型 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="shutdownConfig.bodyType">
|
||||
<option value="html">HTML</option>
|
||||
<option value="url">读取URL</option>
|
||||
<option value="redirectURL">跳转URL</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="shutdownConfig.bodyType == 'url'">
|
||||
<td class="title">显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" v-model="shutdownConfig.url" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="shutdownConfig.bodyType == 'redirectURL'">
|
||||
<td class="title">跳转到URL *</td>
|
||||
<td>
|
||||
<input type="text" v-model="shutdownConfig.url" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将会跳转到此URL。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="shutdownConfig.bodyType == 'html'">
|
||||
<td>显示页面HTML *</td>
|
||||
<td>
|
||||
<textarea name="body" ref="shutdownHTMLBody" v-model="shutdownConfig.body"></textarea>
|
||||
<p class="comment"><a href="" @click.prevent="addShutdownHTMLTemplate">[使用模板]</a>。填写页面的HTML内容,支持请求变量。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>状态码</td>
|
||||
<td><input type="text" size="3" maxlength="3" name="shutdownStatus" style="width:5.2em" placeholder="状态码" v-model="shutdownStatus"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ui margin"></div>
|
||||
</div>`
|
||||
})
|
||||
@@ -17,6 +17,7 @@ Vue.component("http-pages-box", {
|
||||
height: "26em",
|
||||
callback: function (resp) {
|
||||
that.pages.push(resp.data.page)
|
||||
that.notifyChange()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -26,6 +27,7 @@ Vue.component("http-pages-box", {
|
||||
height: "26em",
|
||||
callback: function (resp) {
|
||||
Vue.set(that.pages, pageIndex, resp.data.page)
|
||||
that.notifyChange()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -33,28 +35,98 @@ Vue.component("http-pages-box", {
|
||||
let that = this
|
||||
teaweb.confirm("确定要移除此页面吗?", function () {
|
||||
that.pages.$remove(pageIndex)
|
||||
that.notifyChange()
|
||||
})
|
||||
},
|
||||
notifyChange: function () {
|
||||
let parent = this.$el.parentNode
|
||||
while (true) {
|
||||
if (parent == null) {
|
||||
break
|
||||
}
|
||||
if (parent.tagName == "FORM") {
|
||||
break
|
||||
}
|
||||
parent = parent.parentNode
|
||||
}
|
||||
if (parent != null) {
|
||||
setTimeout(function () {
|
||||
Tea.runActionOn(parent)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="pagesJSON" :value="JSON.stringify(pages)"/>
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td class="title">自定义页面</td>
|
||||
<td>
|
||||
<div v-if="pages.length > 0">
|
||||
<div class="ui label small basic" v-for="(page,index) in pages">
|
||||
{{page.status}} -> <span v-if="page.bodyType == 'url'">{{page.url}}</span><span v-if="page.bodyType == 'html'">[HTML内容]</span> <a href="" title="修改" @click.prevent="updatePage(index, page.id)"><i class="icon pencil small"></i></a> <a href="" title="删除" @click.prevent="removePage(index)"><i class="icon remove"></i></a>
|
||||
|
||||
<div v-if="pages.length > 0">
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="two wide">响应状态码</th>
|
||||
<th>页面类型</th>
|
||||
<th class="two wide">新状态码</th>
|
||||
<th>例外URL</th>
|
||||
<th>限制URL</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="(page,index) in pages">
|
||||
<td>
|
||||
<a href="" @click.prevent="updatePage(index, page.id)">
|
||||
<span v-if="page.status != null && page.status.length == 1">{{page.status[0]}}</span>
|
||||
<span v-else>{{page.status}}</span>
|
||||
|
||||
<i class="icon expand small"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td style="word-break: break-all">
|
||||
<div v-if="page.bodyType == 'url'">
|
||||
{{page.url}}
|
||||
<div>
|
||||
<grey-label>读取URL</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="ui button small" type="button" @click.prevent="addPage()">+</button>
|
||||
</div>
|
||||
<p class="comment">根据响应状态码返回一些自定义页面,比如404,500等错误页面。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div v-if="page.bodyType == 'redirectURL'">
|
||||
{{page.url}}
|
||||
<div>
|
||||
<grey-label>跳转URL</grey-label>
|
||||
<grey-label v-if="page.newStatus > 0">{{page.newStatus}}</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="page.bodyType == 'html'">
|
||||
[HTML内容]
|
||||
<div>
|
||||
<grey-label v-if="page.newStatus > 0">{{page.newStatus}}</grey-label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="page.newStatus > 0">{{page.newStatus}}</span>
|
||||
<span v-else class="disabled">保持</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="page.exceptURLPatterns != null && page.exceptURLPatterns">
|
||||
<span v-for="urlPattern in page.exceptURLPatterns" class="ui basic label small">{{urlPattern.pattern}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="page.onlyURLPatterns != null && page.onlyURLPatterns">
|
||||
<span v-for="urlPattern in page.onlyURLPatterns" class="ui basic label small">{{urlPattern.pattern}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" title="修改" @click.prevent="updatePage(index, page.id)">修改</a>
|
||||
<a href="" title="删除" @click.prevent="removePage(index)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="margin-top: 1em">
|
||||
<button class="ui button small" type="button" @click.prevent="addPage()">+添加自定义页面</button>
|
||||
</div>
|
||||
<div class="ui margin"></div>
|
||||
</div>`
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
// 指标图表
|
||||
Vue.component("metric-chart", {
|
||||
props: ["v-chart", "v-stats", "v-item"],
|
||||
props: ["v-chart", "v-stats", "v-item", "v-column" /** in column? **/],
|
||||
mounted: function () {
|
||||
this.load()
|
||||
},
|
||||
@@ -188,7 +188,7 @@ Vue.component("metric-chart", {
|
||||
color: teaweb.DefaultChartColor
|
||||
},
|
||||
areaStyle: {},
|
||||
barWidth: "20em"
|
||||
barWidth: "10em"
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -346,7 +346,7 @@ Vue.component("metric-chart", {
|
||||
color: teaweb.DefaultChartColor
|
||||
},
|
||||
areaStyle: {},
|
||||
barWidth: "20em"
|
||||
barWidth: "10em"
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -414,7 +414,7 @@ Vue.component("metric-chart", {
|
||||
return time
|
||||
}
|
||||
},
|
||||
template: `<div style="float: left" :style="{'width': width}">
|
||||
template: `<div style="float: left" :style="{'width': this.vColumn ? '' : width}" :class="{'ui column':this.vColumn}">
|
||||
<h4>{{chart.name}} <span>({{valueTypeName}})</span></h4>
|
||||
<div class="ui divider"></div>
|
||||
<div style="height: 14em; padding-bottom: 1em; " :id="chartId" :class="{'scroll-box': chart.type == 'table'}"></div>
|
||||
|
||||
@@ -26,7 +26,8 @@ Vue.component("reverse-proxy-box", {
|
||||
maxConns: 0,
|
||||
maxIdleConns: 0,
|
||||
followRedirects: false,
|
||||
retry50X: true
|
||||
retry50X: false,
|
||||
retry40X: false
|
||||
}
|
||||
}
|
||||
if (reverseProxyConfig.addHeaders == null) {
|
||||
@@ -236,6 +237,38 @@ Vue.component("reverse-proxy-box", {
|
||||
<p class="comment">开启后将自动刷新缓冲区数据到客户端,在类似于SSE(server-sent events)等场景下很有用。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动重试50X</td>
|
||||
<td>
|
||||
<checkbox v-model="reverseProxyConfig.retry50X"></checkbox>
|
||||
<p class="comment">选中后,表示当源站返回状态码为50X(比如502、504等)时,自动重试其他源站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动重试40X</td>
|
||||
<td>
|
||||
<checkbox v-model="reverseProxyConfig.retry40X"></checkbox>
|
||||
<p class="comment">选中后,表示当源站返回状态码为40X(403或404)时,自动重试其他源站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix'">
|
||||
<td>PROXY Protocol</td>
|
||||
<td>
|
||||
<checkbox name="proxyProtocolIsOn" v-model="reverseProxyConfig.proxyProtocol.isOn"></checkbox>
|
||||
<p class="comment">选中后表示启用PROXY Protocol,每次连接源站时都会在头部写入客户端地址信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix' && reverseProxyConfig.proxyProtocol.isOn">
|
||||
<td>PROXY Protocol版本</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="proxyProtocolVersion" v-model="reverseProxyConfig.proxyProtocol.version">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
<p class="comment" v-if="reverseProxyConfig.proxyProtocol.version == 1">发送类似于<code-label>PROXY TCP4 192.168.1.1 192.168.1.10 32567 443</code-label>的头部信息。</p>
|
||||
<p class="comment" v-if="reverseProxyConfig.proxyProtocol.version == 2">发送二进制格式的头部信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="family == null || family == 'http'">
|
||||
<td class="color-border">源站连接失败超时时间</td>
|
||||
<td>
|
||||
@@ -300,31 +333,6 @@ Vue.component("reverse-proxy-box", {
|
||||
<p class="comment">源站保持等待的空闲超时时间,0表示使用默认时间。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动重试50X</td>
|
||||
<td>
|
||||
<checkbox v-model="reverseProxyConfig.retry50X"></checkbox>
|
||||
<p class="comment">选中后,表示当源站返回状态码为50X(比如502、504)时,自动重试。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix'">
|
||||
<td>PROXY Protocol</td>
|
||||
<td>
|
||||
<checkbox name="proxyProtocolIsOn" v-model="reverseProxyConfig.proxyProtocol.isOn"></checkbox>
|
||||
<p class="comment">选中后表示启用PROXY Protocol,每次连接源站时都会在头部写入客户端地址信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix' && reverseProxyConfig.proxyProtocol.isOn">
|
||||
<td>PROXY Protocol版本</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="proxyProtocolVersion" v-model="reverseProxyConfig.proxyProtocol.version">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
<p class="comment" v-if="reverseProxyConfig.proxyProtocol.version == 1">发送类似于<code-label>PROXY TCP4 192.168.1.1 192.168.1.10 32567 443</code-label>的头部信息。</p>
|
||||
<p class="comment" v-if="reverseProxyConfig.proxyProtocol.version == 2">发送二进制格式的头部信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
|
||||
@@ -592,7 +592,8 @@ window.teaweb = {
|
||||
itemStyle: {
|
||||
color: this.DefaultChartColor
|
||||
},
|
||||
barWidth: "20em"
|
||||
barWidth: "10em",
|
||||
areaStyle: {}
|
||||
}
|
||||
],
|
||||
animation: true,
|
||||
|
||||
2
web/public/js/utils.min.js
vendored
2
web/public/js/utils.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -41,10 +41,37 @@
|
||||
}
|
||||
|
||||
.column:hover {
|
||||
background: rgba(0, 0, 0, .03)!important;
|
||||
background: rgba(0, 0, 0, .03) !important;
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grid.chart-grid {
|
||||
margin-top: 1em !important;
|
||||
margin-left: 0.4em !important;
|
||||
|
||||
.column {
|
||||
margin-bottom: 1em;
|
||||
border: 1px rgba(0, 0, 0, .1) solid;
|
||||
border-right: 0;
|
||||
|
||||
.menu {
|
||||
margin-top: -0.6em !important;
|
||||
margin-bottom: -0.6em !important;
|
||||
}
|
||||
|
||||
h4 {
|
||||
span {
|
||||
font-size: 0.8em;
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column.with-border {
|
||||
border-right: 1px rgba(0, 0, 0, .1) solid;
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,26 @@ body.expanded .right-box {
|
||||
.grid.counter-chart .column:hover a {
|
||||
display: inline;
|
||||
}
|
||||
.grid.chart-grid {
|
||||
margin-top: 1em !important;
|
||||
margin-left: 0.4em !important;
|
||||
}
|
||||
.grid.chart-grid .column {
|
||||
margin-bottom: 1em;
|
||||
border: 1px rgba(0, 0, 0, 0.1) solid;
|
||||
border-right: 0;
|
||||
}
|
||||
.grid.chart-grid .column .menu {
|
||||
margin-top: -0.6em !important;
|
||||
margin-bottom: -0.6em !important;
|
||||
}
|
||||
.grid.chart-grid .column h4 span {
|
||||
font-size: 0.8em;
|
||||
color: grey;
|
||||
}
|
||||
.grid.chart-grid .column.with-border {
|
||||
border-right: 1px rgba(0, 0, 0, 0.1) solid;
|
||||
}
|
||||
/** 通用 **/
|
||||
* {
|
||||
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -107,6 +107,7 @@ secret: "{{node.secret}}"</source-code-box>
|
||||
<source-code-box id="rpc-code" type="text/yaml">rpc.endpoints: [ {{apiEndpoints}} ]
|
||||
nodeId: "{{node.uniqueId}}"
|
||||
secret: "{{node.secret}}"</source-code-box>
|
||||
<p class="comment">每个节点的配置文件内容均不相同,不能混用。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{$layout}
|
||||
{$template "/clusters/cluster/node/node_menu"}
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
|
||||
<div>
|
||||
<second-menu>
|
||||
<menu-item @click.prevent="createThreshold">[添加阈值]</menu-item>
|
||||
</second-menu>
|
||||
</div>
|
||||
|
||||
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
|
||||
<table class="ui table selectable celled" v-if="thresholds.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>监控项</th>
|
||||
<th>参数</th>
|
||||
<th>操作符</th>
|
||||
<th>对比值</th>
|
||||
<th>统计时间段</th>
|
||||
<th class="two wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="threshold in thresholds">
|
||||
<td>{{threshold.itemName}}</td>
|
||||
<td>{{threshold.paramName}}</td>
|
||||
<td>{{threshold.operatorName}}</td>
|
||||
<td>{{threshold.value}}</td>
|
||||
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="threshold.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,41 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.createThreshold = function () {
|
||||
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/createPopup", {
|
||||
clusterId: this.clusterId,
|
||||
nodeId: this.nodeId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateThreshold = function (thresholdId) {
|
||||
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/updatePopup", {
|
||||
thresholdId: thresholdId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteThreshold = function (thresholdId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除这个阈值吗?", function () {
|
||||
that.$post("/clusters/cluster/settings/thresholds/delete")
|
||||
.params({
|
||||
thresholdId: thresholdId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
<first-menu>
|
||||
<menu-item :href="'.?clusterId=' + clusterId" code="index">DNS设置</menu-item>
|
||||
<menu-item :href="'.records?clusterId=' + clusterId" code="records">解析记录</menu-item>
|
||||
</first-menu>
|
||||
@@ -3,7 +3,8 @@
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
<p class="comment"><a :href="'/dns/clusters/cluster?clusterId=' + clusterId">查看DNS解析记录 »</a></p>
|
||||
{$template "menu"}
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||
<csrf-token></csrf-token>
|
||||
@@ -61,7 +62,7 @@
|
||||
<td>包含Ln节点</td>
|
||||
<td>
|
||||
<checkbox name="includingLnNodes" v-model="includingLnNodes"></checkbox>
|
||||
<p class="comment">选中后,表示域名解析中包含L2及以上级别节点。</p>
|
||||
<p class="comment">选中后,表示域名解析中包含L2及以上级别节点,也就意味着用户请求可能会被分配到这些节点。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
203
web/views/@default/clusters/cluster/settings/dns/records.html
Normal file
203
web/views/@default/clusters/cluster/settings/dns/records.html
Normal file
@@ -0,0 +1,203 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
{$template "menu"}
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">DNS子域名</td>
|
||||
<td>
|
||||
<span v-if="dnsInfo.domainName.length > 0"><var>{{dnsInfo.dnsName}}</var>.{{dnsInfo.domainName}}</span>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DNS服务商</td>
|
||||
<td>
|
||||
<div v-if="dnsInfo.providerName.length > 0">
|
||||
<link-icon :href="'/dns/providers/provider?providerId=' + dnsInfo.providerId">{{dnsInfo.providerTypeName}} - {{dnsInfo.providerName}}</link-icon>
|
||||
</div>
|
||||
<span v-else-if="dnsInfo.domainName.length == 0" class="disabled">请先设置域名</span>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>自动设置CNAME记录</td>
|
||||
<td>
|
||||
<span v-if="dnsInfo.cnameRecords.length == 0" class="disabled">暂时还没有设置。</span>
|
||||
<div v-else>
|
||||
<span v-for="record in dnsInfo.cnameRecords" class="ui label basic small">{{record}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="dnsInfo.domainName.length > 0">
|
||||
<td>操作</td>
|
||||
<td>
|
||||
<div v-if="!isSyncing">
|
||||
<link-red v-if="dnsHasChanges" @click.prevent="syncCluster(cluster.id)">检测到解析记录有变化,需要同步</link-red>
|
||||
<a href="" @click.prevent="syncCluster(cluster.id)" v-else>DNS服务商同步</a>
|
||||
</div>
|
||||
<span v-else>DNS服务商同步中...</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- 当前任务 -->
|
||||
<div v-if="tasks.length > 0">
|
||||
<h3>正在执行的任务</h3>
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>对象</th>
|
||||
<th>任务</th>
|
||||
<th>状态</th>
|
||||
<th>触发时间</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="task in tasks">
|
||||
<td>
|
||||
<span v-if="(task.type == 'clusterChange' || task.type == 'clusterNodesChange') && task.cluster != null">{{task.cluster.name}}
|
||||
<link-icon :href="'/dns/clusters/cluster?clusterId=' + task.cluster.id" target="_top"></link-icon>
|
||||
</span>
|
||||
<span v-if="task.type == 'nodeChange'">{{task.node.name}}</span>
|
||||
<span v-if="task.type == 'serverChange'">{{task.server.name}}</span>
|
||||
<span v-if="task.type == 'domainChange'">{{task.domain.name}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="task.type == 'clusterChange' || task.type == 'clusterNodesChange'">集群</span>
|
||||
<span v-if="task.type == 'nodeChange'">节点</span>
|
||||
<span v-if="task.type == 'serverChange'">网站</span>
|
||||
<span v-if="task.type == 'domainChange'">域名</span>
|
||||
</td>
|
||||
<td style="word-break: break-word; width: 26em">
|
||||
<span v-if="task.isDone" class="red">{{task.error}}</span>
|
||||
<span v-else>正在同步...</span>
|
||||
</td>
|
||||
<td>{{task.updatedTime}}</td>
|
||||
<td>
|
||||
<a href="" title="删除" class="remove-btn" @click.prevent="deleteTask(task.id)"><i class="icon remove small grey"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 问题合集 -->
|
||||
<div v-if="issues.length > 0">
|
||||
<h3>需要修复的问题</h3>
|
||||
<table class="ui table selectable celled" v-if="issues.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">问题对象</th>
|
||||
<th>问题描述</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="issue in issues">
|
||||
<td>
|
||||
<div v-if="issue.type == 'cluster'">
|
||||
集群 "{{issue.target}}" <link-icon :href="'/clusters/cluster?clusterId=' + issue.targetId"></link-icon>
|
||||
</div>
|
||||
<div v-if="issue.type == 'node'">
|
||||
集群 "{{issue.params.clusterName}}" 节点 "{{issue.target}}" <link-icon :href="'/clusters/cluster/node?clusterId=' + issue.params.clusterId + '&nodeId=' + issue.targetId"></link-icon>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span>{{issue.description}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="issue.type == 'cluster'">
|
||||
<link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red>
|
||||
</div>
|
||||
<div v-if="issue.type == 'node'">
|
||||
<link-red @click.prevent="updateNode(issue.params.clusterId, issue.targetId)">修复</link-red>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>
|
||||
|
||||
<p class="comment">下面的DNS解析记录也可以手工在DNS服务商提供的管理平台添加。</p>
|
||||
|
||||
<!-- 节点DNS解析记录 -->
|
||||
<h3>节点DNS解析记录 <span> ({{nodes.length}}个)</span></h3>
|
||||
<p class="comment" v-if="nodes.length == 0">暂时没有需要设置的DNS记录。</p>
|
||||
<table class="ui table selectable celled" v-if="nodes.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>节点</th>
|
||||
<th>子域名</th>
|
||||
<th>记录类型</th>
|
||||
<th>记录值</th>
|
||||
<th>线路</th>
|
||||
<th>状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="node in nodes">
|
||||
<td><link-icon :href="'/clusters/cluster/node?clusterId=' + node.clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon></td>
|
||||
<td>
|
||||
<span v-if="dnsInfo.dnsName.length > 0">{{dnsInfo.dnsName}}</span>
|
||||
<link-red v-else @click.prevent="updateCluster(cluster.id)">没有设置</link-red>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="node.ipAddr.indexOf(':') > -1">AAAA</span>
|
||||
<span v-else>A</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="node.ipAddr.length > 0">{{node.ipAddr}}</span>
|
||||
<link-red title="点击设置" v-else @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">没有设置</link-red>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="node.route.code.length > 0">{{node.route.name}}</span>
|
||||
<link-red v-else title="点击设置" @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">没有设置</link-red>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="node.isBackup" class="red">备用节点</span>
|
||||
<span v-else-if="node.isOffline" class="red">已下线</span>
|
||||
<div v-else="">
|
||||
<span v-if="node.isInstalled">
|
||||
<span class="green" v-if="node.isResolved">已解析</span>
|
||||
<span v-else class="red">未解析</span>
|
||||
</span>
|
||||
<link-red :href="'/clusters/cluster/node/install?clusterId=' + cluster.id + '&nodeId=' + node.id" v-if="!node.isInstalled" title="节点未安装"><span class="red">未安装</span></link-red>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<link-popup @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">修改</link-popup>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- 网站解析记录 -->
|
||||
<h3>网站解析记录 <span> ({{servers.length}}个)</span></h3>
|
||||
<p class="comment" v-if="servers.length == 0">暂时没有需要设置的DNS记录。</p>
|
||||
<table class="ui table selectable celled" v-if="servers.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>网站</th>
|
||||
<th>子域名</th>
|
||||
<th>记录类型</th>
|
||||
<th>记录值</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="server in servers">
|
||||
<td><link-icon :href="'/servers/server?serverId=' + server.id">{{server.name}}</link-icon> </td>
|
||||
<td>{{server.dnsName}}</td>
|
||||
<td>CNAME</td>
|
||||
<td>
|
||||
<span v-if="dnsInfo.domainName.length > 0"><var>{{dnsInfo.dnsName}}</var>.{{dnsInfo.domainName}}.</span>
|
||||
<link-red title="点击设置" v-else @click.prevent="updateCluster(cluster.id)">没有设置</link-red>
|
||||
</td>
|
||||
<td>
|
||||
<span class="green" v-if="server.isResolved">已解析</span>
|
||||
<span v-else class="red">未解析</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
56
web/views/@default/clusters/cluster/settings/dns/records.js
Normal file
56
web/views/@default/clusters/cluster/settings/dns/records.js
Normal file
@@ -0,0 +1,56 @@
|
||||
Tea.context(function () {
|
||||
this.updateCluster = function (clusterId) {
|
||||
teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, {
|
||||
height: "25em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateNode = function (clusterId, nodeId, ipAddrId) {
|
||||
teaweb.popup("/dns/issues/updateNodePopup?clusterId=" + clusterId + "&nodeId=" + nodeId + "&ipAddrId=" + (ipAddrId ? ipAddrId : 0), {
|
||||
width: "46em",
|
||||
height: "26em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.isSyncing = false
|
||||
this.syncCluster = function (clusterId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要执行数据同步吗?", function () {
|
||||
that.isSyncing = true
|
||||
that.$post("/dns/clusters/sync")
|
||||
.params({clusterId: clusterId})
|
||||
.done(function () {
|
||||
that.isSyncing = false
|
||||
that.dnsHasChanges = false
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("同步成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteTask = function (taskId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除这个任务吗?", function () {
|
||||
that.$post("/dns/tasks/delete")
|
||||
.params({
|
||||
taskId: taskId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title color-border">禁止未绑定域名访问</td>
|
||||
<td class="title">禁止未绑定域名访问</td>
|
||||
<td>
|
||||
<checkbox name="httpAllMatchDomainStrictly" v-model="config.httpAll.matchDomainStrictly"></checkbox>
|
||||
<p class="comment">选中后,表示禁止未在网站绑定的域名和IP访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">处理未绑定域名方式</td>
|
||||
<td>
|
||||
<radio name="httpAllDomainMismatchActionCode" :v-value="'page'" v-model="httpAllDomainMismatchActionCode">显示提示页面</radio>
|
||||
@@ -49,14 +49,14 @@
|
||||
</tr>
|
||||
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">允许例外的域名</td>
|
||||
<td>允许例外的域名</td>
|
||||
<td>
|
||||
<domains-box name="httpAllAllowMismatchDomainsJSON" :v-domains="config.httpAll.allowMismatchDomains"></domains-box>
|
||||
<p class="comment">允许这些域名不经过绑定就可以直接访问网站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">默认域名</td>
|
||||
<td>默认域名</td>
|
||||
<td>
|
||||
<input type="text" name="httpAllDefaultDomain" v-model="config.httpAll.defaultDomain"/>
|
||||
<p class="comment">例外域名或使用节点IP访问时使用的默认域名;如果指定的域名在集群里已经绑定到某个网站,则相当于直接访问该网站。</p>
|
||||
@@ -70,7 +70,7 @@
|
||||
<p class="comment">选中后,表示允许直接使用节点IP访问网站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<tr v-show="config.httpAll.matchDomainStrictly && config.httpAll.allowNodeIP">
|
||||
<td class="color-border">访问节点IP显示自定义内容</td>
|
||||
<td>
|
||||
<checkbox name="httpAllNodeIPShowPage" v-model="config.httpAll.nodeIPShowPage"></checkbox>
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>添加阈值</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||
<input type="hidden" name="nodeId" :value="nodeId"/>
|
||||
<input type="hidden" name="sumMethod" value="avg"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">监控项 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
|
||||
<option v-for="item in items" :value="item.code">{{item.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{itemDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>参数 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
|
||||
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{paramDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>操作符 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
|
||||
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>对比值</td>
|
||||
<td>
|
||||
<input type="text" name="value" style="width: 6em" maxlength="10"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>统计时间段 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="duration" value="5" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
<!-- TODO 将来支持更多时间范围 -->
|
||||
<input type="hidden" name="durationUnit" value="minute"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>消息</td>
|
||||
<td>
|
||||
<textarea rows="2" maxlength="100" name="message"></textarea>
|
||||
<p class="comment">触发阈值时的消息提示。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>消息通知间隔</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="notifyDuration" value="10" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,43 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyPopup
|
||||
this.threshold = {
|
||||
item: this.items[0].code,
|
||||
param: "",
|
||||
operator: this.operators[0].code
|
||||
}
|
||||
this.$delay(function () {
|
||||
this.changeItem()
|
||||
this.changeParam()
|
||||
})
|
||||
|
||||
this.itemDescription = ""
|
||||
this.itemParams = []
|
||||
|
||||
this.changeItem = function () {
|
||||
let that = this
|
||||
this.threshold.param = ""
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
that.threshold.param = v.params[0].code
|
||||
that.paramDescription = v.params[0].description
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.paramDescription = ""
|
||||
|
||||
this.changeParam = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
v.params.forEach(function (param) {
|
||||
if (param.code == that.threshold.param) {
|
||||
that.paramDescription = param.description
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
<first-menu>
|
||||
<menu-item @click.prevent="createThreshold">[添加阈值]</menu-item>
|
||||
</first-menu>
|
||||
|
||||
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
|
||||
<table class="ui table selectable celled" v-if="thresholds.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>监控项</th>
|
||||
<th>参数</th>
|
||||
<th>操作符</th>
|
||||
<th>对比值</th>
|
||||
<th>统计时间段</th>
|
||||
<th class="two wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="threshold in thresholds">
|
||||
<td>{{threshold.itemName}}
|
||||
<div v-if="threshold.node != null" style="margin-top: 0.3em">
|
||||
<a :href="'/clusters/cluster/node/settings/thresholds?clusterId=' + clusterId + '&nodeId=' + threshold.node.id" class="ui label basic tiny" title="节点专属阈值设置"><span class="small">节点:{{threshold.node.name}}</span></a>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{threshold.paramName}}</td>
|
||||
<td>{{threshold.operatorName}}</td>
|
||||
<td>{{threshold.value}}</td>
|
||||
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="threshold.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,40 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.createThreshold = function () {
|
||||
teaweb.popup(Tea.url(".createPopup", {
|
||||
clusterId: this.clusterId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateThreshold = function (thresholdId) {
|
||||
teaweb.popup(Tea.url(".updatePopup", {
|
||||
thresholdId: thresholdId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteThreshold = function (thresholdId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除这个阈值吗?", function () {
|
||||
that.$post(".delete")
|
||||
.params({
|
||||
thresholdId: thresholdId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,92 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>修改阈值</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="thresholdId" :value="threshold.id"/>
|
||||
<input type="hidden" name="sumMethod" value="avg"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">监控项 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
|
||||
<option v-for="item in items" :value="item.code">{{item.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{itemDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>参数 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
|
||||
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{paramDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>操作符 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
|
||||
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>对比值</td>
|
||||
<td>
|
||||
<input type="text" name="value" style="width: 6em" maxlength="10" v-model="threshold.value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>统计时间段 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="duration" value="5" style="width: 5em" v-model="threshold.duration"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
<!-- TODO 将来支持更多时间范围 -->
|
||||
<input type="hidden" name="durationUnit" value="minute" v-model="threshold.durationUnit"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>消息</td>
|
||||
<td>
|
||||
<textarea rows="2" maxlength="100" name="message" v-model="threshold.message"></textarea>
|
||||
<p class="comment">触发阈值时的消息提示。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>消息通知间隔</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="notifyDuration" v-model="threshold.notifyDuration" value="10" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<checkbox name="isOn" value="1" v-model="threshold.isOn"></checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,48 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyPopup
|
||||
this.$delay(function () {
|
||||
this.initItem()
|
||||
this.changeParam()
|
||||
})
|
||||
|
||||
this.itemDescription = ""
|
||||
this.itemParams = []
|
||||
|
||||
this.initItem = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.changeItem = function () {
|
||||
let that = this
|
||||
this.threshold.param = ""
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
that.threshold.param = v.params[0].code
|
||||
that.paramDescription = v.params[0].description
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.paramDescription = ""
|
||||
|
||||
this.changeParam = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
v.params.forEach(function (param) {
|
||||
if (param.code == that.threshold.param) {
|
||||
that.paramDescription = param.description
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
<first-menu>
|
||||
<!--<menu-item href="/clusters/monitors" code="index">任务</menu-item>-->
|
||||
<!--<menu-item href="/clusters/monitors/logs" code="log">日志</menu-item>-->
|
||||
<menu-item href="/clusters/monitors/reporters" code="reporter">终端</menu-item>
|
||||
<menu-item href="/clusters/monitors/groups" code="group">分组</menu-item>
|
||||
<menu-item href="/clusters/monitors/settings" code="setting">设置</menu-item>
|
||||
</first-menu>
|
||||
@@ -1,15 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>创建分组</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">分组名称</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,16 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>修改分组</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="groupId" :value="group.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">分组名称</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="group.name"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,23 +0,0 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<second-menu>
|
||||
<menu-item @click.prevent="createGroup">[添加分组]</menu-item>
|
||||
</second-menu>
|
||||
|
||||
<p class="comment" v-if="groups.length == 0">暂时还没有分组。</p>
|
||||
<table class="ui table selectable celled" v-if="groups.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>分组名称</th>
|
||||
<th class="two wide">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="group in groups">
|
||||
<td>{{group.name}}</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateGroup(group.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteGroup(group.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1,31 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.createGroup = function () {
|
||||
teaweb.popup(".createPopup", {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateGroup = function (groupId) {
|
||||
teaweb.popup(".group.updatePopup?groupId=" + groupId, {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteGroup = function (groupId) {
|
||||
teaweb.confirm("确定要删除此分组吗?", function () {
|
||||
this.$post(".group.delete")
|
||||
.params({
|
||||
groupId: groupId
|
||||
})
|
||||
.refresh()
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,60 +0,0 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
<form class="ui form" method="get" action="/clusters/monitors">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
集群:
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">查看</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="tasks.length == 0">暂时还没有任务。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="tasks.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>节点</th>
|
||||
<th>IP</th>
|
||||
<th class="one wide">端口</th>
|
||||
<th>综合延时</th>
|
||||
<th>综合级别</th>
|
||||
<th>综合连通率</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="task in tasks">
|
||||
<td>
|
||||
<span v-if="task.node.id > 0">{{task.node.name}}<link-icon :href="'/clusters/cluster/node?nodeId=' + task.node.id + '&clusterId=' + clusterId"></link-icon></span>
|
||||
</td>
|
||||
<td>
|
||||
{{task.ip}}<link-icon :href="'/clusters/ip-addrs/addr?addrId=' + task.addr.id"></link-icon>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="task.port > 0">{{task.port}}</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="task.costMs > 0" :class="task.color">{{task.costMs}}ms</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="task.color">{{task.levelName}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="task.connectivity > 0" :class="task.color">{{task.connectivity}}%</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
@@ -1,18 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.tasks.forEach(function (v) {
|
||||
switch (v.level) {
|
||||
case "good":
|
||||
v.color = "green"
|
||||
break
|
||||
case "normal":
|
||||
v.color = "blue"
|
||||
break
|
||||
case "bad":
|
||||
v.color = "orange"
|
||||
break
|
||||
case "broken":
|
||||
v.color = "red"
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,2 +0,0 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
@@ -1,47 +0,0 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>添加终端</h3>
|
||||
<form class="ui form" data-tea-success="success" data-tea-action="$">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">终端名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属分组</td>
|
||||
<td>
|
||||
<report-node-groups-selector></report-node-groups-selector>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>区域</td>
|
||||
<td>
|
||||
<input type="text" name="location" maxlength="50"/>
|
||||
<p class="comment">终端所在地域。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>网络提供商</td>
|
||||
<td>
|
||||
<input type="text" name="isp" maxlength="50"/>
|
||||
<p class="comment">终端所属网络供应商。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>允许的终端IP</td>
|
||||
<td>
|
||||
<values-box name="allowIPs"></values-box>
|
||||
<p class="comment">如果不为空,则只有这些IP才能连接API。支持单个IP、CIDR格式的IP段和IP1-IP2这样的IP段。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,77 +0,0 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<second-menu>
|
||||
<menu-item @click.prevent="createReporter">[添加终端]</menu-item>
|
||||
</second-menu>
|
||||
|
||||
<form class="ui form" method="get" action="/clusters/monitors/reporters">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field" v-if="groups.length > 0">
|
||||
<select class="ui dropdown auto-width" name="groupId" v-model="groupId">
|
||||
<option value="0">[全部分组]</option>
|
||||
<option v-for="group in groups" :value="group.id">{{group.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" v-model="keyword" placeholder="名称、地域、IP..."/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
|
||||
<a href="/clusters/monitors/reporters" v-if="groupId > 0 || keyword.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<p class="comment" v-if="reporters.length == 0">暂时还没有终端。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="reporters.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>终端名称</th>
|
||||
<th>区域</th>
|
||||
<th>网络提供商</th>
|
||||
<th>当前IP</th>
|
||||
<th class="one wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="reporter in reporters">
|
||||
<td>
|
||||
<a :href="Tea.url('.reporter', {reporterId: reporter.id})"><keyword :v-word="keyword">{{reporter.name}}</keyword></a>
|
||||
<div v-if="reporter.groups.length > 0">
|
||||
<grey-label v-for="group in reporter.groups">{{group.name}}</grey-label>
|
||||
</div>
|
||||
<div v-if="reporter.shouldUpgrade">
|
||||
<span class="red small" title="需要升级">v{{reporter.status.buildVersion}} -> v{{reporter.newVersion}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="reporter.location.length > 0"><keyword :v-word="keyword">{{reporter.location}}</keyword></span>
|
||||
<span v-else-if="reporter.status.location.length > 0" class="grey"><keyword :v-word="keyword">{{reporter.status.location}}</keyword></span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="reporter.isp.length > 0"><keyword :v-word="keyword">{{reporter.isp}}</keyword></span>
|
||||
<span v-else-if="reporter.status.isp.length > 0" class="grey"><keyword :v-word="keyword">{{reporter.status.isp}}</keyword></span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="reporter.status.ip.length > 0"><keyword :v-word="keyword">{{reporter.status.ip}}</keyword></span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="!reporter.isOn" class="red">禁用</span>
|
||||
<span v-else-if="!reporter.isActive" class="red">离线</span>
|
||||
<span v-else class="green">在线</span>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="Tea.url('.reporter', {reporterId: reporter.id})">详情</a>
|
||||
<a href="" @click.prevent="deleteReporter(reporter.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
@@ -1,25 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.createReporter = function () {
|
||||
teaweb.popup(".createPopup", {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteReporter = function (reporterId) {
|
||||
teaweb.confirm("确定要删除此终端吗?", function () {
|
||||
this.$post(".reporter.delete")
|
||||
.params({
|
||||
reporterId: reporterId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
<first-menu>
|
||||
<menu-item href="/clusters/monitors/reporters">所有终端</menu-item>
|
||||
<span class="disabled item">|</span>
|
||||
<menu-item :href="'/clusters/monitors/reporters/reporter?reporterId=' + reporter.id" code="reporter">"{{reporter.name}}"详情</menu-item>
|
||||
<menu-item :href="'/clusters/monitors/reporters/reporter/results?reporterId=' + reporter.id" code="result">监控结果</menu-item>
|
||||
<menu-item :href="'/clusters/monitors/reporters/reporter/logs?reporterId=' + reporter.id" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/clusters/monitors/reporters/reporter/update?reporterId=' + reporter.id" code="update">修改</menu-item>
|
||||
</first-menu>
|
||||
@@ -1,85 +0,0 @@
|
||||
{$layout}
|
||||
{$template "/code_editor"}
|
||||
{$template "reporter_menu"}
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">终端名称</td>
|
||||
<td>
|
||||
{{reporter.name}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>状态</td>
|
||||
<td>
|
||||
<span v-if="!reporter.isOn" class="red">禁用</span>
|
||||
<span v-else-if="!reporter.isActive" class="red">离线</span>
|
||||
<span v-else class="green">在线</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属分组</td>
|
||||
<td>
|
||||
<div v-if="reporter.groups.length > 0">
|
||||
<span v-for="group in reporter.groups" class="ui label basic tiny">{{group.name}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">全部分组</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>区域</td>
|
||||
<td>
|
||||
<span v-if="reporter.location.length > 0">{{reporter.location}}</span>
|
||||
<span v-else-if="reporter.status.location.length > 0" class="grey">{{reporter.status.location}}</span>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>网络提供商</td>
|
||||
<td>
|
||||
<span v-if="reporter.isp.length > 0">{{reporter.isp}}</span>
|
||||
<span v-else-if="reporter.status.isp.length > 0" class="grey">{{reporter.status.isp}}</span>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>允许的终端IP</td>
|
||||
<td>
|
||||
<div v-if="reporter.allowIPs != null && reporter.allowIPs.length > 0">
|
||||
<span v-for="ip in reporter.allowIPs" class="ui label tiny basic">{{ip}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="reporter.status.buildVersion.length > 0">
|
||||
<td>版本号</td>
|
||||
<td>v{{reporter.status.buildVersion}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4>安装信息</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点ID</td>
|
||||
<td>{{reporter.uniqueId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>节点Secret</td>
|
||||
<td>{{reporter.secret}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>配置文件</td>
|
||||
<td>
|
||||
configs/api_reporter.yaml
|
||||
<download-link :v-element="'rpc-code'" :v-file="'api_reporter.yaml'">[下载]</download-link >
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>配置内容</td>
|
||||
<td>
|
||||
<source-code-box id="rpc-code" type="text/yaml">rpc.endpoints: [ {{apiEndpoints}} ]
|
||||
nodeId: "{{reporter.uniqueId}}"
|
||||
secret: "{{reporter.secret}}"</source-code-box>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1,52 +0,0 @@
|
||||
{$layout}
|
||||
{$template "reporter_menu"}
|
||||
{$template "/datepicker"}
|
||||
|
||||
<div class="margin"></div>
|
||||
|
||||
<form method="get" :action="Tea.url('$')" class="ui form" autocomplete="off">
|
||||
<input type="hidden" name="reporterId" :value="reporter.id"/>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" value="" style="width:8em" id="day-from-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" value="" style="width:8em" id="day-to-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="level" v-model="level">
|
||||
<option value="">[级别]</option>
|
||||
<option value="error">错误</option>
|
||||
<option value="warning">警告</option>
|
||||
<option value="info">信息</option>
|
||||
<option value="success">成功</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" style="width:10em" v-model="keyword" placeholder="关键词"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
</div>
|
||||
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||
<a :href="Tea.url('.logs', { reporterId:reporter.id })">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
||||
|
||||
<table class="ui table selectable" v-if="logs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="log in logs">
|
||||
<td>
|
||||
<node-log-row :v-log="log" :v-keyword="keyword"></node-log-row>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
@@ -1,6 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.$delay(function () {
|
||||
teaweb.datepicker("day-from-picker")
|
||||
teaweb.datepicker("day-to-picker")
|
||||
})
|
||||
})
|
||||
@@ -1,53 +0,0 @@
|
||||
{$layout}
|
||||
{$template "reporter_menu"}
|
||||
|
||||
<div class="ui margin"></div>
|
||||
<form class="ui form" action="/clusters/monitors/reporters/reporter/results">
|
||||
<input type="hidden" name="reporterId" :value="reporter.id"/>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="level" v-model="level">
|
||||
<option value="">[级别]</option>
|
||||
<option v-for="level in levels" :value="level.code">{{level.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
<a :href="Tea.url('$', {reporterId: reporter.id})" v-if="level.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="results.length == 0">暂时还没有监控结果。</p>
|
||||
<table class="ui table celled selectable" v-if="results.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="two wide">类型</th>
|
||||
<th class="two wide">检测时间</th>
|
||||
<th class="four wide">对象</th>
|
||||
<th class="one wide">级别</th>
|
||||
<th class="two wide">耗时</th>
|
||||
<th>错误信息</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="result in results">
|
||||
<td>{{result.typeName}}</td>
|
||||
<td>{{result.updatedTime}}</td>
|
||||
<td>{{result.targetDesc}}
|
||||
<span v-if="result.type == 'ipAddr'"><link-icon :href="'/clusters/ip-addrs/addr?addrId=' + result.targetId"></link-icon></span>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="result.color">{{result.levelName}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="result.isOk" :class="result.color">{{result.costMs}}ms</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="!result.isOk" class="red">{{result.error}}</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
@@ -1,18 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.results.forEach(function (v) {
|
||||
switch (v.level) {
|
||||
case "good":
|
||||
v.color = "green"
|
||||
break
|
||||
case "normal":
|
||||
v.color = "blue"
|
||||
break
|
||||
case "bad":
|
||||
v.color = "orange"
|
||||
break
|
||||
case "broken":
|
||||
v.color = "red"
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,54 +0,0 @@
|
||||
{$layout}
|
||||
{$template "reporter_menu"}
|
||||
|
||||
<form class="ui form" data-tea-success="success" data-tea-action="$">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="reporterId" :value="reporter.id"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">终端名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="reporter.name"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属分组</td>
|
||||
<td>
|
||||
<report-node-groups-selector :v-group-ids="reporter.groupIds"></report-node-groups-selector>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>区域</td>
|
||||
<td>
|
||||
<input type="text" name="location" maxlength="50" v-model="reporter.location"/>
|
||||
<p class="comment">终端所在地域。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>网络提供商</td>
|
||||
<td>
|
||||
<input type="text" name="isp" maxlength="50" v-model="reporter.isp"/>
|
||||
<p class="comment">终端所属网络供应商。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>允许的终端IP</td>
|
||||
<td>
|
||||
<values-box name="allowIPs" :values="reporter.allowIPs"></values-box>
|
||||
<p class="comment">如果不为空,则只有这些IP才能连接API。支持单个IP、CIDR格式的IP段和IP1-IP2这样的IP段。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<checkbox name="isOn" v-model="reporter.isOn"></checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,3 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifySuccess("保存成功", Tea.url(".", {reporterId: this.reporter.id}))
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">提醒连通性阈值</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" maxlength="4" size="2" name="minNotifyConnectivity" v-model="setting.minNotifyConnectivity"/>
|
||||
<span class="ui label">%</span>
|
||||
</div>
|
||||
<p class="comment">取值0到100,当单个被监控对象连通性低于此值时(不包含此值)发送消息提醒。<span v-if="setting.minNotifyConnectivity == 100">100%表示只要有任何访问异常的情形都会发送提醒。</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>通知URL</td>
|
||||
<td>
|
||||
<input type="text" name="notifyWebHookURL" maxlength="1000" v-model="setting.notifyWebHookURL" placeholder="https://..."/>
|
||||
<p class="comment">当达到连通性阈值时将监控对象的信息发送到此URL上。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -1,3 +0,0 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyReloadSuccess("保存成功")
|
||||
})
|
||||
@@ -84,7 +84,7 @@
|
||||
</div>
|
||||
|
||||
<div class="ui column">
|
||||
<h4>服务<link-icon href="/servers" v-if="dashboard.canGoServers"></link-icon></h4>
|
||||
<h4>网站<link-icon href="/servers" v-if="dashboard.canGoServers"></link-icon></h4>
|
||||
<div class="value"><span>{{dashboard.countServers}}</span>个</div>
|
||||
</div>
|
||||
|
||||
@@ -94,30 +94,34 @@
|
||||
</div>
|
||||
</columns-grid>
|
||||
|
||||
<div class="ui divider" v-show="!isLoading"></div>
|
||||
<chart-columns-grid>
|
||||
<div class="ui column">
|
||||
<div class="ui menu text blue" v-show="!isLoading">
|
||||
<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 class="ui divider"></div>
|
||||
|
||||
<div class="ui menu tabular" v-show="!isLoading">
|
||||
<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 class="chart-box" id="hourly-traffic-chart-box" v-show="trafficTab == 'hourly'"></div>
|
||||
|
||||
<!-- 按小时统计 -->
|
||||
<div class="chart-box" id="hourly-traffic-chart-box" v-show="trafficTab == 'hourly'"></div>
|
||||
<!-- 按日统计 -->
|
||||
<div class="chart-box" id="daily-traffic-chart-box" v-show="trafficTab == 'daily'"></div>
|
||||
</div>
|
||||
|
||||
<!-- 按日统计 -->
|
||||
<div class="chart-box" id="daily-traffic-chart-box" v-show="trafficTab == 'daily'"></div>
|
||||
<div class="ui column">
|
||||
<!-- 域名排行 -->
|
||||
<h4 v-show="!isLoading">域名访问排行 <span>(24小时)</span></h4>
|
||||
<div class="ui divider"></div>
|
||||
<div class="chart-box" id="top-domains-chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 域名排行 -->
|
||||
<h4 v-show="!isLoading">域名访问排行 <span>(24小时)</span></h4>
|
||||
<div class="chart-box" id="top-domains-chart"></div>
|
||||
|
||||
<!-- 指标 -->
|
||||
<div class="ui divider" v-if="metricCharts.length > 0"></div>
|
||||
<metric-board>
|
||||
<!-- 指标 -->
|
||||
<metric-chart v-for="chart in metricCharts"
|
||||
:key="chart.id"
|
||||
:v-chart="chart.chart"
|
||||
:v-stats="chart.stats"
|
||||
:v-item="chart.item">
|
||||
:v-item="chart.item"
|
||||
:v-column="true">
|
||||
</metric-chart>
|
||||
</metric-board>
|
||||
</chart-columns-grid>
|
||||
@@ -13,7 +13,7 @@
|
||||
<td><link-icon :href="'/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</link-icon></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>子域名</td>
|
||||
<td>DNS子域名</td>
|
||||
<td>
|
||||
<span v-if="dnsInfo.domainName.length > 0"><var>{{dnsInfo.dnsName}}</var>.{{dnsInfo.domainName}}</span>
|
||||
<span v-else class="disabled">没有设置</span>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>启用当前域名</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="domain.isOn"/>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<option value="">[请选择]</option>
|
||||
<option v-for="type in types" :value="type.code">{{type.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{typeDescription}} <span v-if="!teaIsPlus">购买商业版可获得更多厂商支持。</span></p>
|
||||
<p class="comment" v-if="typeDescription.length > 0">{{typeDescription}} 系统会保留原有域名下的域名解析,请放心使用。<span v-if="!teaIsPlus">购买商业版可获得更多厂商支持。</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -30,14 +30,14 @@
|
||||
<tr>
|
||||
<td>密钥ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramId" maxlength="100"/>
|
||||
<input type="text" name="paramId" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">获取方法参考:<a href="https://docs.dnspod.cn/account/5f2d466de8320f1a740d9ff3/" target="_blank">文档</a> </p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>密钥Token *</td>
|
||||
<td>
|
||||
<input type="text" name="paramToken" maxlength="100"/>
|
||||
<input type="text" name="paramToken" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">获取方法参考:<a href="https://docs.dnspod.cn/account/5f2d466de8320f1a740d9ff3/" target="_blank">文档</a> </p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -57,21 +57,21 @@
|
||||
<tr>
|
||||
<td>AccessKeyId *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAliDNSAccessKeyId" maxlength="100"/>
|
||||
<input type="text" name="paramAliDNSAccessKeyId" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">登录阿里云控制台 -- 在"访问控制"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AccessKeySecret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAliDNSAccessKeySecret" maxlength="100"/>
|
||||
<input type="text" name="paramAliDNSAccessKeySecret" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">登录阿里云控制台 -- 在"访问控制"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>区域ID<optional-label></optional-label></td>
|
||||
<td>
|
||||
<input type="text" name="paramAliDNSRegionId" maxlength="100"/>
|
||||
<input type="text" name="paramAliDNSRegionId" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">阿里云产品所在区域代号,通常不需要填写。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -82,21 +82,21 @@
|
||||
<tr>
|
||||
<td>AccessKeyId *</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiAccessKeyId" maxlength="100"/>
|
||||
<input type="text" name="paramHuaweiAccessKeyId" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">登录华为云控制台 -- 在"我的凭证 -- 访问密钥"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AccessKeySecret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiAccessKeySecret" maxlength="100"/>
|
||||
<input type="text" name="paramHuaweiAccessKeySecret" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">登录华为云控制台 -- 在"我的凭证 -- 访问密钥"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>终端节点</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiEndpoint" maxlength="100"/>
|
||||
<input type="text" name="paramHuaweiEndpoint" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">选填项。可以填写终端节点(Endpoint)区域代号或者域名,参考 <a href="https://developer.huaweicloud.com/endpoint?DNS" target="_blank">https://developer.huaweicloud.com/endpoint?DNS</a>(如果此链接失效,请到华为云开发者中心自行查找)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -107,14 +107,14 @@
|
||||
<tr>
|
||||
<td>API密钥 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramCloudFlareAPIKey" maxlength="100"/>
|
||||
<input type="text" name="paramCloudFlareAPIKey" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">在个人资料中的"API令牌"--"API密钥"--"Global API Key"中获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>账号邮箱 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramCloudFlareEmail" maxlength="100"/>
|
||||
<input type="text" name="paramCloudFlareEmail" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">登录账号使用的邮箱。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -125,14 +125,14 @@
|
||||
<tr>
|
||||
<td>Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramGoDaddyKey" maxlength="100"/>
|
||||
<p class="comment">可以在GoDaddy<a href="https://developer.godaddy.com/keys" target="_blank">开发者中心</a>添加。</p>
|
||||
<input type="text" name="paramGoDaddyKey" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">可以在GoDaddy<a href="https://developer.godaddy.com/keys" target="_blank">开发者中心</a>创建,创建时Environment选择Production。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramGoDaddySecret" maxlength="100"/>
|
||||
<input type="text" name="paramGoDaddySecret" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -142,21 +142,21 @@
|
||||
<tr>
|
||||
<td>用户认证ID<em>(auth-id)</em></td>
|
||||
<td>
|
||||
<input type="text" name="paramClouDNSAuthId" maxlength="20"/>
|
||||
<input type="text" name="paramClouDNSAuthId" maxlength="20" spellcheck="false"/>
|
||||
<p class="comment">和子用户认证ID二选一。可以在ClouDNS<a href="https://www.cloudns.net/api-settings/" target="_blank">API设置页面</a>添加。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>子用户认证ID<em>(sub-auth-id)</em></td>
|
||||
<td>
|
||||
<input type="text" name="paramClouDNSSubAuthId" maxlength="20"/>
|
||||
<input type="text" name="paramClouDNSSubAuthId" maxlength="20" spellcheck="false"/>
|
||||
<p class="comment">和用户认证ID二选一。可以在ClouDNS<a href="https://www.cloudns.net/api-settings/" target="_blank">API设置页面</a>添加。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>认证密码 *<em>(auth-password)</em></td>
|
||||
<td>
|
||||
<input type="password" name="paramClouDNSAuthPassword" maxlength="100"/>
|
||||
<input type="password" name="paramClouDNSAuthPassword" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">用户或者子用户的认证密码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -167,14 +167,14 @@
|
||||
<tr>
|
||||
<td>API Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSComKey" maxlength="100"/>
|
||||
<input type="text" name="paramDNSComKey" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">在DNS.COM控制台账号中心--API设置中创建和查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Secret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSComSecret" maxlength="100"/>
|
||||
<input type="text" name="paramDNSComSecret" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">在DNS.COM控制台账号中心--API设置中创建和查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -186,18 +186,86 @@
|
||||
<td>API ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSLaAPIId" maxlength="100"/>
|
||||
<p class="comment">在DNS.LA控制台我的账户菜单--API设置中创建和查看。</p>
|
||||
<p class="comment">在DNS.LA控制台--账户信息中查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API密钥 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSLaSecret" maxlength="100"/>
|
||||
<p class="comment">在DNS.LA控制台我的账户菜单--API设置中创建和查看。</p>
|
||||
<input type="text" name="paramDNSLaSecret" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">在DNS.LA控制台--账户信息中查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- VolcEngine -->
|
||||
<tbody v-if="type == 'volcEngine'">
|
||||
<tr>
|
||||
<td>Access Key ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramVolcEngineAccessKeyId" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">在火山引擎“访问控制--API访问密钥”中获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret Access Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramVolcEngineAccessKeySecret" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Amazon Route 53 -->
|
||||
<tbody v-if="type == 'amazonRoute53'">
|
||||
<tr>
|
||||
<td>Access Key ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53AccessKeyId" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret Access Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53AccessKeySecret" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API区域</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53Region" maxlength="100" spellcheck="false"/>
|
||||
<p class="comment">通常不需要填写。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Microsoft Azure DNS -->
|
||||
<tbody v-if="type == 'azureDNS'">
|
||||
<tr>
|
||||
<td>Subscription ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAzureDNSSubscriptionId" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tenant ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAzureDNSTenantId" maxlength="100" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client ID *</td>
|
||||
<td><input type="text" name="paramAzureDNSClientId" maxlength="100" spellcheck="false"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client Secret Value *</td>
|
||||
<td><input type="text" name="paramAzureDNSClientSecret" maxlength="100" spellcheck="false"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Resource Group Name *</td>
|
||||
<td><input type="text" name="paramAzureDNSResourceGroupName" maxlength="100" spellcheck="false"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- EdgeDNS -->
|
||||
<tbody v-if="type == 'localEdgeDNS'">
|
||||
<tr>
|
||||
|
||||
@@ -145,6 +145,70 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- VolcEngine -->
|
||||
<tbody v-if="provider.type == 'volcEngine'">
|
||||
<tr>
|
||||
<td class="color-border">Access Key ID</td>
|
||||
<td>{{provider.apiParams.accessKeyId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Secret Access Key</td>
|
||||
<td>{{provider.apiParams.accessKeySecret}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Amazon Route 53 -->
|
||||
<tbody v-if="provider.type == 'amazonRoute53'">
|
||||
<tr>
|
||||
<td class="color-border">Access Key ID</td>
|
||||
<td>{{provider.apiParams.accessKeyId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Secret Access Key</td>
|
||||
<td>{{provider.apiParams.accessKeySecret}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">API区域</td>
|
||||
<td>
|
||||
<span v-if="provider.apiParams.region != null && provider.apiParams.region.length > 0">{{provider.apiParams.region}}</span>
|
||||
<span v-else class="disabled">暂未设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody v-if="provider.type == 'azureDNS'">
|
||||
<tr>
|
||||
<td class="color-border">Subscription ID</td>
|
||||
<td>
|
||||
{{provider.apiParams.subscriptionId}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Tenant ID</td>
|
||||
<td>
|
||||
{{provider.apiParams.tenantId}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Client ID</td>
|
||||
<td>
|
||||
{{provider.apiParams.clientId}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Client Secret Value</td>
|
||||
<td>
|
||||
{{provider.apiParams.clientSecret}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="color-border">Resource Group Name</td>
|
||||
<td>
|
||||
{{provider.apiParams.resourceGroupName}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Local EdgeDNS -->
|
||||
<tbody v-if="provider.type == 'localEdgeDNS'">
|
||||
<tr>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<tr>
|
||||
<td>密钥Token *</td>
|
||||
<td>
|
||||
<input type="text" name="paramToken" maxlength="100" v-model="provider.params.token"/>
|
||||
<input type="text" name="paramToken" maxlength="100" v-model="provider.params.token" spellcheck="false"/>
|
||||
<p class="comment">获取方法参考:<a href="https://docs.dnspod.cn/account/5f2d466de8320f1a740d9ff3/" target="_blank">文档</a> </p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -56,14 +56,14 @@
|
||||
<tr>
|
||||
<td>AccessKeyId *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAliDNSAccessKeyId" maxlength="100" v-model="provider.params.accessKeyId"/>
|
||||
<input type="text" name="paramAliDNSAccessKeyId" maxlength="100" v-model="provider.params.accessKeyId" spellcheck="false"/>
|
||||
<p class="comment">登录阿里云控制台 -- 在"访问控制"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AccessKeySecret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAliDNSAccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret"/>
|
||||
<input type="text" name="paramAliDNSAccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret" spellcheck="false"/>
|
||||
<p class="comment">登录阿里云控制台 -- 在"访问控制"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -81,21 +81,21 @@
|
||||
<tr>
|
||||
<td>AccessKeyId *</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiAccessKeyId" maxlength="100" v-model="provider.params.accessKeyId"/>
|
||||
<input type="text" name="paramHuaweiAccessKeyId" maxlength="100" v-model="provider.params.accessKeyId" spellcheck="false"/>
|
||||
<p class="comment">登录华为云控制台 -- 在"我的凭证 -- 访问密钥"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AccessKeySecret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiAccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret"/>
|
||||
<input type="text" name="paramHuaweiAccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret" spellcheck="false"/>
|
||||
<p class="comment">登录华为云控制台 -- 在"我的凭证 -- 访问密钥"中创建和获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>终端节点</td>
|
||||
<td>
|
||||
<input type="text" name="paramHuaweiEndpoint" maxlength="100" v-model="provider.params.endpoint"/>
|
||||
<input type="text" name="paramHuaweiEndpoint" maxlength="100" v-model="provider.params.endpoint" spellcheck="false"/>
|
||||
<p class="comment">选填项。可以填写终端节点(Endpoint)区域代号或者域名,参考 <a href="https://developer.huaweicloud.com/endpoint?DNS" target="_blank">https://developer.huaweicloud.com/endpoint?DNS</a>(如果此链接失效,请到华为云开发者中心自行查找)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -107,14 +107,14 @@
|
||||
<tr>
|
||||
<td>API密钥 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramCloudFlareAPIKey" maxlength="100" v-model="provider.params.apiKey"/>
|
||||
<input type="text" name="paramCloudFlareAPIKey" maxlength="100" v-model="provider.params.apiKey" spellcheck="false"/>
|
||||
<p class="comment">在个人资料中的"API令牌"--"API密钥"--"Global API Key"中获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>账号邮箱 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramCloudFlareEmail" maxlength="100" v-model="provider.params.email"/>
|
||||
<input type="text" name="paramCloudFlareEmail" maxlength="100" v-model="provider.params.email" spellcheck="false"/>
|
||||
<p class="comment">登录账号使用的邮箱。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -125,14 +125,14 @@
|
||||
<tr>
|
||||
<td>Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramGoDaddyKey" maxlength="100" v-model="provider.params.key"/>
|
||||
<p class="comment">可以在GoDaddy<a href="https://developer.godaddy.com/keys" target="_blank">开发者中心</a>添加。</p>
|
||||
<input type="text" name="paramGoDaddyKey" maxlength="100" v-model="provider.params.key" spellcheck="false"/>
|
||||
<p class="comment">可以在GoDaddy<a href="https://developer.godaddy.com/keys" target="_blank">开发者中心</a>创建,创建时Environment选择Production。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramGoDaddySecret" maxlength="100" v-model="provider.params.secret"/>
|
||||
<input type="text" name="paramGoDaddySecret" maxlength="100" v-model="provider.params.secret" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -142,7 +142,7 @@
|
||||
<tr>
|
||||
<td>用户认证ID<em>(auth-id)</em></td>
|
||||
<td>
|
||||
<input type="text" name="paramClouDNSAuthId" maxlength="20" v-model="provider.params.authId"/>
|
||||
<input type="text" name="paramClouDNSAuthId" maxlength="20" v-model="provider.params.authId" spellcheck="false"/>
|
||||
<p class="comment">和子用户认证ID二选一。可以在ClouDNS<a href="https://www.cloudns.net/api-settings/" target="_blank">API设置页面</a>添加。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -156,7 +156,7 @@
|
||||
<tr>
|
||||
<td>认证密码 *<em>(auth-password)</em></td>
|
||||
<td>
|
||||
<input type="password" name="paramClouDNSAuthPassword" maxlength="100" v-model="provider.params.authPassword"/>
|
||||
<input type="password" name="paramClouDNSAuthPassword" maxlength="100" v-model="provider.params.authPassword" spellcheck="false"/>
|
||||
<p class="comment">用户或者子用户的认证密码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -167,14 +167,14 @@
|
||||
<tr>
|
||||
<td>API Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSComKey" maxlength="100" v-model="provider.params.key"/>
|
||||
<input type="text" name="paramDNSComKey" maxlength="100" v-model="provider.params.key" spellcheck="false"/>
|
||||
<p class="comment">在DNS.COM控制台账号中心--API设置中创建和查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Secret *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSComSecret" maxlength="100" v-model="provider.params.secret"/>
|
||||
<input type="text" name="paramDNSComSecret" maxlength="100" v-model="provider.params.secret" spellcheck="false"/>
|
||||
<p class="comment">在DNS.COM控制台账号中心--API设置中创建和查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -185,19 +185,87 @@
|
||||
<tr>
|
||||
<td>API ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSLaAPIId" maxlength="100" v-model="provider.params.apiId"/>
|
||||
<p class="comment">在DNS.LA控制台我的账户菜单--API设置中创建和查看。</p>
|
||||
<input type="text" name="paramDNSLaAPIId" maxlength="100" v-model="provider.params.apiId" spellcheck="false"/>
|
||||
<p class="comment">在DNS.LA控制台--账户信息中查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API密钥 *</td>
|
||||
<td>
|
||||
<input type="text" name="paramDNSLaSecret" maxlength="100" v-model="provider.params.secret"/>
|
||||
<p class="comment">在DNS.LA控制台我的账户菜单--API设置中创建和查看。</p>
|
||||
<input type="text" name="paramDNSLaSecret" maxlength="100" v-model="provider.params.secret" spellcheck="false"/>
|
||||
<p class="comment">在DNS.LA控制台--账户信息中查看。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- VolcEngine -->
|
||||
<tbody v-if="provider.type == 'volcEngine'">
|
||||
<tr>
|
||||
<td>Access Key ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramVolcEngineAccessKeyId" maxlength="100" v-model="provider.params.accessKeyId" spellcheck="false"/>
|
||||
<p class="comment">在火山引擎“访问控制--API访问密钥”中获取。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret Access Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramVolcEngineAccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Amazon Route 53 -->
|
||||
<tbody v-if="provider.type == 'amazonRoute53'">
|
||||
<tr>
|
||||
<td>Access Key ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53AccessKeyId" maxlength="100" v-model="provider.params.accessKeyId" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret Access Key *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53AccessKeySecret" maxlength="100" v-model="provider.params.accessKeySecret" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API区域</td>
|
||||
<td>
|
||||
<input type="text" name="paramAmazonRoute53Region" maxlength="100" v-model="provider.params.region" spellcheck="false"/>
|
||||
<p class="comment">通常不需要填写。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- Microsoft Azure DNS -->
|
||||
<tbody v-if="provider.type == 'azureDNS'">
|
||||
<tr>
|
||||
<td>Subscription ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAzureDNSSubscriptionId" maxlength="100" v-model="provider.params.subscriptionId" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tenant ID *</td>
|
||||
<td>
|
||||
<input type="text" name="paramAzureDNSTenantId" maxlength="100" v-model="provider.params.tenantId" spellcheck="false"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client ID *</td>
|
||||
<td><input type="text" name="paramAzureDNSClientId" maxlength="100" v-model="provider.params.clientId" spellcheck="false"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client Secret Value *</td>
|
||||
<td><input type="text" name="paramAzureDNSClientSecret" maxlength="100" v-model="provider.params.clientSecret" spellcheck="false"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Resource Group Name *</td>
|
||||
<td><input type="text" name="paramAzureDNSResourceGroupName" maxlength="100" v-model="provider.params.resourceGroupName" spellcheck="false"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- EdgeDNS -->
|
||||
<tbody v-if="provider.type == 'localEdgeDNS'">
|
||||
<tr>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<td>包含Ln节点</td>
|
||||
<td>
|
||||
<checkbox name="includingLnNodes" v-model="includingLnNodes"></checkbox>
|
||||
<p class="comment">选中后,表示域名解析中包含L2及以上级别节点。</p>
|
||||
<p class="comment">选中后,表示域名解析中包含L2及以上级别节点,也就意味着用户请求可能会被分配到这些节点。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -105,7 +105,7 @@ Tea.context(function () {
|
||||
this.doDNS = function () {
|
||||
this.isRequesting = true
|
||||
let that = this
|
||||
|
||||
let taskCreated = false
|
||||
this.$post("$")
|
||||
.params({
|
||||
platformUserId: this.platformUserId,
|
||||
@@ -120,6 +120,7 @@ Tea.context(function () {
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.taskId = resp.data.taskId
|
||||
taskCreated = true
|
||||
|
||||
this.isRequesting = true
|
||||
this.$post(".run")
|
||||
@@ -136,7 +137,9 @@ Tea.context(function () {
|
||||
})
|
||||
})
|
||||
.done(function () {
|
||||
this.isRequesting = false
|
||||
if (!taskCreated) {
|
||||
this.isRequesting = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>验证码动作设置</td>
|
||||
<td>人机识别动作配置</td>
|
||||
<td>
|
||||
<http-firewall-captcha-options-viewer :v-captcha-options="firewallPolicy.captchaOptions"></http-firewall-captcha-options-viewer>
|
||||
</td>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>验证码动作配置</td>
|
||||
<td>人机识别动作配置</td>
|
||||
<td>
|
||||
<http-firewall-captcha-options :v-captcha-options="firewallPolicy.captchaOptions"></http-firewall-captcha-options>
|
||||
</td>
|
||||
|
||||
@@ -22,13 +22,6 @@
|
||||
{{chart.typeName}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>宽度</td>
|
||||
<td>
|
||||
<span v-if="chart.widthDiv == 0">100%</span>
|
||||
<span v-else>1/{{chart.widthDiv}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>对象数限制</td>
|
||||
<td>
|
||||
|
||||
@@ -21,17 +21,6 @@
|
||||
<p class="comment" v-if="typeDefinition != null"><i class="icon" :class="typeDefinition.icon"></i> {{typeDefinition.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>宽度</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="widthDiv">
|
||||
<option value="0">100%</option>
|
||||
<option value="2">1/2</option>
|
||||
<option value="3">1/3</option>
|
||||
<option value="4">1/4</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<tr>
|
||||
<th>图表名称</th>
|
||||
<th>类型</th>
|
||||
<th>宽度</th>
|
||||
<th class="two wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
@@ -20,10 +19,6 @@
|
||||
<tr v-for="chart in charts">
|
||||
<td>{{chart.name}}</td>
|
||||
<td>{{chart.typeName}}</td>
|
||||
<td>
|
||||
<span v-if="chart.widthDiv > 0">1/{{chart.widthDiv}}</span>
|
||||
<span v-else>1</span>
|
||||
</td>
|
||||
<td><label-on :v-is-on="chart.isOn"></label-on></td>
|
||||
<td>
|
||||
<a :href="Tea.url('.chart', {chartId: chart.id})">详情</a>
|
||||
|
||||
@@ -22,17 +22,6 @@
|
||||
<p class="comment" v-if="typeDefinition != null"><i class="icon" :class="typeDefinition.icon"></i> {{typeDefinition.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>宽度</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="widthDiv" v-model="chart.widthDiv">
|
||||
<option value="0">100%</option>
|
||||
<option value="2">1/2</option>
|
||||
<option value="3">1/3</option>
|
||||
<option value="4">1/4</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td><checkbox name="isOn" value="1" v-model="chart.isOn"></checkbox></td>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{$layout "layout_popup"}
|
||||
<h3>添加自定义页面</h3>
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="exceptURLPatternsJSON" :value="JSON.stringify(exceptURLPatterns)"/>
|
||||
<input type="hidden" name="onlyURLPatternsJSON" :value="JSON.stringify(onlyURLPatterns)"/>
|
||||
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td class="title">响应状态码 *</td>
|
||||
@@ -17,27 +20,47 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="bodyType == 'url'">
|
||||
<td>显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="bodyType == 'html'">
|
||||
<td>显示页面HTML *</td>
|
||||
<td>
|
||||
<textarea name="body" ref="htmlBody"></textarea>
|
||||
<textarea name="body" ref="htmlBody" rows="7"></textarea>
|
||||
<p class="comment"><a href="" @click.prevent="addHTMLTemplate">[使用模板]</a>。填写页面的HTML内容,支持请求变量。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>新状态码</td>
|
||||
<td>
|
||||
<input type="text" name="newStatus" size="3" placeholder="状态码" maxlength="3" style="width:5.2em"/>
|
||||
<p class="comment">可以用来修改响应的状态码,不填表示不改变原有状态码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="bodyType == 'url'">
|
||||
<td>显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="bodyType == 'redirectURL'">
|
||||
<td>跳转到URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html"/>
|
||||
<p class="comment">将会跳转到此URL。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>例外URL <tip-icon content="对这些URL将不做任何限制。"></tip-icon></td>
|
||||
<td><url-patterns-box v-model="exceptURLPatterns"></url-patterns-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>限制URL <tip-icon content="只对这些URL做限制。"></tip-icon></td>
|
||||
<td><url-patterns-box v-model="onlyURLPatterns"></url-patterns-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>新状态码</td>
|
||||
<td>
|
||||
<input type="text" name="newStatus" size="3" placeholder="状态码" maxlength="3" style="width:5.2em"/>
|
||||
<p class="comment">可以用来修改响应的状态码,不填表示不改变原有状态码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -22,4 +22,7 @@ Tea.context(function () {
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
this.exceptURLPatterns = []
|
||||
this.onlyURLPatterns = []
|
||||
})
|
||||
@@ -2,6 +2,8 @@
|
||||
<h3>修改自定义页面</h3>
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="pageId" :value="pageConfig.id"/>
|
||||
<input type="hidden" name="exceptURLPatternsJSON" :value="JSON.stringify(pageConfig.exceptURLPatterns)"/>
|
||||
<input type="hidden" name="onlyURLPatternsJSON" :value="JSON.stringify(pageConfig.onlyURLPatterns)"/>
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td class="title">响应状态码 *</td>
|
||||
@@ -18,13 +20,6 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="bodyType == 'url'">
|
||||
<td>显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html" v-model="pageConfig.url"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="bodyType == 'html'">
|
||||
<td>显示页面HTML *</td>
|
||||
<td>
|
||||
@@ -32,13 +27,40 @@
|
||||
<p class="comment"><a href="" @click.prevent="addHTMLTemplate">[使用模板]</a>。填写页面的HTML内容,支持请求变量。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>新状态码</td>
|
||||
<td>
|
||||
<input type="text" name="newStatus" size="3" placeholder="状态码" maxlength="3" style="width:5.2em" v-model="newStatus"/>
|
||||
<p class="comment">可以用来修改响应的状态码,不填表示不改变原有状态码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="bodyType == 'url'">
|
||||
<td>显示页面URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html" v-model="pageConfig.url"/>
|
||||
<p class="comment">将从此URL中读取内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="bodyType == 'redirectURL'">
|
||||
<td>跳转到URL *</td>
|
||||
<td>
|
||||
<input type="text" name="url" maxlength="500" placeholder="类似于 https://example.com/page.html" v-model="pageConfig.url"/>
|
||||
<p class="comment">将会跳转到此URL。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>例外URL <tip-icon content="对这些URL将不做任何限制。"></tip-icon></td>
|
||||
<td><url-patterns-box v-model="pageConfig.exceptURLPatterns"></url-patterns-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>限制URL <tip-icon content="只对这些URL做限制。"></tip-icon></td>
|
||||
<td><url-patterns-box v-model="pageConfig.onlyURLPatterns"></url-patterns-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>新状态码</td>
|
||||
<td>
|
||||
<input type="text" name="newStatus" size="3" placeholder="状态码" maxlength="3" style="width:5.2em" v-model="newStatus"/>
|
||||
<p class="comment">可以用来修改响应的状态码,不填表示不改变原有状态码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -151,13 +151,32 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>匹配条件</td>
|
||||
<td><http-request-conds-box :v-conds="redirect.conds" @change="changeConds"></http-request-conds-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用当前跳转</td>
|
||||
<td><checkbox name="isOn" value="1" v-model="redirect.isOn"></checkbox></td>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>例外域名</td>
|
||||
<td>
|
||||
<domains-box name="exceptDomainsJSON" :v-domains="redirect.exceptDomains"></domains-box>
|
||||
<p class="comment">这些域名<strong>不</strong>执行跳转。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>仅限域名</td>
|
||||
<td>
|
||||
<domains-box name="onlyDomainsJSON" :v-domains="redirect.onlyDomains"></domains-box>
|
||||
<p class="comment">只有这些域名执行跳转。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>匹配条件</td>
|
||||
<td><http-request-conds-box :v-conds="redirect.conds" @change="changeConds"></http-request-conds-box></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用当前跳转</td>
|
||||
<td><checkbox name="isOn" value="1" v-model="redirect.isOn"></checkbox></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<div class="right-box with-menu">
|
||||
<!-- 审核中 -->
|
||||
<div v-show="isAuditing">
|
||||
<div class="margin"></div>
|
||||
<warning-message>当前域名正在审核中,请审核域名的所有人、备案情况等。</warning-message>
|
||||
<form method="post" class="ui form" data-tea-action=".audit" data-tea-success="auditSuccess">
|
||||
<csrf-token></csrf-token>
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
{$ if .featureIsOn}
|
||||
<warning-message v-if="!wafIsOn">当前WAF未启用,设置将在<a :href="'/servers/server/settings/waf?serverId=' + serverId">[启用]</a>后生效。</warning-message>
|
||||
<warning-message v-if="clusterFirewallPolicy != null && clusterFirewallPolicy.mode != 'defend'">当前网站所在集群的WAF策略模式为
|
||||
<span v-if="clusterFirewallPolicy.modeInfo != null"><strong>{{clusterFirewallPolicy.modeInfo.name}}</strong></span>
|
||||
<span v-else>非防御模式</span>,当前设置将不会生效。
|
||||
</warning-message>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="firewallPolicyId" :value="firewallPolicyId"/>
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
{$ if .featureIsOn}
|
||||
<warning-message v-if="!wafIsOn">当前WAF未启用,设置将在<a :href="'/servers/server/settings/waf?serverId=' + serverId">[启用]</a>后生效。</warning-message>
|
||||
<warning-message v-if="clusterFirewallPolicy != null && clusterFirewallPolicy.mode != 'defend'">当前网站所在集群的WAF策略模式为
|
||||
<span v-if="clusterFirewallPolicy.modeInfo != null"><strong>{{clusterFirewallPolicy.modeInfo.name}}</strong></span>
|
||||
<span v-else>非防御模式</span>,当前设置将不会生效。
|
||||
</warning-message>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="firewallPolicyId" :value="firewallPolicyId"/>
|
||||
|
||||
@@ -62,7 +62,7 @@ Tea.context(function () {
|
||||
itemStyle: {
|
||||
color: teaweb.DefaultChartColor
|
||||
},
|
||||
barWidth: "20em"
|
||||
barWidth: "10em"
|
||||
}
|
||||
],
|
||||
animation: true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user