初步实现多集群共享节点

This commit is contained in:
刘祥超
2021-07-31 22:23:07 +08:00
parent 3614b9f3b7
commit 867215e2af
38 changed files with 610 additions and 201 deletions

View File

@@ -11,7 +11,7 @@ import (
"strconv" "strconv"
) )
// 创建节点 // CreateNodeAction 创建节点
type CreateNodeAction struct { type CreateNodeAction struct {
actionutils.ParentAction actionutils.ParentAction
} }
@@ -57,6 +57,8 @@ func (this *CreateNodeAction) RunGet(params struct {
} }
for _, route := range routesResp.Routes { for _, route := range routesResp.Routes {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{ dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"domainId": domainId,
"domainName": clusterDNSResp.Domain.Name,
"name": route.Name, "name": route.Name,
"code": route.Code, "code": route.Code,
}) })

View File

@@ -3,6 +3,7 @@ package node
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
@@ -37,6 +38,7 @@ func (this *DetailAction) RunGet(params struct {
return return
} }
// 主集群
var clusterMap maps.Map = nil var clusterMap maps.Map = nil
if node.NodeCluster != nil { if node.NodeCluster != nil {
clusterId := node.NodeCluster.Id clusterId := node.NodeCluster.Id
@@ -55,6 +57,16 @@ func (this *DetailAction) RunGet(params struct {
} }
} }
// 从集群
var secondaryClustersMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClustersMaps = append(secondaryClustersMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isOn": cluster.IsOn,
})
}
// IP地址 // IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{ ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
NodeId: params.NodeId, NodeId: params.NodeId,
@@ -64,6 +76,7 @@ func (this *DetailAction) RunGet(params struct {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
var ipAddresses = ipAddressesResp.Addresses
ipAddressMaps := []maps.Map{} ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses { for _, addr := range ipAddressesResp.Addresses {
ipAddressMaps = append(ipAddressMaps, maps.Map{ ipAddressMaps = append(ipAddressMaps, maps.Map{
@@ -75,33 +88,56 @@ func (this *DetailAction) RunGet(params struct {
} }
// DNS相关 // DNS相关
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId}) var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var recordMaps = []maps.Map{}
var routeMaps = []maps.Map{}
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
dnsRouteMaps := []maps.Map{} var dnsInfo = dnsInfoResp.Node
recordName := "" if len(dnsInfo.DnsDomainName) == 0 || len(dnsInfo.NodeClusterDNSName) == 0 {
recordValue := "" continue
if dnsInfoResp.Node != nil { }
recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName var domainName = dnsInfo.DnsDomainName
recordValue = dnsInfoResp.Node.IpAddr
for _, dnsInfo := range dnsInfoResp.Node.Routes { // 默认线路
dnsRouteMaps = append(dnsRouteMaps, maps.Map{ if len(dnsInfo.Routes) == 0 {
"name": dnsInfo.Name, dnsInfo.Routes = append(dnsInfo.Routes, &pb.DNSRoute{})
"code": dnsInfo.Code, } else {
for _, route := range dnsInfo.Routes {
routeMaps = append(routeMaps, maps.Map{
"domainName": domainName,
"code": route.Code,
"name": route.Name,
}) })
} }
} }
if len(dnsRouteMaps) == 0 {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{ for _, addr := range ipAddresses {
"name": "", if !addr.CanAccess {
"code": "", continue
}
for _, route := range dnsInfo.Routes {
var recordType = "A"
if utils.IsIPv6(addr.Ip) {
recordType = "AAAA"
}
recordMaps = append(recordMaps, maps.Map{
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
"type": recordType,
"route": route.Name,
"value": addr.Ip,
}) })
} }
this.Data["dnsRoutes"] = dnsRouteMaps }
this.Data["dnsRecordName"] = recordName }
this.Data["dnsRecordValue"] = recordValue
// 登录信息 // 登录信息
var loginMap maps.Map = nil var loginMap maps.Map = nil
@@ -221,6 +257,7 @@ func (this *DetailAction) RunGet(params struct {
"name": node.Name, "name": node.Name,
"ipAddresses": ipAddressMaps, "ipAddresses": ipAddressMaps,
"cluster": clusterMap, "cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap, "login": loginMap,
"installDir": node.InstallDir, "installDir": node.InstallDir,
"isInstalled": node.IsInstalled, "isInstalled": node.IsInstalled,
@@ -228,6 +265,8 @@ func (this *DetailAction) RunGet(params struct {
"secret": node.Secret, "secret": node.Secret,
"maxCPU": node.MaxCPU, "maxCPU": node.MaxCPU,
"isOn": node.IsOn, "isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"status": maps.Map{ "status": maps.Map{
"isActive": status.IsActive, "isActive": status.IsActive,

View File

@@ -66,44 +66,64 @@ func (this *UpdateAction) RunGet(params struct {
} }
// DNS相关 // DNS相关
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId}) var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var allDNSRouteMaps = map[int64][]maps.Map{} // domain id => routes
var routeMaps = map[int64][]maps.Map{} // domain id => routes
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
nodeDNS := dnsInfoResp.Node var dnsInfo = dnsInfoResp.Node
dnsRouteMaps := []maps.Map{} if dnsInfo.DnsDomainId <= 0 || len(dnsInfo.DnsDomainName) == 0 {
if nodeDNS != nil { continue
for _, dnsInfo := range nodeDNS.Routes { }
dnsRouteMaps = append(dnsRouteMaps, maps.Map{ var domainId = dnsInfo.DnsDomainId
"name": dnsInfo.Name, var domainName = dnsInfo.DnsDomainName
"code": dnsInfo.Code, if len(dnsInfo.Routes) > 0 {
for _, route := range dnsInfo.Routes {
routeMaps[domainId] = append(routeMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"code": route.Code,
"name": route.Name,
}) })
} }
} }
this.Data["dnsRoutes"] = dnsRouteMaps
this.Data["allDNSRoutes"] = []maps.Map{} // 所有线路选项
if nodeDNS != nil {
this.Data["dnsDomainId"] = nodeDNS.DnsDomainId
} else {
this.Data["dnsDomainId"] = 0
}
if nodeDNS != nil && nodeDNS.DnsDomainId > 0 {
routesMaps := []maps.Map{}
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfoResp.Node.DnsDomainId}) routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfoResp.Node.DnsDomainId})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
for _, route := range routesResp.Routes { for _, route := range routesResp.Routes {
routesMaps = append(routesMaps, maps.Map{ allDNSRouteMaps[domainId] = append(allDNSRouteMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"name": route.Name, "name": route.Name,
"code": route.Code, "code": route.Code,
}) })
} }
this.Data["allDNSRoutes"] = routesMaps
} }
var domainRoutes = []maps.Map{}
for _, m := range routeMaps {
domainRoutes = append(domainRoutes, m...)
}
this.Data["dnsRoutes"] = domainRoutes
var allDomainRoutes = []maps.Map{}
for _, m := range allDNSRouteMaps {
allDomainRoutes = append(allDomainRoutes, m...)
}
this.Data["allDNSRoutes"] = allDomainRoutes
// 登录信息 // 登录信息
var loginMap maps.Map = nil var loginMap maps.Map = nil
if node.Login != nil { if node.Login != nil {
@@ -188,7 +208,7 @@ func (this *UpdateAction) RunGet(params struct {
} }
} }
this.Data["node"] = maps.Map{ var m = maps.Map{
"id": node.Id, "id": node.Id,
"name": node.Name, "name": node.Name,
"ipAddresses": ipAddressMaps, "ipAddresses": ipAddressMaps,
@@ -202,23 +222,29 @@ func (this *UpdateAction) RunGet(params struct {
"maxCacheMemoryCapacity": maxCacheMemoryCapacity, "maxCacheMemoryCapacity": maxCacheMemoryCapacity,
} }
// 所有集群 if node.NodeCluster != nil {
resp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClusters(this.AdminContext(), &pb.FindAllEnabledNodeClustersRequest{}) m["primaryCluster"] = maps.Map{
if err != nil { "id": node.NodeCluster.Id,
this.ErrorPage(err) "name": node.NodeCluster.Name,
} }
if err != nil { } else {
this.ErrorPage(err) m["primaryCluster"] = nil
return
} }
clusterMaps := []maps.Map{}
for _, cluster := range resp.NodeClusters { if len(node.SecondaryNodeClusters) > 0 {
clusterMaps = append(clusterMaps, maps.Map{ var secondaryClusterMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": cluster.Id, "id": cluster.Id,
"name": cluster.Name, "name": cluster.Name,
}) })
} }
this.Data["clusters"] = clusterMaps m["secondaryClusters"] = secondaryClusterMaps
} else {
m["secondaryClusters"] = []interface{}{}
}
this.Data["node"] = m
this.Show() this.Show()
} }
@@ -230,7 +256,8 @@ func (this *UpdateAction) RunPost(params struct {
RegionId int64 RegionId int64
Name string Name string
IPAddressesJSON []byte `alias:"ipAddressesJSON"` IPAddressesJSON []byte `alias:"ipAddressesJSON"`
ClusterId int64 PrimaryClusterId int64
SecondaryClusterIds []byte
GrantId int64 GrantId int64
SshHost string SshHost string
SshPort int SshPort int
@@ -256,8 +283,17 @@ func (this *UpdateAction) RunPost(params struct {
Require("请输入节点名称") Require("请输入节点名称")
// TODO 检查cluster // TODO 检查cluster
if params.ClusterId <= 0 { if params.PrimaryClusterId <= 0 {
this.Fail("请选择所在集群") this.Fail("请选择节点所在集群")
}
var secondaryClusterIds = []int64{}
if len(params.SecondaryClusterIds) > 0 {
err := json.Unmarshal(params.SecondaryClusterIds, &secondaryClusterIds)
if err != nil {
this.ErrorPage(err)
return
}
} }
// IP地址 // IP地址
@@ -329,7 +365,8 @@ func (this *UpdateAction) RunPost(params struct {
NodeGroupId: params.GroupId, NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId, NodeRegionId: params.RegionId,
Name: params.Name, Name: params.Name,
NodeClusterId: params.ClusterId, NodeClusterId: params.PrimaryClusterId,
SecondaryNodeClusterIds: secondaryClusterIds,
NodeLogin: loginInfo, NodeLogin: loginInfo,
MaxCPU: params.MaxCPU, MaxCPU: params.MaxCPU,
IsOn: params.IsOn, IsOn: params.IsOn,

View File

@@ -156,6 +156,16 @@ func (this *NodesAction) RunGet(params struct {
dnsRouteNames = append(dnsRouteNames, route.Name) dnsRouteNames = append(dnsRouteNames, route.Name)
} }
// 从集群
var secondaryClusterMaps []maps.Map
for _, secondaryCluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": secondaryCluster.Id,
"name": secondaryCluster.Name,
"isOn": secondaryCluster.IsOn,
})
}
nodeMaps = append(nodeMaps, maps.Map{ nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id, "id": node.Id,
"name": node.Name, "name": node.Name,
@@ -183,6 +193,7 @@ func (this *NodesAction) RunGet(params struct {
"id": node.NodeCluster.Id, "id": node.NodeCluster.Id,
"name": node.NodeCluster.Name, "name": node.NodeCluster.Name,
}, },
"secondaryClusters": secondaryClusterMaps,
"isSynced": isSynced, "isSynced": isSynced,
"ipAddresses": ipAddresses, "ipAddresses": ipAddresses,
"group": groupMap, "group": groupMap,

View File

@@ -63,6 +63,14 @@ func (this *IndexAction) RunPost(params struct {
// 创建日志 // 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改集群 %d DNS设置", params.ClusterId) defer this.CreateLog(oplogs.LevelInfo, "修改集群 %d DNS设置", params.ClusterId)
if params.DnsDomainId <= 0 {
this.Fail("请选择集群的主域名")
}
params.Must.
Field("dnsName", params.DnsName).
Require("请输入DNS子域名")
// 检查DNS名称 // 检查DNS名称
if len(params.DnsName) > 0 { if len(params.DnsName) > 0 {
if !domainutils.ValidateDomainFormat(params.DnsName) { if !domainutils.ValidateDomainFormat(params.DnsName) {

View File

@@ -235,6 +235,16 @@ func (this *IndexAction) searchNodes(keyword string) {
dnsRouteNames = append(dnsRouteNames, route.Name) dnsRouteNames = append(dnsRouteNames, route.Name)
} }
// 从集群
var secondaryClusterMaps []maps.Map
for _, secondaryCluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": secondaryCluster.Id,
"name": secondaryCluster.Name,
"isOn": secondaryCluster.IsOn,
})
}
nodeMaps = append(nodeMaps, maps.Map{ nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id, "id": node.Id,
"name": node.Name, "name": node.Name,
@@ -260,6 +270,7 @@ func (this *IndexAction) searchNodes(keyword string) {
"id": node.NodeCluster.Id, "id": node.NodeCluster.Id,
"name": node.NodeCluster.Name, "name": node.NodeCluster.Name,
}, },
"secondaryClusters": secondaryClusterMaps,
"isSynced": isSynced, "isSynced": isSynced,
"ipAddresses": ipAddresses, "ipAddresses": ipAddresses,
"group": groupMap, "group": groupMap,

View File

@@ -20,6 +20,7 @@ func init() {
EndHelpers(). EndHelpers().
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)). Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Post("/options", new(OptionsAction)). Post("/options", new(OptionsAction)).
GetPost("/selectPopup", new(SelectPopupAction)).
EndAll() EndAll()
}) })

View File

@@ -0,0 +1,64 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
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/lists"
"github.com/iwind/TeaGo/maps"
)
type SelectPopupAction struct {
actionutils.ParentAction
}
func (this *SelectPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectPopupAction) RunGet(params struct {
SelectedClusterIds string
}) {
var selectedIds = utils.SplitNumbers(params.SelectedClusterIds)
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var clusterMaps = []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
// 节点数
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{NodeClusterId: cluster.Id})
if err != nil {
this.ErrorPage(err)
return
}
var countNodes = countNodesResp.Count
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isOn": cluster.IsOn,
"countNodes": countNodes,
"isSelected": lists.ContainsInt64(selectedIds, cluster.Id),
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}

View File

@@ -43,16 +43,21 @@ func (this *ClustersPopupAction) RunGet(params struct {
for _, cluster := range clustersResp.NodeClusters { for _, cluster := range clustersResp.NodeClusters {
isOk := false isOk := false
if len(cluster.Name) > 0 { if len(cluster.Name) > 0 {
for _, recordType := range []string{"A", "AAAA"} {
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{ checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
DnsDomainId: params.DomainId, DnsDomainId: params.DomainId,
Name: cluster.DnsName, Name: cluster.DnsName,
Type: "A", Type: recordType,
}) })
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return
} }
isOk = checkResp.IsOk if checkResp.IsOk {
isOk = true
break
}
}
} }
clusterMaps = append(clusterMaps, maps.Map{ clusterMaps = append(clusterMaps, maps.Map{

View File

@@ -33,12 +33,17 @@ func ValidateDomainFormat(domain string) bool {
} }
// ConvertRoutesToMaps 转换线路列表 // ConvertRoutesToMaps 转换线路列表
func ConvertRoutesToMaps(routes []*pb.DNSRoute) []maps.Map { func ConvertRoutesToMaps(info *pb.NodeDNSInfo) []maps.Map {
if info == nil {
return []maps.Map{}
}
result := []maps.Map{} result := []maps.Map{}
for _, route := range routes { for _, route := range info.Routes {
result = append(result, maps.Map{ result = append(result, maps.Map{
"name": route.Name, "name": route.Name,
"code": route.Code, "code": route.Code,
"domainId": info.DnsDomainId,
"domainName": info.DnsDomainName,
}) })
} }
return result return result

View File

@@ -1,6 +1,7 @@
package domains package domains
import ( import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
@@ -55,10 +56,14 @@ func (this *NodesPopupAction) RunGet(params struct {
// 检查是否有域名解析记录 // 检查是否有域名解析记录
isOk := false isOk := false
if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 { if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{ checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
DnsDomainId: params.DomainId, DnsDomainId: params.DomainId,
Name: cluster.DnsName, Name: cluster.DnsName,
Type: "A", Type: recordType,
Route: route.Code, Route: route.Code,
Value: node.IpAddr, Value: node.IpAddr,
}) })

View File

@@ -20,11 +20,15 @@ func (this *UpdateNodePopupAction) Init() {
} }
func (this *UpdateNodePopupAction) RunGet(params struct { func (this *UpdateNodePopupAction) RunGet(params struct {
ClusterId int64
NodeId int64 NodeId int64
}) { }) {
this.Data["nodeId"] = params.NodeId this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId}) dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return
@@ -35,7 +39,7 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
return return
} }
this.Data["ipAddr"] = dnsInfo.IpAddr this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo.Routes) this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName this.Data["domainName"] = dnsInfo.DnsDomainName
@@ -52,11 +56,15 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
allRouteMaps = append(allRouteMaps, maps.Map{ allRouteMaps = append(allRouteMaps, maps.Map{
"name": route.Name, "name": route.Name,
"code": route.Code, "code": route.Code,
"domainName": dnsInfo.DnsDomainName,
"domainId": dnsInfo.DnsDomainId,
}) })
} }
// 筛选 // 筛选
this.Data["routes"] = domainutils.ConvertRoutesToMaps(domainutils.FilterRoutes(dnsInfo.Routes, routesResp.Routes)) var routes = domainutils.FilterRoutes(dnsInfo.Routes, routesResp.Routes)
dnsInfo.Routes = routes
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
} }
} }
this.Data["allRoutes"] = allRouteMaps this.Data["allRoutes"] = allRouteMaps

View File

@@ -10,12 +10,16 @@ type DeleteAction struct {
} }
func (this *DeleteAction) RunPost(params struct { func (this *DeleteAction) RunPost(params struct {
ClusterId int64
NodeId int64 NodeId int64
}) { }) {
// 创建日志 // 创建日志
defer this.CreateLogInfo("删除节点", params.NodeId) defer this.CreateLogInfo("从集群 %d 中删除节点 %d", params.ClusterId, params.NodeId)
_, err := this.RPC().NodeRPC().DeleteNode(this.AdminContext(), &pb.DeleteNodeRequest{NodeId: params.NodeId}) _, err := this.RPC().NodeRPC().DeleteNodeFromNodeCluster(this.AdminContext(), &pb.DeleteNodeFromNodeClusterRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return

View File

@@ -1,3 +1,4 @@
// 单个集群选择
Vue.component("cluster-selector", { Vue.component("cluster-selector", {
mounted: function () { mounted: function () {
let that = this let that = this

View File

@@ -0,0 +1,28 @@
// 显示节点的多个集群
Vue.component("node-clusters-labels", {
props: ["v-primary-cluster", "v-secondary-clusters", "size"],
data: function () {
let cluster = this.vPrimaryCluster
let secondaryClusters = this.vSecondaryClusters
if (secondaryClusters == null) {
secondaryClusters = []
}
let labelSize = this.size
if (labelSize == null) {
labelSize = "small"
}
if (labelSize == "tiny") {
labelSize += " olive"
}
return {
cluster: cluster,
secondaryClusters: secondaryClusters,
labelSize: labelSize
}
},
template: `<div>
<a v-if="cluster != null" :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label basic" :class="labelSize" title="主集群" style="margin-bottom: 0.3em;">{{cluster.name}}</a>
<a v-for="c in secondaryClusters" :href="'/clusters/cluster?clusterId=' + c.id" class="ui label basic" :class="labelSize" title="从集群" style="margin-bottom: 0.3em;"><span class="grey" style="text-decoration: none">{{c.name}}</span></a>
</div>`
})

View File

@@ -0,0 +1,98 @@
// 一个节点的多个集群选择器
Vue.component("node-clusters-selector", {
props: ["v-primary-cluster", "v-secondary-clusters"],
data: function () {
let primaryCluster = this.vPrimaryCluster
let secondaryClusters = this.vSecondaryClusters
if (secondaryClusters == null) {
secondaryClusters = []
}
return {
primaryClusterId: (primaryCluster == null) ? 0 : primaryCluster.id,
secondaryClusterIds: secondaryClusters.map(function (v) {
return v.id
}),
primaryCluster: primaryCluster,
secondaryClusters: secondaryClusters
}
},
methods: {
addPrimary: function () {
let that = this
let selectedClusterIds = [this.primaryClusterId].concat(this.secondaryClusterIds)
teaweb.popup("/clusters/selectPopup?selectedClusterIds=" + selectedClusterIds.join(",") + "&mode=single", {
height: "38em",
width: "50em",
callback: function (resp) {
if (resp.data.cluster != null) {
that.primaryCluster = resp.data.cluster
that.primaryClusterId = that.primaryCluster.id
that.notifyChange()
}
}
})
},
removePrimary: function () {
this.primaryClusterId = 0
this.primaryCluster = null
this.notifyChange()
},
addSecondary: function () {
let that = this
let selectedClusterIds = [this.primaryClusterId].concat(this.secondaryClusterIds)
teaweb.popup("/clusters/selectPopup?selectedClusterIds=" + selectedClusterIds.join(",") + "&mode=multiple", {
height: "38em",
width: "50em",
callback: function (resp) {
if (resp.data.cluster != null) {
that.secondaryClusterIds.push(resp.data.cluster.id)
that.secondaryClusters.push(resp.data.cluster)
that.notifyChange()
}
}
})
},
removeSecondary: function (index) {
this.secondaryClusterIds.$remove(index)
this.secondaryClusters.$remove(index)
this.notifyChange()
},
notifyChange: function () {
this.$emit("change", {
clusterId: this.primaryClusterId
})
}
},
template: `<div>
<input type="hidden" name="primaryClusterId" :value="primaryClusterId"/>
<input type="hidden" name="secondaryClusterIds" :value="JSON.stringify(secondaryClusterIds)"/>
<table class="ui table">
<tr>
<td class="title">主集群</td>
<td>
<div v-if="primaryCluster != null">
<div class="ui label basic small">{{primaryCluster.name}} &nbsp; <a href="" title="删除" @click.prevent="removePrimary"><i class="icon remove small"></i></a> </div>
</div>
<div style="margin-top: 0.6em" v-if="primaryClusterId == 0">
<button class="ui button tiny" type="button" @click.prevent="addPrimary">+</button>
</div>
<p class="comment">多个集群配置有冲突时,优先使用主集群配置。</p>
</td>
</tr>
<tr>
<td>从集群</td>
<td>
<div v-if="secondaryClusters.length > 0">
<div class="ui label basic small" v-for="(cluster, index) in secondaryClusters"><span class="grey">{{cluster.name}}</span> &nbsp; <a href="" title="删除" @click.prevent="removeSecondary(index)"><i class="icon remove small"></i></a> </div>
</div>
<div style="margin-top: 0.6em">
<button class="ui button tiny" type="button" @click.prevent="addSecondary">+</button>
</div>
</td>
</tr>
</table>
</div>`
})

View File

@@ -8,7 +8,7 @@ Vue.component("dns-route-selector", {
return { return {
routes: routes, routes: routes,
routeCodes: routes.$map(function (k, v) { routeCodes: routes.$map(function (k, v) {
return v.code return v.code + "@" + v.domainId
}), }),
isAdding: false, isAdding: false,
routeCode: "" routeCode: ""
@@ -31,7 +31,7 @@ Vue.component("dns-route-selector", {
} }
let that = this let that = this
let route = this.vAllRoutes.$find(function (k, v) { let route = this.vAllRoutes.$find(function (k, v) {
return v.code == that.routeCode return v.code + "@" + v.domainId == that.routeCode
}) })
if (route == null) { if (route == null) {
return return
@@ -53,8 +53,8 @@ Vue.component("dns-route-selector", {
template: `<div> template: `<div>
<input type="hidden" name="dnsRoutesJSON" :value="JSON.stringify(routeCodes)"/> <input type="hidden" name="dnsRoutesJSON" :value="JSON.stringify(routeCodes)"/>
<div v-if="routes.length > 0"> <div v-if="routes.length > 0">
<tiny-basic-label v-for="route in routes" :key="route.code"> <tiny-basic-label v-for="route in routes" :key="route.code + '@' + route.domainId">
{{route.name}} <a href="" @click.prevent="remove(route)"><i class="icon remove"></i></a> {{route.name}} <span class="grey small">{{route.domainName}}</span><a href="" @click.prevent="remove(route)"><i class="icon remove"></i></a>
</tiny-basic-label> </tiny-basic-label>
<div class="ui divider"></div> <div class="ui divider"></div>
</div> </div>
@@ -64,7 +64,7 @@ Vue.component("dns-route-selector", {
<div class="ui field"> <div class="ui field">
<select class="ui dropdown auto-width" v-model="routeCode"> <select class="ui dropdown auto-width" v-model="routeCode">
<option value="">[请选择]</option> <option value="">[请选择]</option>
<option v-for="route in vAllRoutes" :value="route.code">{{route.name}}</option> <option v-for="route in vAllRoutes" :value="route.code + '@' + route.domainId">{{route.name}}{{route.domainName}}</option>
</select> </select>
</div> </div>
<div class="ui field"> <div class="ui field">

View File

@@ -317,6 +317,27 @@ window.teaweb = {
Swal.fire(config); Swal.fire(config);
}, },
toast: function (message, timeout, callback) {
if (timeout == null) {
timeout = 2000
}
var width = "20em";
if (message.length > 30) {
width = "30em";
}
Swal.fire({
text: message,
icon: "info",
width: width,
timer: timeout,
showConfirmButton: false,
onAfterClose: function () {
if (typeof callback == "function") {
callback()
}
}
});
},
successToast: function (message, timeout, callback) { successToast: function (message, timeout, callback) {
if (timeout == null) { if (timeout == null) {
timeout = 2000 timeout = 2000

File diff suppressed because one or more lines are too long

View File

@@ -440,6 +440,7 @@ body.expanded .main {
.main-menu.theme1 { .main-menu.theme1 {
background: #14539A !important; background: #14539A !important;
.menu { .menu {
background: #14539A !important; background: #14539A !important;
} }
@@ -447,6 +448,7 @@ body.expanded .main {
.main-menu.theme2 { .main-menu.theme2 {
background: #276AC6 !important; background: #276AC6 !important;
.menu { .menu {
background: #276AC6 !important; background: #276AC6 !important;
} }
@@ -454,6 +456,7 @@ body.expanded .main {
.main-menu.theme3 { .main-menu.theme3 {
background: #007D9C !important; background: #007D9C !important;
.menu { .menu {
background: #007D9C !important; background: #007D9C !important;
} }

View File

@@ -7,6 +7,6 @@
<menu-item :href="'/clusters/cluster/node/detail?clusterId=' + clusterId + '&nodeId=' + node.id" code="node" v-if="teaIsPlus">节点详情</menu-item> <menu-item :href="'/clusters/cluster/node/detail?clusterId=' + clusterId + '&nodeId=' + node.id" code="node" v-if="teaIsPlus">节点详情</menu-item>
<menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + node.id" code="threshold" v-if="teaIsPlus">阈值设置</menu-item> <menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + node.id" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
<menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + node.id" code="log">运行日志</menu-item> <menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + node.id" code="log">运行日志</menu-item>
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id" code="update">修改设置</menu-item>
<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" code="install">安装节点</menu-item> <menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" code="install">安装节点</menu-item>
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id" code="update">修改设置</menu-item>
</second-menu> </second-menu>

View File

@@ -12,6 +12,12 @@
<td>状态</td> <td>状态</td>
<td><label-on :v-is-on="node.isOn"></label-on></td> <td><label-on :v-is-on="node.isOn"></label-on></td>
</tr> </tr>
<tr>
<td>所属集群</td>
<td>
<node-clusters-labels :v-primary-cluster="node.cluster" :v-secondary-clusters="node.secondaryClusters"></node-clusters-labels>
</td>
</tr>
<tr> <tr>
<td>IP地址</td> <td>IP地址</td>
<td> <td>
@@ -29,13 +35,13 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr v-if="dnsRoutes.length > 0 && dnsRoutes[0].name.length > 0"> <tr v-if="node.routes.length > 0">
<td>DNS线路</td> <td>DNS线路</td>
<td> <td>
<span class="ui label tiny basic" v-for="route in dnsRoutes">{{route.name}}</span> <span class="ui label tiny basic" v-for="route in node.routes">{{route.name}} <span class="grey small">{{route.domainName}}</span></span>
</td> </td>
</tr> </tr>
<tr v-if="dnsRecordName.length > 0 && dnsRecordValue.length > 0"> <tr v-if="node.records.length > 0">
<td>DNS记录</td> <td>DNS记录</td>
<td> <td>
<table class="ui table celled"> <table class="ui table celled">
@@ -47,20 +53,16 @@
<th>记录值</th> <th>记录值</th>
</tr> </tr>
</thead> </thead>
<tbody v-for="address in node.ipAddresses" v-if="address.canAccess">
<tr v-for="route in dnsRoutes"> <tr v-for="record in node.records">
<td>{{dnsRecordName}}</td> <td>{{record.name}}</td>
<td>{{record.type}}</td>
<td> <td>
<span v-if="address.ip.indexOf(':') > -1">AAAA</span> <span v-if="record.route.length > 0">{{record.route}}</span>
<span v-else>A</span>
</td>
<td>
<span v-if="route.name.length > 0">{{route.name}}</span>
<span v-else class="disabled">默认</span> <span v-else class="disabled">默认</span>
</td> </td>
<td>{{address.ip}}</td> <td>{{record.value}}</td>
</tr> </tr>
</tbody>
</table> </table>
<p class="comment">通过设置A记录可以将集群上的服务请求转发到不同线路的节点上。</p> <p class="comment">通过设置A记录可以将集群上的服务请求转发到不同线路的节点上。</p>
</td> </td>

View File

@@ -13,6 +13,12 @@
<input type="text" name="name" maxlength="50" ref="focus" v-model="node.name"/> <input type="text" name="name" maxlength="50" ref="focus" v-model="node.name"/>
</td> </td>
</tr> </tr>
<tr>
<td>所属集群</td>
<td>
<node-clusters-selector :v-primary-cluster="node.primaryCluster" :v-secondary-clusters="node.secondaryClusters" @change="changeClusters"></node-clusters-selector>
</td>
</tr>
<tr> <tr>
<td>IP地址 *</td> <td>IP地址 *</td>
<td> <td>
@@ -23,19 +29,10 @@
<tr v-if="allDNSRoutes.length > 0"> <tr v-if="allDNSRoutes.length > 0">
<td>DNS线路</td> <td>DNS线路</td>
<td> <td>
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector> <dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
<p class="comment">当前节点对应的DNS线路可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有其他限制。</p> <p class="comment">当前节点对应的DNS线路可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有其他限制。</p>
</td> </td>
</tr> </tr>
<tr>
<td>所属集群</td>
<td>
<select class="ui dropdown" name="clusterId" style="width:10em" v-model="clusterId">
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
</select>
</td>
</tr>
<tr> <tr>
<td>所属区域</td> <td>所属区域</td>
<td> <td>

View File

@@ -1,27 +1,32 @@
Tea.context(function () { Tea.context(function () {
this.clusterId = 0; this.clusterId = 0
if (this.node.cluster != null && this.node.cluster.id > 0) { if (this.node.cluster != null && this.node.cluster.id > 0) {
this.clusterId = this.node.cluster.id; this.clusterId = this.node.cluster.id
} }
this.success = NotifySuccess("保存成功", "/clusters/cluster/node?clusterId=" + this.clusterId + "&nodeId=" + this.node.id); this.success = function () {
let that = this
teaweb.success("保存成功", function () {
window.location = "/clusters/cluster/node/detail?clusterId=" + that.clusterId + "&nodeId=" + that.node.id
})
}
// IP地址相关 // IP地址相关
this.ipAddresses = this.node.ipAddresses; this.ipAddresses = this.node.ipAddresses
// 认证相关 // 认证相关
this.grant = null; this.grant = null
this.sshHost = ""; this.sshHost = ""
this.sshPort = ""; this.sshPort = ""
this.loginId = 0; this.loginId = 0
if (this.node.login != null) { if (this.node.login != null) {
this.loginId = this.node.login.id; this.loginId = this.node.login.id
if (this.node.login.params != null) { if (this.node.login.params != null) {
this.sshHost = this.node.login.params.host; this.sshHost = this.node.login.params.host
if (this.node.login.params.port > 0) { if (this.node.login.params.port > 0) {
this.sshPort = this.node.login.params.port; this.sshPort = this.node.login.params.port
} }
} }
@@ -31,7 +36,11 @@ Tea.context(function () {
name: this.node.login.grant.name, name: this.node.login.grant.name,
method: this.node.login.grant.method, method: this.node.login.grant.method,
methodName: this.node.login.grant.methodName methodName: this.node.login.grant.methodName
};
} }
} }
}); }
this.changeClusters = function (info) {
this.clusterId = info.clusterId
}
})

View File

@@ -68,10 +68,13 @@
<tr v-for="node in nodes"> <tr v-for="node in nodes">
<td>{{node.name}} <td>{{node.name}}
<div style="margin-top: 0.5em" v-if="node.region != null"> <div style="margin-top: 0.5em" v-if="node.region != null">
<tiny-basic-label>区域:{{node.region.name}}</tiny-basic-label> <tiny-basic-label class="olive">区域:{{node.region.name}}</tiny-basic-label>
</div> </div>
<div style="margin-top: 0.5em" v-if="node.group != null"> <div style="margin-top: 0.5em" v-if="node.group != null">
<tiny-basic-label>分组:{{node.group.name}}</tiny-basic-label> <tiny-basic-label class="olive">分组:{{node.group.name}}</tiny-basic-label>
</div>
<div style="margin-top: 0.5em">
<node-clusters-labels :v-primary-cluster="node.cluster" :v-secondary-clusters="node.secondaryClusters" size="tiny"></node-clusters-labels>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -2,9 +2,10 @@ Tea.context(function () {
this.teaweb = teaweb this.teaweb = teaweb
this.deleteNode = function (nodeId) { this.deleteNode = function (nodeId) {
teaweb.confirm("确定要删除这个节点吗?", function () { teaweb.confirm("确定要从当前集群中删除这个节点吗?", function () {
this.$post("/nodes/delete") this.$post("/nodes/delete")
.params({ .params({
clusterId: this.clusterId,
nodeId: nodeId nodeId: nodeId
}) })
.refresh(); .refresh();

View File

@@ -9,14 +9,14 @@
<table class="ui table selectable definition"> <table class="ui table selectable definition">
<tr v-if="hasDomains"> <tr v-if="hasDomains">
<td>选择主域名</td> <td>选择主域名 *</td>
<td> <td>
<dns-domain-selector :v-domain-id="domainId" :v-domain-name="domainName"></dns-domain-selector> <dns-domain-selector :v-domain-id="domainId" :v-domain-name="domainName"></dns-domain-selector>
<p class="comment">用于生成集群节点和网站服务的DNS解析记录。</p> <p class="comment">用于生成集群节点和网站服务的DNS解析记录。</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="title">DNS子域名</td> <td class="title">DNS子域名 *</td>
<td> <td>
<div class="ui input right labeled"> <div class="ui input right labeled">
<input type="text" name="dnsName" maxlength="64" style="width:10em" v-model="dnsName"/> <input type="text" name="dnsName" maxlength="64" style="width:10em" v-model="dnsName"/>

View File

@@ -0,0 +1,4 @@
h4 span {
font-size: 0.8em;
}
/*# sourceMappingURL=create.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["create.less"],"names":[],"mappings":"AAAA,EAAG;EACF,gBAAA","file":"create.css"}

View File

@@ -13,17 +13,19 @@
<td>默认缓存设置 *</td> <td>默认缓存设置 *</td>
<td> <td>
<http-cache-policy-selector></http-cache-policy-selector> <http-cache-policy-selector></http-cache-policy-selector>
<p class="comment">此全局设置不会强制应用到每个网站服务。</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>默认WAF设置 *</td> <td>默认WAF设置 *</td>
<td> <td>
<http-firewall-policy-selector></http-firewall-policy-selector> <http-firewall-policy-selector></http-firewall-policy-selector>
<p class="comment">此全局设置不会强制应用到每个网站服务。</p>
</td> </td>
</tr> </tr>
</table> </table>
<h4>节点安装选项</h4> <h4>节点安装选项 &nbsp;<span class="grey small">(可选)</span></h4>
<table class="ui table selectable definition"> <table class="ui table selectable definition">
<tr> <tr>
<td class="title">默认SSH登录方式</td> <td class="title">默认SSH登录方式</td>
@@ -49,7 +51,7 @@
</tr> </tr>
</table> </table>
<h4>DNS设置选项</h4> <h4>DNS设置选项 &nbsp;<span class="grey small">(可选)</span></h4>
<table class="ui table selectable definition"> <table class="ui table selectable definition">
<tr v-if="hasDomains"> <tr v-if="hasDomains">
<td>选择主域名</td> <td>选择主域名</td>

View File

@@ -0,0 +1,3 @@
h4 span {
font-size: 0.8em;
}

View File

@@ -93,7 +93,7 @@
<tr v-for="node in nodes"> <tr v-for="node in nodes">
<td><keyword :v-word="keyword">{{node.name}}</keyword> <td><keyword :v-word="keyword">{{node.name}}</keyword>
<div style="margin-top: 0.5em"> <div style="margin-top: 0.5em">
<span class="ui label tiny">集群:{{node.cluster.name}}</span> <node-clusters-labels :v-primary-cluster="node.cluster" :v-secondary-clusters="node.secondaryClusters" size="tiny"></node-clusters-labels>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -0,0 +1,25 @@
{$layout "layout_popup"}
<h3>选择集群</h3>
<table class="ui table celled selectable">
<thead>
<tr>
<th>集群名称</th>
<th>节点数</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="cluster in clusters">
<td>{{cluster.name}}</td>
<td>{{cluster.countNodes}}</td>
<td><label-on :v-is-on="cluster.isOn"></label-on></td>
<td>
<span class="disabled" v-if="cluster.isSelected">已选择</span>
<a href="" @click.prevent="select(cluster)" v-if="!cluster.isSelected">选择</a>
</td>
</tr>
</table>
<div v-html="page"></div>

View File

@@ -0,0 +1,11 @@
Tea.context(function () {
this.select = function (cluster) {
NotifyPopup({
code: 200,
message: "",
data: {
cluster: cluster
}
})
}
})

View File

@@ -71,7 +71,7 @@
<link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red> <link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red>
</div> </div>
<div v-if="issue.type == 'node'"> <div v-if="issue.type == 'node'">
<link-red @click.prevent="updateNode(issue.targetId)">修复</link-red> <link-red @click.prevent="updateNode(issue.params.clusterId, issue.targetId)">修复</link-red>
</div> </div>
</td> </td>
</tr> </tr>
@@ -108,18 +108,18 @@
</td> </td>
<td> <td>
<span v-if="node.ipAddr.length > 0">{{node.ipAddr}}</span> <span v-if="node.ipAddr.length > 0">{{node.ipAddr}}</span>
<link-red title="点击设置" v-else @click.prevent="updateNode(node.id)">没有设置</link-red> <link-red title="点击设置" v-else @click.prevent="updateNode(node.clusterId, node.id)">没有设置</link-red>
</td> </td>
<td> <td>
<span v-if="node.route.code.length > 0">{{node.route.name}}</span> <span v-if="node.route.code.length > 0">{{node.route.name}}</span>
<link-red v-else title="点击设置" @click.prevent="updateNode(node.id)">没有设置</link-red> <link-red v-else title="点击设置" @click.prevent="updateNode(node.clusterId, node.id)">没有设置</link-red>
</td> </td>
<td> <td>
<span class="green" v-if="node.isResolved">已解析</span> <span class="green" v-if="node.isResolved">已解析</span>
<span v-else class="red">未解析</span> <span v-else class="red">未解析</span>
</td> </td>
<td> <td>
<link-popup @click.prevent="updateNode(node.id)">修改</link-popup> <link-popup @click.prevent="updateNode(node.clusterId, node.id)">修改</link-popup>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -10,8 +10,8 @@ Tea.context(function () {
}) })
} }
this.updateNode = function (nodeId) { this.updateNode = function (clusterId, nodeId) {
teaweb.popup("/dns/issues/updateNodePopup?nodeId=" + nodeId, { teaweb.popup("/dns/issues/updateNodePopup?clusterId=" + clusterId + "&nodeId=" + nodeId, {
height: "26em", height: "26em",
callback: function () { callback: function () {
teaweb.success("保存成功", function () { teaweb.success("保存成功", function () {

View File

@@ -43,7 +43,7 @@
<link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red> <link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red>
</div> </div>
<div v-if="issue.type == 'node'"> <div v-if="issue.type == 'node'">
<link-red @click.prevent="updateNode(issue.targetId)">修复</link-red> <link-red @click.prevent="updateNode(issue.params.clusterId, issue.targetId)">修复</link-red>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -17,9 +17,9 @@ Tea.context(function () {
}) })
} }
this.updateNode = function (nodeId) { this.updateNode = function (clusterId, nodeId) {
let that = this let that = this
teaweb.popup("/dns/issues/updateNodePopup?nodeId=" + nodeId, { teaweb.popup("/dns/issues/updateNodePopup?clusterId=" + clusterId + "&nodeId=" + nodeId, {
callback: function () { callback: function () {
teaweb.success("保存成功", function () { teaweb.success("保存成功", function () {
that.reload() that.reload()