Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package boards
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type DataAction struct {
actionutils.ParentAction
}
func (this *DataAction) RunPost(params struct {
ClusterId int64
NodeId int64
}) {
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"isActive": resp.IsActive,
"trafficInBytes": resp.TrafficInBytes,
"trafficOutBytes": resp.TrafficOutBytes,
"countConnections": resp.CountConnections,
"countRequests": resp.CountRequests,
"countAttackRequests": resp.CountAttackRequests,
"cpuUsage": resp.CpuUsage,
"memoryUsage": resp.MemoryUsage,
"memoryTotalSize": resp.MemoryTotalSize,
"load": resp.Load,
"cacheDiskSize": resp.CacheDiskSize,
"cacheMemorySize": resp.CacheMemorySize,
"monthlyTrafficBytes": resp.MonthlyTrafficBytes,
"todayTrafficBytes": resp.DailyTrafficBytes,
"yesterdayTrafficBytes": resp.LastDailyTrafficBytes,
}
this.Success()
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package boards
import (
"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"
"time"
)
type DomainStatsAction struct {
actionutils.ParentAction
}
func (this *DomainStatsAction) RunPost(params struct {
NodeId int64
}) {
var hourFrom = timeutil.Format("YmdH", time.Now().Add(-23*time.Hour))
var hourTo = timeutil.Format("YmdH")
resp, err := this.RPC().ServerDomainHourlyStatRPC().ListTopServerDomainStatsWithServerId(this.AdminContext(), &pb.ListTopServerDomainStatsWithServerIdRequest{
NodeId: params.NodeId,
HourFrom: hourFrom,
HourTo: hourTo,
Size: 10,
})
if err != nil {
this.ErrorPage(err)
return
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.DomainStats {
statMaps = append(statMaps, maps.Map{
"serverId": stat.ServerId,
"domain": stat.Domain,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
})
}
this.Data["topDomainStats"] = statMaps
}
this.Success()
}

View File

@@ -0,0 +1,304 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package boards
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"strconv"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "board")
this.SecondMenu("nodes")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
if !teaconst.IsPlus {
this.RedirectURL("/clusters/cluster/node?clusterId=" + strconv.FormatInt(params.ClusterId, 10) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
return
}
pbNode, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// disk information
this.Data["diskWritingSpeedMB"] = 0
this.Data["diskWritingSpeedMBDelta"] = 0
if !utils.JSONIsNull(pbNode.StatusJSON) {
var statusConfig = &nodeconfigs.NodeStatus{}
err = json.Unmarshal(pbNode.StatusJSON, statusConfig)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["diskWritingSpeedMB"] = statusConfig.DiskWritingSpeedMB
}
// max cache capacity
this.Data["cacheDiskCapacityBytes"] = 0
if pbNode.MaxCacheDiskCapacity == nil {
pbNode.MaxCacheDiskCapacity = &pb.SizeCapacity{Count: 0, Unit: "byte"}
}
var capacityBytes = (&shared.SizeCapacity{
Count: pbNode.MaxCacheDiskCapacity.Count,
Unit: pbNode.MaxCacheDiskCapacity.Unit,
}).Bytes()
if capacityBytes <= 0 && params.ClusterId > 0 {
// lookup cache policy for cluster
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
if clusterResp.NodeCluster != nil && clusterResp.NodeCluster.HttpCachePolicyId > 0 {
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: clusterResp.NodeCluster.HttpCachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var cachePolicyJSON = cachePolicyResp.HttpCachePolicyJSON
if !utils.JSONIsNull(cachePolicyJSON) {
var cachePolicy = &serverconfigs.HTTPCachePolicy{}
err = json.Unmarshal(cachePolicyJSON, cachePolicy)
if err != nil {
this.ErrorPage(err)
return
}
if cachePolicy.IsOn && cachePolicy.Type == serverconfigs.CachePolicyStorageFile && cachePolicy.Capacity != nil {
capacityBytes = cachePolicy.Capacity.Bytes()
}
}
}
}
if capacityBytes > 0 {
this.Data["cacheDiskCapacityBytes"] = capacityBytes
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
NodeId int64
}) {
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"isActive": resp.IsActive,
"trafficInBytes": resp.TrafficInBytes,
"trafficOutBytes": resp.TrafficOutBytes,
"countConnections": resp.CountConnections,
"countRequests": resp.CountRequests,
"countAttackRequests": resp.CountAttackRequests,
"cpuUsage": resp.CpuUsage,
"memoryUsage": resp.MemoryUsage,
"memoryTotalSize": resp.MemoryTotalSize,
"load": resp.Load,
"cacheDiskSize": resp.CacheDiskSize,
"cacheMemorySize": resp.CacheMemorySize,
"monthlyTrafficBytes": resp.MonthlyTrafficBytes,
"todayTrafficBytes": resp.DailyTrafficBytes,
"yesterdayTrafficBytes": resp.LastDailyTrafficBytes,
}
// 24小时流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
})
}
this.Data["hourlyStats"] = statMaps
}
// 15天流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
})
}
this.Data["dailyStats"] = 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
}
// CacheDirs
{
var statMaps = []maps.Map{}
for _, stat := range resp.CacheDirsValues {
var m = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &m)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": m,
})
}
this.Data["cacheDirValues"] = statMaps
}
// Network Packets
{
var hasData = false
var statMaps = []maps.Map{}
for _, stat := range resp.NetworkPacketsValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
var tcpInPPS = valueMap.GetUint64("tcpInPPS")
var udpInPPS = valueMap.GetUint64("udpInPPS")
var icmpInPPS = valueMap.GetUint64("icmpInPPS")
if tcpInPPS > 0 || udpInPPS > 0 || icmpInPPS > 0 {
hasData = true
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"tcpInPPS": tcpInPPS,
"udpInPPS": udpInPPS,
"icmpInPPS": icmpInPPS,
})
}
if !hasData {
statMaps = []maps.Map{}
}
this.Data["networkPacketsValues"] = statMaps
}
// 指标
{
var chartMaps = []maps.Map{}
for _, chart := range resp.MetricDataCharts {
var statMaps = []maps.Map{}
for _, stat := range chart.MetricStats {
statMaps = append(statMaps, maps.Map{
"keys": stat.Keys,
"time": stat.Time,
"value": stat.Value,
"count": stat.SumCount,
"total": stat.SumTotal,
})
}
chartMaps = append(chartMaps, maps.Map{
"chart": maps.Map{
"id": chart.MetricChart.Id,
"name": chart.MetricChart.Name,
"widthDiv": chart.MetricChart.WidthDiv,
"isOn": chart.MetricChart.IsOn,
"maxItems": chart.MetricChart.MaxItems,
"type": chart.MetricChart.Type,
},
"item": maps.Map{
"id": chart.MetricChart.MetricItem.Id,
"name": chart.MetricChart.MetricItem.Name,
"period": chart.MetricChart.MetricItem.Period,
"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
"valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"valueTypeName": serverconfigs.FindMetricValueName(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"keys": chart.MetricChart.MetricItem.Keys,
},
"stats": statMaps,
})
}
this.Data["metricCharts"] = chartMaps
}
this.Success()
}

View File

@@ -0,0 +1,403 @@
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/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"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 DetailAction struct {
actionutils.ParentAction
}
func (this *DetailAction) Init() {
this.Nav("", "node", "node")
this.SecondMenu("nodes")
}
func (this *DetailAction) RunGet(params struct {
NodeId int64
ClusterId int64
}) {
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
// 主集群
var clusterMap maps.Map = nil
if node.NodeCluster != nil {
var clusterId = node.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NodeCluster
if cluster != nil {
clusterMap = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
}
}
}
// 从集群
var secondaryClustersMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClustersMaps = append(secondaryClustersMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isOn": cluster.IsOn,
})
}
// 当前访问集群的DNS设置
clusterDNSInfo, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["dnsIsExcludingLnNode"] = clusterDNSInfo != nil && !clusterDNSInfo.IncludingLnNodes && node.Level > 1
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipAddresses = ipAddressesResp.NodeIPAddresses
var ipAddressMaps = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
// 是否有备用IP
var originIP = addr.Ip
if len(addr.BackupIP) > 0 {
addr.Ip = addr.BackupIP
}
// 专属集群
var addrClusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
addrClusterMaps = append(addrClusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"originIP": originIP,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"clusters": addrClusterMaps,
"thresholds": thresholds,
})
}
// DNS相关
var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var recordMaps = []maps.Map{}
var routeMaps = []maps.Map{}
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var dnsInfo = dnsInfoResp.Node
if len(dnsInfo.DnsDomainName) == 0 || len(dnsInfo.NodeClusterDNSName) == 0 {
continue
}
var domainName = dnsInfo.DnsDomainName
// 默认线路
if len(dnsInfo.Routes) == 0 {
dnsInfo.Routes = append(dnsInfo.Routes, &pb.DNSRoute{})
} else {
for _, route := range dnsInfo.Routes {
routeMaps = append(routeMaps, maps.Map{
"domainName": domainName,
"code": route.Code,
"name": route.Name,
})
}
}
for _, addr := range ipAddresses {
if !addr.CanAccess || !addr.IsUp || !addr.IsOn {
continue
}
// 过滤集群
if len(addr.NodeClusters) > 0 {
var inCluster = false
for _, addrCluster := range addr.NodeClusters {
if addrCluster.Id == cluster.Id {
inCluster = true
}
}
if !inCluster {
continue
}
}
for _, route := range dnsInfo.Routes {
var recordType = "A"
if iputils.IsIPv6(addr.Ip) {
recordType = "AAAA"
}
recordMaps = append(recordMaps, maps.Map{
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
"type": recordType,
"route": route.Name,
"value": addr.Ip,
"clusterName": cluster.Name,
"isBackup": dnsInfo.IsBackupForCluster || dnsInfo.IsBackupForGroup,
"isOffline": dnsInfo.IsOffline,
})
}
}
}
// 登录信息
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
}
}
var grantMap = maps.Map{}
var 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,
}
}
// 运行状态
var status = &nodeconfigs.NodeStatus{}
this.Data["nodeDatetime"] = ""
this.Data["nodeTimeDiff"] = 0
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().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
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 groupMap maps.Map = nil
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
// 区域
var regionMap maps.Map = nil
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var maxCacheMemoryCapacity maps.Map
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
}
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var lnAddrs = node.LnAddrs
if lnAddrs == nil {
lnAddrs = []string{}
}
// 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,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"offlineDay": node.OfflineDay,
"isOffline": len(node.OfflineDay) > 0 && node.OfflineDay < timeutil.Format("Ymd"),
"isBackupForCluster": node.IsBackupForCluster,
"isBackupForGroup": node.IsBackupForGroup,
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"connectionCount": status.ConnectionCount,
"buildVersion": status.BuildVersion,
"cpuPhysicalCount": status.CPUPhysicalCount,
"cpuLogicalCount": status.CPULogicalCount,
"load1m": numberutils.FormatFloat2(status.Load1m),
"load5m": numberutils.FormatFloat2(status.Load5m),
"load15m": numberutils.FormatFloat2(status.Load15m),
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
"exePath": status.ExePath,
"apiSuccessPercent": status.APISuccessPercent,
"apiAvgCostSeconds": status.APIAvgCostSeconds,
"diskWritingSpeedMB": status.DiskWritingSpeedMB,
},
"group": groupMap,
"region": regionMap,
"maxCacheDiskCapacity": maxCacheDiskCapacity,
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
}
this.Show()
}

View File

@@ -0,0 +1,34 @@
package node
import (
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"strconv"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "node")
this.SecondMenu("nodes")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
if teaconst.IsPlus {
this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
} else {
this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
}
}

View File

@@ -0,0 +1,155 @@
package node
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/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"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"
"path/filepath"
"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
// 节点
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 最近运行目录
var exeRoot = ""
if len(node.StatusJSON) > 0 {
var nodeStatus = &nodeconfigs.NodeStatus{}
err = json.Unmarshal(node.StatusJSON, nodeStatus)
if err == nil {
var exePath = nodeStatus.ExePath
if len(exePath) > 0 {
exeRoot = filepath.Dir(filepath.Dir(exePath))
}
}
}
this.Data["exeRoot"] = exeRoot
// 安装信息
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.NodeCluster != nil {
clusterId := node.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
cluster := clusterResp.NodeCluster
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
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
continue
}
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
}
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["installDir"] = node.InstallDir
nodeMap["isInstalled"] = node.IsInstalled
nodeMap["uniqueId"] = node.UniqueId
nodeMap["secret"] = node.Secret
nodeMap["cluster"] = clusterMap
// 安装文件
var installerFiles = clusterutils.ListInstallerFiles()
this.Data["installerFiles"] = installerFiles
// 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
}) {
_, err := this.RPC().NodeRPC().InstallNode(this.AdminContext(), &pb.InstallNodeRequest{
NodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.Node_LogInstallNode, params.NodeId)
this.Success()
}

View File

@@ -0,0 +1,109 @@
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/nodelogutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
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
Tag string
}) {
// 初始化节点信息(用于菜单)
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["tags"] = nodelogutils.FindNodeCommonTags(this.LangCode())
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
this.Data["tag"] = params.Tag
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleNode,
NodeId: params.NodeId,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Tag: params.Tag,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count, 20)
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: params.NodeId,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Tag: params.Tag,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
logs := []maps.Map{}
for _, log := range logsResp.NodeLogs {
// 服务信息
var serverMap = maps.Map{"id": 0}
if log.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: log.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
serverMap = maps.Map{"id": server.Id, "name": server.Name}
}
}
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,
"server": serverMap,
})
}
this.Data["logs"] = logs
this.Data["page"] = page.AsHTML()
this.Show()
}

View File

@@ -0,0 +1,106 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package nodeutils
import (
"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/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strconv"
)
// InitNodeInfo 初始化节点信息
func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Node, error) {
// 节点信息(用于菜单)
nodeResp, err := parentAction.RPC().NodeRPC().FindEnabledNode(parentAction.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
if err != nil {
return nil, err
}
if nodeResp.Node == nil {
return nil, errors.New("node '" + strconv.FormatInt(nodeId, 10) + "' not found")
}
var node = nodeResp.Node
info, err := parentAction.RPC().NodeRPC().FindEnabledNodeConfigInfo(parentAction.AdminContext(), &pb.FindEnabledNodeConfigInfoRequest{NodeId: nodeId})
if err != nil {
return nil, err
}
var groupMap maps.Map
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
parentAction.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"isOn": node.IsOn,
"isUp": node.IsUp,
"group": groupMap,
"level": node.Level,
}
var clusterId int64 = 0
if node.NodeCluster != nil {
parentAction.Data["clusterId"] = node.NodeCluster.Id
clusterId = node.NodeCluster.Id
}
// 左侧菜单
var prefix = "/clusters/cluster/node"
var query = "clusterId=" + types.String(clusterId) + "&nodeId=" + types.String(nodeId)
var menuItem = parentAction.Data.GetString("secondMenuItem")
var menuItems = []maps.Map{
{
"name": parentAction.Lang(codes.NodeMenu_SettingBasic),
"url": prefix + "/update?" + query,
"isActive": menuItem == "basic",
},
{
"name": parentAction.Lang(codes.NodeMenu_SettingDNS),
"url": prefix + "/settings/dns?" + query,
"isActive": menuItem == "dns",
"isOn": info.HasDNSInfo,
},
{
"name": parentAction.Lang(codes.NodeMenu_SettingCache),
"url": prefix + "/settings/cache?" + query,
"isActive": menuItem == "cache",
"isOn": info.HasCacheInfo,
},
{
"name": parentAction.Lang(codes.NodeMenu_SettingDDoSProtection),
"url": prefix + "/settings/ddos-protection?" + query,
"isActive": menuItem == "ddosProtection",
"isOn": info.HasDDoSProtection,
},
{
"name": "-",
"url": "",
},
}
menuItems = filterMenuItems(menuItems, menuItem, prefix, query, info, parentAction.LangCode())
menuItems = append(menuItems, []maps.Map{
{
"name": parentAction.Lang(codes.NodeMenu_SettingSSH),
"url": prefix + "/settings/ssh?" + query,
"isActive": menuItem == "ssh",
"isOn": info.HasSSH,
},
{
"name": parentAction.Lang(codes.NodeMenu_SettingSystem),
"url": prefix + "/settings/system?" + query,
"isActive": menuItem == "system",
"isOn": info.HasSystemSettings,
},
}...)
parentAction.Data["leftMenuItems"] = menuItems
return nodeResp.Node, nil
}

View File

@@ -0,0 +1,13 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package nodeutils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
func filterMenuItems(menuItems []maps.Map, menuItem string, prefix string, query string, info *pb.FindEnabledNodeConfigInfoResponse, langCode string) []maps.Map {
return menuItems
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nodeutils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
plusutils "github.com/TeaOSLab/EdgePlus/pkg/utils"
"github.com/iwind/TeaGo/maps"
)
func filterMenuItems(menuItems []maps.Map, menuItem string, prefix string, query string, info *pb.FindEnabledNodeConfigInfoResponse, langCode string) []maps.Map {
if teaconst.IsPlus {
if plusutils.CheckComponent(plusutils.ComponentCodeScheduling) {
menuItems = append(menuItems, maps.Map{
"name": langs.Message(langCode, codes.NodeMenu_SettingSchedule),
"url": prefix + "/settings/schedule?" + query,
"isActive": menuItem == "schedule",
"isOn": info.HasScheduleSettings,
})
}
menuItems = append(menuItems, []maps.Map{
{
"name": langs.Message(langCode, codes.NodeMenu_SettingThresholds),
"url": prefix + "/settings/thresholds?" + query,
"isActive": menuItem == "threshold",
"isOn": info.HasThresholds,
},
}...)
}
return menuItems
}

View File

@@ -0,0 +1,16 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nodeutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestInstallLocalNode(t *testing.T) {
err := nodeutils.InstallLocalNode()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,143 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("cache")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var maxCacheMemoryCapacity maps.Map
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
}
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var diskSubDirs = []*serverconfigs.CacheDir{}
if len(node.CacheDiskSubDirsJSON) > 0 {
err = json.Unmarshal(node.CacheDiskSubDirsJSON, &diskSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
nodeMap["cacheDiskDir"] = node.CacheDiskDir
nodeMap["cacheDiskSubDirs"] = diskSubDirs
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCacheDiskCapacityJSON []byte
CacheDiskDir string
CacheDiskSubDirsJSON []byte
MaxCacheMemoryCapacityJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeCache_LogUpdateNodeCacheSettings, params.NodeId)
// 缓存硬盘 & 内存容量
var pbMaxCacheDiskCapacity *pb.SizeCapacity
if len(params.MaxCacheDiskCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheDiskCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheDiskCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
var pbMaxCacheMemoryCapacity *pb.SizeCapacity
if len(params.MaxCacheMemoryCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheMemoryCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheMemoryCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
if len(params.CacheDiskSubDirsJSON) > 0 {
var cacheSubDirs = []*serverconfigs.CacheDir{}
err := json.Unmarshal(params.CacheDiskSubDirsJSON, &cacheSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
NodeId: params.NodeId,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
CacheDiskDir: params.CacheDiskDir,
CacheDiskSubDirsJSON: params.CacheDiskSubDirsJSON,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,138 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ddosProtection
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"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("", "node", "update")
this.SecondMenu("ddosProtection")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
// 集群设置
clusterProtectionResp, err := this.RPC().NodeClusterRPC().FindNodeClusterDDoSProtection(this.AdminContext(), &pb.FindNodeClusterDDoSProtectionRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var clusterDDoSProtectionIsOn = false
if len(clusterProtectionResp.DdosProtectionJSON) > 0 {
var clusterDDoSProtection = &ddosconfigs.ProtectionConfig{}
err = json.Unmarshal(clusterProtectionResp.DdosProtectionJSON, clusterDDoSProtection)
if err != nil {
this.ErrorPage(err)
return
}
clusterDDoSProtectionIsOn = clusterDDoSProtection.IsOn()
}
this.Data["clusterDDoSProtectionIsOn"] = clusterDDoSProtectionIsOn
// 节点设置
ddosProtectionResp, err := this.RPC().NodeRPC().FindNodeDDoSProtection(this.AdminContext(), &pb.FindNodeDDoSProtectionRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var ddosProtectionConfig = ddosconfigs.DefaultProtectionConfig()
if len(ddosProtectionResp.DdosProtectionJSON) > 0 {
err = json.Unmarshal(ddosProtectionResp.DdosProtectionJSON, ddosProtectionConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = ddosProtectionConfig
this.Data["defaultConfigs"] = nodeconfigs.DefaultConfigs
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
DdosProtectionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.DDoSProtection_LogUpdateNodeDDoSProtection, params.NodeId)
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 < nodeconfigs.DefaultTCPMinConnectionsPerIP {
this.FailField("tcpMaxConnectionsPerIP", "TCP: 单IP TCP最大连接数不能小于"+types.String(nodeconfigs.DefaultTCPMinConnectionsPerIP))
}
if tcpConfig.NewConnectionsMinutelyRate > 0 && tcpConfig.NewConnectionsMinutelyRate < nodeconfigs.DefaultTCPNewConnectionsMinMinutelyRate {
this.FailField("tcpNewConnectionsMinutelyRate", "TCP: 单IP连接速率不能小于"+types.String(nodeconfigs.DefaultTCPNewConnectionsMinMinutelyRate))
}
if tcpConfig.NewConnectionsSecondlyRate > 0 && tcpConfig.NewConnectionsSecondlyRate < nodeconfigs.DefaultTCPNewConnectionsMinSecondlyRate {
this.FailField("tcpNewConnectionsSecondlyRate", "TCP: 单IP连接速率不能小于"+types.String(nodeconfigs.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().NodeRPC().UpdateNodeDDoSProtection(this.AdminContext(), &pb.UpdateNodeDDoSProtectionRequest{
NodeId: params.NodeId,
DdosProtectionJSON: params.DdosProtectionJSON,
})
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.
package ddosProtection
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
)
type StatusAction struct {
actionutils.ParentAction
}
func (this *StatusAction) RunPost(params struct {
NodeId int64
}) {
results, err := nodeutils.SendMessageToNodeIds(this.AdminContext(), []int64{params.NodeId}, messageconfigs.MessageCodeCheckLocalFirewall, &messageconfigs.CheckLocalFirewallMessage{
Name: "nftables",
}, 10)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["results"] = results
this.Success()
}

View File

@@ -0,0 +1,143 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dns
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("dns")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// DNS相关
var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var allDNSRouteMaps = map[int64][]maps.Map{} // domain id => routes
var routeMaps = map[int64][]maps.Map{} // domain id => routes
var domainIds = []int64{}
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var dnsInfo = dnsInfoResp.Node
if dnsInfo.DnsDomainId <= 0 || len(dnsInfo.DnsDomainName) == 0 {
continue
}
var domainId = dnsInfo.DnsDomainId
// remove same domain
if lists.ContainsInt64(domainIds, domainId) {
continue
}
domainIds = append(domainIds, domainId)
var domainName = dnsInfo.DnsDomainName
if len(dnsInfo.Routes) > 0 {
for _, route := range dnsInfo.Routes {
routeMaps[domainId] = append(routeMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"code": route.Code,
"name": route.Name,
})
}
}
// 所有线路选项
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfoResp.Node.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range routesResp.Routes {
allDNSRouteMaps[domainId] = append(allDNSRouteMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"name": route.Name,
"code": route.Code,
})
}
}
var domainRoutes = []maps.Map{}
for _, m := range routeMaps {
domainRoutes = append(domainRoutes, m...)
}
this.Data["dnsRoutes"] = domainRoutes
var allDomainRoutes = []maps.Map{}
for _, m := range allDNSRouteMaps {
allDomainRoutes = append(allDomainRoutes, m...)
}
this.Data["allDNSRoutes"] = allDomainRoutes
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
DnsDomainId int64
DnsRoutesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeDNS_LogUpdateNodeDNS, params.NodeId)
var rawRouteCodes = []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &rawRouteCodes)
if err != nil {
this.ErrorPage(err)
return
}
}
// remove duplications
var dnsRouteCodes = []string{}
for _, routeCode := range rawRouteCodes {
if !lists.ContainsString(dnsRouteCodes, routeCode) {
dnsRouteCodes = append(dnsRouteCodes, routeCode)
}
}
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: "",
DnsDomainId: 0,
Routes: dnsRouteCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package scheduleactions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type CopyToClusterAction struct {
actionutils.ParentAction
}
func (this *CopyToClusterAction) RunPost(params struct {
NodeId int64
}) {
defer this.CreateLogInfo(codes.NodeAction_LogCopyNodeActionsToCluster, params.NodeId)
_, err := this.RPC().NodeRPC().CopyNodeActionsToNodeCluster(this.AdminContext(), &pb.CopyNodeActionsToNodeClusterRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package scheduleactions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type CopyToGroupAction struct {
actionutils.ParentAction
}
func (this *CopyToGroupAction) RunPost(params struct {
NodeId int64
}) {
defer this.CreateLogInfo(codes.NodeAction_LogCopyNodeActionsToGroup, params.NodeId)
_, err := this.RPC().NodeRPC().CopyNodeActionsToNodeGroup(this.AdminContext(), &pb.CopyNodeActionsToNodeGroupRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,135 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package scheduleactions
import (
"encoding/json"
"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/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"regexp"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
// 选项
this.Data["params"] = nodeconfigs.FindAllNodeActionParamDefinitions()
this.Data["operators"] = nodeconfigs.FindAllNodeActionOperatorDefinitions()
this.Data["actions"] = nodeconfigs.FindAllNodeActionDefinitions()
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
NodeId int64
CondsJSON []byte
ActionJSON []byte
DurationMinutes int16
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var createdActionId int64
defer func() {
this.CreateLogInfo(codes.NodeAction_LogCreateNodeAction, createdActionId)
}()
// 校验条件
if len(params.CondsJSON) == 0 {
this.Fail("请设置条件")
return
}
var condsConfig = &nodeconfigs.NodeActionCondsConfig{}
err := json.Unmarshal(params.CondsJSON, condsConfig)
if err != nil {
this.Fail("条件解析错误:" + err.Error())
return
}
// 校验动作
if len(params.ActionJSON) == 0 {
this.Fail("请选择要执行的动作")
return
}
var actionConfig = nodeconfigs.NewNodeActionConfig()
err = json.Unmarshal(params.ActionJSON, actionConfig)
if err != nil {
this.Fail("动作解析错误:" + err.Error())
return
}
if len(actionConfig.Code) == 0 {
this.Fail("请选择要执行的动作")
return
}
// 校验动作参数
switch actionConfig.Code {
case nodeconfigs.NodeActionCodeWebHook:
actionParamsJSON, err := json.Marshal(actionConfig.Params)
if err != nil {
this.Fail("解析WebHook参数失败" + err.Error())
return
}
var actionParams = &nodeconfigs.NodeActionCodeWebHookParams{}
err = json.Unmarshal(actionParamsJSON, actionParams)
if err != nil {
this.Fail("解析WebHook参数失败" + err.Error())
return
}
if len(actionParams.URL) == 0 {
this.Fail("请输入WebHook URL参数")
return
}
if !regexp.MustCompile(`^(?i)(http|https)://`).MatchString(actionParams.URL) {
this.Fail("请输入正确的WebHook URL参数")
return
}
}
// 时间限制
if params.DurationMinutes <= 0 {
this.Fail("请输入持续时间")
return
}
durationJSON, err := (&shared.TimeDuration{
Count: types.Int64(params.DurationMinutes),
Unit: shared.TimeDurationUnitMinute,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().NodeActionRPC().CreateNodeAction(this.AdminContext(), &pb.CreateNodeActionRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
CondsJSON: params.CondsJSON,
ActionJSON: params.ActionJSON,
DurationJSON: durationJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
createdActionId = createResp.NodeActionId
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package scheduleactions
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 {
ActionId int64
}) {
defer this.CreateLogInfo(codes.NodeAction_LogDeleteNodeAction, params.ActionId)
_, err := this.RPC().NodeActionRPC().DeleteNodeAction(this.AdminContext(), &pb.DeleteNodeActionRequest{NodeActionId: params.ActionId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,33 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package scheduleactions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateActionsAction struct {
actionutils.ParentAction
}
func (this *UpdateActionsAction) RunPost(params struct {
NodeId int64
ActionIds []int64
}) {
defer this.CreateLogInfo(codes.NodeAction_LogSortNodeActions, params.NodeId)
if len(params.ActionIds) == 0 {
this.Success()
return
}
_, err := this.RPC().NodeActionRPC().UpdateNodeActionOrders(this.AdminContext(), &pb.UpdateNodeActionOrdersRequest{NodeActionIds: params.ActionIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,175 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package scheduleactions
import (
"encoding/json"
"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/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ActionId int64
}) {
actionResp, err := this.RPC().NodeActionRPC().FindNodeAction(this.AdminContext(), &pb.FindNodeActionRequest{NodeActionId: params.ActionId})
if err != nil {
this.ErrorPage(err)
return
}
var action = actionResp.NodeAction
if action == nil {
this.NotFound("nodeAction", params.ActionId)
return
}
// 条件
var condsConfig = &nodeconfigs.NodeActionCondsConfig{}
err = json.Unmarshal(action.CondsJSON, condsConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 动作
var actionConfig = nodeconfigs.NewNodeActionConfig()
err = json.Unmarshal(action.ActionJSON, actionConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 时间
var duration = &shared.TimeDuration{}
if len(action.DurationJSON) > 0 {
err = json.Unmarshal(action.DurationJSON, duration)
if err != nil {
this.ErrorPage(err)
return
}
}
var durationMinutes = duration.Seconds() / 60 // 目前只支持分钟
this.Data["action"] = maps.Map{
"id": action.Id,
"isOn": action.IsOn,
"conds": condsConfig,
"action": actionConfig,
"durationMinutes": durationMinutes,
}
// 选项
this.Data["params"] = nodeconfigs.FindAllNodeActionParamDefinitions()
this.Data["operators"] = nodeconfigs.FindAllNodeActionOperatorDefinitions()
this.Data["actions"] = nodeconfigs.FindAllNodeActionDefinitions()
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
ActionId int64
CondsJSON []byte
ActionJSON []byte
DurationMinutes int16
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeAction_LogUpdateNodeAction, params.ActionId)
// 校验条件
if len(params.CondsJSON) == 0 {
this.Fail("请设置条件")
return
}
var condsConfig = &nodeconfigs.NodeActionCondsConfig{}
err := json.Unmarshal(params.CondsJSON, condsConfig)
if err != nil {
this.Fail("条件解析错误:" + err.Error())
return
}
// 校验动作
if len(params.ActionJSON) == 0 {
this.Fail("请选择要执行的动作")
return
}
var actionConfig = nodeconfigs.NewNodeActionConfig()
err = json.Unmarshal(params.ActionJSON, actionConfig)
if err != nil {
this.Fail("动作解析错误:" + err.Error())
return
}
if len(actionConfig.Code) == 0 {
this.Fail("请选择要执行的动作")
return
}
// 校验动作参数
switch actionConfig.Code {
case nodeconfigs.NodeActionCodeWebHook:
actionParamsJSON, err := json.Marshal(actionConfig.Params)
if err != nil {
this.Fail("解析WebHook参数失败" + err.Error())
return
}
var actionParams = &nodeconfigs.NodeActionCodeWebHookParams{}
err = json.Unmarshal(actionParamsJSON, actionParams)
if err != nil {
this.Fail("解析WebHook参数失败" + err.Error())
return
}
if len(actionParams.URL) == 0 {
this.Fail("请输入WebHook URL参数")
return
}
if !regexp.MustCompile(`^(?i)(http|https)://`).MatchString(actionParams.URL) {
this.Fail("请输入正确的WebHook URL参数")
return
}
}
// 时间限制
if params.DurationMinutes <= 0 {
this.Fail("请输入持续时间")
return
}
durationJSON, err := (&shared.TimeDuration{
Count: types.Int64(params.DurationMinutes),
Unit: shared.TimeDurationUnitMinute,
}).AsJSON()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeActionRPC().UpdateNodeAction(this.AdminContext(), &pb.UpdateNodeActionRequest{
NodeActionId: params.ActionId,
CondsJSON: params.CondsJSON,
ActionJSON: params.ActionJSON,
DurationJSON: durationJSON,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,237 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package nodeschedule
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"regexp"
"strings"
"time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("schedule")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
// 检查权限
if !teaconst.IsPlus {
return
}
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
// 基础设置
scheduleInfoResp, err := this.RPC().NodeRPC().FindNodeScheduleInfo(this.AdminContext(), &pb.FindNodeScheduleInfoRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var scheduleInfo = scheduleInfoResp.ScheduleInfo
if scheduleInfo == nil {
this.NotFound("node", params.NodeId)
return
}
if len(scheduleInfo.OfflineDay) == 8 {
scheduleInfo.OfflineDay = scheduleInfo.OfflineDay[:4] + "-" + scheduleInfo.OfflineDay[4:6] + "-" + scheduleInfo.OfflineDay[6:]
}
if scheduleInfo.BackupIPs == nil {
scheduleInfo.BackupIPs = []string{}
}
// 当前节点所属集群
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.NotFound("node", params.NodeId)
return
}
var clusterName = ""
if node.NodeCluster != nil {
clusterName = node.NodeCluster.Name
}
// 当前节点所属分组
var groupName = ""
if node.NodeGroup != nil {
groupName = node.NodeGroup.Name
}
// 动作状态
var actionStatus = &nodeconfigs.NodeActionStatus{}
if len(scheduleInfo.ActionStatusJSON) > 0 {
err = json.Unmarshal(scheduleInfo.ActionStatusJSON, actionStatus)
if err != nil {
this.ErrorPage(err)
return
}
}
var actionStatusExpiresTime = ""
if actionStatus.ExpiresAt > 0 {
actionStatusExpiresTime = timeutil.FormatTime("Y-m-d H:i:s", actionStatus.ExpiresAt)
}
this.Data["schedule"] = maps.Map{
"id": params.NodeId,
"offlineDay": scheduleInfo.OfflineDay,
"isOffline": len(scheduleInfo.OfflineDay) > 0 && scheduleInfo.OfflineDay < timeutil.Format("Y-m-d" /** 前面的OfflineDay已经被转换 **/),
"isNearlyOffline": len(scheduleInfo.OfflineDay) > 0 && scheduleInfo.OfflineDay < timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, 3)),
"isBackupForCluster": scheduleInfo.IsBackupForCluster,
"isBackupForGroup": scheduleInfo.IsBackupForGroup,
"backupIPs": scheduleInfo.BackupIPs,
"nodeClusterName": clusterName,
"nodeGroupName": groupName,
"actionStatus": actionStatus,
"actionStatusExpiresTime": actionStatusExpiresTime,
}
// 所有动作
actionsResp, err := this.RPC().NodeActionRPC().FindAllNodeActions(this.AdminContext(), &pb.FindAllNodeActionsRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
var actionMaps = []maps.Map{}
for _, action := range actionsResp.NodeActions {
// conds
var condsConfig = nodeconfigs.NewNodeActionCondsConfig()
err = json.Unmarshal(action.CondsJSON, condsConfig)
if err != nil {
this.ErrorPage(err)
return
}
// action
var actionConfig = nodeconfigs.NewNodeActionConfig()
err = json.Unmarshal(action.ActionJSON, actionConfig)
if err != nil {
this.ErrorPage(err)
return
}
var actionDef = nodeconfigs.FindNodeActionDefinition(actionConfig.Code)
if actionDef == nil {
continue
}
// duration
var duration = &shared.TimeDuration{}
if len(action.DurationJSON) > 0 {
err = json.Unmarshal(action.DurationJSON, duration)
if err != nil {
this.ErrorPage(err)
return
}
}
var durationDescription = duration.Description()
// special durations
for _, cond := range condsConfig.Conds {
switch cond.Param {
case nodeconfigs.NodeActionParamDailyTrafficOut:
durationDescription = "当天"
case nodeconfigs.NodeActionParamMonthlyTrafficOut:
durationDescription = "当月"
}
}
actionMaps = append(actionMaps, maps.Map{
"id": action.Id,
"conds": condsConfig,
"action": actionConfig,
"actionName": actionDef.Name,
"durationDescription": durationDescription,
"isOn": action.IsOn,
})
}
this.Data["actions"] = actionMaps
// 选项
this.Data["params"] = nodeconfigs.FindAllNodeActionParamDefinitions()
this.Data["operators"] = nodeconfigs.FindAllNodeActionOperatorDefinitions()
var actionMap = map[string]*shared.Definition{} // code => name
for _, actionDef := range nodeconfigs.FindAllNodeActionDefinitions() {
actionMap[actionDef.Code] = actionDef
}
this.Data["actionMap"] = actionMap
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
OfflineDay string
IsBackupForCluster bool
IsBackupForGroup bool
BackupIPs []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeSchedule_LogUpdateNodeScheduleBasic)
// 校验参数
if len(params.OfflineDay) > 0 {
if !regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`).MatchString(params.OfflineDay) {
this.Fail("租期结束日期格式错误")
return
}
params.OfflineDay = strings.ReplaceAll(params.OfflineDay, "-", "")
}
for _, backupIP := range params.BackupIPs {
if net.ParseIP(backupIP) == nil {
this.Fail("备用IP '" + backupIP + "' IP错误")
return
}
}
_, err := this.RPC().NodeRPC().UpdateNodeScheduleInfo(this.AdminContext(), &pb.UpdateNodeScheduleInfoRequest{
NodeId: params.NodeId,
OfflineDay: params.OfflineDay,
IsBackupForCluster: params.IsBackupForCluster,
IsBackupForGroup: params.IsBackupForGroup,
BackupIPs: params.BackupIPs,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

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

View File

@@ -0,0 +1,165 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ssh
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"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"
"net"
"regexp"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("ssh")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hostIsAutoFilled"] = false
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
var loginParams = maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
var grantMap = maps.Map{}
var 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,
}
}
if loginMap == nil {
addressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{NodeId: node.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(addressesResp.NodeIPAddresses) > 0 {
this.Data["hostIsAutoFilled"] = true
loginMap = maps.Map{
"id": 0,
"name": "",
"type": "ssh",
"params": maps.Map{
"host": addressesResp.NodeIPAddresses[0].Ip,
"port": 22,
"grantId": 0,
},
"grant": nil,
}
}
} else {
var loginParams = loginMap.GetMap("params")
if len(loginParams.GetString("host")) == 0 {
addressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{NodeId: node.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(addressesResp.NodeIPAddresses) > 0 {
this.Data["hostIsAutoFilled"] = true
loginParams["host"] = addressesResp.NodeIPAddresses[0].Ip
}
}
if loginParams.GetInt("port") == 0 {
loginParams["port"] = 22
}
loginMap["params"] = loginParams
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["login"] = loginMap
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
LoginId int64
GrantId int64
SshHost string
SshPort int
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeSSH_LogUpdateNodeSSH, params.NodeId)
// 检查IP地址
if regexp.MustCompile(`^\d+\.\d+\.\d+\.\d+$`).MatchString(params.SshHost) && net.ParseIP(params.SshHost) == nil {
this.Fail("SSH主机地址 '" + params.SshHost + "' IP格式错误")
}
// TODO 检查登录授权
var 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().NodeRPC().UpdateNodeLogin(this.AdminContext(), &pb.UpdateNodeLoginRequest{
NodeId: params.NodeId,
NodeLogin: loginInfo,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,31 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ssh
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type TestAction struct {
actionutils.ParentAction
}
func (this *TestAction) RunPost(params struct {
GrantId int64
Host string
Port int32
}) {
resp, err := this.RPC().NodeGrantRPC().TestNodeGrant(this.AdminContext(), &pb.TestNodeGrantRequest{
NodeGrantId: params.GrantId,
Host: params.Host,
Port: params.Port,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isOk"] = resp.IsOk
this.Data["error"] = resp.Error
this.Success()
}

View File

@@ -0,0 +1,147 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package system
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/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 IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("system")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 获取节点信息
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCPU"] = node.MaxCPU
// DNS
dnsResolverResp, err := this.RPC().NodeRPC().FindNodeDNSResolver(this.AdminContext(), &pb.FindNodeDNSResolverRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
if len(dnsResolverResp.DnsResolverJSON) > 0 {
err = json.Unmarshal(dnsResolverResp.DnsResolverJSON, dnsResolverConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["dnsResolverConfig"] = dnsResolverConfig
// API相关
apiConfigResp, err := this.RPC().NodeRPC().FindNodeAPIConfig(this.AdminContext(), &pb.FindNodeAPIConfigRequest{NodeId: 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 *IndexAction) RunPost(params struct {
NodeId int64
MaxCPU int32
DnsResolverJSON []byte
ApiNodeAddrsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NodeSystem_LogUpdateNodeSystemSettings, params.NodeId)
if params.MaxCPU < 0 {
this.Fail("CPU线程数不能小于0")
}
// 系统设置
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
NodeId: params.NodeId,
MaxCPU: params.MaxCPU,
})
if err != nil {
this.ErrorPage(err)
return
}
// DNS解析设置
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = dnsResolverConfig.Init()
if err != nil {
this.Fail("校验DNS解析配置失败" + err.Error())
}
_, err = this.RPC().NodeRPC().UpdateNodeDNSResolver(this.AdminContext(), &pb.UpdateNodeDNSResolverRequest{
NodeId: params.NodeId,
DnsResolverJSON: params.DnsResolverJSON,
})
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().NodeRPC().UpdateNodeAPIConfig(this.AdminContext(), &pb.UpdateNodeAPIConfigRequest{
NodeId: params.NodeId,
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,64 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package thresholds2
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("threshold")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
// 列出所有阈值
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
Role: "node",
NodeClusterId: params.ClusterId,
NodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
thresholdMaps := []maps.Map{}
for _, threshold := range thresholdsResp.NodeThresholds {
thresholdMaps = append(thresholdMaps, maps.Map{
"id": threshold.Id,
"itemName": nodeconfigs.FindNodeValueItemName(threshold.Item),
"paramName": nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param),
"paramIsPercent": nodeconfigs.CheckNodeValueItemParamIsPercent(threshold.Item, threshold.Param),
"operatorName": nodeconfigs.FindNodeValueOperatorName(threshold.Operator),
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
"sumMethodName": nodeconfigs.FindNodeValueSumMethodName(threshold.SumMethod),
"duration": threshold.Duration,
"durationUnitName": nodeconfigs.FindNodeValueDurationUnitName(threshold.DurationUnit),
"isOn": threshold.IsOn,
})
}
this.Data["thresholds"] = thresholdMaps
this.Show()
}

View File

@@ -0,0 +1,30 @@
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().NodeRPC().StartNode(this.AdminContext(), &pb.StartNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.Node_LogStartNodeRemotely, params.NodeId)
if resp.IsOk {
this.Success()
}
this.Fail("启动失败:" + resp.Error)
}

View File

@@ -0,0 +1,46 @@
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().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
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,30 @@
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().NodeRPC().StopNode(this.AdminContext(), &pb.StopNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.Node_LogStopNodeRemotely, params.NodeId)
if resp.IsOk {
this.Success()
}
this.Fail("执行失败:" + resp.Error)
}

View File

@@ -0,0 +1,33 @@
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 SyncDomainAction struct {
actionutils.ParentAction
}
func (this *SyncDomainAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLogInfo(codes.DNS_LogSyncDomain, params.DomainId)
// 执行同步
resp, err := this.RPC().DNSDomainRPC().SyncDNSDomainData(this.AdminContext(), &pb.SyncDNSDomainDataRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if resp.IsOk {
this.Success()
} else {
this.Data["shouldFix"] = resp.ShouldFix
this.Fail(resp.Error)
}
this.Success()
}

View File

@@ -0,0 +1,32 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
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 UninstallAction struct {
actionutils.ParentAction
}
func (this *UninstallAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeRPC().UninstallNode(this.AdminContext(), &pb.UninstallNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.Node_LogUninstallNodeRemotely, params.NodeId)
if resp.IsOk {
this.Success()
}
this.Fail("执行失败:" + resp.Error)
}

View File

@@ -0,0 +1,29 @@
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 UpAction struct {
actionutils.ParentAction
}
func (this *UpAction) RunPost(params struct {
NodeId int64
}) {
defer this.CreateLogInfo(codes.Node_LogUpNode, params.NodeId)
_, err := this.RPC().NodeRPC().UpdateNodeUp(this.AdminContext(), &pb.UpdateNodeUpRequest{
NodeId: params.NodeId,
IsUp: true,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,275 @@
package node
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"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/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("basic")
}
func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
var clusterMap maps.Map = nil
if node.NodeCluster != nil {
clusterMap = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipAddressMaps = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
// 阈值
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
// 专属集群
var clusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
"clusters": clusterMaps,
})
}
// 分组
var groupMap maps.Map = nil
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
// 区域
var regionMap maps.Map = nil
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
}
var nodeMap = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
"enableIPLists": node.EnableIPLists,
}
if node.LnAddrs == nil {
nodeMap["lnAddrs"] = []string{}
} else {
nodeMap["lnAddrs"] = node.LnAddrs
}
if node.NodeCluster != nil {
nodeMap["primaryCluster"] = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
} else {
nodeMap["primaryCluster"] = nil
}
if len(node.SecondaryNodeClusters) > 0 {
var secondaryClusterMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
nodeMap["secondaryClusters"] = secondaryClusterMaps
} else {
nodeMap["secondaryClusters"] = []interface{}{}
}
this.Data["node"] = nodeMap
this.Data["canUpdateLevel"] = this.CanUpdateLevel(2)
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
LoginId int64
NodeId int64
GroupId int64
RegionId int64
Name string
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
PrimaryClusterId int64
SecondaryClusterIds []byte
IsOn bool
Level int32
LnAddrs []string
EnableIPLists bool
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.Node_LogUpdateNode, params.NodeId)
if params.NodeId <= 0 {
this.Fail("要操作的节点不存在")
}
params.Must.
Field("name", params.Name).
Require("请输入节点名称")
// TODO 检查cluster
if params.PrimaryClusterId <= 0 {
this.Fail("请选择节点所在主集群")
}
var secondaryClusterIds = []int64{}
if len(params.SecondaryClusterIds) > 0 {
err := json.Unmarshal(params.SecondaryClusterIds, &secondaryClusterIds)
if err != nil {
this.ErrorPage(err)
return
}
}
// IP地址
var 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地址")
}
// 保存
if !this.CanUpdateLevel(params.Level) {
this.Fail("没有权限修改节点级别:" + types.String(params.Level))
}
// 检查Ln节点地址
var lnAddrs = []string{}
if params.Level > 1 {
for _, lnAddr := range params.LnAddrs {
if len(lnAddr) == 0 {
continue
}
// 处理 host:port
host, _, err := net.SplitHostPort(lnAddr)
if err == nil {
lnAddr = host
}
if net.ParseIP(lnAddr) == nil {
this.Fail("L2级别访问地址 '" + lnAddr + "' 格式错误,请纠正后再提交")
}
lnAddrs = append(lnAddrs, lnAddr)
}
}
_, err := this.RPC().NodeRPC().UpdateNode(this.AdminContext(), &pb.UpdateNodeRequest{
NodeId: params.NodeId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Name: params.Name,
NodeClusterId: params.PrimaryClusterId,
SecondaryNodeClusterIds: secondaryClusterIds,
IsOn: params.IsOn,
Level: params.Level,
LnAddrs: lnAddrs,
EnableIPLists: params.EnableIPLists,
})
if err != nil {
this.ErrorPage(err)
return
}
// 禁用老的IP地址
_, err = this.RPC().NodeIPAddressRPC().DisableAllNodeIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
// 添加新的IP地址
err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, nodeconfigs.NodeRoleNode, params.IPAddressesJSON)
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,117 @@
package node
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/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
)
type UpdateDNSPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateDNSPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateDNSPopupAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
dnsInfo := dnsInfoResp.Node
if dnsInfo == nil {
this.NotFound("node", params.NodeId)
return
}
this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName
// 读取所有线路
allRouteMaps := []maps.Map{}
if dnsInfo.DnsDomainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
if len(routesResp.Routes) > 0 {
for _, route := range routesResp.Routes {
allRouteMaps = append(allRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"domainName": dnsInfo.DnsDomainName,
"domainId": dnsInfo.DnsDomainId,
})
}
// 筛选
var routes = domainutils.FilterRoutes(dnsInfo.Routes, routesResp.Routes)
dnsInfo.Routes = routes
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
}
}
this.Data["allRoutes"] = allRouteMaps
this.Show()
}
func (this *UpdateDNSPopupAction) RunPost(params struct {
NodeId int64
IpAddr string
DomainId int64
DnsRoutesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 操作日志
defer this.CreateLogInfo(codes.NodeDNS_LogUpdateNodeDNS, params.NodeId)
routes := []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
if err != nil {
this.ErrorPage(err)
return
}
}
params.Must.
Field("ipAddr", params.IpAddr).
Require("请输入IP地址")
if net.ParseIP(params.IpAddr) == nil {
this.FailField("ipAddr", "请输入正确的IP地址")
}
// 执行修改
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: params.IpAddr,
DnsDomainId: params.DomainId,
Routes: routes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,30 @@
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.Node_LogUpdateNodeInstallationStatus, params.NodeId)
_, err := this.RPC().NodeRPC().UpdateNodeIsInstalled(this.AdminContext(), &pb.UpdateNodeIsInstalledRequest{
NodeId: params.NodeId,
IsInstalled: params.IsInstalled,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,35 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
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 UpdateIsOnAction struct {
actionutils.ParentAction
}
func (this *UpdateIsOnAction) RunPost(params struct {
NodeId int64
IsOn bool
}) {
if params.IsOn {
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOn, params.NodeId)
} else {
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOff, params.NodeId)
}
_, err := this.RPC().NodeRPC().UpdateNodeIsOn(this.AdminContext(), &pb.UpdateNodeIsOnRequest{
NodeId: params.NodeId,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
package node
func (this *UpdateAction) CanUpdateLevel(level int32) bool {
return level <= 1
}

View File

@@ -0,0 +1,13 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package node
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
)
func (this *UpdateAction) CanUpdateLevel(level int32) bool {
return (teaconst.IsPlus && plus.AllowComponent(plus.ComponentCodeL2Node)) || level <= 1
}