Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
775b46cf8f | ||
|
|
c99eb54811 | ||
|
|
7917c68b6f | ||
|
|
75ce1490cc | ||
|
|
6f1cd668b6 | ||
|
|
5b7cb59997 | ||
|
|
67242ec80e | ||
|
|
2af5e059eb | ||
|
|
1d992b0e97 | ||
|
|
0c650987d6 | ||
|
|
e8af3960f8 | ||
|
|
1d95087abb | ||
|
|
c350fceef3 | ||
|
|
e6970abcb8 | ||
|
|
9cf5f56e59 | ||
|
|
c2ebf81e6c | ||
|
|
19a2af8bfa | ||
|
|
df5c1262d2 | ||
|
|
26340110c1 | ||
|
|
452f03bb78 | ||
|
|
609794b2d8 | ||
|
|
554d0daf6d | ||
|
|
e4015282a5 | ||
|
|
a7453a4baa | ||
|
|
913a98b4de | ||
|
|
89f391ca22 | ||
|
|
e585149a7e |
@@ -6,3 +6,4 @@
|
||||
./build.sh linux mips64
|
||||
./build.sh linux mips64le
|
||||
./build.sh darwin amd64
|
||||
./build.sh darwin arm64
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
||||
@@ -13,7 +14,14 @@ func main() {
|
||||
app := apps.NewAppCmd().
|
||||
Version(teaconst.Version).
|
||||
Product(teaconst.ProductName).
|
||||
Usage(teaconst.ProcessName + " [-v|start|stop|restart|service|daemon]")
|
||||
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset]").
|
||||
Option("-h", "show this help").
|
||||
Option("-v", "show version").
|
||||
Option("start", "start the service").
|
||||
Option("stop", "stop the service").
|
||||
Option("service", "register service into systemd").
|
||||
Option("daemon", "start the service with daemon").
|
||||
Option("reset", "reset configs")
|
||||
|
||||
app.On("daemon", func() {
|
||||
nodes.NewAdminNode().Daemon()
|
||||
@@ -26,6 +34,14 @@ func main() {
|
||||
}
|
||||
fmt.Println("done")
|
||||
})
|
||||
app.On("reset", func() {
|
||||
err := configs.ResetAPIConfig()
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]reset failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println("done")
|
||||
})
|
||||
app.Run(func() {
|
||||
adminNode := nodes.NewAdminNode()
|
||||
adminNode.Run()
|
||||
|
||||
@@ -59,6 +59,49 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// ResetAPIConfig 重置配置
|
||||
func ResetAPIConfig() error {
|
||||
filename := "api.yaml"
|
||||
|
||||
{
|
||||
configFile := Tea.ConfigFile(filename)
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置 ~/.edge-admin/api.yaml
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
configFile := homeDir + "/." + teaconst.ProcessName + "/" + filename
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置 /etc/edge-admin/api.yaml
|
||||
{
|
||||
configFile := "/etc/" + teaconst.ProcessName + "/" + filename
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteFile 写入API配置
|
||||
func (this *APIConfig) WriteFile(path string) error {
|
||||
data, err := yaml.Marshal(this)
|
||||
@@ -84,7 +127,7 @@ func (this *APIConfig) WriteFile(path string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 写入 /etc/.edge-admin
|
||||
// 写入 /etc/edge-admin
|
||||
{
|
||||
dir := "/etc/" + teaconst.ProcessName
|
||||
stat, err := os.Stat(dir)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.0.13"
|
||||
Version = "0.1.0"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -91,6 +91,14 @@ func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
|
||||
return pb.NewNodeIPAddressServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) NodeValueRPC() pb.NodeValueServiceClient {
|
||||
return pb.NewNodeValueServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) NodeThresholdRPC() pb.NodeThresholdServiceClient {
|
||||
return pb.NewNodeThresholdServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) ServerRPC() pb.ServerServiceClient {
|
||||
return pb.NewServerServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -212,6 +220,10 @@ func (this *RPCClient) HTTPAccessLogRPC() pb.HTTPAccessLogServiceClient {
|
||||
return pb.NewHTTPAccessLogServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) HTTPFastcgiRPC() pb.HTTPFastcgiServiceClient {
|
||||
return pb.NewHTTPFastcgiServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) SSLCertRPC() pb.SSLCertServiceClient {
|
||||
return pb.NewSSLCertServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -336,6 +348,10 @@ func (this *RPCClient) AuthorityNodeRPC() pb.AuthorityNodeServiceClient {
|
||||
return pb.NewAuthorityNodeServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) LatestItemRPC() pb.LatestItemServiceClient {
|
||||
return pb.NewLatestItemServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
// Context 构造Admin上下文
|
||||
func (this *RPCClient) Context(adminId int64) context.Context {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -30,7 +30,7 @@ func (this *Piece) IncreaseInt64(key uint64, delta int64, expiredAt int64) (resu
|
||||
this.locker.Lock()
|
||||
item, ok := this.m[key]
|
||||
if ok {
|
||||
result := types.Int64(item.Value) + delta
|
||||
result = types.Int64(item.Value) + delta
|
||||
item.Value = result
|
||||
item.expiredAt = expiredAt
|
||||
} else {
|
||||
|
||||
@@ -22,8 +22,12 @@ func FormatBytes(bytes int64) string {
|
||||
return fmt.Sprintf("%.2fMB", float64(bytes)/1024/1024)
|
||||
} else if bytes < 1024*1024*1024*1024 {
|
||||
return fmt.Sprintf("%.2fGB", float64(bytes)/1024/1024/1024)
|
||||
} else if bytes < 1024*1024*1024*1024*1024 {
|
||||
return fmt.Sprintf("%.2fTB", float64(bytes)/1024/1024/1024/1024)
|
||||
} else if bytes < 1024*1024*1024*1024*1024*1024 {
|
||||
return fmt.Sprintf("%.2fPB", float64(bytes)/1024/1024/1024/1024/1024)
|
||||
} else {
|
||||
return fmt.Sprintf("%.2fPB", float64(bytes)/1024/1024/1024/1024)
|
||||
return fmt.Sprintf("%.2fEB", float64(bytes)/1024/1024/1024/1024/1024/1024)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +40,11 @@ func FormatBits(bits int64) string {
|
||||
return fmt.Sprintf("%.2fMB", float64(bits)/1000/1000)
|
||||
} else if bits < 1000*1000*1000*1000 {
|
||||
return fmt.Sprintf("%.2fGB", float64(bits)/1000/1000/1000)
|
||||
} else if bits < 1000*1000*1000*1000*1000 {
|
||||
return fmt.Sprintf("%.2fTB", float64(bits)/1000/1000/1000/1000)
|
||||
} else if bits < 1000*1000*1000*1000*1000*1000 {
|
||||
return fmt.Sprintf("%.2fPB", float64(bits)/1000/1000/1000/1000/1000)
|
||||
} else {
|
||||
return fmt.Sprintf("%.2fPB", float64(bits)/1000/1000/1000/1000)
|
||||
return fmt.Sprintf("%.2fEB", float64(bits)/1000/1000/1000/1000/1000/1000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,5 +203,15 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
// 记录最近访问
|
||||
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
|
||||
ItemType: "cluster",
|
||||
ItemId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
|
||||
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
@@ -37,6 +39,14 @@ func init() {
|
||||
Post("/node/start", new(node.StartAction)).
|
||||
Post("/node/stop", new(node.StopAction)).
|
||||
Post("/node/up", new(node.UpAction)).
|
||||
Get("/node/monitor", new(monitor.IndexAction)).
|
||||
Post("/node/monitor/cpu", new(monitor.CpuAction)).
|
||||
Post("/node/monitor/memory", new(monitor.MemoryAction)).
|
||||
Post("/node/monitor/load", new(monitor.LoadAction)).
|
||||
Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)).
|
||||
Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)).
|
||||
Post("/node/monitor/connections", new(monitor.ConnectionsAction)).
|
||||
Get("/node/thresholds", new(thresholds.IndexAction)).
|
||||
|
||||
// 分组相关
|
||||
Get("/groups", new(groups.IndexAction)).
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ConnectionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ConnectionsAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemConnections,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]int64{} // YmdHi => count
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": numberutils.FormatInt64(total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": "0",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CpuAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CpuAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemCPU,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]float32{} // YmdHi => usage
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("usage") * 100
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": fmt.Sprintf("%.2f%%", total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": "0.0%",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "node", "monitor")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LoadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *LoadAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemLoad,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]float32{} // YmdHi => load5m
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("load5m")
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": fmt.Sprintf("5分钟: %.2f", total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": "5分钟: 0.0",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MemoryAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *MemoryAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemMemory,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]float32{} // YmdHi => usage
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("usage") * 100
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": fmt.Sprintf("%.2f%%", total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": "0.0%",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrafficInAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TrafficInAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemTrafficIn,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]int64{} // YmdHi => bytes
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": numberutils.FormatBytes(total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": numberutils.FormatBytes(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrafficOutAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TrafficOutAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
|
||||
Role: "node",
|
||||
NodeId: params.NodeId,
|
||||
Item: nodeconfigs.NodeValueItemTrafficOut,
|
||||
Range: nodeconfigs.NodeValueRangeMinute,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
valuesMap := map[string]int64{} // YmdHi => bytes
|
||||
for _, v := range resp.NodeValues {
|
||||
if len(v.ValueJSON) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
valueMap := maps.Map{}
|
||||
err = json.Unmarshal(v.ValueJSON, &valueMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
|
||||
}
|
||||
|
||||
// 过去一个小时
|
||||
result := []maps.Map{}
|
||||
for i := 60; i >= 1; i-- {
|
||||
timestamp := time.Now().Unix() - int64(i)*60
|
||||
minute := timeutil.FormatTime("YmdHi", timestamp)
|
||||
total, ok := valuesMap[minute]
|
||||
if ok {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": total,
|
||||
"text": numberutils.FormatBytes(total),
|
||||
})
|
||||
} else {
|
||||
result = append(result, maps.Map{
|
||||
"label": timeutil.FormatTime("H:i", timestamp),
|
||||
"value": 0,
|
||||
"text": numberutils.FormatBytes(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["values"] = result
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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"
|
||||
@@ -114,17 +115,17 @@ func (this *NodeAction) RunGet(params struct {
|
||||
grantMap := maps.Map{}
|
||||
grantId := loginParams.GetInt64("grantId")
|
||||
if grantId > 0 {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: grantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.Grant != nil {
|
||||
if grantResp.NodeGrant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grantResp.Grant.Id,
|
||||
"name": grantResp.Grant.Name,
|
||||
"method": grantResp.Grant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.Grant.Method),
|
||||
"id": grantResp.NodeGrant.Id,
|
||||
"name": grantResp.NodeGrant.Name,
|
||||
"method": grantResp.NodeGrant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,6 +186,33 @@ func (this *NodeAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存硬盘 & 内存容量
|
||||
var maxCacheDiskCapacity maps.Map = nil
|
||||
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 = nil
|
||||
if node.MaxCacheMemoryCapacity != nil {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": node.MaxCacheMemoryCapacity.Count,
|
||||
"unit": node.MaxCacheMemoryCapacity.Unit,
|
||||
}
|
||||
} else {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": 0,
|
||||
"unit": "gb",
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
@@ -199,24 +227,29 @@ func (this *NodeAction) RunGet(params struct {
|
||||
"isOn": node.IsOn,
|
||||
|
||||
"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": fmt.Sprintf("%.2f", status.Load1m),
|
||||
"load5m": fmt.Sprintf("%.2f", status.Load5m),
|
||||
"load15m": fmt.Sprintf("%.2f", status.Load15m),
|
||||
"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),
|
||||
},
|
||||
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
|
||||
"maxCacheDiskCapacity": maxCacheDiskCapacity,
|
||||
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "node", "threshold")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
// 列出所有阈值
|
||||
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
thresholdMaps := []maps.Map{}
|
||||
for _, threshold := range thresholdsResp.NodeThresholds {
|
||||
thresholdMaps = append(thresholdMaps, maps.Map{
|
||||
"id": threshold.Id,
|
||||
"itemName": nodeconfigs.FindNodeValueItemName(threshold.Item),
|
||||
"paramName": nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param),
|
||||
"operatorName": nodeconfigs.FindNodeValueOperatorName(threshold.Operator),
|
||||
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
|
||||
"sumMethodName": nodeconfigs.FindNodeValueSumMethodName(threshold.SumMethod),
|
||||
"duration": threshold.Duration,
|
||||
"durationUnitName": nodeconfigs.FindNodeValueDurationUnitName(threshold.DurationUnit),
|
||||
"isOn": threshold.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["thresholds"] = thresholdMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"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/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
@@ -114,17 +115,17 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
grantMap := maps.Map{}
|
||||
grantId := loginParams.GetInt64("grantId")
|
||||
if grantId > 0 {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: grantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.Grant != nil {
|
||||
if grantResp.NodeGrant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grantResp.Grant.Id,
|
||||
"name": grantResp.Grant.Name,
|
||||
"method": grantResp.Grant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.Grant.Method),
|
||||
"id": grantResp.NodeGrant.Id,
|
||||
"name": grantResp.NodeGrant.Name,
|
||||
"method": grantResp.NodeGrant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,16 +157,45 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存硬盘 & 内存容量
|
||||
var maxCacheDiskCapacity maps.Map = nil
|
||||
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 = nil
|
||||
if node.MaxCacheMemoryCapacity != nil {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": node.MaxCacheMemoryCapacity.Count,
|
||||
"unit": node.MaxCacheMemoryCapacity.Unit,
|
||||
}
|
||||
} else {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": 0,
|
||||
"unit": "gb",
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddresses": ipAddressMaps,
|
||||
"cluster": clusterMap,
|
||||
"login": loginMap,
|
||||
"maxCPU": node.MaxCPU,
|
||||
"isOn": node.IsOn,
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddresses": ipAddressMaps,
|
||||
"cluster": clusterMap,
|
||||
"login": loginMap,
|
||||
"maxCPU": node.MaxCPU,
|
||||
"isOn": node.IsOn,
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
"maxCacheDiskCapacity": maxCacheDiskCapacity,
|
||||
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
|
||||
}
|
||||
|
||||
// 所有集群
|
||||
@@ -190,18 +220,20 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
LoginId int64
|
||||
NodeId int64
|
||||
GroupId int64
|
||||
RegionId int64
|
||||
Name string
|
||||
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
|
||||
ClusterId int64
|
||||
GrantId int64
|
||||
SshHost string
|
||||
SshPort int
|
||||
MaxCPU int32
|
||||
IsOn bool
|
||||
LoginId int64
|
||||
NodeId int64
|
||||
GroupId int64
|
||||
RegionId int64
|
||||
Name string
|
||||
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
|
||||
ClusterId int64
|
||||
GrantId int64
|
||||
SshHost string
|
||||
SshPort int
|
||||
MaxCPU int32
|
||||
IsOn bool
|
||||
MaxCacheDiskCapacityJSON []byte
|
||||
MaxCacheMemoryCapacityJSON []byte
|
||||
|
||||
DnsDomainId int64
|
||||
DnsRoutesJSON []byte
|
||||
@@ -258,18 +290,49 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
}.AsJSON(),
|
||||
}
|
||||
|
||||
// 缓存硬盘 & 内存容量
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// 保存
|
||||
_, err := this.RPC().NodeRPC().UpdateNode(this.AdminContext(), &pb.UpdateNodeRequest{
|
||||
NodeId: params.NodeId,
|
||||
GroupId: params.GroupId,
|
||||
RegionId: params.RegionId,
|
||||
Name: params.Name,
|
||||
NodeClusterId: params.ClusterId,
|
||||
Login: loginInfo,
|
||||
MaxCPU: params.MaxCPU,
|
||||
IsOn: params.IsOn,
|
||||
DnsDomainId: params.DnsDomainId,
|
||||
DnsRoutes: dnsRouteCodes,
|
||||
NodeId: params.NodeId,
|
||||
GroupId: params.GroupId,
|
||||
RegionId: params.RegionId,
|
||||
Name: params.Name,
|
||||
NodeClusterId: params.ClusterId,
|
||||
Login: loginInfo,
|
||||
MaxCPU: params.MaxCPU,
|
||||
IsOn: params.IsOn,
|
||||
DnsDomainId: params.DnsDomainId,
|
||||
DnsRoutes: dnsRouteCodes,
|
||||
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
|
||||
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -36,12 +36,12 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var grantMap interface{} = nil
|
||||
|
||||
if cluster.GrantId > 0 {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: cluster.GrantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: cluster.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
grant := grantResp.Grant
|
||||
grant := grantResp.NodeGrant
|
||||
if grant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grant.Id,
|
||||
@@ -62,7 +62,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
// RunPost 保存设置
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
Name string
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/health"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/message"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/services"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/thresholds"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/toa"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/waf"
|
||||
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||
@@ -59,6 +60,13 @@ func init() {
|
||||
GetPost("/updatePopup", new(firewallActions.UpdatePopupAction)).
|
||||
Post("/delete", new(firewallActions.DeleteAction)).
|
||||
|
||||
// 阈值
|
||||
Prefix("/clusters/cluster/settings/thresholds").
|
||||
Get("", new(thresholds.IndexAction)).
|
||||
GetPost("/createPopup", new(thresholds.CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(thresholds.UpdatePopupAction)).
|
||||
Post("/delete", new(thresholds.DeleteAction)).
|
||||
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (this *SelectedReceiversAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
ServerId int64
|
||||
}) {
|
||||
receiversResp, err := this.RPC().MessageReceiverRPC().FindAllMessageReceivers(this.AdminContext(), &pb.FindAllMessageReceiversRequest{
|
||||
receiversResp, err := this.RPC().MessageReceiverRPC().FindAllEnabledMessageReceivers(this.AdminContext(), &pb.FindAllEnabledMessageReceiversRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
ServerId: params.ServerId,
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/actions"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["clusterId"] = params.ClusterId
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["items"] = nodeconfigs.FindAllNodeValueItemDefinitions()
|
||||
this.Data["operators"] = nodeconfigs.FindAllNodeValueOperatorDefinitions()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
Item string
|
||||
Param string
|
||||
SumMethod string
|
||||
Operator string
|
||||
Value string
|
||||
Duration int32
|
||||
DurationUnit string
|
||||
Message string
|
||||
NotifyDuration int32
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
if params.ClusterId <= 0 && params.NodeId >= 0 {
|
||||
this.Fail("集群或者节点至少需要填写其中一个参数")
|
||||
}
|
||||
|
||||
valueJSON, err := json.Marshal(params.Value)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
resp, err := this.RPC().NodeThresholdRPC().CreateNodeThreshold(this.AdminContext(), &pb.CreateNodeThresholdRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
Item: params.Item,
|
||||
Param: params.Param,
|
||||
Operator: params.Operator,
|
||||
ValueJSON: valueJSON,
|
||||
Message: params.Message,
|
||||
Duration: params.Duration,
|
||||
DurationUnit: params.DurationUnit,
|
||||
SumMethod: params.SumMethod,
|
||||
NotifyDuration: params.NotifyDuration,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
defer this.CreateLogInfo("创建节点阈值 %d", resp.NodeThresholdId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// DeleteAction 删除阈值
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
ThresholdId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除阈值 %d", params.ThresholdId)
|
||||
|
||||
_, err := this.RPC().NodeThresholdRPC().DeleteNodeThreshold(this.AdminContext(), &pb.DeleteNodeThresholdRequest{NodeThresholdId: params.ThresholdId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "setting", "setting")
|
||||
this.SecondMenu("threshold")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
}) {
|
||||
// 列出所有阈值
|
||||
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: 0,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
thresholdMaps := []maps.Map{}
|
||||
for _, threshold := range thresholdsResp.NodeThresholds {
|
||||
var nodeMap maps.Map = nil
|
||||
if threshold.Node != nil {
|
||||
nodeMap = maps.Map{
|
||||
"id": threshold.Node.Id,
|
||||
"name": threshold.Node.Name,
|
||||
}
|
||||
}
|
||||
|
||||
thresholdMaps = append(thresholdMaps, maps.Map{
|
||||
"id": threshold.Id,
|
||||
"itemName": nodeconfigs.FindNodeValueItemName(threshold.Item),
|
||||
"paramName": nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param),
|
||||
"operatorName": nodeconfigs.FindNodeValueOperatorName(threshold.Operator),
|
||||
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
|
||||
"sumMethodName": nodeconfigs.FindNodeValueSumMethodName(threshold.SumMethod),
|
||||
"duration": threshold.Duration,
|
||||
"durationUnitName": nodeconfigs.FindNodeValueDurationUnitName(threshold.DurationUnit),
|
||||
"isOn": threshold.IsOn,
|
||||
"node": nodeMap,
|
||||
})
|
||||
}
|
||||
this.Data["thresholds"] = thresholdMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
ThresholdId int64
|
||||
}) {
|
||||
// 通用参数
|
||||
this.Data["items"] = nodeconfigs.FindAllNodeValueItemDefinitions()
|
||||
this.Data["operators"] = nodeconfigs.FindAllNodeValueOperatorDefinitions()
|
||||
|
||||
// 阈值详情
|
||||
thresholdResp, err := this.RPC().NodeThresholdRPC().FindEnabledNodeThreshold(this.AdminContext(), &pb.FindEnabledNodeThresholdRequest{NodeThresholdId: params.ThresholdId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
threshold := thresholdResp.NodeThreshold
|
||||
if threshold == nil {
|
||||
this.NotFound("nodeThreshold", params.ThresholdId)
|
||||
return
|
||||
}
|
||||
|
||||
valueInterface := new(interface{})
|
||||
err = json.Unmarshal(threshold.ValueJSON, valueInterface)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["threshold"] = maps.Map{
|
||||
"id": threshold.Id,
|
||||
"item": threshold.Item,
|
||||
"param": threshold.Param,
|
||||
"message": threshold.Message,
|
||||
"notifyDuration": threshold.NotifyDuration,
|
||||
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
|
||||
"operator": threshold.Operator,
|
||||
"duration": threshold.Duration,
|
||||
"durationUnit": threshold.DurationUnit,
|
||||
"isOn": threshold.IsOn,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
ThresholdId int64
|
||||
Item string
|
||||
Param string
|
||||
SumMethod string
|
||||
Operator string
|
||||
Value string
|
||||
Duration int32
|
||||
DurationUnit string
|
||||
Message string
|
||||
NotifyDuration int32
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改节点阈值 %d", params.ThresholdId)
|
||||
|
||||
valueJSON, err := json.Marshal(params.Value)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().NodeThresholdRPC().UpdateNodeThreshold(this.AdminContext(), &pb.UpdateNodeThresholdRequest{
|
||||
NodeThresholdId: params.ThresholdId,
|
||||
Item: params.Item,
|
||||
Param: params.Param,
|
||||
Operator: params.Operator,
|
||||
ValueJSON: valueJSON,
|
||||
Message: params.Message,
|
||||
NotifyDuration: params.NotifyDuration,
|
||||
Duration: params.Duration,
|
||||
DurationUnit: params.DurationUnit,
|
||||
SumMethod: params.SumMethod,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -58,17 +58,17 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
|
||||
|
||||
// 认证信息
|
||||
grantId := loginParams.GetInt64("grantId")
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: 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.Grant != nil {
|
||||
if grantResp.NodeGrant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grantResp.Grant.Id,
|
||||
"name": grantResp.Grant.Name,
|
||||
"method": grantResp.Grant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.Grant.Method),
|
||||
"id": grantResp.NodeGrant.Id,
|
||||
"name": grantResp.NodeGrant.Name,
|
||||
"method": grantResp.NodeGrant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
|
||||
}
|
||||
}
|
||||
this.Data["grant"] = grantMap
|
||||
|
||||
@@ -24,10 +24,10 @@ func NewClusterHelper() *ClusterHelper {
|
||||
return &ClusterHelper{}
|
||||
}
|
||||
|
||||
func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) {
|
||||
func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
|
||||
action := actionPtr.Object()
|
||||
if action.Request.Method != http.MethodGet {
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
action.Data["teaMenu"] = "clusters"
|
||||
@@ -67,6 +67,8 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) {
|
||||
action.Data["leftMenuItems"] = this.createSettingMenu(cluster, secondMenuItem)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 设置菜单
|
||||
@@ -117,10 +119,21 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
|
||||
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
|
||||
})
|
||||
if teaconst.IsPlus {
|
||||
hasThresholds, _ := this.checkThresholds(cluster.Id)
|
||||
items = append(items, maps.Map{
|
||||
"name": "阈值设置",
|
||||
"url": "/clusters/cluster/settings/thresholds?clusterId=" + clusterId,
|
||||
"isActive": selectedItem == "threshold",
|
||||
"isOn": hasThresholds,
|
||||
})
|
||||
}
|
||||
if teaconst.IsPlus {
|
||||
hasMessageReceivers, _ := this.checkMessages(cluster.Id)
|
||||
items = append(items, maps.Map{
|
||||
"name": "消息通知",
|
||||
"url": "/clusters/cluster/settings/message?clusterId=" + clusterId,
|
||||
"isActive": selectedItem == "message",
|
||||
"isOn": hasMessageReceivers,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -176,3 +189,34 @@ func (this *ClusterHelper) checkFirewallActions(clusterId int64) (bool, error) {
|
||||
}
|
||||
return resp.Count > 0, nil
|
||||
}
|
||||
|
||||
// 检查阈值是否已经设置
|
||||
func (this *ClusterHelper) checkThresholds(clusterId int64) (bool, error) {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
resp, err := rpcClient.NodeThresholdRPC().CountAllEnabledNodeThresholds(rpcClient.Context(0), &pb.CountAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: clusterId,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return resp.Count > 0, nil
|
||||
}
|
||||
|
||||
// 检查消息通知是否已经设置
|
||||
func (this *ClusterHelper) checkMessages(clusterId int64) (bool, error) {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
resp, err := rpcClient.MessageReceiverRPC().CountAllEnabledMessageReceivers(rpcClient.Context(0), &pb.CountAllEnabledMessageReceiversRequest{
|
||||
NodeClusterId: clusterId,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return resp.Count > 0, nil
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建SSH认证 %d", createResp.GrantId)
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建SSH认证 %d", createResp.NodeGrantId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -65,14 +65,14 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": createResp.GrantId,
|
||||
"id": createResp.NodeGrantId,
|
||||
"name": params.Name,
|
||||
"method": params.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(params.Method),
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建SSH认证 %d", createResp.GrantId)
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建SSH认证 %d", createResp.NodeGrantId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func (this *DeleteAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 删除
|
||||
_, err = this.RPC().NodeGrantRPC().DisableNodeGrant(this.AdminContext(), &pb.DisableNodeGrantRequest{GrantId: params.GrantId})
|
||||
_, err = this.RPC().NodeGrantRPC().DisableNodeGrant(this.AdminContext(), &pb.DisableNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -19,19 +19,19 @@ func (this *GrantAction) Init() {
|
||||
func (this *GrantAction) RunGet(params struct {
|
||||
GrantId int64
|
||||
}) {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: params.GrantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.Grant == nil {
|
||||
if grantResp.NodeGrant == nil {
|
||||
this.WriteString("can not find the grant")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 处理节点专用的认证
|
||||
|
||||
grant := grantResp.Grant
|
||||
grant := grantResp.NodeGrant
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"name": grant.Name,
|
||||
|
||||
@@ -33,7 +33,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
return
|
||||
}
|
||||
grantMaps := []maps.Map{}
|
||||
for _, grant := range grantsResp.Grants {
|
||||
for _, grant := range grantsResp.NodeGrants {
|
||||
// 集群数
|
||||
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithGrantId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithGrantIdRequest{GrantId: grant.Id})
|
||||
if err != nil {
|
||||
|
||||
@@ -24,6 +24,7 @@ func init() {
|
||||
GetPost("/selectPopup", new(SelectPopupAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
GetPost("/test", new(TestAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func (this *SelectPopupAction) RunGet(params struct{}) {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
grants := grantsResp.Grants
|
||||
grants := grantsResp.NodeGrants
|
||||
grantMaps := []maps.Map{}
|
||||
for _, grant := range grants {
|
||||
grantMaps = append(grantMaps, maps.Map{
|
||||
@@ -52,12 +52,12 @@ func (this *SelectPopupAction) RunPost(params struct {
|
||||
this.Success()
|
||||
}
|
||||
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: params.GrantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
grant := grantResp.Grant
|
||||
grant := grantResp.NodeGrant
|
||||
if grant == nil {
|
||||
this.Fail("找不到要使用的认证")
|
||||
}
|
||||
|
||||
78
internal/web/actions/default/clusters/grants/test.go
Normal file
78
internal/web/actions/default/clusters/grants/test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package grants
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TestAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestAction) Init() {
|
||||
this.Nav("", "", "test")
|
||||
}
|
||||
|
||||
func (this *TestAction) RunGet(params struct {
|
||||
GrantId int64
|
||||
}) {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.NodeGrant == nil {
|
||||
this.WriteString("can not find the grant")
|
||||
return
|
||||
}
|
||||
|
||||
grant := grantResp.NodeGrant
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"name": grant.Name,
|
||||
"method": grant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grant.Method),
|
||||
"username": grant.Username,
|
||||
"password": strings.Repeat("*", len(grant.Password)),
|
||||
"privateKey": grant.PrivateKey,
|
||||
"description": grant.Description,
|
||||
"su": grant.Su,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *TestAction) RunPost(params struct {
|
||||
GrantId int64
|
||||
Host string
|
||||
Port int32
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("host", params.Host).
|
||||
Require("请输入节点主机地址").
|
||||
Field("port", params.Port).
|
||||
Gt(0, "请输入正确的端口号").
|
||||
Lt(65535, "请输入正确的端口号")
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -22,19 +22,19 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["methods"] = grantutils.AllGrantMethods()
|
||||
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: params.GrantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.Grant == nil {
|
||||
if grantResp.NodeGrant == nil {
|
||||
this.WriteString("can not find the grant")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 处理节点专用的认证
|
||||
|
||||
grant := grantResp.Grant
|
||||
grant := grantResp.NodeGrant
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"name": grant.Name,
|
||||
@@ -84,7 +84,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
// TODO 检查grantId是否存在
|
||||
|
||||
_, err := this.RPC().NodeGrantRPC().UpdateNodeGrant(this.AdminContext(), &pb.UpdateNodeGrantRequest{
|
||||
GrantId: params.GrantId,
|
||||
NodeGrantId: params.GrantId,
|
||||
Name: params.Name,
|
||||
Method: params.Method,
|
||||
Username: params.Username,
|
||||
|
||||
@@ -22,18 +22,18 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["methods"] = grantutils.AllGrantMethods()
|
||||
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: params.GrantId})
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: params.GrantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if grantResp.Grant == nil {
|
||||
if grantResp.NodeGrant == nil {
|
||||
this.WriteString("找不到要操作的对象")
|
||||
return
|
||||
}
|
||||
|
||||
grant := grantResp.Grant
|
||||
grant := grantResp.NodeGrant
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"nodeId": grant.NodeId,
|
||||
@@ -82,7 +82,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
|
||||
// 执行修改
|
||||
_, err := this.RPC().NodeGrantRPC().UpdateNodeGrant(this.AdminContext(), &pb.UpdateNodeGrantRequest{
|
||||
GrantId: params.GrantId,
|
||||
NodeGrantId: params.GrantId,
|
||||
Name: params.Name,
|
||||
Method: params.Method,
|
||||
Username: params.Username,
|
||||
|
||||
@@ -24,9 +24,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
SearchType string
|
||||
}) {
|
||||
isSearching := len(params.Keyword) > 0
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["searchType"] = params.SearchType
|
||||
this.Data["isSearching"] = len(params.Keyword) > 0
|
||||
this.Data["isSearching"] = isSearching
|
||||
|
||||
// 搜索节点
|
||||
if params.SearchType == "node" && len(params.Keyword) > 0 {
|
||||
@@ -34,6 +35,23 @@ func (this *IndexAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// 常用的节点
|
||||
latestClusterMaps := []maps.Map{}
|
||||
if !isSearching {
|
||||
clustersResp, err := this.RPC().NodeClusterRPC().FindLatestNodeClusters(this.AdminContext(), &pb.FindLatestNodeClustersRequest{Size: 6})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, cluster := range clustersResp.NodeClusters {
|
||||
latestClusterMaps = append(latestClusterMaps, maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["latestClusters"] = latestClusterMaps
|
||||
|
||||
// 搜索集群
|
||||
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{
|
||||
Keyword: params.Keyword,
|
||||
|
||||
@@ -77,8 +77,10 @@ func (this *PricesAction) formatBits(bits int64) string {
|
||||
sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
|
||||
} else if bits < 1_000_000_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
|
||||
} else {
|
||||
} else if bits < 1_000_000_000_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
|
||||
} else {
|
||||
sizeHuman = fmt.Sprintf("%.2fEBPS", float64(bits)/1000/1000/1000/1000/1000/1000)
|
||||
}
|
||||
return sizeHuman
|
||||
}
|
||||
|
||||
@@ -92,5 +92,27 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["dailyTrafficStats"] = statMaps
|
||||
}
|
||||
|
||||
// 版本升级
|
||||
this.Data["nodeUpgradeInfo"] = maps.Map{
|
||||
"count": resp.NodeUpgradeInfo.CountNodes,
|
||||
"version": resp.NodeUpgradeInfo.NewVersion,
|
||||
}
|
||||
this.Data["monitorNodeUpgradeInfo"] = maps.Map{
|
||||
"count": resp.MonitorNodeUpgradeInfo.CountNodes,
|
||||
"version": resp.MonitorNodeUpgradeInfo.NewVersion,
|
||||
}
|
||||
this.Data["apiNodeUpgradeInfo"] = maps.Map{
|
||||
"count": resp.ApiNodeUpgradeInfo.CountNodes,
|
||||
"version": resp.ApiNodeUpgradeInfo.NewVersion,
|
||||
}
|
||||
this.Data["userNodeUpgradeInfo"] = maps.Map{
|
||||
"count": resp.UserNodeUpgradeInfo.CountNodes,
|
||||
"version": resp.UserNodeUpgradeInfo.NewVersion,
|
||||
}
|
||||
this.Data["authorityNodeUpgradeInfo"] = maps.Map{
|
||||
"count": resp.AuthorityNodeUpgradeInfo.CountNodes,
|
||||
"version": resp.AuthorityNodeUpgradeInfo.NewVersion,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -30,10 +30,29 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["auditingFlag"] = params.AuditingFlag
|
||||
this.Data["checkDNS"] = params.CheckDNS
|
||||
|
||||
isSearching := params.AuditingFlag == 1 || params.ClusterId > 0 || params.GroupId > 0 || len(params.Keyword) > 0
|
||||
|
||||
if params.AuditingFlag > 0 {
|
||||
this.Data["firstMenuItem"] = "auditing"
|
||||
}
|
||||
|
||||
// 常用的服务
|
||||
latestServerMaps := []maps.Map{}
|
||||
if !isSearching {
|
||||
serversResp, err := this.RPC().ServerRPC().FindLatestServers(this.AdminContext(), &pb.FindLatestServersRequest{Size: 6})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, server := range serversResp.Servers {
|
||||
latestServerMaps = append(latestServerMaps, maps.Map{
|
||||
"id": server.Id,
|
||||
"name": server.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["latestServers"] = latestServerMaps
|
||||
|
||||
// 审核中的数量
|
||||
countAuditingResp, err := this.RPC().ServerRPC().CountAllEnabledServersMatch(this.AdminContext(), &pb.CountAllEnabledServersMatchRequest{
|
||||
AuditingFlag: 1,
|
||||
|
||||
@@ -24,6 +24,16 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["serverId"] = params.ServerId
|
||||
this.Data["requestId"] = params.RequestId
|
||||
|
||||
// 记录最近使用
|
||||
_, err := this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
|
||||
ItemType: "server",
|
||||
ItemId: params.ServerId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
|
||||
@@ -60,13 +60,17 @@ func (this *IndexAction) RunPost(params struct {
|
||||
// 日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改Web %d 的缓存设置", params.WebId)
|
||||
|
||||
// TODO 校验配置
|
||||
// 校验配置
|
||||
cacheConfig := &serverconfigs.HTTPCacheConfig{}
|
||||
err := json.Unmarshal(params.CacheJSON, cacheConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = cacheConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("检查配置失败:" + err.Error())
|
||||
}
|
||||
|
||||
// 去除不必要的部分
|
||||
for _, cacheRef := range cacheConfig.CacheRefs {
|
||||
|
||||
@@ -14,9 +14,10 @@ type CondJSComponent struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Component string `json:"component"`
|
||||
IsRequest bool `json:"isRequest"`
|
||||
}
|
||||
|
||||
// 读取所有可用的条件
|
||||
// ReadAllAvailableCondTypes 读取所有可用的条件
|
||||
func ReadAllAvailableCondTypes() []*CondJSComponent {
|
||||
result := []*CondJSComponent{}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"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"
|
||||
"net"
|
||||
)
|
||||
|
||||
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 {
|
||||
Address string
|
||||
ParamsJSON []byte
|
||||
ReadTimeout int64
|
||||
PoolSize int32
|
||||
PathInfoPattern string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
var fastcgiId = int64(0)
|
||||
defer func() {
|
||||
if fastcgiId > 0 {
|
||||
this.CreateLogInfo("创建Fastcgi %d", fastcgiId)
|
||||
} else {
|
||||
this.CreateLogInfo("创建Fastcgi")
|
||||
}
|
||||
}()
|
||||
|
||||
params.Must.
|
||||
Field("address", params.Address).
|
||||
Require("请输入Fastcgi地址")
|
||||
|
||||
_, _, err := net.SplitHostPort(params.Address)
|
||||
if err != nil {
|
||||
this.FailField("address", "请输入正确的Fastcgi地址")
|
||||
}
|
||||
|
||||
readTimeoutJSON, err := json.Marshal(&shared.TimeDuration{
|
||||
Count: params.ReadTimeout,
|
||||
Unit: "second",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().HTTPFastcgiRPC().CreateHTTPFastcgi(this.AdminContext(), &pb.CreateHTTPFastcgiRequest{
|
||||
IsOn: params.IsOn,
|
||||
Address: params.Address,
|
||||
ParamsJSON: params.ParamsJSON,
|
||||
ReadTimeoutJSON: readTimeoutJSON,
|
||||
ConnTimeoutJSON: nil, // TODO 将来支持
|
||||
PoolSize: params.PoolSize,
|
||||
PathInfoPattern: params.PathInfoPattern,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
fastcgiId = createResp.HttpFastcgiId
|
||||
|
||||
configResp, err := this.RPC().HTTPFastcgiRPC().FindEnabledHTTPFastcgiConfig(this.AdminContext(), &pb.FindEnabledHTTPFastcgiConfigRequest{HttpFastcgiId: fastcgiId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
configJSON := configResp.HttpFastcgiJSON
|
||||
config := &serverconfigs.HTTPFastcgiConfig{}
|
||||
err = json.Unmarshal(configJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["fastcgi"] = config
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "setting", "index")
|
||||
this.SecondMenu("fastcgi")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
}) {
|
||||
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["webId"] = webConfig.Id
|
||||
this.Data["fastcgiRef"] = webConfig.FastcgiRef
|
||||
this.Data["fastcgiConfigs"] = webConfig.FastcgiList
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
WebId int64
|
||||
FastcgiRefJSON []byte
|
||||
FastcgiJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改Web %d 的Fastcgi设置", params.WebId)
|
||||
|
||||
// TODO 检查配置
|
||||
|
||||
fastcgiRef := &serverconfigs.HTTPFastcgiRef{}
|
||||
err := json.Unmarshal(params.FastcgiRefJSON, fastcgiRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
fastcgiRefJSON, err := json.Marshal(fastcgiRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFastcgi(this.AdminContext(), &pb.UpdateHTTPWebFastcgiRequest{
|
||||
WebId: params.WebId,
|
||||
FastcgiJSON: fastcgiRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
|
||||
Helper(serverutils.NewServerHelper()).
|
||||
Prefix("/servers/server/settings/fastcgi").
|
||||
GetPost("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"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"
|
||||
"net"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
FastcgiId int64
|
||||
}) {
|
||||
configResp, err := this.RPC().HTTPFastcgiRPC().FindEnabledHTTPFastcgiConfig(this.AdminContext(), &pb.FindEnabledHTTPFastcgiConfigRequest{HttpFastcgiId: params.FastcgiId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
configJSON := configResp.HttpFastcgiJSON
|
||||
config := &serverconfigs.HTTPFastcgiConfig{}
|
||||
err = json.Unmarshal(configJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["fastcgi"] = config
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
FastcgiId int64
|
||||
Address string
|
||||
ParamsJSON []byte
|
||||
ReadTimeout int64
|
||||
PoolSize int32
|
||||
PathInfoPattern string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改Fastcgi %d", params.FastcgiId)
|
||||
|
||||
params.Must.
|
||||
Field("address", params.Address).
|
||||
Require("请输入Fastcgi地址")
|
||||
|
||||
_, _, err := net.SplitHostPort(params.Address)
|
||||
if err != nil {
|
||||
this.FailField("address", "请输入正确的Fastcgi地址")
|
||||
}
|
||||
|
||||
readTimeoutJSON, err := json.Marshal(&shared.TimeDuration{
|
||||
Count: params.ReadTimeout,
|
||||
Unit: "second",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPFastcgiRPC().UpdateHTTPFastcgi(this.AdminContext(), &pb.UpdateHTTPFastcgiRequest{
|
||||
HttpFastcgiId: params.FastcgiId,
|
||||
IsOn: params.IsOn,
|
||||
Address: params.Address,
|
||||
ParamsJSON: params.ParamsJSON,
|
||||
ReadTimeoutJSON: readTimeoutJSON,
|
||||
ConnTimeoutJSON: nil, // TODO 将来支持
|
||||
PoolSize: params.PoolSize,
|
||||
PathInfoPattern: params.PathInfoPattern,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
configResp, err := this.RPC().HTTPFastcgiRPC().FindEnabledHTTPFastcgiConfig(this.AdminContext(), &pb.FindEnabledHTTPFastcgiConfigRequest{HttpFastcgiId: params.FastcgiId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
configJSON := configResp.HttpFastcgiJSON
|
||||
config := &serverconfigs.HTTPFastcgiConfig{}
|
||||
err = json.Unmarshal(configJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["fastcgi"] = config
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -40,12 +40,12 @@ func (this *IndexAction) RunGet(params struct {
|
||||
IsOn: true,
|
||||
}
|
||||
if gzipId > 0 {
|
||||
resp, err := this.RPC().HTTPGzipRPC().FindEnabledHTTPGzipConfig(this.AdminContext(), &pb.FindEnabledGzipConfigRequest{GzipId: gzipId})
|
||||
resp, err := this.RPC().HTTPGzipRPC().FindEnabledHTTPGzipConfig(this.AdminContext(), &pb.FindEnabledGzipConfigRequest{HttpGzipId: gzipId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(resp.GzipJSON, gzipConfig)
|
||||
err = json.Unmarshal(resp.HttpGzipJSON, gzipConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -111,11 +111,11 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
if params.GzipId > 0 {
|
||||
_, err := this.RPC().HTTPGzipRPC().UpdateHTTPGzip(this.AdminContext(), &pb.UpdateHTTPGzipRequest{
|
||||
GzipId: params.GzipId,
|
||||
Level: types.Int32(params.Level),
|
||||
MinLength: minLength,
|
||||
MaxLength: maxLength,
|
||||
CondsJSON: params.CondsJSON,
|
||||
HttpGzipId: params.GzipId,
|
||||
Level: types.Int32(params.Level),
|
||||
MinLength: minLength,
|
||||
MaxLength: maxLength,
|
||||
CondsJSON: params.CondsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -132,7 +132,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
gzipRef.GzipId = resp.GzipId
|
||||
gzipRef.GzipId = resp.HttpGzipId
|
||||
}
|
||||
|
||||
gzipRefJSON, err := json.Marshal(gzipRef)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// 服务基本信息设置
|
||||
// IndexAction 服务基本信息设置
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
@@ -102,10 +102,20 @@ func (this *IndexAction) RunGet(params struct {
|
||||
typeName := serverType.GetString("name")
|
||||
this.Data["typeName"] = typeName
|
||||
|
||||
// 记录最近使用
|
||||
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
|
||||
ItemType: "server",
|
||||
ItemId: params.ServerId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
// 保存
|
||||
// RunPost 保存
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
ServerId int64
|
||||
Name string
|
||||
|
||||
@@ -57,13 +57,17 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改Web %d 的缓存设置", params.WebId)
|
||||
|
||||
// TODO 校验配置
|
||||
// 校验配置
|
||||
cacheConfig := &serverconfigs.HTTPCacheConfig{}
|
||||
err := json.Unmarshal(params.CacheJSON, cacheConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = cacheConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("检查配置失败:" + err.Error())
|
||||
}
|
||||
|
||||
// 去除不必要的部分
|
||||
for _, cacheRef := range cacheConfig.CacheRefs {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
LocationId int64
|
||||
}) {
|
||||
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithLocationId(this.AdminContext(), params.LocationId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["webId"] = webConfig.Id
|
||||
this.Data["fastcgiRef"] = webConfig.FastcgiRef
|
||||
this.Data["fastcgiConfigs"] = webConfig.FastcgiList
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
WebId int64
|
||||
FastcgiRefJSON []byte
|
||||
FastcgiJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改Web %d 的Fastcgi设置", params.WebId)
|
||||
|
||||
// TODO 检查配置
|
||||
|
||||
fastcgiRef := &serverconfigs.HTTPFastcgiRef{}
|
||||
err := json.Unmarshal(params.FastcgiRefJSON, fastcgiRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
fastcgiRefJSON, err := json.Marshal(fastcgiRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFastcgi(this.AdminContext(), &pb.UpdateHTTPWebFastcgiRequest{
|
||||
WebId: params.WebId,
|
||||
FastcgiJSON: fastcgiRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package fastcgi
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/locationutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
|
||||
Helper(locationutils.NewLocationHelper()).
|
||||
Helper(serverutils.NewServerHelper()).
|
||||
Data("tinyMenuItem", "fastcgi").
|
||||
Prefix("/servers/server/settings/locations/fastcgi").
|
||||
GetPost("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -39,12 +39,12 @@ func (this *IndexAction) RunGet(params struct {
|
||||
IsOn: true,
|
||||
}
|
||||
if gzipId > 0 {
|
||||
resp, err := this.RPC().HTTPGzipRPC().FindEnabledHTTPGzipConfig(this.AdminContext(), &pb.FindEnabledGzipConfigRequest{GzipId: gzipId})
|
||||
resp, err := this.RPC().HTTPGzipRPC().FindEnabledHTTPGzipConfig(this.AdminContext(), &pb.FindEnabledGzipConfigRequest{HttpGzipId: gzipId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(resp.GzipJSON, gzipConfig)
|
||||
err = json.Unmarshal(resp.HttpGzipJSON, gzipConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -109,10 +109,10 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
if params.GzipId > 0 {
|
||||
_, err := this.RPC().HTTPGzipRPC().UpdateHTTPGzip(this.AdminContext(), &pb.UpdateHTTPGzipRequest{
|
||||
GzipId: params.GzipId,
|
||||
Level: types.Int32(params.Level),
|
||||
MinLength: minLength,
|
||||
MaxLength: maxLength,
|
||||
HttpGzipId: params.GzipId,
|
||||
Level: types.Int32(params.Level),
|
||||
MinLength: minLength,
|
||||
MaxLength: maxLength,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -128,7 +128,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
gzipId := resp.GzipId
|
||||
gzipId := resp.HttpGzipId
|
||||
gzipRef.GzipId = gzipId
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package locations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
@@ -24,11 +28,36 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["webId"] = webConfig.Id
|
||||
|
||||
locationMaps := []maps.Map{}
|
||||
if webConfig.Locations != nil {
|
||||
this.Data["locations"] = webConfig.Locations
|
||||
} else {
|
||||
this.Data["locations"] = []interface{}{}
|
||||
for _, location := range webConfig.Locations {
|
||||
err := location.ExtractPattern()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
jsonData, err := json.Marshal(location)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
m := maps.Map{}
|
||||
err = json.Unmarshal(jsonData, &m)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
pieces := strings.Split(location.Pattern, " ")
|
||||
if len(pieces) == 2 {
|
||||
m["pattern"] = pieces[1]
|
||||
m["patternTypeName"] = serverconfigs.FindLocationPatternTypeName(location.PatternType())
|
||||
} else {
|
||||
m["pattern"] = location.Pattern
|
||||
m["patternTypeName"] = serverconfigs.FindLocationPatternTypeName(serverconfigs.HTTPLocationPatternTypePrefix)
|
||||
}
|
||||
locationMaps = append(locationMaps, m)
|
||||
}
|
||||
}
|
||||
this.Data["locations"] = locationMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -149,6 +149,12 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
|
||||
"isActive": secondMenuItem == "websocket",
|
||||
"isOn": locationConfig != nil && locationConfig.Web != nil && locationConfig.Web.WebsocketRef != nil && locationConfig.Web.WebsocketRef.IsPrior,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "Fastcgi",
|
||||
"url": "/servers/server/settings/locations/fastcgi?serverId=" + serverIdString + "&locationId=" + locationIdString,
|
||||
"isActive": secondMenuItem == "fastcgi",
|
||||
"isOn": locationConfig != nil && locationConfig.Web != nil && locationConfig.Web.FastcgiRef != nil && locationConfig.Web.FastcgiRef.IsPrior,
|
||||
})
|
||||
|
||||
return menuItems
|
||||
}
|
||||
|
||||
@@ -146,5 +146,15 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["provinceStats"] = provinceStatMaps
|
||||
this.Data["cityStats"] = cityStatMaps
|
||||
|
||||
// 记录最近使用
|
||||
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
|
||||
ItemType: "server",
|
||||
ItemId: params.ServerId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -311,6 +311,12 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
"isActive": secondMenuItem == "websocket",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebsocketRef != nil && serverConfig.Web.WebsocketRef.IsOn,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "Fastcgi",
|
||||
"url": "/servers/server/settings/fastcgi?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "fastcgi",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.FastcgiRef != nil && serverConfig.Web.FastcgiRef.IsOn,
|
||||
})
|
||||
} else if serverConfig.IsTCPFamily() {
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "TCP",
|
||||
|
||||
@@ -107,6 +107,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
||||
}
|
||||
action.Data["teaShowOpenSourceInfo"] = config.ShowOpenSourceInfo
|
||||
action.Data["teaIsSuper"] = false
|
||||
action.Data["teaIsPlus"] = teaconst.IsPlus
|
||||
action.Data["teaDemoEnabled"] = teaconst.IsDemo
|
||||
action.Data["teaShowFinance"] = configloaders.ShowFinance()
|
||||
if !action.Data.Has("teaSubMenu") {
|
||||
@@ -222,6 +223,7 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
|
||||
"module": configloaders.AdminModuleCodeFinance,
|
||||
"name": "财务管理",
|
||||
"icon": "yen sign",
|
||||
"isOn": teaconst.IsPlus,
|
||||
},
|
||||
{
|
||||
"code": "admins",
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/charset"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/dns"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/fastcgi"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/gzip"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/headers"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/http"
|
||||
@@ -57,6 +58,7 @@ import (
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/accessLog"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/cache"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/charset"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/fastcgi"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/gzip"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/headers"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/locations/http"
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
Vue.component("http-access-log-box", {
|
||||
props: ["v-access-log"],
|
||||
data: function () {
|
||||
let accessLog = this.vAccessLog
|
||||
if (accessLog.header != null && accessLog.header.Upgrade != null && accessLog.header.Upgrade.values != null && accessLog.header.Upgrade.values.$contains("websocket")) {
|
||||
if (accessLog.scheme == "http") {
|
||||
accessLog.scheme = "ws"
|
||||
} else if (accessLog.scheme == "https") {
|
||||
accessLog.scheme = "wss"
|
||||
}
|
||||
}
|
||||
return {
|
||||
accessLog: this.vAccessLog
|
||||
accessLog: accessLog
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -40,7 +48,7 @@ Vue.component("http-access-log-box", {
|
||||
}
|
||||
},
|
||||
template: `<div :style="{'color': (accessLog.status >= 400) ? '#dc143c' : ''}" ref="box">
|
||||
<span v-if="accessLog.region != null && accessLog.region.length > 0" class="grey">[{{accessLog.region}}]</span> {{accessLog.remoteAddr}} [{{accessLog.timeLocal}}] <em>"{{accessLog.requestMethod}} {{accessLog.scheme}}://{{accessLog.host}}{{accessLog.requestURI}} <a :href="accessLog.scheme + '://' + accessLog.host + accessLog.requestURI" target="_blank" title="新窗口打开" class="disabled"><i class="external icon tiny"></i> </a> {{accessLog.proto}}" </em> {{accessLog.status}} <span v-if="accessLog.attrs != null && accessLog.attrs['cache_cached'] == '1'">[cached]</span> <span v-if="accessLog.attrs != null && accessLog.attrs['waf.action'] != null && accessLog.attrs['waf.action'].length > 0">[waf {{accessLog.attrs['waf.action']}}]</span> - 耗时:{{formatCost(accessLog.requestTime)}} ms
|
||||
<span v-if="accessLog.region != null && accessLog.region.length > 0" class="grey">[{{accessLog.region}}]</span> {{accessLog.remoteAddr}} [{{accessLog.timeLocal}}] <em>"{{accessLog.requestMethod}} {{accessLog.scheme}}://{{accessLog.host}}{{accessLog.requestURI}} <a :href="accessLog.scheme + '://' + accessLog.host + accessLog.requestURI" target="_blank" title="新窗口打开" class="disabled"><i class="external icon tiny"></i> </a> {{accessLog.proto}}" </em> {{accessLog.status}} <code-label v-if="accessLog.attrs != null && accessLog.attrs['cache.status'] == 'HIT'">cache hit</code-label> <code-label v-if="accessLog.attrs != null && accessLog.attrs['waf.action'] != null && accessLog.attrs['waf.action'].length > 0">waf {{accessLog.attrs['waf.action']}}</code-label> - 耗时:{{formatCost(accessLog.requestTime)}} ms
|
||||
<a href="" @click.prevent="showLog" title="查看详情"><i class="icon expand"></i></a>
|
||||
</div>`
|
||||
})
|
||||
@@ -6,6 +6,7 @@ Vue.component("http-cache-config-box", {
|
||||
cacheConfig = {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
addStatusHeader: true,
|
||||
cacheRefs: []
|
||||
}
|
||||
}
|
||||
@@ -105,13 +106,22 @@ Vue.component("http-cache-config-box", {
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-show="isOn()">
|
||||
<tr>
|
||||
<td>自动添加X-Cache Header</td>
|
||||
<td>
|
||||
<checkbox v-model="cacheConfig.addStatusHeader"></checkbox>
|
||||
<p class="comment">选中后自动在响应Header中增加<code-label>X-Cache: BYPASS|MISS|HIT</code-label>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div v-show="isOn()">
|
||||
<table class="ui table selectable celled" v-show="cacheConfig.cacheRefs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>条件</th>
|
||||
<th>缓存条件</th>
|
||||
<th class="width10">缓存时间</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
|
||||
@@ -13,7 +13,8 @@ Vue.component("http-cache-ref-box", {
|
||||
skipCacheControlValues: ["private", "no-cache", "no-store"],
|
||||
skipSetCookie: true,
|
||||
enableRequestCachePragma: false,
|
||||
conds: null
|
||||
conds: null,
|
||||
allowChunkedEncoding: true
|
||||
}
|
||||
}
|
||||
if (ref.life == null) {
|
||||
@@ -43,7 +44,7 @@ Vue.component("http-cache-ref-box", {
|
||||
},
|
||||
template: `<tbody>
|
||||
<tr>
|
||||
<td class="title">匹配条件 *</td>
|
||||
<td class="title">匹配条件分组 *</td>
|
||||
<td>
|
||||
<http-request-conds-box :v-conds="ref.conds" @change="changeConds"></http-request-conds-box>
|
||||
|
||||
@@ -72,6 +73,13 @@ Vue.component("http-cache-ref-box", {
|
||||
<size-capacity-box :v-value="ref.maxSize" @change="changeMaxSize"></size-capacity-box>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible">
|
||||
<td>支持分片内容</td>
|
||||
<td>
|
||||
<checkbox name="allowChunkedEncoding" value="1" v-model="ref.allowChunkedEncoding"></checkbox>
|
||||
<p class="comment">选中后,Gzip和Chunked内容可以直接缓存,无需检查内容长度。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible">
|
||||
<td>状态码列表</td>
|
||||
<td>
|
||||
|
||||
@@ -102,6 +102,75 @@ Vue.component("http-cond-url-prefix", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL精准匹配
|
||||
Vue.component("http-cond-url-eq", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
let cond = this.vCond
|
||||
if (cond == null) {
|
||||
cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "eq",
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value"/>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL正则匹配
|
||||
Vue.component("http-cond-url-regexp", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
let cond = this.vCond
|
||||
if (cond == null) {
|
||||
cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "regexp",
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value"/>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 排除URL正则匹配
|
||||
Vue.component("http-cond-url-not-regexp", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
let cond = this.vCond
|
||||
if (cond == null) {
|
||||
cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "not regexp",
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value"/>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 根据MimeType
|
||||
Vue.component("http-cond-mime-type", {
|
||||
props: ["v-cond"],
|
||||
|
||||
90
web/public/js/components/server/http-fastcgi-box.js
Normal file
90
web/public/js/components/server/http-fastcgi-box.js
Normal file
@@ -0,0 +1,90 @@
|
||||
Vue.component("http-fastcgi-box", {
|
||||
props: ["v-fastcgi-ref", "v-fastcgi-configs", "v-is-location"],
|
||||
data: function () {
|
||||
let fastcgiRef = this.vFastcgiRef
|
||||
if (fastcgiRef == null) {
|
||||
fastcgiRef = {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
fastcgiIds: []
|
||||
}
|
||||
}
|
||||
let fastcgiConfigs = this.vFastcgiConfigs
|
||||
if (fastcgiConfigs == null) {
|
||||
fastcgiConfigs = []
|
||||
} else {
|
||||
fastcgiRef.fastcgiIds = fastcgiConfigs.map(function (v) {
|
||||
return v.id
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
fastcgiRef: fastcgiRef,
|
||||
fastcgiConfigs: fastcgiConfigs,
|
||||
advancedVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isOn: function () {
|
||||
return (!this.vIsLocation || this.fastcgiRef.isPrior) && this.fastcgiRef.isOn
|
||||
},
|
||||
createFastcgi: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/server/settings/fastcgi/createPopup", {
|
||||
height: "26em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("添加成功", function () {
|
||||
that.fastcgiConfigs.push(resp.data.fastcgi)
|
||||
that.fastcgiRef.fastcgiIds.push(resp.data.fastcgi.id)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
updateFastcgi: function (fastcgiId, index) {
|
||||
let that = this
|
||||
teaweb.popup("/servers/server/settings/fastcgi/updatePopup?fastcgiId=" + fastcgiId, {
|
||||
callback: function (resp) {
|
||||
teaweb.success("修改成功", function () {
|
||||
Vue.set(that.fastcgiConfigs, index, resp.data.fastcgi)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
removeFastcgi: function (index) {
|
||||
this.fastcgiRef.fastcgiIds.$remove(index)
|
||||
this.fastcgiConfigs.$remove(index)
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="fastcgiRefJSON" :value="JSON.stringify(fastcgiRef)"/>
|
||||
<table class="ui table definition selectable">
|
||||
<prior-checkbox :v-config="fastcgiRef" v-if="vIsLocation"></prior-checkbox>
|
||||
<tbody v-show="(!this.vIsLocation || this.fastcgiRef.isPrior)">
|
||||
<tr>
|
||||
<td class="title">是否启用配置</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" v-model="fastcgiRef.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody v-if="isOn()">
|
||||
<tr>
|
||||
<td>Fastcgi服务</td>
|
||||
<td>
|
||||
<div v-show="fastcgiConfigs.length > 0" style="margin-bottom: 0.5em">
|
||||
<div class="ui label basic small" :class="{disabled: !fastcgi.isOn}" v-for="(fastcgi, index) in fastcgiConfigs">
|
||||
{{fastcgi.address}} <a href="" title="修改" @click.prevent="updateFastcgi(fastcgi.id, index)"><i class="ui icon pencil small"></i></a> <a href="" title="删除" @click.prevent="removeFastcgi(index)"><i class="ui icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divided"></div>
|
||||
</div>
|
||||
<button type="button" class="ui button tiny" @click.prevent="createFastcgi()">+</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
})
|
||||
@@ -138,7 +138,7 @@ Vue.component("http-header-policy-box", {
|
||||
<div v-if="(!vIsLocation || requestHeaderRef.isPrior) && type == 'request'">
|
||||
<h3>设置请求Header <a href="" @click.prevent="addSettingHeader(vRequestHeaderPolicy.id)">[添加新Header]</a></h3>
|
||||
<p class="comment" v-if="requestSettingHeaders.length == 0">暂时还没有Header。</p>
|
||||
<table class="ui table selectable" v-if="requestSettingHeaders.length > 0">
|
||||
<table class="ui table selectable celled" v-if="requestSettingHeaders.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
@@ -179,8 +179,9 @@ Vue.component("http-header-policy-box", {
|
||||
|
||||
<div v-if="type == 'response'">
|
||||
<h3>设置响应Header <a href="" @click.prevent="addSettingHeader(vResponseHeaderPolicy.id)">[添加新Header]</a></h3>
|
||||
<p class="comment" style="margin-top: 0; padding-top: 0">将会覆盖已有的同名Header。</p>
|
||||
<p class="comment" v-if="responseSettingHeaders.length == 0">暂时还没有Header。</p>
|
||||
<table class="ui table selectable" v-if="responseSettingHeaders.length > 0">
|
||||
<table class="ui table selectable celled" v-if="responseSettingHeaders.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
|
||||
@@ -63,7 +63,8 @@ Vue.component("http-request-conds-box", {
|
||||
<div v-if="conds.groups.length > 0">
|
||||
<table class="ui table">
|
||||
<tr v-for="(group, groupIndex) in conds.groups">
|
||||
<td style="background: white">
|
||||
<td class="title" :style="{'border-bottom':(groupIndex < conds.groups.length-1) ? '1px solid rgba(34,36,38,.15)':''}">分组{{groupIndex+1}}</td>
|
||||
<td style="background: white;" :style="{'border-bottom':(groupIndex < conds.groups.length-1) ? '1px solid rgba(34,36,38,.15)':''}">
|
||||
<var v-for="(cond, index) in group.conds" style="font-style: normal;display: inline-block; margin-bottom:0.5em">
|
||||
<span class="ui label tiny">
|
||||
<var v-if="cond.type.length == 0" style="font-style: normal">{{cond.param}} <var>{{cond.operator}}</var></var>
|
||||
@@ -74,7 +75,7 @@ Vue.component("http-request-conds-box", {
|
||||
<var v-if="index < group.conds.length - 1"> {{group.connector}} </var>
|
||||
</var>
|
||||
</td>
|
||||
<td style="width: 5em; background: white">
|
||||
<td style="width: 5em; background: white" :style="{'border-bottom':(groupIndex < conds.groups.length-1) ? '1px solid rgba(34,36,38,.15)':''}">
|
||||
<a href="" title="修改" @click.prevent="updateGroup(groupIndex, group)"><i class="icon pencil small"></i></a> <a href="" title="删除" @click.prevent="removeGroup(groupIndex)"><i class="icon remove"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -85,6 +86,10 @@ Vue.component("http-request-conds-box", {
|
||||
<div>
|
||||
<button class="ui button tiny" type="button" @click.prevent="addGroup()">+</button>
|
||||
</div>
|
||||
<p class="comment">
|
||||
<span v-if="conds.connector == 'or'">只要满足其中一个条件分组即可。</span>
|
||||
<span v-if="conds.connector == 'and'">需要满足所有条件分组。</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -10,10 +10,16 @@ Vue.component("http-request-conds-view", {
|
||||
}
|
||||
}
|
||||
return {
|
||||
conds: conds,
|
||||
initConds: conds,
|
||||
components: window.REQUEST_COND_COMPONENTS
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 之所以使用computed,是因为需要动态更新
|
||||
conds: function () {
|
||||
return this.initConds
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
typeName: function (cond) {
|
||||
let c = this.components.$find(function (k, v) {
|
||||
|
||||
@@ -5,7 +5,7 @@ Vue.component("http-websocket-box", {
|
||||
if (websocketRef == null) {
|
||||
websocketRef = {
|
||||
isPrior: false,
|
||||
isOn: true,
|
||||
isOn: false,
|
||||
websocketId: 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ Vue.component("ssl-certs-box", {
|
||||
<input type="hidden" name="certIdsJSON" :value="JSON.stringify(certIds())"/>
|
||||
<div v-if="certs != null && certs.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in certs">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeCert(index)"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider" v-if="buttonsVisible()"></div>
|
||||
</div>
|
||||
|
||||
@@ -354,7 +354,7 @@ Vue.component("ssl-config-box", {
|
||||
<td>
|
||||
<div v-if="policy.certs != null && policy.certs.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in policy.certs">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeCert(index)"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
@@ -3,18 +3,42 @@
|
||||
"type": "url-extension",
|
||||
"name": "URL扩展名",
|
||||
"description": "根据URL中的文件路径扩展名进行过滤",
|
||||
"component": "http-cond-url-extension"
|
||||
"component": "http-cond-url-extension",
|
||||
"isRequest": true
|
||||
},
|
||||
{
|
||||
"type": "url-prefix",
|
||||
"name": "路径前缀",
|
||||
"name": "URL前缀",
|
||||
"description": "根据URL中的文件路径前缀进行过滤",
|
||||
"component": "http-cond-url-prefix"
|
||||
"component": "http-cond-url-prefix",
|
||||
"isRequest": true
|
||||
},
|
||||
{
|
||||
"type": "url-eq",
|
||||
"name": "URL精准匹配",
|
||||
"description": "检查URL中的文件路径是否一致",
|
||||
"component": "http-cond-url-eq",
|
||||
"isRequest": true
|
||||
},
|
||||
{
|
||||
"type": "url-regexp",
|
||||
"name": "URL正则匹配",
|
||||
"description": "使用正则表达式检查URL中的文件路径是否一致",
|
||||
"component": "http-cond-url-regexp",
|
||||
"isRequest": true
|
||||
},
|
||||
{
|
||||
"type": "url-not-regexp",
|
||||
"name": "排除URL正则匹配",
|
||||
"description": "使用正则表达式检查URL中的文件路径是否一致,如果一致,则不匹配",
|
||||
"component": "http-cond-url-not-regexp",
|
||||
"isRequest": true
|
||||
},
|
||||
{
|
||||
"type": "mime-type",
|
||||
"name": "内容MimeType",
|
||||
"description": "根据服务器返回的内容的MimeType进行过滤",
|
||||
"component": "http-cond-mime-type"
|
||||
"description": "根据服务器返回的内容的MimeType进行过滤。注意:当用于缓存条件时,此条件需要结合别的请求条件使用。",
|
||||
"component": "http-cond-mime-type",
|
||||
"isRequest": false
|
||||
}
|
||||
]
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
<!-- 模块 -->
|
||||
<div v-for="module in teaModules">
|
||||
<a class="item" :href="Tea.url(module.code)" :class="{active:teaMenu == module.code && teaSubMenu.length == 0, separator:module.code.length == 0, expend: teaMenu == module.code}">
|
||||
<a class="item" :href="Tea.url(module.code)" :class="{active:teaMenu == module.code && teaSubMenu.length == 0, separator:module.code.length == 0, expend: teaMenu == module.code}" v-if="module.isOn !== false">
|
||||
<span v-if="module.code.length > 0">
|
||||
<i class="window restore outline icon" v-if="module.icon == null"></i>
|
||||
<i class="ui icon" v-if="module.icon != null" :class="module.icon"></i>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<menu-item :href="'/clusters/cluster?clusterId=' + clusterId">节点列表</menu-item>
|
||||
<span class="item">|</span>
|
||||
<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/monitor?clusterId=' + clusterId + '&nodeId=' + nodeId" code="monitor" v-if="teaIsPlus">监控图表</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + nodeId" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
|
||||
</second-menu>
|
||||
@@ -0,0 +1,4 @@
|
||||
.chart-box {
|
||||
height: 20em;
|
||||
}
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,YAAA","file":"index.css"}
|
||||
25
web/views/@default/clusters/cluster/node/monitor/index.html
Normal file
25
web/views/@default/clusters/cluster/node/monitor/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{$layout}
|
||||
{$template "../node_menu"}
|
||||
|
||||
{$var "header"}
|
||||
<!-- echart -->
|
||||
<script type="text/javascript" src="/js/echarts/echarts.min.js"></script>
|
||||
{$end}
|
||||
|
||||
<h4>上行流量(字节)</h4>
|
||||
<div class="chart-box" id="traffic-in-chart"></div>
|
||||
|
||||
<h4>下行流量(字节)</h4>
|
||||
<div class="chart-box" id="traffic-out-chart"></div>
|
||||
|
||||
<h4>连接数</h4>
|
||||
<div class="chart-box" id="connections-chart"></div>
|
||||
|
||||
<h4>CPU</h4>
|
||||
<div class="chart-box" id="cpu-chart"></div>
|
||||
|
||||
<h4>内存</h4>
|
||||
<div class="chart-box" id="memory-chart"></div>
|
||||
|
||||
<h4>负载</h4>
|
||||
<div class="chart-box" id="load-chart"></div>
|
||||
253
web/views/@default/clusters/cluster/node/monitor/index.js
Normal file
253
web/views/@default/clusters/cluster/node/monitor/index.js
Normal file
@@ -0,0 +1,253 @@
|
||||
Tea.context(function () {
|
||||
this.$delay(function () {
|
||||
this.loadTrafficInChart()
|
||||
this.loadTrafficOutChart()
|
||||
this.loadConnectionsChart()
|
||||
this.loadCPUChart()
|
||||
this.loadMemoryChart()
|
||||
this.loadLoadChart()
|
||||
|
||||
let that = this
|
||||
window.addEventListener("resize", function () {
|
||||
that.resizeChart("traffic-in-chart")
|
||||
that.resizeChart("traffic-out-chart")
|
||||
that.resizeChart("connections-chart")
|
||||
that.resizeChart("cpu-chart")
|
||||
that.resizeChart("memory-chart")
|
||||
that.resizeChart("load-chart")
|
||||
})
|
||||
})
|
||||
|
||||
this.loadTrafficInChart = function () {
|
||||
this.$post(".trafficIn")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
let max = values.map(function (v) {
|
||||
return v.value
|
||||
}).$max() / 1024 / 1024
|
||||
if (max < 1) {
|
||||
return 1
|
||||
}
|
||||
if (max < 10) {
|
||||
return 10
|
||||
}
|
||||
if (max < 100) {
|
||||
return 100
|
||||
}
|
||||
return null
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value / 1024 / 1024
|
||||
}
|
||||
this.reloadChart("traffic-in-chart", "", values, "M", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadTrafficInChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.loadTrafficOutChart = function () {
|
||||
this.$post(".trafficOut")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
let max = values.map(function (v) {
|
||||
return v.value
|
||||
}).$max() / 1024 / 1024
|
||||
if (max < 1) {
|
||||
return 1
|
||||
}
|
||||
if (max < 10) {
|
||||
return 10
|
||||
}
|
||||
if (max < 100) {
|
||||
return 100
|
||||
}
|
||||
return null
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value / 1024 / 1024
|
||||
}
|
||||
this.reloadChart("traffic-out-chart", "", values, "M", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadTrafficOutChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.loadConnectionsChart = function () {
|
||||
this.$post(".connections")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
let max = values.map(function (v) {
|
||||
return v.value
|
||||
}).$max()
|
||||
if (max < 10) {
|
||||
return 10
|
||||
}
|
||||
if (max < 100) {
|
||||
return 100
|
||||
}
|
||||
if (max < 1000) {
|
||||
return 1000
|
||||
}
|
||||
return null
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value
|
||||
}
|
||||
this.reloadChart("connections-chart", "", values, "", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadConnectionsChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.loadCPUChart = function () {
|
||||
this.$post(".cpu")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
return 100
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value
|
||||
}
|
||||
this.reloadChart("cpu-chart", "", values, "%", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadCPUChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.loadMemoryChart = function () {
|
||||
this.$post(".memory")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
return 100
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value
|
||||
}
|
||||
this.reloadChart("memory-chart", "", values, "%", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadMemoryChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.loadLoadChart = function () {
|
||||
this.$post(".load")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
let values = resp.data.values
|
||||
let maxFunc = function () {
|
||||
let max = values.map(function (v) {
|
||||
return v.value
|
||||
}).$max()
|
||||
if (max < 10) {
|
||||
return 10
|
||||
}
|
||||
return null
|
||||
}
|
||||
let valueFunc = function (v) {
|
||||
return v.value
|
||||
}
|
||||
this.reloadChart("load-chart", "5分钟", values, "", maxFunc, valueFunc)
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.loadLoadChart()
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
this.reloadChart = function (chartId, name, stats, unit, maxFunc, valueFunc) {
|
||||
let chartBox = document.getElementById(chartId)
|
||||
if (chartBox == null) {
|
||||
return
|
||||
}
|
||||
let chart = echarts.init(chartBox)
|
||||
let option = {
|
||||
xAxis: {
|
||||
data: stats.map(function (stat) {
|
||||
return stat.label
|
||||
})
|
||||
},
|
||||
yAxis: {
|
||||
max: maxFunc(),
|
||||
axisLabel: {
|
||||
formatter: function (value) {
|
||||
return value + unit
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: "item",
|
||||
formatter: function (args) {
|
||||
return stats[args.dataIndex].label + ": " + stats[args.dataIndex].text
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
top: 10,
|
||||
right: 20,
|
||||
bottom: 20
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: name,
|
||||
type: "line",
|
||||
data: stats.map(valueFunc),
|
||||
itemStyle: {
|
||||
color: "#9DD3E8"
|
||||
},
|
||||
areaStyle: {}
|
||||
}
|
||||
],
|
||||
animation: true
|
||||
}
|
||||
chart.setOption(option)
|
||||
chart.resize()
|
||||
}
|
||||
|
||||
this.resizeChart = function (chartId) {
|
||||
let chartBox = document.getElementById(chartId)
|
||||
if (chartBox == null) {
|
||||
return
|
||||
}
|
||||
let chart = echarts.init(chartBox)
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
.chart-box {
|
||||
height: 20em;
|
||||
}
|
||||
@@ -122,6 +122,24 @@
|
||||
<span v-else class="disabled">没有限制。</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存磁盘容量</td>
|
||||
<td>
|
||||
<span v-if="node.maxCacheDiskCapacity == null || node.maxCacheDiskCapacity.count <= 0" class="disabled">没有限制</span>
|
||||
<div v-else>
|
||||
<size-capacity-view :v-value="node.maxCacheDiskCapacity"></size-capacity-view>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存内存容量</td>
|
||||
<td>
|
||||
<span v-if="node.maxCacheMemoryCapacity == null || node.maxCacheMemoryCapacity.count <= 0" class="disabled">没有限制</span>
|
||||
<div v-else>
|
||||
<size-capacity-view :v-value="node.maxCacheMemoryCapacity"></size-capacity-view>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="ui divider"></div>
|
||||
@@ -147,7 +165,7 @@
|
||||
<tbody v-show="node.status.isActive">
|
||||
<tr>
|
||||
<td>CPU用量</td>
|
||||
<td>{{node.status.cpuUsageText}}</td>
|
||||
<td>{{node.status.cpuUsageText}} <span v-if="node.status.cpuPhysicalCount > 0" class="small grey">({{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程)</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内存用量</td>
|
||||
@@ -157,22 +175,21 @@
|
||||
<td>连接数</td>
|
||||
<td>{{node.status.connectionCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>负载</td>
|
||||
<td>{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}} <tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存用量</td>
|
||||
<td>
|
||||
磁盘:{{node.status.cacheTotalDiskSize}} 内存:{{node.status.cacheTotalMemorySize}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>版本</td>
|
||||
<td>v{{node.status.buildVersion}}
|
||||
<a :href="'/clusters/cluster/upgradeRemote?clusterId=' + clusterId" v-if="shouldUpgrade"><span class="red">发现新版本v{{newVersion}} »</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU</td>
|
||||
<td>
|
||||
<span v-if="node.status.cpuPhysicalCount > 0">{{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程</span>
|
||||
<span v-else class="disabled">-/-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>负载</td>
|
||||
<td>{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}} <tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{$layout}
|
||||
{$template "../node_menu"}
|
||||
|
||||
<div style="margin-top: -1em">
|
||||
<second-menu>
|
||||
<menu-item @click.prevent="createThreshold">添加阈值</menu-item>
|
||||
</second-menu>
|
||||
</div>
|
||||
|
||||
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
|
||||
<table class="ui table selectable celled" v-if="thresholds.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>监控项</th>
|
||||
<th>参数</th>
|
||||
<th>操作符</th>
|
||||
<th>对比值</th>
|
||||
<th>统计时间段</th>
|
||||
<th class="two wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="threshold in thresholds">
|
||||
<td>{{threshold.itemName}}</td>
|
||||
<td>{{threshold.paramName}}</td>
|
||||
<td>{{threshold.operatorName}}</td>
|
||||
<td>{{threshold.value}}</td>
|
||||
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="threshold.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
41
web/views/@default/clusters/cluster/node/thresholds/index.js
Normal file
41
web/views/@default/clusters/cluster/node/thresholds/index.js
Normal file
@@ -0,0 +1,41 @@
|
||||
Tea.context(function () {
|
||||
this.createThreshold = function () {
|
||||
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/createPopup", {
|
||||
clusterId: this.clusterId,
|
||||
nodeId: this.nodeId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateThreshold = function (thresholdId) {
|
||||
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/updatePopup", {
|
||||
thresholdId: thresholdId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteThreshold = function (thresholdId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除这个阈值吗?", function () {
|
||||
that.$post(".delete")
|
||||
.params({
|
||||
thresholdId: thresholdId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,97 +1,111 @@
|
||||
{$layout}
|
||||
|
||||
{$template "node_menu"}
|
||||
{$template "node_menu"}
|
||||
|
||||
<h3>修改节点</h3>
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="nodeId" :value="node.id"/>
|
||||
<input type="hidden" name="loginId" :value="loginId"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="node.name"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP地址 *</td>
|
||||
<td>
|
||||
<node-ip-addresses-box :v-ip-addresses="ipAddresses"></node-ip-addresses-box>
|
||||
<p class="comment">用于访问节点和域名解析等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="allDNSRoutes.length > 0">
|
||||
<td>DNS线路</td>
|
||||
<td>
|
||||
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
||||
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
|
||||
<p class="comment">当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属集群</td>
|
||||
<td>
|
||||
<select class="ui dropdown" name="clusterId" style="width:10em" v-model="clusterId">
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<h3>修改节点</h3>
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="nodeId" :value="node.id"/>
|
||||
<input type="hidden" name="loginId" :value="loginId"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="node.name"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP地址 *</td>
|
||||
<td>
|
||||
<node-ip-addresses-box :v-ip-addresses="ipAddresses"></node-ip-addresses-box>
|
||||
<p class="comment">用于访问节点和域名解析等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="allDNSRoutes.length > 0">
|
||||
<td>DNS线路</td>
|
||||
<td>
|
||||
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
||||
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
|
||||
<p class="comment">当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属集群</td>
|
||||
<td>
|
||||
<select class="ui dropdown" name="clusterId" style="width:10em" v-model="clusterId">
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属区域</td>
|
||||
<td>
|
||||
<node-region-selector :v-region="node.region"></node-region-selector>
|
||||
<p class="comment">设置区域后才能根据区域进行流量统计和计费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属分组</td>
|
||||
<td>
|
||||
<node-group-selector :v-cluster-id="clusterId" :v-group="node.group"></node-group-selector>
|
||||
<p class="comment">仅用来筛选服务。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>所属区域</td>
|
||||
<td>SSH主机地址</td>
|
||||
<td>
|
||||
<node-region-selector :v-region="node.region"></node-region-selector>
|
||||
<p class="comment">设置区域后才能根据区域进行流量统计和计费。</p>
|
||||
<input type="text" name="sshHost" maxlength="64" v-model="sshHost"/>
|
||||
<p class="comment">比如192.168.1.100</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属分组</td>
|
||||
<td>
|
||||
<node-group-selector :v-cluster-id="clusterId" :v-group="node.group"></node-group-selector>
|
||||
<p class="comment">仅用来筛选服务。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>SSH主机地址</td>
|
||||
<td>
|
||||
<input type="text" name="sshHost" maxlength="64" v-model="sshHost"/>
|
||||
<p class="comment">比如192.168.1.100</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH主机端口</td>
|
||||
<td>
|
||||
<input type="text" name="sshPort" maxlength="5" v-model="sshPort"/>
|
||||
<p class="comment">比如22。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH登录认证</td>
|
||||
<td>
|
||||
<grant-selector :v-grant="grant"></grant-selector>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU线程数</td>
|
||||
<td>
|
||||
<input type="text" name="maxCPU" v-model="node.maxCPU"/>
|
||||
<p class="comment">当前节点可以使用的最多的CPU线程数,如果为0表示可以使用全部CPU。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="node.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">如果不启用此节点,此节点上的所有服务将不能访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
<tr>
|
||||
<td>SSH主机端口</td>
|
||||
<td>
|
||||
<input type="text" name="sshPort" maxlength="5" v-model="sshPort"/>
|
||||
<p class="comment">比如22。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH登录认证</td>
|
||||
<td>
|
||||
<grant-selector :v-grant="grant"></grant-selector>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU线程数</td>
|
||||
<td>
|
||||
<input type="text" name="maxCPU" v-model="node.maxCPU" style="width:5em" maxlength="5em"/>
|
||||
<p class="comment">当前节点可以使用的最多的CPU线程数,如果为0表示可以使用全部CPU。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存磁盘容量</td>
|
||||
<td>
|
||||
<size-capacity-box :v-value="node.maxCacheDiskCapacity" :v-name="'maxCacheDiskCapacityJSON'"></size-capacity-box>
|
||||
<p class="comment">缓存能使用的磁盘的最大容量,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存内存容量</td>
|
||||
<td>
|
||||
<size-capacity-box :v-value="node.maxCacheMemoryCapacity" :v-name="'maxCacheMemoryCapacityJSON'"></size-capacity-box>
|
||||
<p class="comment">缓存能使用的内存的最大容量,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="node.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">如果不启用此节点,此节点上的所有服务将不能访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,87 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>添加阈值</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||
<input type="hidden" name="nodeId" :value="nodeId"/>
|
||||
<input type="hidden" name="sumMethod" value="avg"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">监控项 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
|
||||
<option v-for="item in items" :value="item.code">{{item.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{itemDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>参数 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
|
||||
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{paramDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>操作符 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
|
||||
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>对比值</td>
|
||||
<td>
|
||||
<input type="text" name="value" style="width: 6em" maxlength="10"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>统计时间段 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="duration" value="5" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
<!-- TODO 将来支持更多时间范围 -->
|
||||
<input type="hidden" name="durationUnit" value="minute"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>消息</td>
|
||||
<td>
|
||||
<textarea rows="2" maxlength="100" name="message"></textarea>
|
||||
<p class="comment">触发阈值时的消息提示。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>消息通知间隔</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="notifyDuration" value="10" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,42 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyPopup
|
||||
this.threshold = {
|
||||
item: this.items[0].code,
|
||||
param: "",
|
||||
operator: this.operators[0].code
|
||||
}
|
||||
this.$delay(function () {
|
||||
this.changeItem()
|
||||
this.changeParam()
|
||||
})
|
||||
|
||||
this.itemDescription = ""
|
||||
this.itemParams = []
|
||||
|
||||
this.changeItem = function () {
|
||||
let that = this
|
||||
this.threshold.param = ""
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
that.threshold.param = v.params[0].code
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.paramDescription = ""
|
||||
|
||||
this.changeParam = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
v.params.forEach(function (param) {
|
||||
if (param.code == that.threshold.param) {
|
||||
that.paramDescription = param.description
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
{$layout}
|
||||
{$template "/left_menu"}
|
||||
|
||||
<div class="right-box">
|
||||
<first-menu>
|
||||
<menu-item @click.prevent="createThreshold">添加阈值</menu-item>
|
||||
</first-menu>
|
||||
|
||||
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
|
||||
<table class="ui table selectable celled" v-if="thresholds.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>监控项</th>
|
||||
<th>参数</th>
|
||||
<th>操作符</th>
|
||||
<th>对比值</th>
|
||||
<th>统计时间段</th>
|
||||
<th class="two wide">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="threshold in thresholds">
|
||||
<td>{{threshold.itemName}}
|
||||
<div v-if="threshold.node != null" style="margin-top: 0.3em">
|
||||
<a :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + threshold.node.id" class="ui label basic tiny" title="节点专属阈值设置"><span class="small">节点:{{threshold.node.name}}</span></a>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{threshold.paramName}}</td>
|
||||
<td>{{threshold.operatorName}}</td>
|
||||
<td>{{threshold.value}}</td>
|
||||
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="threshold.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
Tea.context(function () {
|
||||
this.createThreshold = function () {
|
||||
teaweb.popup(Tea.url(".createPopup", {
|
||||
clusterId: this.clusterId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.updateThreshold = function (thresholdId) {
|
||||
teaweb.popup(Tea.url(".updatePopup", {
|
||||
thresholdId: thresholdId
|
||||
}), {
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.deleteThreshold = function (thresholdId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除这个阈值吗?", function () {
|
||||
that.$post(".delete")
|
||||
.params({
|
||||
thresholdId: thresholdId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,92 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>修改阈值</h3>
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="thresholdId" :value="threshold.id"/>
|
||||
<input type="hidden" name="sumMethod" value="avg"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">监控项 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
|
||||
<option v-for="item in items" :value="item.code">{{item.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{itemDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>参数 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
|
||||
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
|
||||
</select>
|
||||
<p class="comment">{{paramDescription}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>操作符 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
|
||||
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>对比值</td>
|
||||
<td>
|
||||
<input type="text" name="value" style="width: 6em" maxlength="10" v-model="threshold.value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>统计时间段 *</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="duration" value="5" style="width: 5em" v-model="threshold.duration"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
<!-- TODO 将来支持更多时间范围 -->
|
||||
<input type="hidden" name="durationUnit" value="minute" v-model="threshold.durationUnit"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>消息</td>
|
||||
<td>
|
||||
<textarea rows="2" maxlength="100" name="message" v-model="threshold.message"></textarea>
|
||||
<p class="comment">触发阈值时的消息提示。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>消息通知间隔</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="notifyDuration" v-model="threshold.notifyDuration" value="10" style="width: 5em"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>
|
||||
<checkbox name="isOn" value="1" v-model="threshold.isOn"></checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,47 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyPopup
|
||||
this.$delay(function () {
|
||||
this.initItem()
|
||||
this.changeParam()
|
||||
})
|
||||
|
||||
this.itemDescription = ""
|
||||
this.itemParams = []
|
||||
|
||||
this.initItem = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.changeItem = function () {
|
||||
let that = this
|
||||
this.threshold.param = ""
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
that.itemDescription = v.description
|
||||
that.itemParams = v.params
|
||||
that.threshold.param = v.params[0].code
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.paramDescription = ""
|
||||
|
||||
this.changeParam = function () {
|
||||
let that = this
|
||||
this.items.forEach(function (v) {
|
||||
if (v.code == that.threshold.item) {
|
||||
v.params.forEach(function (param) {
|
||||
if (param.code == that.threshold.param) {
|
||||
that.paramDescription = param.description
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -2,5 +2,6 @@
|
||||
<menu-item href="/clusters/grants">认证列表</menu-item>
|
||||
<span class="item">|</span>
|
||||
<menu-item :href="'/clusters/grants/grant?grantId=' + grant.id" code="index">{{grant.name}}详情</menu-item>
|
||||
<menu-item :href="'/clusters/grants/test?grantId=' + grant.id" code="test">测试</menu-item>
|
||||
<menu-item :href="'/clusters/grants/update?grantId=' + grant.id" code="update">修改</menu-item>
|
||||
</first-menu>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<td>名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="100" ref="focus"/>
|
||||
<p class="comment">起一个容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -52,7 +53,7 @@
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>描述</td>
|
||||
<td>备注</td>
|
||||
<td>
|
||||
<textarea name="description" rows="3"></textarea>
|
||||
</td>
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
|
||||
<tbody v-if="moreOptionsVisible">
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>描述</td>
|
||||
<td>备注</td>
|
||||
<td>
|
||||
<textarea name="description" rows="3"></textarea>
|
||||
</td>
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
<!-- 用户名/密码 -->
|
||||
<tbody v-if="grant.method == 'user'">
|
||||
<tr>
|
||||
<td>SSH用户名</td>
|
||||
<td>
|
||||
{{grant.username}}
|
||||
<p class="comment">SSH登录用户名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH用户名</td>
|
||||
<td>
|
||||
{{grant.username}}
|
||||
<p class="comment">SSH登录用户名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH密码</td>
|
||||
<td>{{grant.password}}
|
||||
@@ -34,17 +34,17 @@
|
||||
|
||||
<!-- 私钥 -->
|
||||
<tbody v-if="grant.method == 'privateKey'">
|
||||
<tr>
|
||||
<td>RSA私钥</td>
|
||||
<td>
|
||||
{{grant.privateKey}}
|
||||
<p class="comment">用来生成登录SSH公钥的私钥</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RSA私钥</td>
|
||||
<td>
|
||||
{{grant.privateKey}}
|
||||
<p class="comment">用来生成登录SSH公钥的私钥</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tr>
|
||||
<td>描述</td>
|
||||
<td>备注</td>
|
||||
<td>
|
||||
<span v-if="grant.description.length > 0">{{grant.description}}</span>
|
||||
<span v-if="grant.description.length == 0">-</span>
|
||||
@@ -56,12 +56,12 @@
|
||||
<h3>使用此认证的集群</h3>
|
||||
<div>
|
||||
<p v-if="clusters.length == 0" class="comment">暂时还没有集群使用此认证。</p>
|
||||
<a :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label small" v-for="cluster in clusters">{{cluster.name}}</a>
|
||||
<a :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label small basic" v-for="cluster in clusters">{{cluster.name}}</a>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<h3>使用此认证的节点</h3>
|
||||
<div>
|
||||
<p v-if="nodes.length == 0" class="comment">暂时还没有节点使用此认证。</p>
|
||||
<a :href="'/clusters/cluster/node?clusterId=' + node.cluster.id + '&nodeId=' + node.id" class="ui label small" :class="{red:!node.isOn}" v-for="node in nodes">{{node.name}}<span class="small">({{node.cluster.name}})</span></a>
|
||||
<a :href="'/clusters/cluster/node?clusterId=' + node.cluster.id + '&nodeId=' + node.id" class="ui label small basic" :class="{red:!node.isOn}" v-for="node in nodes">{{node.name}}<span class="small">({{node.cluster.name}})</span></a>
|
||||
</div>
|
||||
|
||||
59
web/views/@default/clusters/grants/test.html
Normal file
59
web/views/@default/clusters/grants/test.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{$layout}
|
||||
{$template "grant_menu"}
|
||||
|
||||
<div class="ui message">可以在这里测试SSH主机连接是否正常。</div>
|
||||
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-before="requestBefore" data-tea-done="requestDone">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="grantId" :value="grant.id"/>
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td class="title">节点主机地址 *</td>
|
||||
<td>
|
||||
<input type="text" name="host" placeholder="x.x.x.x" style="width: 10em" ref="focus"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>节点主机端口 *</td>
|
||||
<td>
|
||||
<input type="text" name="port" style="width: 5em" size="5" maxlength="5"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">认证方式</td>
|
||||
<td>
|
||||
{{grant.methodName}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 用户名/密码 -->
|
||||
<tbody v-if="grant.method == 'user'">
|
||||
<tr>
|
||||
<td>SSH用户名</td>
|
||||
<td>
|
||||
{{grant.username}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH密码</td>
|
||||
<td>{{grant.password}}
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<!-- 私钥 -->
|
||||
<tbody v-if="grant.method == 'privateKey'">
|
||||
<tr>
|
||||
<td>RSA私钥</td>
|
||||
<td>
|
||||
{{grant.privateKey}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="ui message green" v-if="resp != null && resp.isOk">连接成功!</div>
|
||||
<div class="ui message red" v-if="resp != null && !resp.isOk">连接失败:{{resp.error}}</div>
|
||||
|
||||
<submit-btn v-if="!isRequesting">提交测试</submit-btn>
|
||||
<button class="ui button disabled" v-if="isRequesting">连接中...</button>
|
||||
</form>
|
||||
17
web/views/@default/clusters/grants/test.js
Normal file
17
web/views/@default/clusters/grants/test.js
Normal file
@@ -0,0 +1,17 @@
|
||||
Tea.context(function () {
|
||||
this.isRequesting = false
|
||||
this.resp = null
|
||||
|
||||
this.success = function (resp) {
|
||||
this.resp = resp.data
|
||||
}
|
||||
|
||||
this.requestBefore = function () {
|
||||
this.isRequesting = true
|
||||
this.resp = null
|
||||
}
|
||||
|
||||
this.requestDone = function () {
|
||||
this.isRequesting = false
|
||||
}
|
||||
})
|
||||
@@ -9,6 +9,7 @@
|
||||
<td>名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="100" ref="focus" v-model="grant.name"/>
|
||||
<p class="comment">起一个容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -49,7 +50,7 @@
|
||||
</tbody>
|
||||
|
||||
<tr>
|
||||
<td>描述</td>
|
||||
<td>备注</td>
|
||||
<td>
|
||||
<textarea name="description" rows="3" v-model="grant.description"></textarea>
|
||||
</td>
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
|
||||
<tbody v-if="moreOptionsVisible">
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>描述</td>
|
||||
<td>备注</td>
|
||||
<td>
|
||||
<textarea name="description" rows="3" v-model="grant.description"></textarea>
|
||||
</td>
|
||||
|
||||
@@ -11,8 +11,14 @@
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
</div>
|
||||
<div class="ui field" v-if="latestClusters.length > 0">
|
||||
<a href="" @click.prevent="showLatest()">常用<i class="icon angle" :class="{down: !latestVisible, up: latestVisible}"></i> </a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="ui segment" v-if="latestVisible">
|
||||
常用集群:<span v-for="cluster in latestClusters"><a :href="'/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a> </span>
|
||||
</div>
|
||||
|
||||
<div class="ui tabular menu" v-if="isSearching">
|
||||
<a :href="'/clusters?searchType=cluster&keyword=' + keyword" class="item" :class="{active: searchType == '' || searchType == 'cluster'}">集群({{countClusters}})</a>
|
||||
|
||||
7
web/views/@default/clusters/index.js
Normal file
7
web/views/@default/clusters/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
Tea.context(function () {
|
||||
this.latestVisible = false
|
||||
|
||||
this.showLatest = function () {
|
||||
this.latestVisible = !this.latestVisible
|
||||
}
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user