This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,123 @@
package ns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
type AddPortPopupAction struct {
actionutils.ParentAction
}
func (this *AddPortPopupAction) Init() {
this.Nav("", "", "")
}
func (this *AddPortPopupAction) RunGet(params struct {
Protocol string
From string
SupportRange bool
}) {
this.Data["from"] = params.From
var protocols = serverconfigs.FindAllServerProtocols()
if len(params.Protocol) > 0 {
result := []maps.Map{}
for _, p := range protocols {
if p.GetString("code") == params.Protocol {
result = append(result, p)
}
}
protocols = result
}
this.Data["protocols"] = protocols
this.Data["supportRange"] = params.SupportRange
this.Show()
}
func (this *AddPortPopupAction) RunPost(params struct {
SupportRange bool
Protocol string
Address string
Must *actions.Must
}) {
// 校验地址
var addr = maps.Map{
"protocol": params.Protocol,
"host": "",
"portRange": "",
"minPort": 0,
"maxPort": 0,
}
var portRegexp = regexp.MustCompile(`^\d+$`)
if portRegexp.MatchString(params.Address) { // 单个端口
addr["portRange"] = this.checkPort(params.Address)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(params.Address) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(params.Address)
} else if strings.Contains(params.Address, ":") { // IP:Port
index := strings.LastIndex(params.Address, ":")
addr["host"] = strings.TrimSpace(params.Address[:index])
port := strings.TrimSpace(params.Address[index+1:])
if portRegexp.MatchString(port) {
addr["portRange"] = this.checkPort(port)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(port) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(port)
} else {
this.FailField("address", "请输入正确的端口或者网络地址")
}
} else {
this.FailField("address", "请输入正确的端口或者网络地址")
}
this.Data["address"] = addr
this.Success()
}
func (this *AddPortPopupAction) checkPort(port string) (portRange string) {
var intPort = types.Int(port)
if intPort < 1 {
this.FailField("address", "端口号不能小于1")
}
if intPort > 65535 {
this.FailField("address", "端口号不能大于65535")
}
return port
}
func (this *AddPortPopupAction) checkPortRange(port string) (portRange string, minPort int, maxPort int) {
var pieces = strings.Split(port, "-")
var piece1 = strings.TrimSpace(pieces[0])
var piece2 = strings.TrimSpace(pieces[1])
var port1 = types.Int(piece1)
var port2 = types.Int(piece2)
if port1 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port1 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port2 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port2 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port1 > port2 {
port1, port2 = port2, port1
}
return types.String(port1) + "-" + types.String(port2), port1, port2
}

View File

@@ -0,0 +1,173 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"regexp"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
RequestId string
Keyword string
Day string
ClusterId int64
NodeId int64
RecordType string
}) {
day := strings.ReplaceAll(params.Day, "-", "")
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
day = timeutil.Format("Ymd")
}
this.Data["keyword"] = params.Keyword
this.Data["day"] = day[:4] + "-" + day[4:6] + "-" + day[6:]
this.Data["path"] = this.Request.URL.Path
this.Data["clusterId"] = params.ClusterId
this.Data["nodeId"] = params.NodeId
this.Data["recordType"] = params.RecordType
var size = int64(20)
resp, err := this.RPC().NSAccessLogRPC().ListNSAccessLogs(this.AdminContext(), &pb.ListNSAccessLogsRequest{
RequestId: params.RequestId,
NsClusterId: params.ClusterId,
NsNodeId: params.NodeId,
NsDomainId: 0,
NsRecordId: 0,
RecordType: params.RecordType,
Size: size,
Day: day,
Keyword: params.Keyword,
Reverse: false,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipList = []string{}
var nodeIds = []int64{}
var domainIds = []int64{}
if len(resp.NsAccessLogs) == 0 {
this.Data["accessLogs"] = []interface{}{}
} else {
this.Data["accessLogs"] = resp.NsAccessLogs
for _, accessLog := range resp.NsAccessLogs {
// IP
if len(accessLog.RemoteAddr) > 0 {
// 去掉端口
ip, _, err := net.SplitHostPort(accessLog.RemoteAddr)
if err == nil {
accessLog.RemoteAddr = ip
if !lists.ContainsString(ipList, ip) {
ipList = append(ipList, ip)
}
}
}
// 节点
if !lists.ContainsInt64(nodeIds, accessLog.NsNodeId) {
nodeIds = append(nodeIds, accessLog.NsNodeId)
}
// 域名
if !lists.ContainsInt64(domainIds, accessLog.NsDomainId) {
domainIds = append(domainIds, accessLog.NsDomainId)
}
}
}
this.Data["hasMore"] = resp.HasMore
this.Data["nextRequestId"] = resp.RequestId
// 上一个requestId
this.Data["hasPrev"] = false
this.Data["lastRequestId"] = ""
if len(params.RequestId) > 0 {
this.Data["hasPrev"] = true
prevResp, err := this.RPC().NSAccessLogRPC().ListNSAccessLogs(this.AdminContext(), &pb.ListNSAccessLogsRequest{
RequestId: params.RequestId,
NsClusterId: params.ClusterId,
NsNodeId: params.NodeId,
NsDomainId: 0,
NsRecordId: 0,
RecordType: params.RecordType,
Day: day,
Keyword: params.Keyword,
Size: size,
Reverse: true,
})
if err != nil {
this.ErrorPage(err)
return
}
if int64(len(prevResp.NsAccessLogs)) == size {
this.Data["lastRequestId"] = prevResp.RequestId
}
}
// 根据IP查询区域
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
// 节点信息
var nodeMap = map[int64]interface{}{} // node id => { ... }
for _, nodeId := range nodeIds {
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: nodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.NsNode
if node != nil {
nodeMap[node.Id] = maps.Map{
"id": node.Id,
"name": node.Name,
"cluster": maps.Map{
"id": node.NsCluster.Id,
"name": node.NsCluster.Name,
},
}
}
}
this.Data["nodes"] = nodeMap
// 域名信息
var domainMap = map[int64]interface{}{} // domain id => { ... }
for _, domainId := range domainIds {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.NsDomain
if domain != nil {
domainMap[domain.Id] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
}
}
this.Data["domains"] = domainMap
// 所有记录类型
this.Data["recordTypes"] = dnsconfigs.FindAllRecordTypeDefinitions()
this.Show()
}

View File

@@ -0,0 +1,23 @@
//go:build plus
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "accessLog").
Prefix("/ns/clusters/accessLogs").
Get("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,129 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cluster
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
"github.com/miekg/dns"
"regexp"
"sync"
)
type CheckPortsAction struct {
actionutils.ParentAction
}
func (this *CheckPortsAction) RunPost(params struct {
ClusterId int64
Ip []string
}) {
this.Data["results"] = []maps.Map{}
this.Data["port"] = ""
if len(params.Ip) == 0 {
this.Success()
return
}
// 集群设置
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NsCluster
if cluster == nil || !cluster.IsOn || !cluster.CheckingPorts {
this.Success()
return
}
// UDP端口设置
udpConfigResp, err := this.RPC().NSClusterRPC().FindNSClusterUDPConfig(this.AdminContext(), &pb.FindNSClusterUDPConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var udpJSON = udpConfigResp.UdpJSON
if len(udpJSON) == 0 {
this.Success()
return
}
var udpConfig = &serverconfigs.UDPProtocolConfig{}
err = json.Unmarshal(udpJSON, udpConfig)
if err != nil {
this.ErrorPage(err)
return
}
if !udpConfig.IsOn || len(udpConfig.Listen) == 0 {
this.Success()
return
}
var portString = udpConfig.Listen[0].PortRange
if !regexp.MustCompile(`^\d+$`).MatchString(portString) {
this.Success()
return
}
this.Data["port"] = portString
var client = new(dns.Client)
var resultMaps = []maps.Map{}
var locker = &sync.Mutex{}
var wg = sync.WaitGroup{}
wg.Add(len(params.Ip))
for _, ip := range params.Ip {
go func(ip string) {
defer wg.Done()
var queryErr error
var retries = 2
for i := 1; i <= retries; i++ {
var questionMsg = new(dns.Msg)
var answerMsg *dns.Msg
questionMsg.SetQuestion("ping.", dns.TypeA)
answerMsg, _, queryErr = client.Exchange(questionMsg, ip+":"+portString)
if queryErr == nil {
var isValid = false
if answerMsg != nil && len(answerMsg.Answer) > 0 {
var answer = answerMsg.Answer[0]
aAnswer, ok := answer.(*dns.A)
if ok && aAnswer.A.String() == "127.0.0.1" {
isValid = true
} else {
queryErr = errors.New("invalid answer: " + answer.String())
break
}
}
if !isValid {
queryErr = errors.New("invalid answer")
}
break
}
}
locker.Lock()
var errorString = ""
if queryErr != nil {
errorString = queryErr.Error()
}
resultMaps = append(resultMaps, maps.Map{
"ip": ip,
"isOk": queryErr == nil,
"err": errorString,
})
locker.Unlock()
}(ip)
}
wg.Wait()
this.Data["results"] = resultMaps
this.Success()
}

View File

@@ -0,0 +1,129 @@
//go:build plus
package cluster
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
// CreateNodeAction 创建节点
type CreateNodeAction struct {
actionutils.ParentAction
}
func (this *CreateNodeAction) Init() {
this.Nav("", "node", "create")
this.SecondMenu("nodes")
}
func (this *CreateNodeAction) RunGet(params struct {
ClusterId int64
}) {
this.Show()
}
func (this *CreateNodeAction) RunPost(params struct {
Name string
IpAddressesJSON []byte
ClusterId int64
Must *actions.Must
}) {
params.Must.
Field("name", params.Name).
Require("请输入节点名称")
if len(params.IpAddressesJSON) == 0 {
this.Fail("请至少添加一个IP地址")
}
// 检查cluster
if params.ClusterId <= 0 {
this.Fail("请选择所在集群")
}
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
if clusterResp.NsCluster == nil {
this.Fail("选择的集群不存在")
}
// IP地址
ipAddresses := []maps.Map{}
if len(params.IpAddressesJSON) > 0 {
err := json.Unmarshal(params.IpAddressesJSON, &ipAddresses)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(ipAddresses) == 0 {
this.Fail("请至少输入一个IP地址")
}
// 保存
createResp, err := this.RPC().NSNodeRPC().CreateNSNode(this.AdminContext(), &pb.CreateNSNodeRequest{
Name: params.Name,
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
nodeId := createResp.NsNodeId
// IP地址
for _, addrMap := range ipAddresses {
addressId := addrMap.GetInt64("id")
if addressId > 0 {
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
NodeIPAddressId: addressId,
NodeId: nodeId,
})
} else {
var ipStrings = addrMap.GetString("ip")
result, _ := utils.ExtractIP(ipStrings)
if len(result) == 1 {
// 单个创建
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleDNS,
Name: addrMap.GetString("name"),
Ip: result[0],
CanAccess: addrMap.GetBool("canAccess"),
IsUp: addrMap.GetBool("isUp"),
})
} else if len(result) > 1 {
// 批量创建
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(this.AdminContext(), &pb.CreateNodeIPAddressesRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleDNS,
Name: addrMap.GetString("name"),
IpList: result,
CanAccess: addrMap.GetBool("canAccess"),
IsUp: addrMap.GetBool("isUp"),
GroupValue: ipStrings,
})
}
}
if err != nil {
this.ErrorPage(err)
return
}
}
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogCreateNSNode, nodeId)
this.Success()
}

View File

@@ -0,0 +1,51 @@
//go:build plus
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) Init() {
this.Nav("", "delete", "index")
this.SecondMenu("nodes")
}
func (this *DeleteAction) RunGet(params struct {
ClusterId int64
}) {
countResp, err := this.RPC().NSDomainRPC().CountAllNSDomains(this.AdminContext(), &pb.CountAllNSDomainsRequest{
NsClusterId: params.ClusterId,
Status: dnsconfigs.NSDomainStatusVerified,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countDomains"] = countResp.Count
this.Show()
}
func (this *DeleteAction) RunPost(params struct {
ClusterId int64
}) {
// 创建日志
defer this.CreateLogInfo(codes.NSCluster_LogDeleteNSCluster, params.ClusterId)
// 删除
_, err := this.RPC().NSClusterRPC().DeleteNSCluster(this.AdminContext(), &pb.DeleteNSCluster{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteNodeAction struct {
actionutils.ParentAction
}
func (this *DeleteNodeAction) RunPost(params struct {
NodeId int64
}) {
defer this.CreateLogInfo(codes.NSNode_LogDeleteNSNode, params.NodeId)
_, err := this.RPC().NSNodeRPC().DeleteNSNode(this.AdminContext(), &pb.DeleteNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,156 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package cluster
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "index")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
InstalledState int
ActiveState int
Keyword string
}) {
this.Data["installState"] = params.InstalledState
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
// 最新版本
versionResp, err := this.RPC().NSNodeRPC().FindLatestNSNodeVersion(this.AdminContext(), &pb.FindLatestNSNodeVersionRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var latestVersion = versionResp.Version
this.Data["latestVersion"] = latestVersion
countAllResp, err := this.RPC().NSNodeRPC().CountAllNSNodesMatch(this.AdminContext(), &pb.CountAllNSNodesMatchRequest{
NsClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countAll"] = countAllResp.Count
countResp, err := this.RPC().NSNodeRPC().CountAllNSNodesMatch(this.AdminContext(), &pb.CountAllNSNodesMatchRequest{
NsClusterId: params.ClusterId,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
nodesResp, err := this.RPC().NSNodeRPC().ListNSNodesMatch(this.AdminContext(), &pb.ListNSNodesMatchRequest{
Offset: page.Offset,
Size: page.Size,
NsClusterId: params.ClusterId,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.NsNodes {
// 状态
status := &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
logs.Error(err)
continue
}
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
}
// IP
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: node.Id,
Role: nodeconfigs.NodeRoleDNS,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipAddresses = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
"isOk": node.InstallStatus.IsOk,
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": node.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"load1m": numberutils.FormatFloat2(status.Load1m),
"load5m": numberutils.FormatFloat2(status.Load5m),
"load15m": numberutils.FormatFloat2(status.Load15m),
"version": status.BuildVersion,
},
"ipAddresses": ipAddresses,
})
}
this.Data["nodes"] = nodeMaps
// 记录最近访问
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
ItemType: "nsCluster",
ItemId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Show()
}

View File

@@ -0,0 +1,43 @@
//go:build plus
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/node"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster").
Get("", new(IndexAction)).
GetPost("/delete", new(DeleteAction)).
GetPost("/createNode", new(CreateNodeAction)).
Post("/deleteNode", new(DeleteNodeAction)).
Get("/upgradeRemote", new(UpgradeRemoteAction)).
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
Post("/checkPorts", new(CheckPortsAction)).
// 节点相关
Prefix("/ns/clusters/cluster/node").
Get("", new(node.IndexAction)).
Get("/logs", new(node.LogsAction)).
GetPost("/update", new(node.UpdateAction)).
GetPost("/install", new(node.InstallAction)).
Post("/status", new(node.StatusAction)).
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
Post("/start", new(node.StartAction)).
Post("/stop", new(node.StopAction)).
EndAll()
})
}

View File

@@ -0,0 +1,220 @@
//go:build plus
package node
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "node")
this.SecondMenu("nodes")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.NsNode
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
var clusterMap maps.Map = nil
if node.NsCluster != nil {
clusterId := node.NsCluster.Id
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
cluster := clusterResp.NsCluster
if cluster != nil {
clusterMap = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
}
}
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleDNS,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipAddressMaps = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}
// 运行状态
this.Data["nodeDatetime"] = ""
this.Data["nodeTimeDiff"] = 0
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
this.ErrorPage(err)
return
}
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
if status.Timestamp > 0 {
this.Data["nodeDatetime"] = timeutil.FormatTime("Y-m-d H:i:s", status.Timestamp)
if status.UpdatedAt > 0 {
var diff = status.UpdatedAt - status.Timestamp
if diff < 0 {
diff = -diff
}
this.Data["nodeTimeDiff"] = diff
}
} else if status.UpdatedAt > 0 {
this.Data["nodeDatetime"] = timeutil.FormatTime("Y-m-d H:i:s", status.UpdatedAt)
}
}
// 检查是否有新版本
if len(status.OS) > 0 {
checkVersionResp, err := this.RPC().NSNodeRPC().CheckNSNodeLatestVersion(this.AdminContext(), &pb.CheckNSNodeLatestVersionRequest{
Os: status.OS,
Arch: status.Arch,
CurrentVersion: status.BuildVersion,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
this.Data["newVersion"] = checkVersionResp.NewVersion
} else {
this.Data["shouldUpgrade"] = false
this.Data["newVersion"] = ""
}
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
loginParams := maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method, this.LangCode()),
"username": grantResp.NodeGrant.Username,
}
}
}
loginMap = maps.Map{
"id": node.NodeLogin.Id,
"name": node.NodeLogin.Name,
"type": node.NodeLogin.Type,
"params": loginParams,
"grant": grantMap,
}
}
// API节点地址
var apiNodeAddrStrings = []string{}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(node.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(node.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
for _, addr := range apiNodeAddrs {
if addr.Init() == nil {
apiNodeAddrStrings = append(apiNodeAddrStrings, addr.FullAddresses()...)
}
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"isOn": node.IsOn,
"apiNodeAddrs": apiNodeAddrStrings,
"status": maps.Map{
"isActive": node.IsActive && status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"connectionCount": status.ConnectionCount,
"buildVersion": status.BuildVersion,
"cpuPhysicalCount": status.CPUPhysicalCount,
"cpuLogicalCount": status.CPULogicalCount,
"load1m": fmt.Sprintf("%.2f", status.Load1m),
"load5m": fmt.Sprintf("%.2f", status.Load5m),
"load15m": fmt.Sprintf("%.2f", status.Load15m),
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
"exePath": status.ExePath,
"apiSuccessPercent": status.APISuccessPercent,
"apiAvgCostSeconds": status.APIAvgCostSeconds,
},
"login": loginMap,
}
this.Show()
}

View File

@@ -0,0 +1,143 @@
//go:build plus
package node
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
// InstallAction 安装节点
type InstallAction struct {
actionutils.ParentAction
}
func (this *InstallAction) Init() {
this.Nav("", "node", "install")
this.SecondMenu("nodes")
}
func (this *InstallAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
// 节点
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.NsNode
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
// 安装信息
if node.InstallStatus != nil {
this.Data["installStatus"] = maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
"isOk": node.InstallStatus.IsOk,
"updatedAt": node.InstallStatus.UpdatedAt,
"error": node.InstallStatus.Error,
}
} else {
this.Data["installStatus"] = nil
}
// 集群
var clusterMap maps.Map = nil
if node.NsCluster != nil {
var clusterId = node.NsCluster.Id
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NsCluster
if cluster != nil {
clusterMap = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
}
}
}
// API节点列表
apiNodesResp, err := this.RPC().APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var apiNodes = apiNodesResp.ApiNodes
var apiEndpoints = []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
continue
}
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
}
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"cluster": clusterMap,
}
// SSH主机地址
this.Data["sshAddr"] = ""
if node.NodeLogin != nil && node.NodeLogin.Type == "ssh" && !utils.JSONIsNull(node.NodeLogin.Params) {
var loginParams = maps.Map{}
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
var host = loginParams.GetString("host")
if len(host) > 0 {
var port = loginParams.GetString("port")
if port == "0" {
port = "22"
}
this.Data["sshAddr"] = configutils.QuoteIP(host) + ":" + port
}
}
this.Show()
}
// RunPost 开始安装
func (this *InstallAction) RunPost(params struct {
NodeId int64
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogInstallNSNodeRemotely, params.NodeId)
_, err := this.RPC().NSNodeRPC().InstallNSNode(this.AdminContext(), &pb.InstallNSNodeRequest{
NsNodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,95 @@
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type LogsAction struct {
actionutils.ParentAction
}
func (this *LogsAction) Init() {
this.Nav("", "node", "log")
this.SecondMenu("nodes")
}
func (this *LogsAction) RunGet(params struct {
NodeId int64
DayFrom string
DayTo string
Keyword string
Level string
}) {
this.Data["nodeId"] = params.NodeId
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleDNS,
NodeId: params.NodeId,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count, 20)
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleDNS,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var logs = []maps.Map{}
for _, log := range logsResp.NodeLogs {
logs = append(logs, maps.Map{
"tag": log.Tag,
"description": log.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
})
}
this.Data["logs"] = logs
this.Data["page"] = page.AsHTML()
// 节点信息
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.NsNode
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
}
this.Show()
}

View File

@@ -0,0 +1,32 @@
//go:build plus
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type StartAction struct {
actionutils.ParentAction
}
func (this *StartAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NSNodeRPC().StartNSNode(this.AdminContext(), &pb.StartNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogStartNSNodeRemotely, params.NodeId)
if resp.IsOk {
this.Success()
}
this.Fail("启动失败:" + resp.Error)
}

View File

@@ -0,0 +1,48 @@
//go:build plus
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
// StatusAction 节点状态
type StatusAction struct {
actionutils.ParentAction
}
func (this *StatusAction) RunPost(params struct {
NodeId int64
}) {
// 节点
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.NsNode
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
// 安装信息
if node.InstallStatus != nil {
this.Data["installStatus"] = maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
"isOk": node.InstallStatus.IsOk,
"updatedAt": node.InstallStatus.UpdatedAt,
"error": node.InstallStatus.Error,
"errorCode": node.InstallStatus.ErrorCode,
}
} else {
this.Data["installStatus"] = nil
}
this.Data["isInstalled"] = node.IsInstalled
this.Success()
}

View File

@@ -0,0 +1,32 @@
//go:build plus
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type StopAction struct {
actionutils.ParentAction
}
func (this *StopAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NSNodeRPC().StopNSNode(this.AdminContext(), &pb.StopNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogStopNSNodeRemotely, params.NodeId)
if resp.IsOk {
this.Success()
}
this.Fail("执行失败:" + resp.Error)
}

View File

@@ -0,0 +1,277 @@
//go:build plus
package node
import (
"encoding/json"
"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/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("nodes")
}
func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.NsNode
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
var clusterMap maps.Map = nil
if node.NsCluster != nil {
clusterMap = maps.Map{
"id": node.NsCluster.Id,
"name": node.NsCluster.Name,
}
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleDNS,
})
if err != nil {
this.ErrorPage(err)
return
}
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
loginParams := maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method, this.LangCode()),
"username": grantResp.NodeGrant.Username,
}
}
}
loginMap = maps.Map{
"id": node.NodeLogin.Id,
"name": node.NodeLogin.Name,
"type": node.NodeLogin.Type,
"params": loginParams,
"grant": grantMap,
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"login": loginMap,
}
// 所有集群
resp, err := this.RPC().NSClusterRPC().FindAllNSClusters(this.AdminContext(), &pb.FindAllNSClustersRequest{})
if err != nil {
this.ErrorPage(err)
}
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range resp.NsClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
// API相关
apiConfigResp, err := this.RPC().NSNodeRPC().FindNSNodeAPIConfig(this.AdminContext(), &pb.FindNSNodeAPIConfigRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(apiConfigResp.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(apiConfigResp.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["apiNodeAddrs"] = apiNodeAddrs
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
LoginId int64
GrantId int64
SshHost string
SshPort int
NodeId int64
Name string
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
ClusterId int64
IsOn bool
ApiNodeAddrsJSON []byte
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogUpdateNSNode, params.NodeId)
if params.NodeId <= 0 {
this.Fail("要操作的节点不存在")
}
params.Must.
Field("name", params.Name).
Require("请输入节点名称")
// 检查cluster
if params.ClusterId <= 0 {
this.Fail("请选择所在集群")
}
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
if clusterResp.NsCluster == nil {
this.Fail("选择的集群不存在")
}
// IP地址
ipAddresses := []maps.Map{}
if len(params.IPAddressesJSON) > 0 {
err := json.Unmarshal(params.IPAddressesJSON, &ipAddresses)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(ipAddresses) == 0 {
this.Fail("请至少输入一个IP地址")
}
// TODO 检查登录授权
loginInfo := &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",
Params: maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
}.AsJSON(),
}
// 保存
_, err = this.RPC().NSNodeRPC().UpdateNSNode(this.AdminContext(), &pb.UpdateNSNodeRequest{
NsNodeId: params.NodeId,
Name: params.Name,
NsClusterId: params.ClusterId,
IsOn: params.IsOn,
NodeLogin: loginInfo,
})
if err != nil {
this.ErrorPage(err)
return
}
// 禁用老的IP地址
_, err = this.RPC().NodeIPAddressRPC().DisableAllNodeIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleDNS,
})
if err != nil {
this.ErrorPage(err)
return
}
// 添加新的IP地址
err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, nodeconfigs.NodeRoleDNS, params.IPAddressesJSON)
if err != nil {
this.ErrorPage(err)
return
}
// API节点设置
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(params.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(params.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
for _, addr := range apiNodeAddrs {
err = addr.Init()
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
}
}
_, err = this.RPC().NSNodeRPC().UpdateNSNodeAPIConfig(this.AdminContext(), &pb.UpdateNSNodeAPIConfigRequest{
NsNodeId: params.NodeId,
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,32 @@
//go:build plus
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateInstallStatusAction struct {
actionutils.ParentAction
}
func (this *UpdateInstallStatusAction) RunPost(params struct {
NodeId int64
IsInstalled bool
}) {
// 创建日志
defer this.CreateLogInfo(codes.NSNode_LogUpdateNSNodeInstallationStatus, params.NodeId)
_, err := this.RPC().NSNodeRPC().UpdateNSNodeIsInstalled(this.AdminContext(), &pb.UpdateNSNodeIsInstalledRequest{
NsNodeId: params.NodeId,
IsInstalled: params.IsInstalled,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,75 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package accessLog
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("accessLog")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
accessLogResp, err := this.RPC().NSClusterRPC().FindNSClusterAccessLog(this.AdminContext(), &pb.FindNSClusterAccessLogRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var accessLogRef = &dnsconfigs.NSAccessLogRef{}
if len(accessLogResp.AccessLogJSON) > 0 {
err = json.Unmarshal(accessLogResp.AccessLogJSON, accessLogRef)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["accessLogRef"] = accessLogRef
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
AccessLogJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsAccessLog, params.ClusterId)
var ref = &dnsconfigs.NSAccessLogRef{}
err := json.Unmarshal(params.AccessLogJSON, ref)
if err != nil {
this.Fail("数据格式错误:" + err.Error())
}
err = ref.Init()
if err != nil {
this.Fail("数据格式错误:" + err.Error())
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterAccessLog(this.AdminContext(), &pb.UpdateNSClusterAccessLogRequest{
NsClusterId: params.ClusterId,
AccessLogJSON: params.AccessLogJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package accessLog
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/accessLog").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,83 @@
//go:build plus
package answer
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
// IndexAction UDP设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("answer")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
answerResp, err := this.RPC().NSClusterRPC().FindNSClusterAnswerConfig(this.AdminContext(), &pb.FindNSClusterAnswerConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var config = dnsconfigs.DefaultNSAnswerConfig()
if len(answerResp.AnswerJSON) > 0 {
err := json.Unmarshal(answerResp.AnswerJSON, config)
if err != nil {
this.ErrorPage(err)
}
}
this.Data["config"] = config
this.Data["modes"] = dnsconfigs.FindAllNSAnswerModes()
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Mode string
MaxSize int16
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsAnswer, params.ClusterId)
if !dnsconfigs.IsValidNSAnswerMode(params.Mode) {
this.Fail("不支持的模式'" + params.Mode + "'")
}
var config = dnsconfigs.DefaultNSAnswerConfig()
config.Mode = params.Mode
config.MaxSize = params.MaxSize
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterAnswerConfig(this.AdminContext(), &pb.UpdateNSClusterAnswerConfigRequest{
NsClusterId: params.ClusterId,
AnswerJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package answer
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/answer").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,112 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package ddosProtection
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"net"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("ddosProtection")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
protectionResp, err := this.RPC().NSClusterRPC().FindNSClusterDDoSProtection(this.AdminContext(), &pb.FindNSClusterDDoSProtectionRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var ddosProtectionConfig = ddosconfigs.DefaultProtectionConfig()
if len(protectionResp.DdosProtectionJSON) > 0 {
err = json.Unmarshal(protectionResp.DdosProtectionJSON, ddosProtectionConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = ddosProtectionConfig
this.Data["defaultConfigs"] = dnsconfigs.DefaultConfigs
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
DdosProtectionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsDDoSProtection, params.ClusterId)
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
err := json.Unmarshal(params.DdosProtectionJSON, ddosProtectionConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = ddosProtectionConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
// 校验参数
if ddosProtectionConfig.TCP != nil {
var tcpConfig = ddosProtectionConfig.TCP
if tcpConfig.MaxConnectionsPerIP > 0 && tcpConfig.MaxConnectionsPerIP < dnsconfigs.DefaultTCPMinConnectionsPerIP {
this.FailField("tcpMaxConnectionsPerIP", "TCP: 单IP TCP最大连接数不能小于"+types.String(dnsconfigs.DefaultTCPMinConnectionsPerIP))
}
if tcpConfig.NewConnectionsMinutelyRate > 0 && tcpConfig.NewConnectionsMinutelyRate < dnsconfigs.DefaultTCPNewConnectionsMinMinutelyRate {
this.FailField("tcpNewConnectionsMinutelyRate", "TCP: 单IP连接速率不能小于"+types.String(dnsconfigs.DefaultTCPNewConnectionsMinMinutelyRate))
}
if tcpConfig.NewConnectionsSecondlyRate > 0 && tcpConfig.NewConnectionsSecondlyRate < dnsconfigs.DefaultTCPNewConnectionsMinSecondlyRate {
this.FailField("tcpNewConnectionsSecondlyRate", "TCP: 单IP连接速率不能小于"+types.String(dnsconfigs.DefaultTCPNewConnectionsMinSecondlyRate))
}
// Port
for _, portConfig := range tcpConfig.Ports {
if portConfig.Port > 65535 {
this.Fail("端口号" + types.String(portConfig.Port) + "不能大于65535")
}
}
// IP
for _, ipConfig := range tcpConfig.AllowIPList {
if net.ParseIP(ipConfig.IP) == nil {
this.Fail("白名单IP '" + ipConfig.IP + "' 格式错误")
}
}
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterDDoSProtection(this.AdminContext(), &pb.UpdateNSClusterDDoSProtectionRequest{
NsClusterId: params.ClusterId,
DdosProtectionJSON: params.DdosProtectionJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,26 @@
//go:build plus
package ddosProtection
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/ddos-protection").
GetPost("", new(IndexAction)).
GetPost("/status", new(StatusAction)).
EndAll()
})
}

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package ddosProtection
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/nsnodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/iwind/TeaGo/maps"
)
type StatusAction struct {
actionutils.ParentAction
}
func (this *StatusAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("ddosProtection")
}
func (this *StatusAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
this.Show()
}
func (this *StatusAction) RunPost(params struct {
ClusterId int64
}) {
results, err := nsnodeutils.SendMessageToNSCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeCheckLocalFirewall, &messageconfigs.CheckLocalFirewallMessage{
Name: "nftables",
}, 10)
if err != nil {
this.ErrorPage(err)
return
}
var resultMaps = []maps.Map{}
for _, result := range results {
var resultMap = maps.Map{
"isOk": result.IsOK,
"message": result.Message,
"nodeId": result.NodeId,
"nodeName": result.NodeName,
}
nodeResp, err := this.RPC().NSNodeRPC().FindNSNodeDDoSProtection(this.AdminContext(), &pb.FindNSNodeDDoSProtectionRequest{NsNodeId: result.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
if len(nodeResp.DdosProtectionJSON) > 0 {
var ddosProtection = ddosconfigs.DefaultProtectionConfig()
err = json.Unmarshal(nodeResp.DdosProtectionJSON, ddosProtection)
if err != nil {
this.ErrorPage(err)
return
}
resultMap["isPrior"] = !ddosProtection.IsPriorEmpty()
}
resultMaps = append(resultMaps, resultMap)
}
this.Data["results"] = resultMaps
this.Success()
}

View File

@@ -0,0 +1,195 @@
//go:build plus
package doh
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
// IndexAction DoH设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("doh")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
dohResp, err := this.RPC().NSClusterRPC().FindNSClusterDoHConfig(this.AdminContext(), &pb.FindNSClusterDoHConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var dohConfig = dnsconfigs.NewNSDoHConfig()
if len(dohResp.DohJSON) > 0 {
err := json.Unmarshal(dohResp.DohJSON, dohConfig)
if err != nil {
this.ErrorPage(err)
}
} else {
dohConfig.Listen = []*serverconfigs.NetworkAddressConfig{
{
Protocol: serverconfigs.ProtocolHTTPS,
PortRange: "443",
},
}
}
// SSL配置
var sslPolicy *sslconfigs.SSLPolicy
if dohConfig.SSLPolicyRef != nil && dohConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
SslPolicyId: dohConfig.SSLPolicyRef.SSLPolicyId,
IgnoreData: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var sslPolicyConfigJSON = sslPolicyConfigResp.SslPolicyJSON
if len(sslPolicyConfigJSON) > 0 {
sslPolicy = &sslconfigs.SSLPolicy{}
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
if err != nil {
this.ErrorPage(err)
return
}
}
}
this.Data["dohConfig"] = maps.Map{
"isOn": dohConfig.IsOn,
"listen": dohConfig.Listen,
"sslPolicy": sslPolicy,
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
IsOn bool
Addresses []byte
SslPolicyJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsDoH, params.ClusterId)
var addresses = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.Addresses, &addresses)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
}
// 校验SSL
var sslPolicyId = int64(0)
if params.SslPolicyJSON != nil {
sslPolicy := &sslconfigs.SSLPolicy{}
err = json.Unmarshal(params.SslPolicyJSON, sslPolicy)
if err != nil {
this.ErrorPage(errors.New("解析SSL配置时发生了错误" + err.Error()))
return
}
sslPolicyId = sslPolicy.Id
certsJSON, err := json.Marshal(sslPolicy.CertRefs)
if err != nil {
this.ErrorPage(err)
return
}
hstsJSON, err := json.Marshal(sslPolicy.HSTS)
if err != nil {
this.ErrorPage(err)
return
}
clientCACertsJSON, err := json.Marshal(sslPolicy.ClientCARefs)
if err != nil {
this.ErrorPage(err)
return
}
if sslPolicyId > 0 {
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
SslPolicyId: sslPolicyId,
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
} else {
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
sslPolicyId = resp.SslPolicyId
}
}
var dohConfig = dnsconfigs.NewNSDoHConfig()
dohConfig.IsOn = params.IsOn
dohConfig.Listen = addresses
dohConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
IsOn: true,
SSLPolicyId: sslPolicyId,
}
configData, err := json.Marshal(dohConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterDoH(this.AdminContext(), &pb.UpdateNSClusterDoHRequest{
NsClusterId: params.ClusterId,
DohJSON: configData,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package doh
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/doh").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,117 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("basic")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
clusterResp, err := this.RPC().NSClusterRPC().FindNSCluster(this.AdminContext(), &pb.FindNSClusterRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NsCluster
if cluster == nil {
this.NotFound("nsCluster", params.ClusterId)
return
}
var hosts = cluster.Hosts
if hosts == nil {
hosts = []string{}
}
// 时区
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
if len(cluster.TimeZone) == 0 {
cluster.TimeZone = nodeconfigs.DefaultTimeZoneLocation
}
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(cluster.TimeZone)
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"email": cluster.Email,
"hosts": hosts,
"timeZone": cluster.TimeZone,
"autoRemoteStart": cluster.AutoRemoteStart,
"detectAgents": cluster.DetectAgents,
"checkingPorts": cluster.CheckingPorts,
"isOn": cluster.IsOn,
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Name string
Email string
Hosts []string
TimeZone string
AutoRemoteStart bool
DetectAgents bool
CheckingPorts bool
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsBasic, params.ClusterId)
params.Must.
Field("name", params.Name).
Require("请输入集群名称").
Field("email", params.Email).
Require("请输入正确的管理员电子邮箱地址")
// 校验主机域名
var hosts = []string{}
for _, host := range params.Hosts {
if !domainutils.ValidateDomainFormat(host) {
this.Fail("错误的DNS主机地址 '" + host + "'")
return
}
hosts = append(hosts, host)
}
_, err := this.RPC().NSClusterRPC().UpdateNSCluster(this.AdminContext(), &pb.UpdateNSClusterRequest{
NsClusterId: params.ClusterId,
Name: params.Name,
Email: params.Email,
Hosts: hosts,
TimeZone: params.TimeZone,
AutoRemoteStart: params.AutoRemoteStart,
DetectAgents: params.DetectAgents,
CheckingPorts: params.CheckingPorts,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,70 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package recursion
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("recursion")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
resp, err := this.RPC().NSClusterRPC().FindNSClusterRecursionConfig(this.AdminContext(), &pb.FindNSClusterRecursionConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var config = &dnsconfigs.NSRecursionConfig{}
if len(resp.RecursionJSON) > 0 {
err = json.Unmarshal(resp.RecursionJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
} else {
config.UseLocalHosts = true
}
this.Data["config"] = config
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
RecursionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsRecursion, params.ClusterId)
// TODO 校验域名
_, err := this.RPC().NSClusterRPC().UpdateNSClusterRecursionConfig(this.AdminContext(), &pb.UpdateNSClusterRecursionConfigRequest{
NsClusterId: params.ClusterId,
RecursionJSON: params.RecursionJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package recursion
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/recursion").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,99 @@
//go:build plus
package soa
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
// IndexAction UDP设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("soa")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
soaResp, err := this.RPC().NSClusterRPC().FindNSClusterSOAConfig(this.AdminContext(), &pb.FindNSClusterSOAConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var config = dnsconfigs.DefaultNSSOAConfig()
if len(soaResp.SoaJSON) > 0 {
err := json.Unmarshal(soaResp.SoaJSON, config)
if err != nil {
this.ErrorPage(err)
}
}
this.Data["config"] = config
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
MName string
RName string
RefreshSeconds uint32
RetrySeconds uint32
ExpireSeconds uint32
MinimumTTL uint32
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsSOA, params.ClusterId)
if len(params.MName) > 0 {
if !domainutils.ValidateDomainFormat(params.MName) {
this.FailField("mName", "请输入正确的主要服务器名称")
}
}
if len(params.RName) > 0 {
if !utils.ValidateEmail(params.RName) {
this.FailField("rName", "请输入正确的管理员电子邮箱地址")
}
}
var config = dnsconfigs.DefaultNSSOAConfig()
config.MName = params.MName
config.RName = params.RName
config.RefreshSeconds = params.RefreshSeconds
config.RetrySeconds = params.RetrySeconds
config.ExpireSeconds = params.ExpireSeconds
config.MinimumTTL = params.MinimumTTL
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterSOAConfig(this.AdminContext(), &pb.UpdateNSClusterSOAConfigRequest{
NsClusterId: params.ClusterId,
SoaJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package soa
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/soa").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,86 @@
//go:build plus
package tcp
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
)
// IndexAction TCP设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("tcp")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
tcpResp, err := this.RPC().NSClusterRPC().FindNSClusterTCPConfig(this.AdminContext(), &pb.FindNSClusterTCPConfigRequest{
NsClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
var tcpConfig = &serverconfigs.TCPProtocolConfig{}
if len(tcpResp.TcpJSON) > 0 {
err := json.Unmarshal(tcpResp.TcpJSON, tcpConfig)
if err != nil {
this.ErrorPage(err)
}
} else {
tcpConfig.IsOn = true
}
this.Data["tcpConfig"] = tcpConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Addresses []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsTCP, params.ClusterId)
var addresses = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.Addresses, &addresses)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
}
var tcpConfig = &serverconfigs.TCPProtocolConfig{}
tcpConfig.IsOn = true
tcpConfig.Listen = addresses
configJSON, err := json.Marshal(tcpConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterTCP(this.AdminContext(), &pb.UpdateNSClusterTCPRequest{
NsClusterId: params.ClusterId,
TcpJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package tcp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/tcp").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,189 @@
//go:build plus
package tls
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
// IndexAction TLS设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("tls")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
tlsResp, err := this.RPC().NSClusterRPC().FindNSClusterTLSConfig(this.AdminContext(), &pb.FindNSClusterTLSConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var tlsConfig = &serverconfigs.TLSProtocolConfig{}
if len(tlsResp.TlsJSON) > 0 {
err := json.Unmarshal(tlsResp.TlsJSON, tlsConfig)
if err != nil {
this.ErrorPage(err)
}
} else {
tlsConfig.IsOn = true
}
// SSL配置
var sslPolicy *sslconfigs.SSLPolicy
if tlsConfig.SSLPolicyRef != nil && tlsConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
SslPolicyId: tlsConfig.SSLPolicyRef.SSLPolicyId,
IgnoreData: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var sslPolicyConfigJSON = sslPolicyConfigResp.SslPolicyJSON
if len(sslPolicyConfigJSON) > 0 {
sslPolicy = &sslconfigs.SSLPolicy{}
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
if err != nil {
this.ErrorPage(err)
return
}
}
}
this.Data["tlsConfig"] = maps.Map{
"isOn": tlsConfig.IsOn,
"listen": tlsConfig.Listen,
"sslPolicy": sslPolicy,
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Addresses []byte
SslPolicyJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsTLS, params.ClusterId)
var addresses = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.Addresses, &addresses)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
}
// 校验SSL
var sslPolicyId = int64(0)
if params.SslPolicyJSON != nil {
sslPolicy := &sslconfigs.SSLPolicy{}
err = json.Unmarshal(params.SslPolicyJSON, sslPolicy)
if err != nil {
this.ErrorPage(errors.New("解析SSL配置时发生了错误" + err.Error()))
return
}
sslPolicyId = sslPolicy.Id
certsJSON, err := json.Marshal(sslPolicy.CertRefs)
if err != nil {
this.ErrorPage(err)
return
}
hstsJSON, err := json.Marshal(sslPolicy.HSTS)
if err != nil {
this.ErrorPage(err)
return
}
clientCACertsJSON, err := json.Marshal(sslPolicy.ClientCARefs)
if err != nil {
this.ErrorPage(err)
return
}
if sslPolicyId > 0 {
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
SslPolicyId: sslPolicyId,
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
} else {
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
sslPolicyId = resp.SslPolicyId
}
}
var tlsConfig = &serverconfigs.TLSProtocolConfig{}
tlsConfig.IsOn = true
tlsConfig.Listen = addresses
tlsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
IsOn: true,
SSLPolicyId: sslPolicyId,
}
configData, err := json.Marshal(tlsConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterTLS(this.AdminContext(), &pb.UpdateNSClusterTLSRequest{
NsClusterId: params.ClusterId,
TlsJSON: configData,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package tls
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/tls").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,83 @@
//go:build plus
package udp
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
)
// IndexAction UDP设置
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("udp")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
udpResp, err := this.RPC().NSClusterRPC().FindNSClusterUDPConfig(this.AdminContext(), &pb.FindNSClusterUDPConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var udpConfig = &serverconfigs.UDPProtocolConfig{}
if len(udpResp.UdpJSON) > 0 {
err := json.Unmarshal(udpResp.UdpJSON, udpConfig)
if err != nil {
this.ErrorPage(err)
}
} else {
udpConfig.IsOn = true
}
this.Data["udpConfig"] = udpConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Addresses []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.NSCluster_LogUpdateNSClusterSettingsUDP, params.ClusterId)
var addresses = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.Addresses, &addresses)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
}
var udpConfig = &serverconfigs.UDPProtocolConfig{}
udpConfig.IsOn = true
udpConfig.Listen = addresses
configJSON, err := json.Marshal(udpConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSClusterRPC().UpdateNSClusterUDP(this.AdminContext(), &pb.UpdateNSClusterUDPRequest{
NsClusterId: params.ClusterId,
UdpJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package udp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/udp").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,131 @@
//go:build plus
package cluster
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateNodeSSHAction struct {
actionutils.ParentAction
}
func (this *UpdateNodeSSHAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateNodeSSHAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
if nodeResp.NsNode == nil {
this.NotFound("node", params.NodeId)
return
}
node := nodeResp.NsNode
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
}
if nodeResp.NsNode.NsCluster != nil {
this.Data["clusterId"] = nodeResp.NsNode.NsCluster.Id
} else {
this.Data["clusterId"] = 0
}
// SSH
loginParams := maps.Map{
"host": "",
"port": "",
"grantId": 0,
}
this.Data["loginId"] = 0
if node.NodeLogin != nil {
this.Data["loginId"] = node.NodeLogin.Id
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
}
this.Data["params"] = loginParams
// 认证信息
grantId := loginParams.GetInt64("grantId")
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
}
var grantMap maps.Map = nil
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method, this.LangCode()),
}
}
this.Data["grant"] = grantMap
this.Show()
}
func (this *UpdateNodeSSHAction) RunPost(params struct {
NodeId int64
LoginId int64
SshHost string
SshPort int
GrantId int64
Must *actions.Must
}) {
params.Must.
Field("sshHost", params.SshHost).
Require("请输入SSH主机地址").
Field("sshPort", params.SshPort).
Gt(0, "SSH主机端口需要大于0").
Lt(65535, "SSH主机端口需要小于65535")
if params.GrantId <= 0 {
this.Fail("需要选择或填写至少一个认证信息")
}
login := &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",
Params: maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
}.AsJSON(),
}
_, err := this.RPC().NSNodeRPC().UpdateNSNodeLogin(this.AdminContext(), &pb.UpdateNSNodeLoginRequest{
NsNodeId: params.NodeId,
NodeLogin: login,
})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.NSNodeSSH_LogUpdateNSNodeSSH, params.NodeId)
this.Success()
}

View File

@@ -0,0 +1,17 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cluster
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
type UpgradeRemoteAction struct {
actionutils.ParentAction
}
func (this *UpgradeRemoteAction) Init() {
this.Nav("", "", "")
}
func (this *UpgradeRemoteAction) RunGet(params struct{}) {
this.Show()
}

View File

@@ -0,0 +1,253 @@
//go:build plus
package clusterutils
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
)
// ClusterHelper 单个集群的帮助
type ClusterHelper struct {
helpers.LangHelper
}
func NewClusterHelper() *ClusterHelper {
return &ClusterHelper{}
}
func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
var action = actionPtr.Object()
if action.Request.Method != http.MethodGet {
return true
}
action.Data["teaMenu"] = "ns"
selectedTabbar := action.Data.GetString("mainTab")
clusterId := action.ParamInt64("clusterId")
clusterIdString := strconv.FormatInt(clusterId, 10)
action.Data["clusterId"] = clusterId
if clusterId > 0 {
rpcClient, err := rpc.SharedRPC()
if err != nil {
logs.Error(err)
return
}
clusterResp, err := rpcClient.NSClusterRPC().FindNSCluster(actionPtr.(actionutils.ActionInterface).AdminContext(), &pb.FindNSClusterRequest{
NsClusterId: clusterId,
})
if err != nil {
logs.Error(err)
return
}
var cluster = clusterResp.NsCluster
if cluster == nil {
action.WriteString("can not find ns cluster")
return
}
action.Data["currentCluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
}
var nodeId = action.ParamInt64("nodeId")
var isInCluster = nodeId <= 0
var tabbar = actionutils.NewTabbar()
{
var url = "/ns/clusters"
if !isInCluster {
url = "/ns/clusters/cluster?clusterId=" + clusterIdString
}
tabbar.Add("", "", url, "arrow left", false)
}
{
var url = "/ns/clusters/cluster?clusterId=" + clusterIdString
var item = tabbar.Add(cluster.Name, "", url, "angle right", true)
item.IsTitle = true
}
{
var item = tabbar.Add(this.Lang(action, codes.NSCluster_TabNodes), "", "/ns/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add(this.Lang(action, codes.NSCluster_TabSetting), "", "/ns/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add(this.Lang(action, codes.NSCluster_TabDelete), "", "/ns/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
item.IsDisabled = !isInCluster
}
actionutils.SetTabbar(action, tabbar)
// 左侧菜单
secondMenuItem := action.Data.GetString("secondMenuItem")
switch selectedTabbar {
case "setting":
var menuItems = this.createSettingMenu(cluster, secondMenuItem, actionPtr)
action.Data["leftMenuItems"] = menuItems
// 当前菜单
action.Data["leftMenuActiveItem"] = nil
for _, item := range menuItems {
if item.GetBool("isActive") {
action.Data["leftMenuActiveItem"] = item
break
}
}
}
}
return true
}
// 设置菜单
func (this *ClusterHelper) createSettingMenu(cluster *pb.NSCluster, selectedItem string, actionPtr actions.ActionWrapper) (items []maps.Map) {
var clusterId = types.String(cluster.Id)
// TCP
var tcpConfig = &serverconfigs.TCPProtocolConfig{}
if len(cluster.TcpJSON) > 0 {
err := json.Unmarshal(cluster.TcpJSON, tcpConfig)
if err != nil {
// ignore error
logs.Error(err)
}
}
// TLS
var tlsConfig = &serverconfigs.TLSProtocolConfig{}
if len(cluster.TlsJSON) > 0 {
err := json.Unmarshal(cluster.TlsJSON, tlsConfig)
if err != nil {
// ignore error
logs.Error(err)
}
}
// UDP
var udpConfig = &serverconfigs.UDPProtocolConfig{}
if len(cluster.UdpJSON) > 0 {
err := json.Unmarshal(cluster.UdpJSON, udpConfig)
if err != nil {
// ignore error
logs.Error(err)
}
}
// DoH
var dohConfig = dnsconfigs.NewNSDoHConfig()
if len(cluster.DohJSON) > 0 {
err := json.Unmarshal(cluster.DohJSON, dohConfig)
if err != nil {
// ignore error
logs.Error(err)
}
}
// 应答
var answerConfig = dnsconfigs.DefaultNSAnswerConfig()
var answerConfigIsChanged = false
if len(cluster.AnswerJSON) > 0 {
err := json.Unmarshal(cluster.AnswerJSON, answerConfig)
if err != nil {
// ignore error
} else {
answerConfigIsChanged = !answerConfig.IsSame(dnsconfigs.DefaultNSAnswerConfig())
}
}
// SOA
var soaConfig = dnsconfigs.DefaultNSSOAConfig()
var soaConfigIsChanged = false
if len(cluster.SoaJSON) > 0 {
err := json.Unmarshal(cluster.SoaJSON, soaConfig)
if err != nil {
// ignore error
} else {
soaConfigIsChanged = !soaConfig.IsSame(dnsconfigs.DefaultNSSOAConfig())
}
}
return []maps.Map{
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuBasic),
"url": "/ns/clusters/cluster/settings?clusterId=" + clusterId,
"isActive": selectedItem == "basic",
},
{
"name": "-",
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuUDP),
"url": "/ns/clusters/cluster/settings/udp?clusterId=" + clusterId,
"isActive": selectedItem == "udp",
"isOn": udpConfig != nil && udpConfig.IsOn,
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuTCP),
"url": "/ns/clusters/cluster/settings/tcp?clusterId=" + clusterId,
"isActive": selectedItem == "tcp",
"isOn": tcpConfig != nil && tcpConfig.IsOn,
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuTLS),
"url": "/ns/clusters/cluster/settings/tls?clusterId=" + clusterId,
"isActive": selectedItem == "tls",
"isOn": tlsConfig != nil && tlsConfig.IsOn,
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuDoH),
"url": "/ns/clusters/cluster/settings/doh?clusterId=" + clusterId,
"isActive": selectedItem == "doh",
"isOn": dohConfig != nil && dohConfig.IsOn,
},
{
"name": "-",
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuAccessLogs),
"url": "/ns/clusters/cluster/settings/accessLog?clusterId=" + clusterId,
"isActive": selectedItem == "accessLog",
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuAnswerSetting),
"url": "/ns/clusters/cluster/settings/answer?clusterId=" + clusterId,
"isActive": selectedItem == "answer",
"isOn": answerConfigIsChanged,
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuSOA),
"url": "/ns/clusters/cluster/settings/soa?clusterId=" + clusterId,
"isActive": selectedItem == "soa",
"isOn": soaConfigIsChanged,
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuDNSRecursion),
"url": "/ns/clusters/cluster/settings/recursion?clusterId=" + clusterId,
"isActive": selectedItem == "recursion",
},
{
"name": "-",
},
{
"name": this.Lang(actionPtr, codes.NSCluster_MenuDDoSProtection),
"url": "/ns/clusters/cluster/settings/ddos-protection?clusterId=" + clusterId,
"isActive": selectedItem == "ddosProtection",
},
}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package clusters
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct{}) {
// 默认的访问日志设置
this.Data["accessLogRef"] = &dnsconfigs.NSAccessLogRef{
IsOn: true,
}
this.Show()
}
func (this *CreateAction) RunPost(params struct {
Name string
AccessLogJSON []byte
Hosts []string
Email string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var clusterId int64
defer func() {
this.CreateLogInfo(codes.NSCluster_LogCreateNSCluster, clusterId)
}()
params.Must.
Field("name", params.Name).
Require("请输入集群名称").
Field("email", params.Email).
Require("请输入正确的管理员电子邮箱地址")
// 校验主机域名
var hosts = []string{}
for _, host := range params.Hosts {
if !domainutils.ValidateDomainFormat(host) {
this.Fail("错误的DNS主机地址 '" + host + "'")
return
}
hosts = append(hosts, host)
}
// 校验访问日志设置
var ref = &dnsconfigs.NSAccessLogRef{}
err := json.Unmarshal(params.AccessLogJSON, ref)
if err != nil {
this.Fail("数据格式错误:" + err.Error())
}
err = ref.Init()
if err != nil {
this.Fail("数据格式错误:" + err.Error())
}
resp, err := this.RPC().NSClusterRPC().CreateNSCluster(this.AdminContext(), &pb.CreateNSClusterRequest{
Name: params.Name,
AccessLogJSON: params.AccessLogJSON,
Hosts: hosts,
Email: params.Email,
})
if err != nil {
this.ErrorPage(err)
return
}
clusterId = resp.NsClusterId
this.Success()
}

View File

@@ -0,0 +1,90 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().NSClusterRPC().CountAllNSClusters(this.AdminContext(), &pb.CountAllNSClustersRequest{})
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().NSClusterRPC().ListNSClusters(this.AdminContext(), &pb.ListNSClustersRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var clusterMaps = []maps.Map{}
for _, cluster := range clustersResp.NsClusters {
// 全部节点数量
countNodesResp, err := this.RPC().NSNodeRPC().CountAllNSNodesMatch(this.AdminContext(), &pb.CountAllNSNodesMatchRequest{NsClusterId: cluster.Id})
if err != nil {
this.ErrorPage(err)
return
}
// 在线节点
countActiveNodesResp, err := this.RPC().NSNodeRPC().CountAllNSNodesMatch(this.AdminContext(), &pb.CountAllNSNodesMatchRequest{
NsClusterId: cluster.Id,
ActiveState: types.Int32(configutils.BoolStateYes),
})
if err != nil {
this.ErrorPage(err)
return
}
// 需要升级的节点
countUpgradeNodesResp, err := this.RPC().NSNodeRPC().CountAllUpgradeNSNodesWithNSClusterId(this.AdminContext(), &pb.CountAllUpgradeNSNodesWithNSClusterIdRequest{NsClusterId: cluster.Id})
if err != nil {
this.ErrorPage(err)
return
}
// 域名
countDomainsResp, err := this.RPC().NSDomainRPC().CountAllNSDomains(this.AdminContext(), &pb.CountAllNSDomainsRequest{
NsClusterId: cluster.Id,
Status: dnsconfigs.NSDomainStatusVerified,
})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isOn": cluster.IsOn,
"countAllNodes": countNodesResp.Count,
"countActiveNodes": countActiveNodesResp.Count,
"countUpgradeNodes": countUpgradeNodesResp.Count,
"countDomains": countDomainsResp.Count,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}

View File

@@ -0,0 +1,25 @@
//go:build plus
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
Post("/options", new(OptionsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,94 @@
//go:build plus
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
}
func (this *IndexAction) RunGet(params struct {
DayFrom string
DayTo string
Keyword string
Level string
}) {
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
NodeId: 0,
Role: nodeconfigs.NodeRoleDNS,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: 0,
Role: nodeconfigs.NodeRoleDNS,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var logs = []maps.Map{}
for _, log := range logsResp.NodeLogs {
// 节点信息
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.AdminContext(), &pb.FindNSNodeRequest{NsNodeId: log.NodeId})
if err != nil {
continue
}
node := nodeResp.NsNode
if node == nil || node.NsCluster == nil {
continue
}
logs = append(logs, maps.Map{
"tag": log.Tag,
"description": log.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
"node": maps.Map{
"id": node.Id,
"cluster": maps.Map{
"id": node.NsCluster.Id,
"name": node.NsCluster.Name,
},
"name": node.Name,
},
})
}
this.Data["logs"] = logs
this.Show()
}

View File

@@ -0,0 +1,23 @@
//go:build plus
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "log").
Prefix("/ns/clusters/logs").
Get("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,32 @@
//go:build plus
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct{}) {
clustersResp, err := this.RPC().NSClusterRPC().FindAllNSClusters(this.AdminContext(), &pb.FindAllNSClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NsClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Success()
}

View File

@@ -0,0 +1,135 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"strings"
)
type CreateRecordsAction struct {
actionutils.ParentAction
}
func (this *CreateRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("createRecords")
}
func (this *CreateRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *CreateRecordsAction) RunPost(params struct {
Names string
UserId int64
RecordsJSON []byte
RemoveOld bool
RemoveAll bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogCreateNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
// 检查记录
type record struct {
Name string `json:"name"`
Type string `json:"type"`
Value string `json:"value"`
RouteCodes []string `json:"routeCodes"`
TTL any `json:"ttl"`
}
var records = []*record{}
err = json.Unmarshal(params.RecordsJSON, &records)
if err != nil {
this.Fail("记录参数格式错误:" + err.Error())
return
}
if len(records) == 0 {
this.Fail("请添加至少一个记录")
}
for _, record := range records {
message, ok := domainutils.ValidateRecordValue(record.Type, record.Value)
if !ok {
this.Fail("记录 '" + record.Name + "' 值校验失败:" + message)
}
// ttl
var recordInt = types.Int32(record.TTL)
if recordInt <= 0 {
recordInt = 600
}
record.TTL = recordInt
}
recordsJSON, err := json.Marshal(records)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSRecordRPC().CreateNSRecordsWithDomainNames(this.AdminContext(), &pb.CreateNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
RecordsJSON: recordsJSON,
RemoveOld: params.RemoveOld,
RemoveAll: params.RemoveAll,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,87 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type DeleteDomainsAction struct {
actionutils.ParentAction
}
func (this *DeleteDomainsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("deleteDomains")
}
func (this *DeleteDomainsAction) RunGet(params struct{}) {
this.Show()
}
func (this *DeleteDomainsAction) RunPost(params struct {
Names string
UserId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSDomainsBatch, params.UserId)
if len(params.Names) == 0 {
this.FailField("names", "请输入要删除的域名")
}
var domainNames = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
name = strings.ToLower(name)
domainNames = append(domainNames, name)
}
if len(domainNames) == 0 {
this.FailField("names", "请输入要删除的域名")
}
// 检查域名是否存在
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
// 开始删除
_, err = this.RPC().NSDomainRPC().DeleteNSDomains(this.AdminContext(), &pb.DeleteNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,96 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type DeleteRecordsAction struct {
actionutils.ParentAction
}
func (this *DeleteRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("deleteRecords")
}
func (this *DeleteRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *DeleteRecordsAction) RunPost(params struct {
Names string
UserId int64
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
_, err = this.RPC().NSRecordRPC().DeleteNSRecordsWithDomainNames(this.AdminContext(), &pb.DeleteNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type EnableRecordsAction struct {
actionutils.ParentAction
}
func (this *EnableRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("enableRecords")
}
func (this *EnableRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *EnableRecordsAction) RunPost(params struct {
Names string
UserId int64
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if params.IsOn {
defer this.CreateLogInfo(codes.NS_LogEnableNSRecordsBatch)
} else {
defer this.CreateLogInfo(codes.NS_LogDisableNSRecordsBatch)
}
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordsIsOnWithDomainNames(this.AdminContext(), &pb.UpdateNSRecordsIsOnWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
IsOn: params.IsOn,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,157 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
type ImportRecordsAction struct {
actionutils.ParentAction
}
func (this *ImportRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("importRecords")
}
func (this *ImportRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *ImportRecordsAction) RunPost(params struct {
Records string
UserId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogImportRecordsBatch)
var allRecordTypes = []string{}
for _, t := range dnsconfigs.FindAllUserRecordTypeDefinitions() {
allRecordTypes = append(allRecordTypes, t.Type)
}
var pbRecords = []*pb.ImportNSRecordsRequest_Record{}
var reg = regexp.MustCompile(`\s+`)
var domainNames = []string{}
for _, line := range strings.Split(params.Records, "\n") {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
// 域名 记录名 记录类型 记录值 TTL
var pieces = reg.Split(line, -1)
if len(pieces) < 4 {
this.Fail("'" + line + "' 格式错误,缺少必需项")
}
var domainName = strings.ToLower(strings.TrimSpace(pieces[0]))
var recordName = strings.ToLower(strings.TrimSpace(pieces[1]))
var recordType = strings.ToUpper(pieces[2])
var recordValue = strings.TrimSpace(pieces[3])
var recordTTLString = ""
if len(pieces) > 4 {
recordTTLString = pieces[4]
}
// validate
if len(domainName) == 0 {
this.Fail("'" + line + "' 缺少域名")
}
if !domainutils.ValidateDomainFormat(domainName) {
this.Fail("'" + line + "' 域名格式错误")
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
if len(recordName) > 0 && !domainutils.ValidateRecordName(recordName) {
this.Fail("'" + line + "' 记录名格式错误")
}
if len(recordType) == 0 {
this.Fail("'" + line + "' 缺少记录类型")
}
if !lists.ContainsString(allRecordTypes, recordType) {
this.Fail("'" + line + "' 不支持的记录类型 '" + recordType + "'")
}
message, ok := domainutils.ValidateRecordValue(recordType, recordValue)
if !ok {
this.Fail("'" + line + "' 记录值 '" + recordValue + "' 校验失败:" + message)
}
var recordTTL int32 = 600
if len(recordTTLString) > 0 {
if !regexp.MustCompile(`^\d+$`).MatchString(recordTTLString) {
this.Fail("'" + line + "' 错误的TTL '" + recordTTLString + "'")
}
recordTTL = types.Int32(recordTTLString)
if recordTTL > 10*365*86400 {
recordTTL = 10 * 365 * 86400
}
}
pbRecords = append(pbRecords, &pb.ImportNSRecordsRequest_Record{
NsDomainName: domainName,
Name: recordName,
Type: recordType,
Value: recordValue,
Ttl: recordTTL,
})
}
if len(pbRecords) == 0 {
this.FailField("records", "请输入要导入的记录")
}
// 检查域名是否存在
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
_, err = this.RPC().NSRecordRPC().ImportNSRecords(this.AdminContext(), &pb.ImportNSRecordsRequest{
NsRecords: pbRecords,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,97 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("index")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Show()
}
func (this *IndexAction) RunPost(params struct {
Names string // 如果批量的话
ClusterId int64
UserId int64
GroupId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
defer this.CreateLogInfo(codes.NS_LogCreateNSDomainsBatch)
params.Must.
Field("names", params.Names).
Require("请输入域名").
Field("clusterId", params.ClusterId).
Gt(0, "请选择所属集群")
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateDomainFormat(name) {
this.Fail("域名 '" + name + "' 格式不正确")
}
if !lists.ContainsString(names, name) {
names = append(names, name)
}
}
if len(names) == 0 {
this.FailField("names", "请输入域名")
return
}
// 检查域名是否已经存在
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: names,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
_, err = this.RPC().NSDomainRPC().CreateNSDomains(this.AdminContext(), &pb.CreateNSDomainsRequest{
NsClusterId: params.ClusterId,
UserId: params.UserId,
Names: names,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,113 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package batch
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type UpdateRecordsAction struct {
actionutils.ParentAction
}
func (this *UpdateRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("updateRecords")
}
func (this *UpdateRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *UpdateRecordsAction) RunPost(params struct {
Names string
UserId int64
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
NewName string
NewValue string
NewType string
NewRouteCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogUpdateNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
if len(params.NewValue) > 0 {
message, ok := domainutils.ValidateRecordValue(params.NewType, params.NewValue)
if !ok {
this.Fail("新的记录值 '" + params.NewValue + "' 填写错误:" + message)
}
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordsWithDomainNames(this.AdminContext(), &pb.UpdateNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
NewName: params.NewName,
NewValue: params.NewValue,
NewType: params.NewType,
NewNSRouteCodes: params.NewRouteCodes,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,186 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct{}) {
// 集群数量
countClustersResp, err := this.RPC().NSClusterRPC().CountAllNSClusters(this.AdminContext(), &pb.CountAllNSClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countClusters"] = countClustersResp.Count
this.Show()
}
func (this *CreateAction) RunPost(params struct {
AddingType string
Name string
Names string // 如果批量的话
ClusterId int64
UserId int64
GroupId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
if params.AddingType == "batch" { // 批量添加
defer this.CreateLogInfo(codes.NS_LogCreateNSDomainsBatch)
params.Must.
Field("names", params.Names).
Require("请输入域名").
Field("clusterId", params.ClusterId).
Gt(0, "请选择所属集群")
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateDomainFormat(name) {
this.Fail("域名 '" + name + "' 格式不正确")
}
if !lists.ContainsString(names, name) {
names = append(names, name)
}
}
if len(names) == 0 {
this.FailField("names", "请输入域名")
return
}
// 检查域名是否已经存在
{
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: names,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
}
{
existResp, err := this.RPC().NSDomainRPC().ExistVerifiedNSDomains(this.AdminContext(), &pb.ExistVerifiedNSDomainsRequest{
Names: names,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经由别的用户添加,无法重复添加")
return
}
}
_, err := this.RPC().NSDomainRPC().CreateNSDomains(this.AdminContext(), &pb.CreateNSDomainsRequest{
NsClusterId: params.ClusterId,
UserId: params.UserId,
Names: names,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
} else { // 单个添加
var domainId int64
defer func() {
this.CreateLogInfo(codes.NSDomain_LogCreateNSDomain, domainId)
}()
params.Name = strings.ToLower(params.Name)
params.Must.
Field("name", params.Name).
Require("请输入域名").
Expect(func() (message string, success bool) {
success = domainutils.ValidateDomainFormat(params.Name)
if !success {
message = "请输入正确的域名"
}
return
}).
Field("clusterId", params.ClusterId).
Gt(0, "请选择所属集群")
// 检查域名是否已经存在
{
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.AdminContext(), &pb.ExistNSDomainsRequest{
Names: []string{params.Name},
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
}
{
existResp, err := this.RPC().NSDomainRPC().ExistVerifiedNSDomains(this.AdminContext(), &pb.ExistVerifiedNSDomainsRequest{
Names: []string{params.Name},
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经由别的用户添加,无法重复添加")
return
}
}
createResp, err := this.RPC().NSDomainRPC().CreateNSDomain(this.AdminContext(), &pb.CreateNSDomainRequest{
NsClusterId: params.ClusterId,
UserId: params.UserId,
Name: params.Name,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
domainId = createResp.NsDomainId
this.Success()
}
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
DomainId int64
}) {
defer this.CreateLogInfo(codes.NSDomain_LogDeleteNSDomain, params.DomainId)
_, err := this.RPC().NSDomainRPC().DeleteNSDomain(this.AdminContext(), &pb.DeleteNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2024 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeletePageAction struct {
actionutils.ParentAction
}
func (this *DeletePageAction) RunPost(params struct {
DomainIds []int64
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSDomainsBatch, this.AdminContext())
if len(params.DomainIds) == 0 {
this.Success()
return
}
for _, domainId := range params.DomainIds {
_, err := this.RPC().NSDomainRPC().DeleteNSDomain(this.AdminContext(), &pb.DeleteNSDomainRequest{
NsDomainId: domainId,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,99 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domain
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
}) {
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
var countRecords = this.Data.GetMap("domain").GetInt64("countRecords")
var countKeys = this.Data.GetMap("domain").GetInt64("countKeys")
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
var clusterMap maps.Map
if domain.NsCluster != nil {
clusterMap = maps.Map{
"id": domain.NsCluster.Id,
"name": domain.NsCluster.Name,
}
}
// 用户信息
var userMap maps.Map
if domain.User != nil {
userMap = maps.Map{
"id": domain.User.Id,
"username": domain.User.Username,
"fullname": domain.User.Fullname,
}
}
// 分组信息
var groupMaps = []maps.Map{}
for _, group := range domain.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
"userId": group.UserId,
})
}
// 健康检查
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"isOn": domain.IsOn,
"status": domain.Status,
"statusName": dnsconfigs.NSDomainStatusName(domain.Status),
"cluster": clusterMap,
"user": userMap,
"groups": groupMaps,
"countRecords": countRecords,
"countKeys": countKeys,
"enableHealthCheck": healthCheckConfig.IsOn,
}
this.Show()
}

View File

@@ -0,0 +1,67 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package domain
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateStatusPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateStatusPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateStatusPopupAction) RunGet(params struct {
DomainId int64
}) {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("NSDomain", params.DomainId)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"status": domain.Status,
}
this.Data["statusList"] = dnsconfigs.FindAllNSDomainStatusList()
this.Show()
}
func (this *UpdateStatusPopupAction) RunPost(params struct {
DomainId int64
Status string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainStatus, params.DomainId, params.Status)
_, err := this.RPC().NSDomainRPC().UpdateNSDomainStatus(this.AdminContext(), &pb.UpdateNSDomainStatusRequest{
NsDomainId: params.DomainId,
Status: params.Status,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,142 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domainutils
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
// InitDomain 初始化域名信息
func InitDomain(parent *actionutils.ParentAction, domainId int64) error {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
domainResp, err := rpcClient.NSDomainRPC().FindNSDomain(parent.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
return err
}
var domain = domainResp.NsDomain
if domain == nil {
return errors.New("InitDomain: can not find domain with id '" + types.String(domainId) + "'")
}
// 记录数量
countRecordsResp, err := rpcClient.NSRecordRPC().CountAllNSRecords(parent.AdminContext(), &pb.CountAllNSRecordsRequest{
NsDomainId: domainId,
})
if err != nil {
return err
}
var countRecords = countRecordsResp.Count
// Key数量
countKeysResp, err := rpcClient.NSKeyRPC().CountAllNSKeys(parent.AdminContext(), &pb.CountAllNSKeysRequest{
NsDomainId: domainId,
})
if err != nil {
return err
}
var countKeys = countKeysResp.Count
// 健康检查
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
return err
}
}
parent.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"countRecords": countRecords,
"countKeys": countKeys,
"enableHealthCheck": healthCheckConfig.IsOn,
}
return nil
}
// CheckHealthCheckPermission 检查是否能够修改健康检查
func CheckHealthCheckPermission(rpcClient *rpc.RPCClient, ctx context.Context, domainId int64) (canUpdate bool, err error) {
if domainId <= 0 {
return false, nil
}
domainResp, err := rpcClient.NSDomainRPC().FindNSDomain(ctx, &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
return false, err
}
var domain = domainResp.NsDomain
if domain == nil {
return false, errors.New("the domain is not found")
}
if domain.UserId > 0 {
// 检查套餐
userPlanResp, err := rpcClient.NSUserPlanRPC().FindNSUserPlan(ctx, &pb.FindNSUserPlanRequest{
UserId: domain.UserId,
NsUserPlanId: 0,
})
if err != nil {
return false, err
}
var userPlan = userPlanResp.NsUserPlan
if userPlan == nil || userPlan.NsPlanId == 0 || userPlan.DayTo < timeutil.Format("Ymd") {
return checkHealthCheckInUserSetting(rpcClient, ctx)
} else {
planResp, err := rpcClient.NSPlanRPC().FindNSPlan(ctx, &pb.FindNSPlanRequest{NsPlanId: userPlan.NsPlanId})
if err != nil {
return false, err
}
if planResp.NsPlan == nil || len(planResp.NsPlan.ConfigJSON) == 0 {
return checkHealthCheckInUserSetting(rpcClient, ctx)
}
var planConfig = dnsconfigs.DefaultNSPlanConfig()
err = json.Unmarshal(planResp.NsPlan.ConfigJSON, planConfig)
if err != nil {
return false, err
}
return planConfig.SupportHealthCheck, nil
}
} else {
return checkHealthCheckInUserSetting(rpcClient, ctx)
}
}
// 检查用户基础设置
func checkHealthCheckInUserSetting(rpcClient *rpc.RPCClient, ctx context.Context) (bool, error) {
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(ctx, &pb.ReadSysSettingRequest{
Code: systemconfigs.SettingCodeNSUserConfig,
})
if err != nil {
return false, err
}
var config = dnsconfigs.NewNSUserConfig()
if len(resp.ValueJSON) > 0 {
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
return false, err
}
if config.DefaultPlanConfig == nil {
config.DefaultPlanConfig = dnsconfigs.DefaultNSUserPlanConfig()
}
return config.DefaultPlanConfig.SupportHealthCheck, nil
}
return false, nil
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package groups
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var groupId int64 = 0
defer func() {
this.CreateLogInfo(codes.NSDomainGroup_LogCreateNSDomainGroup, groupId)
}()
params.Must.
Field("name", params.Name).
Require("请输入分组名称")
createResp, err := this.RPC().NSDomainGroupRPC().CreateNSDomainGroup(this.AdminContext(), &pb.CreateNSDomainGroupRequest{
Name: params.Name,
})
if err != nil {
this.ErrorPage(err)
return
}
groupId = createResp.NsDomainGroupId
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package group
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
GroupId int64
}) {
defer this.CreateLogInfo(codes.NSDomainGroup_LogDeleteNSDomainGroup, params.GroupId)
_, err := this.RPC().NSDomainGroupRPC().DeleteNSDomainGroup(this.AdminContext(), &pb.DeleteNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package group
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
GroupId int64
}) {
groupResp, err := this.RPC().NSDomainGroupRPC().FindNSDomainGroup(this.AdminContext(), &pb.FindNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
var group = groupResp.NsDomainGroup
if group == nil {
this.NotFound("nsDomainGroup", params.GroupId)
return
}
this.Data["group"] = maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
}
this.Show()
}

View File

@@ -0,0 +1,70 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package group
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
GroupId int64
}) {
groupResp, err := this.RPC().NSDomainGroupRPC().FindNSDomainGroup(this.AdminContext(), &pb.FindNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
var group = groupResp.NsDomainGroup
if group == nil {
this.NotFound("nsDomainGroup", params.GroupId)
return
}
this.Data["group"] = maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
GroupId int64
Name string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomainGroup_LogUpdateNSDomainGroup, params.GroupId)
params.Must.
Field("name", params.Name).
Require("请输入分组名称")
_, err := this.RPC().NSDomainGroupRPC().UpdateNSDomainGroup(this.AdminContext(), &pb.UpdateNSDomainGroupRequest{
NsDomainGroupId: params.GroupId,
Name: params.Name,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,37 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package groups
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
groupsResp, err := this.RPC().NSDomainGroupRPC().FindAllNSDomainGroups(this.AdminContext(), &pb.FindAllNSDomainGroupsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var groupMaps = []maps.Map{}
for _, group := range groupsResp.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
})
}
this.Data["groups"] = groupMaps
this.Show()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package groups
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct {
UserId int64
}) {
groupsResp, err := this.RPC().NSDomainGroupRPC().FindAllAvailableNSDomainGroups(this.AdminContext(), &pb.FindAllAvailableNSDomainGroupsRequest{
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var groupMaps = []maps.Map{}
for _, group := range groupsResp.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
this.Data["groups"] = groupMaps
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type HealthCheckAction struct {
actionutils.ParentAction
}
func (this *HealthCheckAction) Init() {
this.Nav("", "", "healthCheck")
}
func (this *HealthCheckAction) RunGet(params struct {
DomainId int64
}) {
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["domainId"] = params.DomainId
// 检查当前用户权限
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.AdminContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["canUpdate"] = canUpdate
// 健康检查配置
healthCheckResp, err := this.RPC().NSDomainRPC().FindNSDomainRecordsHealthCheck(this.AdminContext(), &pb.FindNSDomainRecordsHealthCheckRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(healthCheckResp.NsDomainRecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(healthCheckResp.NsDomainRecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = healthCheckConfig
this.Show()
}
func (this *HealthCheckAction) RunPost(params struct {
DomainId int64
RecordsHealthCheckJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainHealthCheck, params.DomainId)
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.AdminContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("当前用户没有权限修改健康检查设置")
return
}
var config = dnsconfigs.NewNSRecordsHealthCheckConfig()
err = json.Unmarshal(params.RecordsHealthCheckJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
err = config.Init()
if err != nil {
this.Fail("校验配置失败:" + err.Error())
return
}
_, err = this.RPC().NSDomainRPC().UpdateNSDomainRecordsHealthCheck(this.AdminContext(), &pb.UpdateNSDomainRecordsHealthCheckRequest{
NsDomainId: params.DomainId,
NsDomainRecordsHealthCheckJSON: params.RecordsHealthCheckJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,117 @@
//go:build plus
package domains
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
UserId int64
GroupId int64
Keyword string
}) {
if !teaconst.IsPlus {
this.RedirectURL("/")
return
}
this.Data["clusterId"] = params.ClusterId
this.Data["userId"] = params.UserId
this.Data["groupId"] = params.GroupId
this.Data["keyword"] = params.Keyword
// 集群数量
countClustersResp, err := this.RPC().NSClusterRPC().CountAllNSClusters(this.AdminContext(), &pb.CountAllNSClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countClusters"] = countClustersResp.Count
// 分页
countResp, err := this.RPC().NSDomainRPC().CountAllNSDomains(this.AdminContext(), &pb.CountAllNSDomainsRequest{
UserId: params.UserId,
NsDomainGroupId: params.GroupId,
NsClusterId: params.ClusterId,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 列表
domainsResp, err := this.RPC().NSDomainRPC().ListNSDomains(this.AdminContext(), &pb.ListNSDomainsRequest{
UserId: params.UserId,
NsClusterId: params.ClusterId,
NsDomainGroupId: params.GroupId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var domainMaps = []maps.Map{}
for _, domain := range domainsResp.NsDomains {
// 集群信息
var clusterMap maps.Map
if domain.NsCluster != nil {
clusterMap = maps.Map{
"id": domain.NsCluster.Id,
"name": domain.NsCluster.Name,
}
}
// 用户信息
var userMap maps.Map
if domain.User != nil {
userMap = maps.Map{
"id": domain.User.Id,
"username": domain.User.Username,
"fullname": domain.User.Fullname,
}
}
// 分组信息
var groupMaps = []maps.Map{}
for _, group := range domain.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
"userId": group.UserId,
})
}
domainMaps = append(domainMaps, maps.Map{
"id": domain.Id,
"name": domain.Name,
"status": domain.Status,
"statusName": dnsconfigs.NSDomainStatusName(domain.Status),
"isOn": domain.IsOn,
"cluster": clusterMap,
"user": userMap,
"groups": groupMaps,
})
}
this.Data["domains"] = domainMaps
this.Show()
}

View File

@@ -0,0 +1,88 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
DomainId int64
}) {
this.Data["domainId"] = params.DomainId
// 所有算法
var algorithmMaps = []maps.Map{}
for _, algo := range dnsconfigs.FindAllKeyAlgorithmTypes() {
algorithmMaps = append(algorithmMaps, maps.Map{
"name": algo.Name,
"code": algo.Code,
})
}
this.Data["algorithms"] = algorithmMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
DomainId int64
Name string
Algo string
Secret string
SecretType string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var keyId int64 = 0
defer func() {
this.CreateLogInfo(codes.NSKey_LogCreateNSKey, keyId)
}()
params.Must.
Field("name", params.Name).
Require("请输入密钥名称").
Field("algo", params.Algo).
Require("请选择算法").
Field("secret", params.Secret).
Require("请输入密码")
// 校验密码
if params.SecretType == dnsconfigs.NSKeySecretTypeBase64 {
_, err := base64.StdEncoding.DecodeString(params.Secret)
if err != nil {
this.FailField("secret", "请输入BASE64格式的密码或者选择明文")
}
}
createResp, err := this.RPC().NSKeyRPC().CreateNSKey(this.AdminContext(), &pb.CreateNSKeyRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
Name: params.Name,
Algo: params.Algo,
Secret: params.Secret,
SecretType: params.SecretType,
})
if err != nil {
this.ErrorPage(err)
return
}
keyId = createResp.NsKeyId
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package keys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
KeyId int64
}) {
defer this.CreateLogInfo(codes.NSKey_LogDeleteNSKey, params.KeyId)
_, err := this.RPC().NSKeyRPC().DeleteNSKey(this.AdminContext(), &pb.DeleteNSKeyRequest{NsKeyId: params.KeyId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,29 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/iwind/TeaGo/rands"
)
type GenerateSecretAction struct {
actionutils.ParentAction
}
func (this *GenerateSecretAction) RunPost(params struct {
SecretType string
}) {
switch params.SecretType {
case dnsconfigs.NSKeySecretTypeClear:
this.Data["secret"] = rands.HexString(128)
case dnsconfigs.NSKeySecretTypeBase64:
this.Data["secret"] = base64.StdEncoding.EncodeToString([]byte(rands.HexString(128)))
default:
this.Data["secret"] = rands.HexString(128)
}
this.Success()
}

View File

@@ -0,0 +1,69 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package keys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "key")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
// 数量
countResp, err := this.RPC().NSKeyRPC().CountAllNSKeys(this.AdminContext(), &pb.CountAllNSKeysRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 列表
keysResp, err := this.RPC().NSKeyRPC().ListNSKeys(this.AdminContext(), &pb.ListNSKeysRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var keyMaps = []maps.Map{}
for _, key := range keysResp.NsKeys {
keyMaps = append(keyMaps, maps.Map{
"id": key.Id,
"name": key.Name,
"secret": key.Secret,
"secretTypeName": dnsconfigs.FindKeySecretTypeName(key.SecretType),
"algoName": dnsconfigs.FindKeyAlgorithmTypeName(key.Algo),
"isOn": key.IsOn,
})
}
this.Data["keys"] = keyMaps
this.Show()
}

View File

@@ -0,0 +1,102 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
KeyId int64
}) {
keyResp, err := this.RPC().NSKeyRPC().FindNSKey(this.AdminContext(), &pb.FindNSKeyRequest{NsKeyId: params.KeyId})
if err != nil {
this.ErrorPage(err)
return
}
var key = keyResp.NsKey
if key == nil {
return
}
this.Data["key"] = maps.Map{
"id": key.Id,
"name": key.Name,
"algo": key.Algo,
"secret": key.Secret,
"secretType": key.SecretType,
"isOn": key.IsOn,
}
// 所有算法
var algorithmMaps = []maps.Map{}
for _, algo := range dnsconfigs.FindAllKeyAlgorithmTypes() {
algorithmMaps = append(algorithmMaps, maps.Map{
"name": algo.Name,
"code": algo.Code,
})
}
this.Data["algorithms"] = algorithmMaps
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
KeyId int64
Name string
Algo string
Secret string
SecretType string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
this.CreateLogInfo(codes.NSKey_LogUpdateNSKey, params.KeyId)
params.Must.
Field("name", params.Name).
Require("请输入密钥名称").
Field("algo", params.Algo).
Require("请选择算法").
Field("secret", params.Secret).
Require("请输入密码")
// 校验密码
if params.SecretType == dnsconfigs.NSKeySecretTypeBase64 {
_, err := base64.StdEncoding.DecodeString(params.Secret)
if err != nil {
this.FailField("secret", "请输入BASE64格式的密码或者选择明文")
}
}
_, err := this.RPC().NSKeyRPC().UpdateNSKey(this.AdminContext(), &pb.UpdateNSKeyRequest{
NsKeyId: params.KeyId,
Name: params.Name,
Algo: params.Algo,
Secret: params.Secret,
SecretType: params.SecretType,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,250 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package records
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
DomainId int64
}) {
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
AddingType string
DomainId int64
Name string
Names string
Type string
Value string
Ttl int32
Description string
RouteCodes []string
Weight int32
MxPriority int32
SrvPriority int32
SrvWeight int32
SrvPort int32
CaaFlag int32
CaaTag string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.Fail("找不到要修改的域名")
}
var domainName = domain.Name
if params.AddingType == "batch" { // 批量添加
defer this.CreateLogInfo(codes.NS_LogCreateNSRecordsBatch)
if len(params.Names) == 0 {
this.FailField("names", "请输入记录名")
}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
this.CheckType(params.Type, params.DomainId, name)
}
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateRecordName(name) {
this.FailField("names", "记录名 '"+name+"' 格式不正确,请修改后提交")
}
// 去掉主域名
name = strings.TrimSuffix(name, "."+domainName)
names = append(names, name)
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// SRV
if params.Type == dnsconfigs.RecordTypeSRV {
if params.SrvPort < 0 || params.SrvPort > 65535 {
this.FailField("srvPort", "请输入有效的端口号")
}
}
// 校验TTL
if params.Ttl <= 0 {
this.Fail("请输入正确的TTL")
}
_, err := this.RPC().NSRecordRPC().CreateNSRecords(this.AdminContext(), &pb.CreateNSRecordsRequest{
NsDomainId: params.DomainId,
Description: params.Description,
Names: names,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
SrvPort: params.SrvPort,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
} else { // 单个添加
var recordId int64
defer func() {
this.CreateLogInfo(codes.NSRecord_LogCreateNSRecord, recordId)
}()
// 校验记录名
if !domainutils.ValidateRecordName(params.Name) {
this.FailField("name", "请输入正确的记录名")
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// SRV
if params.Type == dnsconfigs.RecordTypeSRV {
if params.SrvPort < 0 || params.SrvPort > 65535 {
this.FailField("srvPort", "请输入有效的端口号")
}
}
// 校验TTL
if params.Ttl <= 0 {
this.Fail("请输入正确的TTL")
}
this.CheckType(params.Type, params.DomainId, params.Name)
createResp, err := this.RPC().NSRecordRPC().CreateNSRecord(this.AdminContext(), &pb.CreateNSRecordRequest{
NsDomainId: params.DomainId,
Description: params.Description,
Name: params.Name,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPort: params.SrvPort,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
recordId = createResp.NsRecordId
this.Success()
}
}
func (this *CreatePopupAction) CheckType(recordType dnsconfigs.RecordType, domainId int64, name string) {
//A|AAAA记录和cname记录不可以共存
if recordType == dnsconfigs.RecordTypeA || recordType == dnsconfigs.RecordTypeAAAA {
cnameRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeCNAME,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
if cnameRecordsCountResp.Count > 0 {
this.Fail("已有cname记录请删除cname记录后在尝试添加A记录")
}
} else if recordType == dnsconfigs.RecordTypeCNAME {
aRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeA,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
aaaaRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeAAAA,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
if aRecordsCountResp.Count+aaaaRecordsCountResp.Count > 0 {
this.Fail("已有A记录请删除A记录后在尝试添加cname记录")
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package records
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
RecordId int64
}) {
defer this.CreateLogInfo(codes.NSRecord_LogDeleteNSRecord, params.RecordId)
_, err := this.RPC().NSRecordRPC().DeleteNSRecord(this.AdminContext(), &pb.DeleteNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,154 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package records
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type HealthCheckPopupAction struct {
actionutils.ParentAction
}
func (this *HealthCheckPopupAction) Init() {
this.Nav("", "", "")
}
func (this *HealthCheckPopupAction) RunGet(params struct {
RecordId int64
}) {
// 记录信息
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.AdminContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil || record.NsDomain == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
// 记录健康检查
var recordHealthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
if len(record.HealthCheckJSON) > 0 {
err = json.Unmarshal(record.HealthCheckJSON, recordHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"healthCheckConfig": recordHealthCheckConfig,
}
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: record.NsDomain.Id})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", record.NsDomain.Id)
return
}
// 域名健康检查
var domainHealthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, domainHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["domain"] = maps.Map{
"name": domain.Name,
"healthCheckConfig": domainHealthCheckConfig,
}
// 检查权限
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.AdminContext(), record.NsDomain.Id)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("permission denied")
return
}
this.Show()
}
func (this *HealthCheckPopupAction) RunPost(params struct {
RecordId int64
RecordHealthCheckJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSRecord_LogUpdateNSRecordHealthCheck, params.RecordId)
// 检查权限
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.AdminContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil || record.NsDomain == nil {
this.Fail("找不到要修改的记录")
return
}
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.AdminContext(), record.NsDomain.Id)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("permission denied")
return
}
var healthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
err = json.Unmarshal(params.RecordHealthCheckJSON, healthCheckConfig)
if err != nil {
this.Fail("解析配置失败:" + err.Error())
return
}
err = healthCheckConfig.Init()
if err != nil {
this.Fail("校验配置失败:" + err.Error())
return
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordHealthCheck(this.AdminContext(), &pb.UpdateNSRecordHealthCheckRequest{
NsRecordId: params.RecordId,
NsRecordHealthCheckJSON: params.RecordHealthCheckJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,216 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package records
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "record")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
Type string
Keyword string
RouteCode string
NameOrder string
TypeOrder string
TtlOrder string
UpOrder string
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
// 去除域名中的域名
var domainMap = this.Data.GetMap("domain")
if len(domainMap) > 0 {
var domainName = domainMap.GetString("name")
if len(domainName) > 0 {
params.Keyword = strings.TrimSuffix(params.Keyword, "."+domainName)
}
}
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
this.Data["routeCode"] = params.RouteCode
// 域名的健康检查
domainHealthCheckIsEnabled, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.AdminContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
var domainHealthCheckIsOn = false
if domainHealthCheckIsEnabled {
domainHealthCheckResp, err := this.RPC().NSDomainRPC().FindNSDomainRecordsHealthCheck(this.AdminContext(), &pb.FindNSDomainRecordsHealthCheckRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if len(domainHealthCheckResp.NsDomainRecordsHealthCheckJSON) > 0 {
var domainHealthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
err = json.Unmarshal(domainHealthCheckResp.NsDomainRecordsHealthCheckJSON, domainHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
domainHealthCheckIsOn = domainHealthCheckConfig.IsOn
}
}
this.Data["domainHealthCheckIsOn"] = domainHealthCheckIsOn
// 记录
countResp, err := this.RPC().NSRecordRPC().CountAllNSRecords(this.AdminContext(), &pb.CountAllNSRecordsRequest{
NsDomainId: params.DomainId,
Type: params.Type,
NsRouteCode: params.RouteCode,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
recordsResp, err := this.RPC().NSRecordRPC().ListNSRecords(this.AdminContext(), &pb.ListNSRecordsRequest{
NsDomainId: params.DomainId,
Type: params.Type,
NsRouteCode: params.RouteCode,
Keyword: params.Keyword,
NameAsc: params.NameOrder == "asc",
NameDesc: params.NameOrder == "desc",
TypeAsc: params.TypeOrder == "asc",
TypeDesc: params.TypeOrder == "desc",
TtlAsc: params.TtlOrder == "asc",
TtlDesc: params.TtlOrder == "desc",
UpAsc: params.UpOrder == "asc",
UpDesc: params.UpOrder == "desc",
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var recordMaps = []maps.Map{}
var recordIds = []int64{}
for _, record := range recordsResp.NsRecords {
recordIds = append(recordIds, record.Id)
}
// 统计
var statMap = map[int64]maps.Map{} // recordId => Map
if len(recordIds) > 0 {
statsResp, err := this.RPC().NSRecordHourlyStatRPC().FindNSRecordHourlyStatWithRecordIds(this.AdminContext(), &pb.FindNSRecordHourlyStatWithRecordIdsRequest{NsRecordIds: recordIds})
if err != nil {
this.ErrorPage(err)
return
}
for _, stat := range statsResp.NsRecordHourlyStats {
statMap[stat.NsRecordId] = maps.Map{
"bytes": stat.Bytes,
"countRequests": stat.CountRequests,
"countRequestsFormat": numberutils.FormatCount(stat.CountRequests),
}
}
}
// 列表
for _, record := range recordsResp.NsRecords {
var routeMaps = []maps.Map{}
for _, route := range record.NsRoutes {
routeMaps = append(routeMaps, maps.Map{
"id": route.Id,
"name": route.Name,
})
}
// 统计
recordStatMap, ok := statMap[record.Id]
if !ok {
recordStatMap = maps.Map{
"bytes": 0,
"countRequests": 0,
"countRequestsFormat": "0",
}
}
// 健康检查
var recordHealthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
if domainHealthCheckIsOn && len(record.HealthCheckJSON) > 0 {
err = json.Unmarshal(record.HealthCheckJSON, recordHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
recordMaps = append(recordMaps, maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"mxPriority": record.MxPriority,
"srvPriority": record.SrvPriority,
"srvWeight": record.SrvWeight,
"srvPort": record.SrvPort,
"caaFlag": record.CaaFlag,
"caaTag": record.CaaTag,
"ttl": record.Ttl,
"weight": record.Weight,
"description": record.Description,
"isOn": record.IsOn,
"routes": routeMaps,
"stat": recordStatMap,
"healthCheck": recordHealthCheckConfig,
"isUp": record.IsUp,
})
}
this.Data["records"] = recordMaps
// 是否提示可以精准搜索
this.Data["enableNameSearch"] = false
this.Data["enableValueSearch"] = false
this.Data["searchingKeyword"] = params.Keyword
if countResp.Count > 0 && len(params.Keyword) > 0 {
for _, record := range recordsResp.NsRecords {
if record.Name == params.Keyword {
this.Data["enableNameSearch"] = true
break
}
if record.Value == params.Keyword {
this.Data["enableValueSearch"] = true
break
}
}
}
// 所有记录类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}

View File

@@ -0,0 +1,61 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package records
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type StatPopupAction struct {
actionutils.ParentAction
}
func (this *StatPopupAction) Init() {
this.Nav("", "", "")
}
func (this *StatPopupAction) RunGet(params struct {
RecordId int64
}) {
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.AdminContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
var routeNames = []string{}
for _, route := range record.NsRoutes {
routeNames = append(routeNames, route.Name)
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"routeNames": routeNames,
}
statsResp, err := this.RPC().NSRecordHourlyStatRPC().FindLatestNSRecordsHourlyStats(this.AdminContext(), &pb.FindLatestNSRecordsHourlyStatsRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var statMaps = []maps.Map{}
for _, stat := range statsResp.NsRecordHourlyStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"countRequests": stat.CountRequests,
"hour": stat.Hour,
})
}
this.Data["stats"] = statMaps
this.Show()
}

View File

@@ -0,0 +1,192 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package records
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
RecordId int64
}) {
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.AdminContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"mxPriority": record.MxPriority,
"srvPriority": record.SrvPriority,
"srvWeight": record.SrvWeight,
"srvPort": record.SrvPort,
"caaFlag": record.CaaFlag,
"caaTag": record.CaaTag,
"ttl": record.Ttl,
"weight": record.Weight,
"description": record.Description,
"isOn": record.IsOn,
"routes": record.NsRoutes,
}
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: record.NsDomain.Id})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", record.NsDomain.Id)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
DomainId int64
RecordId int64
Name string
Type string
Value string
MxPriority int32
SrvPriority int32
SrvWeight int32
SrvPort int32
CaaFlag int32
CaaTag string
Ttl int32
Description string
IsOn bool
RouteCodes []string
Weight int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
this.CreateLogInfo(codes.NSRecord_LogUpdateNSRecord, params.RecordId)
this.CheckType(params.Type, params.DomainId, params.Name)
// 校验记录名
if !domainutils.ValidateRecordName(params.Name) {
this.FailField("name", "请输入正确的记录名")
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// SRV
if params.Type == dnsconfigs.RecordTypeSRV {
if params.SrvPort < 0 || params.SrvPort > 65535 {
this.FailField("srvPort", "请输入有效的端口号")
}
}
// 校验TTL
if params.Ttl <= 0 {
this.Fail("请输入正确的TTL")
}
_, err := this.RPC().NSRecordRPC().UpdateNSRecord(this.AdminContext(), &pb.UpdateNSRecordRequest{
NsRecordId: params.RecordId,
Description: params.Description,
Name: params.Name,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
SrvPort: params.SrvPort,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
IsOn: params.IsOn,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}
func (this *UpdatePopupAction) CheckType(recordType dnsconfigs.RecordType, domainId int64, name string) {
//A|AAAA记录和cname记录不可以共存
if recordType == dnsconfigs.RecordTypeA || recordType == dnsconfigs.RecordTypeAAAA {
cnameRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeCNAME,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
if cnameRecordsCountResp.Count > 0 {
this.Fail("已有cname记录请删除cname记录后在尝试添加A记录")
}
} else if recordType == dnsconfigs.RecordTypeCNAME {
aRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeA,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
aaaaRecordsCountResp, err := this.RPC().NSRecordRPC().CountAllNSRecordsWithName(this.AdminContext(), &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Type: dnsconfigs.RecordTypeAAAA,
Name: name,
})
if err != nil {
this.ErrorPage(err)
return
}
if aRecordsCountResp.Count+aaaaRecordsCountResp.Count > 0 {
this.Fail("已有A记录请删除A记录后在尝试添加cname记录")
}
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package records
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateUpAction struct {
actionutils.ParentAction
}
func (this *UpdateUpAction) RunPost(params struct {
RecordId int64
}) {
defer this.CreateLogInfo(codes.NSRecord_LogUpNSRecord, params.RecordId)
_, err := this.RPC().NSRecordRPC().UpdateNSRecordIsUp(this.AdminContext(), &pb.UpdateNSRecordIsUpRequest{
NsRecordId: params.RecordId,
IsUp: true,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,82 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
)
type TsigAction struct {
actionutils.ParentAction
}
func (this *TsigAction) Init() {
this.Nav("", "", "tsig")
}
func (this *TsigAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
// TSIG信息
tsigResp, err := this.RPC().NSDomainRPC().FindNSDomainTSIG(this.AdminContext(), &pb.FindNSDomainTSIGRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var tsigJSON = tsigResp.TsigJSON
var tsigConfig = &dnsconfigs.NSTSIGConfig{}
if len(tsigJSON) > 0 {
err = json.Unmarshal(tsigJSON, tsigConfig)
if err != nil {
// 只是提示错误,仍然允许用户修改
logs.Error(err)
}
}
this.Data["tsig"] = tsigConfig
this.Show()
}
func (this *TsigAction) RunPost(params struct {
DomainId int64
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainTSIG, params.DomainId)
var tsigConfig = &dnsconfigs.NSTSIGConfig{
IsOn: params.IsOn,
}
tsigJSON, err := json.Marshal(tsigConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSDomainRPC().UpdateNSDomainTSIG(this.AdminContext(), &pb.UpdateNSDomainTSIGRequest{
NsDomainId: params.DomainId,
TsigJSON: tsigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,144 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdateAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
var countRecords = this.Data.GetMap("domain").GetInt64("countRecords")
var countKeys = this.Data.GetMap("domain").GetInt64("countKeys")
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.AdminContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
var clusterId = int64(0)
if domain.NsCluster != nil {
clusterId = domain.NsCluster.Id
}
// 用户信息
var userId = int64(0)
if domain.User != nil {
userId = domain.User.Id
}
// 分组信息
var groupId int64
if len(domain.NsDomainGroups) > 0 {
groupId = domain.NsDomainGroups[0].Id
}
// 健康检查
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"isOn": domain.IsOn,
"clusterId": clusterId,
"userId": userId,
"countRecords": countRecords,
"countKeys": countKeys,
"groupId": groupId,
"enableHealthCheck": healthCheckConfig.IsOn,
}
// DNS服务器
if domain.NsCluster == nil || domain.NsCluster.Id <= 0 {
this.WriteString("当前域名(" + domain.Name + ")所在集群已被删除,请删除当前域名后重新添加")
return
}
hostsResp, err := this.RPC().NSClusterRPC().FindNSClusterHosts(this.AdminContext(), &pb.FindNSClusterHostsRequest{
NsClusterId: domain.NsCluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var hosts = hostsResp.Hosts
if hosts == nil {
hosts = []string{}
}
this.Data["dnsHosts"] = hosts
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
DomainId int64
ClusterId int64
UserId int64
GroupId int64
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomain, params.DomainId)
params.Must.
Field("clusterId", params.ClusterId).
Gt(0, "请选择所属集群")
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
_, err := this.RPC().NSDomainRPC().UpdateNSDomain(this.AdminContext(), &pb.UpdateNSDomainRequest{
NsDomainId: params.DomainId,
NsClusterId: params.ClusterId,
UserId: params.UserId,
NsDomainGroupIds: groupIds,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,155 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package ns
import (
"encoding/json"
"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 IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "ns")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Data["board"] = maps.Map{
"countDomains": 0,
"countRecords": 0,
"countClusters": 0,
"countNodes": 0,
"countOfflineNodes": 0,
}
this.Show()
}
func (this *IndexAction) RunPost(params struct{}) {
resp, err := this.RPC().NSRPC().ComposeNSBoard(this.AdminContext(), &pb.ComposeNSBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countDomains": resp.CountNSDomains,
"countRecords": resp.CountNSRecords,
"countClusters": resp.CountNSClusters,
"countNodes": resp.CountNSNodes,
"countOfflineNodes": resp.CountOfflineNSNodes,
}
// 流量排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["hourlyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["dailyStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSDomainStats {
statMaps = append(statMaps, maps.Map{
"domainId": stat.NsDomainId,
"domainName": stat.NsDomainName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSNodeStats {
statMaps = append(statMaps, maps.Map{
"clusterId": stat.NsClusterId,
"nodeId": stat.NsNodeId,
"nodeName": stat.NsNodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("usage"),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("usage"),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("load1m"),
})
}
this.Data["loadValues"] = statMaps
}
this.Success()
}

View File

@@ -0,0 +1,119 @@
//go:build plus
package ns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/batch"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/domain"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/groups"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/groups/group"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/keys"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/records"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/plans"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/plans/plan"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/settings"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/userPlans"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/userPlans/userPlan"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Prefix("/ns").
GetPost("", new(IndexAction)).
GetPost("/addPortPopup", new(AddPortPopupAction)).
// 域名列表
Prefix("/ns/domains").
Data("teaSubMenu", "domain").
Get("", new(domains.IndexAction)).
GetPost("/create", new(domains.CreateAction)).
Post("/delete", new(domains.DeleteAction)).
Post("/deletePage", new(domains.DeletePageAction)).
GetPost("/update", new(domains.UpdateAction)).
GetPost("/tsig", new(domains.TsigAction)).
GetPost("/healthCheck", new(domains.HealthCheckAction)).
// 单个域名
Prefix("/ns/domains/domain").
Data("teaSubMenu", "domain").
Get("", new(domain.IndexAction)).
GetPost("/updateStatusPopup", new(domain.UpdateStatusPopupAction)).
// 域名批量操作
Prefix("/ns/domains/batch").
Data("teaSubMenu", "domainBatch").
GetPost("", new(batch.IndexAction)).
GetPost("/createRecords", new(batch.CreateRecordsAction)).
GetPost("/deleteDomains", new(batch.DeleteDomainsAction)).
GetPost("/updateRecords", new(batch.UpdateRecordsAction)).
GetPost("/enableRecords", new(batch.EnableRecordsAction)).
GetPost("/importRecords", new(batch.ImportRecordsAction)).
GetPost("/deleteRecords", new(batch.DeleteRecordsAction)).
// 域名密钥
Prefix("/ns/domains/keys").
Data("teaSubMenu", "domain").
Get("", new(keys.IndexAction)).
GetPost("/createPopup", new(keys.CreatePopupAction)).
GetPost("/updatePopup", new(keys.UpdatePopupAction)).
Post("/delete", new(keys.DeleteAction)).
Post("/generateSecret", new(keys.GenerateSecretAction)).
// 记录相关
Prefix("/ns/domains/records").
Get("", new(records.IndexAction)).
GetPost("/createPopup", new(records.CreatePopupAction)).
GetPost("/updatePopup", new(records.UpdatePopupAction)).
Post("/delete", new(records.DeleteAction)).
GetPost("/statPopup", new(records.StatPopupAction)).
GetPost("/healthCheckPopup", new(records.HealthCheckPopupAction)).
Post("/updateUp", new(records.UpdateUpAction)).
// 域名分组列表
Prefix("/ns/domains/groups").
Data("teaSubMenu", "domainGroup").
Get("", new(groups.IndexAction)).
GetPost("/createPopup", new(groups.CreatePopupAction)).
Post("/options", new(groups.OptionsAction)).
// 域名分组
Prefix("/ns/domains/groups/group").
Data("teaSubMenu", "domainGroup").
Get("", new(group.IndexAction)).
GetPost("/updatePopup", new(group.UpdatePopupAction)).
Post("/delete", new(group.DeleteAction)).
// 设置
Prefix("/ns/settings").
Get("", new(settings.IndexAction)).
// 套餐管理
Prefix("/ns/plans").
Data("teaSubMenu", "plan").
Get("", new(plans.IndexAction)).
Post("/sort", new(plans.SortAction)).
GetPost("/createPopup", new(plans.CreatePopupAction)).
GetPost("/plan/updatePopup", new(plan.UpdatePopupAction)).
Post("/plan/delete", new(plan.DeleteAction)).
// 用户套餐
Prefix("/ns/userPlans").
Data("teaSubMenu", "userPlan").
Get("", new(userPlans.IndexAction)).
GetPost("/createPopup", new(userPlans.CreatePopupAction)).
GetPost("/userPlan/updatePopup", new(userPlan.UpdatePopupAction)).
Post("/userPlan/delete", new(userPlan.DeleteAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,173 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nsnodeutils
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"sort"
"strconv"
"sync"
)
// MessageResult 和节点消息通讯结果定义
type MessageResult struct {
NodeId int64 `json:"nodeId"`
NodeName string `json:"nodeName"`
IsOK bool `json:"isOk"`
Message string `json:"message"`
}
// SendMessageToNSCluster 向NS集群发送命令消息
func SendMessageToNSCluster(ctx context.Context, clusterId int64, code string, msg interface{}, timeoutSeconds int32) (results []*MessageResult, err error) {
results = []*MessageResult{}
msgJSON, err := json.Marshal(msg)
if err != nil {
return results, err
}
defaultRPCClient, err := rpc.SharedRPC()
if err != nil {
return results, err
}
// 获取所有节点
nodesResp, err := defaultRPCClient.NSNodeRPC().FindAllNSNodesWithNSClusterId(ctx, &pb.FindAllNSNodesWithNSClusterIdRequest{
NsClusterId: clusterId,
})
if err != nil {
return results, err
}
var nodes = nodesResp.NsNodes
if len(nodes) == 0 {
return results, nil
}
var rpcMap = map[int64]*rpc.RPCClient{} // apiNodeId => RPCClient
var locker = &sync.Mutex{}
var wg = &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
// TODO 检查是否在线
if len(node.ConnectedAPINodeIds) == 0 {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: false,
Message: "节点尚未连接到API",
})
locker.Unlock()
wg.Done()
continue
}
// 获取API节点信息
apiNodeId := node.ConnectedAPINodeIds[0]
rpcClient, ok := rpcMap[apiNodeId]
if !ok {
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: false,
Message: "无法读取对应的API节点信息" + err.Error(),
})
locker.Unlock()
wg.Done()
continue
}
if apiNodeResp.ApiNode == nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: false,
Message: "无法读取对应的API节点信息API节点ID" + strconv.FormatInt(apiNodeId, 10),
})
locker.Unlock()
wg.Done()
continue
}
apiNode := apiNodeResp.ApiNode
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
RPCEndpoints: apiNode.AccessAddrs,
NodeId: apiNode.UniqueId,
Secret: apiNode.Secret,
}, false)
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: false,
Message: "初始化API节点错误API节点ID" + strconv.FormatInt(apiNodeId, 10) + "" + err.Error(),
})
locker.Unlock()
wg.Done()
continue
}
rpcMap[apiNodeId] = apiRPCClient
rpcClient = apiRPCClient
}
// 发送消息
go func(node *pb.NSNode) {
defer wg.Done()
result, err := rpcClient.NSNodeRPC().SendCommandToNSNode(ctx, &pb.NSNodeStreamMessage{
NsNodeId: node.Id,
TimeoutSeconds: timeoutSeconds,
Code: code,
DataJSON: msgJSON,
})
if err != nil {
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: false,
Message: "API返回错误" + err.Error(),
})
locker.Unlock()
return
}
locker.Lock()
results = append(results, &MessageResult{
NodeId: node.Id,
NodeName: node.Name,
IsOK: result.IsOk,
Message: result.Message,
})
locker.Unlock()
}(node)
}
wg.Wait()
// 对结果进行排序
if len(results) > 0 {
sort.Slice(results, func(i, j int) bool {
return results[i].NodeId < results[j].NodeId
})
}
// 关闭RPC
for _, rpcClient := range rpcMap {
_ = rpcClient.Close()
}
return
}

View File

@@ -0,0 +1,99 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package plans
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
MonthlyPrice float32
YearlyPrice float32
SupportCountryRoutes bool
SupportChinaProvinceRoutes bool
SupportISPRoutes bool
SupportAgentRoutes bool
SupportPublicRoutes bool
SupportHealthCheck bool
MinTTL int32
MaxDomains int32
MaxRecordsPerDomain int32
MaxLoadBalanceRecordsPerRecord int32
MaxCustomRoutes int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var planId int64
defer func() {
this.CreateLogInfo(codes.NSPlan_LogCreateNSPlan, planId)
}()
params.Must.
Field("name", params.Name).
Require("请输入套餐名称").
Field("monthlyPrice", params.MonthlyPrice).
Gt(0, "请输入按月价格").
Field("yearlyPrice", params.YearlyPrice).
Gt(0, "请输入按年价格")
var config = dnsconfigs.DefaultNSPlanConfig()
config.SupportCountryRoutes = params.SupportCountryRoutes
config.SupportChinaProvinceRoutes = params.SupportChinaProvinceRoutes
config.SupportISPRoutes = params.SupportISPRoutes
config.SupportAgentRoutes = params.SupportAgentRoutes
config.SupportPublicRoutes = params.SupportPublicRoutes
config.SupportHealthCheck = params.SupportHealthCheck
config.MinTTL = params.MinTTL
config.MaxDomains = params.MaxDomains
config.MaxRecordsPerDomain = params.MaxRecordsPerDomain
config.MaxLoadBalanceRecordsPerRecord = params.MaxLoadBalanceRecordsPerRecord
config.MaxCustomRoutes = params.MaxCustomRoutes
err := config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().NSPlanRPC().CreateNSPlan(this.AdminContext(), &pb.CreateNSPlanRequest{
Name: params.Name,
MonthlyPrice: params.MonthlyPrice,
YearlyPrice: params.YearlyPrice,
ConfigJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
planId = createResp.NsPlanId
this.Success()
}

View File

@@ -0,0 +1,54 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package plans
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
// 当前所有套餐
plansResp, err := this.RPC().NSPlanRPC().FindAllNSPlans(this.AdminContext(), &pb.FindAllNSPlansRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var planMaps = []maps.Map{}
for _, plan := range plansResp.NsPlans {
var config = dnsconfigs.DefaultNSPlanConfig()
if len(plan.ConfigJSON) > 0 {
err = json.Unmarshal(plan.ConfigJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
planMaps = append(planMaps, maps.Map{
"id": plan.Id,
"name": plan.Name,
"isOn": plan.IsOn,
"monthlyPriceFormat": fmt.Sprintf("%.2f", plan.MonthlyPrice),
"yearlyPriceFormat": fmt.Sprintf("%.2f", plan.YearlyPrice),
"config": config,
})
}
this.Data["plans"] = planMaps
this.Show()
}

View File

@@ -0,0 +1,42 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package plan
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
PlanId int64
}) {
defer this.CreateLogInfo(codes.NSPlan_LogDeleteNSPlan, params.PlanId)
// 检查是否有用户在使用
countResp, err := this.RPC().NSUserPlanRPC().CountNSUserPlans(this.AdminContext(), &pb.CountNSUserPlansRequest{
NsPlanId: params.PlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("有" + types.String(countResp.Count) + "个用户正在使用此套餐,请转移用户套餐后再删除")
return
}
_, err = this.RPC().NSPlanRPC().DeleteNSPlan(this.AdminContext(), &pb.DeleteNSPlanRequest{NsPlanId: params.PlanId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,134 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package plan
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
PlanId int64
}) {
planResp, err := this.RPC().NSPlanRPC().FindNSPlan(this.AdminContext(), &pb.FindNSPlanRequest{NsPlanId: params.PlanId})
if err != nil {
this.ErrorPage(err)
return
}
var plan = planResp.NsPlan
if plan == nil {
this.NotFound("NSPlan", params.PlanId)
return
}
var config = dnsconfigs.DefaultNSPlanConfig()
if len(plan.ConfigJSON) > 0 {
err = json.Unmarshal(plan.ConfigJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["plan"] = maps.Map{
"id": plan.Id,
"name": plan.Name,
"isOn": plan.IsOn,
"monthlyPrice": plan.MonthlyPrice,
"yearlyPrice": plan.YearlyPrice,
"config": config,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
PlanId int64
Name string
MonthlyPrice float32
YearlyPrice float32
SupportCountryRoutes bool
SupportChinaProvinceRoutes bool
SupportISPRoutes bool
SupportAgentRoutes bool
SupportPublicRoutes bool
SupportHealthCheck bool
MinTTL int32
MaxDomains int32
MaxRecordsPerDomain int32
MaxLoadBalanceRecordsPerRecord int32
MaxCustomRoutes int32
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSPlan_LogUpdateNSPlan, params.PlanId)
params.Must.
Field("name", params.Name).
Require("请输入套餐名称").
Field("monthlyPrice", params.MonthlyPrice).
Gt(0, "请输入按月价格").
Field("yearlyPrice", params.YearlyPrice).
Gt(0, "请输入按年价格")
var config = dnsconfigs.DefaultNSPlanConfig()
config.SupportCountryRoutes = params.SupportCountryRoutes
config.SupportChinaProvinceRoutes = params.SupportChinaProvinceRoutes
config.SupportISPRoutes = params.SupportISPRoutes
config.SupportAgentRoutes = params.SupportAgentRoutes
config.SupportPublicRoutes = params.SupportPublicRoutes
config.SupportHealthCheck = params.SupportHealthCheck
config.MinTTL = params.MinTTL
config.MaxDomains = params.MaxDomains
config.MaxRecordsPerDomain = params.MaxRecordsPerDomain
config.MaxLoadBalanceRecordsPerRecord = params.MaxLoadBalanceRecordsPerRecord
config.MaxCustomRoutes = params.MaxCustomRoutes
err := config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSPlanRPC().UpdateNSPlan(this.AdminContext(), &pb.UpdateNSPlanRequest{
NsPlanId: params.PlanId,
Name: params.Name,
MonthlyPrice: params.MonthlyPrice,
YearlyPrice: params.YearlyPrice,
ConfigJSON: configJSON,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package plans
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SortAction struct {
actionutils.ParentAction
}
func (this *SortAction) RunPost(params struct {
Ids []int64
}) {
defer this.CreateLogInfo(codes.NSPlan_LogSortNSPlans)
_, err := this.RPC().NSPlanRPC().SortNSPlanOrders(this.AdminContext(), &pb.SortNSPlansRequest{NsPlanIds: params.Ids})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package category
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
CategoryId int64
}) {
defer this.CreateLogInfo(codes.NSRouteCategory_LogDeleteNSRouteCategory, params.CategoryId)
_, err := this.RPC().NSRouteCategoryRPC().DeleteNSRouteCategory(this.AdminContext(), &pb.DeleteNSRouteCategoryRequest{NsRouteCategoryId: params.CategoryId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,69 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package category
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
CategoryId int64
}) {
categoryResp, err := this.RPC().NSRouteCategoryRPC().FindNSRouteCategory(this.AdminContext(), &pb.FindNSRouteCategoryRequest{NsRouteCategoryId: params.CategoryId})
if err != nil {
this.ErrorPage(err)
return
}
var category = categoryResp.NsRouteCategory
if category == nil {
this.NotFound("NSRouteCategory", params.CategoryId)
return
}
this.Data["category"] = maps.Map{
"id": category.Id,
"name": category.Name,
"isOn": category.IsOn,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
CategoryId int64
Name string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSRouteCategory_LogUpdateNSRouteCategory, params.CategoryId)
params.Must.
Field("name", params.Name).
Require("请输入分类名称")
_, err := this.RPC().NSRouteCategoryRPC().UpdateNSRouteCategory(this.AdminContext(), &pb.UpdateNSRouteCategoryRequest{
NsRouteCategoryId: params.CategoryId,
Name: params.Name,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,47 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package categories
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var categoryId int64
defer func() {
this.CreateLogInfo(codes.NSRouteCategory_LogCreateNSRouteCategory, categoryId)
}()
params.Must.
Field("name", params.Name).
Require("请输入分类名称")
createResp, err := this.RPC().NSRouteCategoryRPC().CreateNSRouteCategory(this.AdminContext(), &pb.CreateNSRouteCategoryRequest{Name: params.Name})
if err != nil {
this.ErrorPage(err)
return
}
categoryId = createResp.NsRouteCategoryId
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package categories
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "category")
}
func (this *IndexAction) RunGet(params struct{}) {
categoriesResp, err := this.RPC().NSRouteCategoryRPC().FindAllNSRouteCategories(this.AdminContext(), &pb.FindAllNSRouteCategoriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var categoryMaps = []maps.Map{}
for _, category := range categoriesResp.NsRouteCategories {
categoryMaps = append(categoryMaps, maps.Map{
"id": category.Id,
"name": category.Name,
"isOn": category.IsOn,
})
}
this.Data["categories"] = categoryMaps
this.Show()
}

View File

@@ -0,0 +1,32 @@
//go:build plus
package categories
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/routes/categories/category"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeNS)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Data("teaSubMenu", "route").
Prefix("/ns/routes/categories").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Post("/sort", new(SortAction)).
//
GetPost("/category/updatePopup", new(category.UpdatePopupAction)).
Post("/category/delete", new(category.DeleteAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package categories
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SortAction struct {
actionutils.ParentAction
}
func (this *SortAction) RunPost(params struct {
CategoryIds []int64
}) {
defer this.CreateLogInfo(codes.NSRouteCategory_LogSortNSRouteCategories)
_, err := this.RPC().NSRouteCategoryRPC().UpdateNSRouteCategoryOrders(this.AdminContext(), &pb.UpdateNSRouteCategoryOrders{NsRouteCategoryIds: params.CategoryIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,99 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package routes
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct {
ClusterId int64
DomainId int64
UserId int64
}) {
this.Data["clusterId"] = params.ClusterId
this.Data["domainId"] = params.DomainId
this.Data["userId"] = params.UserId
this.Data["rangeTypes"] = dnsconfigs.AllNSRouteRangeTypes()
// 所有分类
categoriesResp, err := this.RPC().NSRouteCategoryRPC().FindAllNSRouteCategories(this.AdminContext(), &pb.FindAllNSRouteCategoriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var categoryMaps = []maps.Map{}
for _, category := range categoriesResp.NsRouteCategories {
categoryMaps = append(categoryMaps, maps.Map{
"id": category.Id,
"name": category.Name,
})
}
this.Data["categories"] = categoryMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
ClusterId int64
DomainId int64
UserId int64
Name string
RangesJSON []byte
IsPublic bool
CategoryId int64
Priority int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var routeId = int64(0)
defer func() {
this.CreateLogInfo(codes.NSRoute_LogCreateNSRoute, routeId)
}()
params.Must.Field("name", params.Name).
Require("请输入线路名称")
ranges, err := dnsconfigs.InitNSRangesFromJSON(params.RangesJSON)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
if len(ranges) == 0 {
this.Fail("请添加线路范围")
}
createResp, err := this.RPC().NSRouteRPC().CreateNSRoute(this.AdminContext(), &pb.CreateNSRouteRequest{
NsClusterId: params.ClusterId,
NsDomainId: params.DomainId,
UserId: params.UserId,
Name: params.Name,
RangesJSON: params.RangesJSON,
IsPublic: params.IsPublic,
NsRouteCategoryId: params.CategoryId,
Priority: params.Priority,
})
if err != nil {
this.ErrorPage(err)
return
}
routeId = createResp.NsRouteId
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package routes
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
RouteId int64
}) {
defer this.CreateLogInfo(codes.NSRoute_LogDeleteNSRoute, params.RouteId)
_, err := this.RPC().NSRouteRPC().DeleteNSRoute(this.AdminContext(), &pb.DeleteNSRouteRequest{NsRouteId: params.RouteId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,61 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package routes
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"strconv"
)
type ExportAgentIPsAction struct {
actionutils.ParentAction
}
func (this *ExportAgentIPsAction) Init() {
this.Nav("", "", "")
}
func (this *ExportAgentIPsAction) RunGet(params struct{}) {
var lastId int64 = 0
var agentIPMaps = []maps.Map{}
for {
resp, err := this.RPC().ClientAgentIPRPC().ListClientAgentIPsAfterId(this.AdminContext(), &pb.ListClientAgentIPsAfterIdRequest{
Id: lastId,
Size: 10000,
})
if err != nil {
this.ErrorPage(err)
return
}
var agentIPs = resp.ClientAgentIPs
if len(agentIPs) == 0 {
break
}
for _, agentIP := range agentIPs {
if lastId < agentIP.Id {
lastId = agentIP.Id
}
if agentIP.ClientAgent == nil {
continue
}
agentIPMaps = append(agentIPMaps, maps.Map{
"ip": agentIP.Ip,
"ptr": agentIP.Ptr,
"agentCode": agentIP.ClientAgent.Code,
})
}
}
agentIPsJSON, err := json.Marshal(agentIPMaps)
if err != nil {
this.ErrorPage(err)
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"AGENT-IP-LIST.json\";")
this.AddHeader("Content-Length", strconv.Itoa(len(agentIPsJSON)))
_, _ = this.Write(agentIPsJSON)
}

View File

@@ -0,0 +1,95 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package routes
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
)
type ImportAgentIPsPopupAction struct {
actionutils.ParentAction
}
func (this *ImportAgentIPsPopupAction) Init() {
this.Nav("", "", "")
}
func (this *ImportAgentIPsPopupAction) RunGet(params struct {
}) {
this.Show()
}
func (this *ImportAgentIPsPopupAction) RunPost(params struct {
File *actions.File
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if params.File == nil {
this.Fail("请选择要导入的文件")
return
}
data, err := params.File.Read()
if err != nil {
this.Fail("读文件失败:" + err.Error())
return
}
var ipMaps = []maps.Map{}
err = json.Unmarshal(data, &ipMaps)
if err != nil {
this.Fail("解析文件失败:" + err.Error())
return
}
var pbIPs = []*pb.CreateClientAgentIPsRequest_AgentIPInfo{}
for _, ipMap := range ipMaps {
var ip = ipMap.GetString("ip")
var ptr = ipMap.GetString("ptr")
var agentCode = ipMap.GetString("agentCode")
if len(ip) == 0 || len(agentCode) == 0 || net.ParseIP(ip) == nil {
continue
}
pbIPs = append(pbIPs, &pb.CreateClientAgentIPsRequest_AgentIPInfo{
AgentCode: agentCode,
Ip: ip,
Ptr: ptr,
})
}
if len(pbIPs) == 0 {
this.Fail("文件中没有包含有效的数据")
return
}
var size = 10000
for {
var pbIPList []*pb.CreateClientAgentIPsRequest_AgentIPInfo
var isEnd = false
if len(pbIPs) > size {
pbIPList = pbIPs[:size]
pbIPs = pbIPs[size:]
} else {
pbIPList = pbIPs
isEnd = true
}
_, err = this.RPC().ClientAgentIPRPC().CreateClientAgentIPs(this.AdminContext(), &pb.CreateClientAgentIPsRequest{AgentIPs: pbIPList})
if err != nil {
this.Fail("导入失败:" + err.Error())
return
}
if isEnd {
break
}
}
this.Success()
}

Some files were not shown because too many files have changed in this diff Show More