Compare commits

..

41 Commits

Author SHA1 Message Date
刘祥超
f738ba9152 缓存支持默认条件 2021-05-24 09:02:55 +08:00
刘祥超
95c27119ac 缓存策略可以根据Key前缀进行批量删除 2021-05-23 22:49:53 +08:00
刘祥超
2b2b516788 优化集群批量操作结果显示 2021-05-23 22:36:52 +08:00
刘祥超
0bb63b5bb7 SSH认证可搜索 2021-05-23 21:12:52 +08:00
刘祥超
48012072d7 SSL/TLS选择证书增加关键词搜索 2021-05-23 20:54:17 +08:00
刘祥超
0053949bc0 在服务列表中显示需要修复的错误 2021-05-23 20:44:51 +08:00
刘祥超
88e7246366 将节点日志级别筛选中的警告从warn改成warning 2021-05-23 20:03:46 +08:00
刘祥超
fbedf3b605 添加端口时校验端口范围 2021-05-23 20:03:14 +08:00
刘祥超
d5659e627d URL跳转支持正则匹配 2021-05-23 17:01:54 +08:00
刘祥超
709d4dc805 增加Licenses目录 2021-05-20 15:39:21 +08:00
刘祥超
37f052e9a5 节点监控图表流量使用秒来计算 2021-05-20 10:38:46 +08:00
刘祥超
58c765e97f 实现日志消息聚合 2021-05-19 20:52:41 +08:00
刘祥超
da332c2756 增加全局的边缘节点日志 2021-05-19 19:03:03 +08:00
刘祥超
e9d329bf8b 变更版本 2021-05-19 19:02:38 +08:00
刘祥超
775b46cf8f 修改版本 2021-05-13 14:36:22 +08:00
刘祥超
c99eb54811 增加edge-admin reset命令,用来清理对应的配置 2021-05-13 14:27:17 +08:00
刘祥超
7917c68b6f 节点详情中增加缓存用量显示 2021-05-13 11:50:15 +08:00
刘祥超
75ce1490cc 节点可以单独设置缓存的磁盘、内存容量 2021-05-12 21:37:43 +08:00
刘祥超
6f1cd668b6 访问日志中增加缓存状态 2021-05-12 16:31:43 +08:00
刘祥超
5b7cb59997 缓存设置中增加自动添加X-Cache Header 2021-05-12 16:09:40 +08:00
刘祥超
67242ec80e 缓存匹配条件增加“排除URL正则匹配” 2021-05-12 16:05:18 +08:00
刘祥超
2af5e059eb 缓存条件增加多个匹配方式/优化缓存设置界面 2021-05-12 15:07:30 +08:00
刘祥超
1d992b0e97 数据看板显示升级提醒 2021-05-11 22:47:21 +08:00
刘祥超
0c650987d6 服务支持fastcgi;路径规则支持匹配后缀 2021-05-10 21:13:09 +08:00
刘祥超
e8af3960f8 在访问日志中显示ws和wss 2021-05-08 20:52:13 +08:00
刘祥超
1d95087abb Websocket默认不开启 2021-05-08 19:51:05 +08:00
刘祥超
c350fceef3 实现一些阈值设置细节 2021-05-05 19:51:13 +08:00
刘祥超
e6970abcb8 实现基础的阈值设置 2021-05-04 21:02:25 +08:00
刘祥超
9cf5f56e59 修复因为ClusterHelper而导致POST Action可能被阻止的情形 2021-05-03 15:31:59 +08:00
刘祥超
c2ebf81e6c 记录和显示最近常用的服务 2021-05-03 15:15:31 +08:00
刘祥超
19a2af8bfa 记录和显示最近常用的集群 2021-05-03 11:32:59 +08:00
刘祥超
df5c1262d2 增加连接数监控图表 2021-04-29 17:06:40 +08:00
刘祥超
26340110c1 实现基本的监控图表 2021-04-29 16:47:45 +08:00
刘祥超
452f03bb78 修改TB、PB、EB的有些计算错误 2021-04-29 15:04:42 +08:00
刘祥超
609794b2d8 修复TLS证书配置时无法删除证书的问题 2021-04-28 14:56:47 +08:00
刘祥超
554d0daf6d 财务管理只有企业版才默认打开 2021-04-28 14:56:19 +08:00
刘祥超
e4015282a5 缓存设置中增加“支持分片内容”选项,用来支持Chunked内容 2021-04-18 22:16:46 +08:00
刘祥超
a7453a4baa 修复ttlcache的一个bug 2021-04-18 21:50:26 +08:00
刘祥超
913a98b4de 变更版本号 2021-04-18 21:25:01 +08:00
刘祥超
89f391ca22 增加SSH认证连接测试功能 2021-04-18 21:19:50 +08:00
刘祥超
e585149a7e 修改文件缓存策略的文件目录最大容量和内存最大容量值 2021-04-18 19:05:35 +08:00
166 changed files with 4329 additions and 509 deletions

View File

@@ -6,3 +6,4 @@
./build.sh linux mips64
./build.sh linux mips64le
./build.sh darwin amd64
./build.sh darwin arm64

1
build/licenses/README.md Normal file
View File

@@ -0,0 +1 @@
这个目录下我们列举了所有需要公开声明的第三方License如果有遗漏烦请告知 iwind.liu@gmail.com。再次感谢这些开源软件项目和贡献人员

View File

@@ -0,0 +1,30 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
As this is fork of the official Go code the same license applies.
Extensions of the original work are copyright (c) 2011 Miek Gieben

View File

@@ -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()

View File

@@ -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)

View File

@@ -1,7 +1,7 @@
package teaconst
const (
Version = "0.0.13"
Version = "0.1.1"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -22,7 +22,7 @@ import (
"time"
)
// RPC客户端
// RPCClient RPC客户端
type RPCClient struct {
apiConfig *configs.APIConfig
conns []*grpc.ClientConn
@@ -30,7 +30,7 @@ type RPCClient struct {
locker sync.Mutex
}
// 构造新的RPC客户端
// NewRPCClient 构造新的RPC客户端
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
if apiConfig == nil {
return nil, errors.New("api config should not be nil")
@@ -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()

View File

@@ -37,7 +37,7 @@ func TestRPC_Dial_HTTP(t *testing.T) {
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{
Endpoints: []string{"127.0.0.1:8003"},
Endpoints: []string{"http://127.0.0.1:8004"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
@@ -58,7 +58,7 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{
Endpoints: []string{"http://127.0.0.1:8003"},
Endpoints: []string{"https://127.0.0.1:8003"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -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()
}

View File

@@ -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)).

View File

@@ -17,13 +17,25 @@ func (this *LogsAction) Init() {
}
func (this *LogsAction) RunGet(params struct {
NodeId int64
NodeId int64
DayFrom string
DayTo string
Keyword string
Level string
}) {
this.Data["nodeId"] = params.NodeId
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: "node",
NodeId: params.NodeId,
Role: "node",
NodeId: params.NodeId,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
@@ -33,10 +45,14 @@ func (this *LogsAction) RunGet(params struct {
page := this.NewPage(count, 20)
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: params.NodeId,
Role: "node",
Offset: page.Offset,
Size: page.Size,
NodeId: params.NodeId,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
})
logs := []maps.Map{}
@@ -47,6 +63,7 @@ func (this *LogsAction) RunGet(params struct {
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
})
}
this.Data["logs"] = logs

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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 / 60,
"text": numberutils.FormatBytes(total / 60),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": numberutils.FormatBytes(0),
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -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 / 60,
"text": numberutils.FormatBytes(total / 60),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": numberutils.FormatBytes(0),
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -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()

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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()
})
}

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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,

View File

@@ -15,8 +15,14 @@ func (this *IndexAction) Init() {
this.Nav("", "grant", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().NodeGrantRPC().CountAllEnabledNodeGrants(this.AdminContext(), &pb.CountAllEnabledNodeGrantsRequest{})
func (this *IndexAction) RunGet(params struct {
Keyword string
}) {
this.Data["keyword"] = params.Keyword
countResp, err := this.RPC().NodeGrantRPC().CountAllEnabledNodeGrants(this.AdminContext(), &pb.CountAllEnabledNodeGrantsRequest{
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
@@ -25,15 +31,16 @@ func (this *IndexAction) RunGet(params struct{}) {
this.Data["page"] = page.AsHTML()
grantsResp, err := this.RPC().NodeGrantRPC().ListEnabledNodeGrants(this.AdminContext(), &pb.ListEnabledNodeGrantsRequest{
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
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 {
@@ -57,6 +64,7 @@ func (this *IndexAction) RunGet(params struct{}) {
"type": grant.Method,
"name": grantutils.FindGrantMethodName(grant.Method),
},
"username": grant.Username,
"countClusters": countClusters,
"countNodes": countNodes,
})

View File

@@ -24,6 +24,7 @@ func init() {
GetPost("/selectPopup", new(SelectPopupAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
GetPost("/test", new(TestAction)).
EndAll()
})
}

View File

@@ -23,14 +23,16 @@ 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{
"id": grant.Id,
"name": grant.Name,
"method": grant.Method,
"methodName": grantutils.FindGrantMethodName(grant.Method),
"id": grant.Id,
"name": grant.Name,
"method": grant.Method,
"methodName": grantutils.FindGrantMethodName(grant.Method),
"username": grant.Username,
"description": grant.Description,
})
}
this.Data["grants"] = grantMaps
@@ -52,12 +54,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("找不到要使用的认证")
}

View 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()
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

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

View File

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

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -6,10 +6,12 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"sort"
"strconv"
"sync"
)
// MessageResult 和节点消息通讯结果定义
type MessageResult struct {
NodeId int64 `json:"nodeId"`
NodeName string `json:"nodeName"`
@@ -17,7 +19,7 @@ type MessageResult struct {
Message string `json:"message"`
}
// 向集群发送命令消息
// SendMessageToCluster 向集群发送命令消息
func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg interface{}, timeoutSeconds int32) (results []*MessageResult, err error) {
results = []*MessageResult{}
@@ -153,10 +155,17 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
}
wg.Wait()
// 对结果进行排序
if len(results) > 0 {
sort.Slice(results, func(i, j int) bool {
return results[i].NodeId < results[j].NodeId
})
}
return
}
// 向一组节点发送命令消息
// SendMessageToNodeIds 向一组节点发送命令消息
func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg interface{}, timeoutSeconds int32) (results []*MessageResult, err error) {
results = []*MessageResult{}
if len(nodeIds) == 0 {
@@ -321,5 +330,12 @@ func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg
}
wg.Wait()
// 对结果进行排序
if len(results) > 0 {
sort.Slice(results, func(i, j int) bool {
return results[i].NodeId < results[j].NodeId
})
}
return
}

View File

@@ -13,7 +13,7 @@ import (
"time"
)
// 选择证书
// SelectPopupAction 选择证书
type SelectPopupAction struct {
actionutils.ParentAction
}
@@ -25,10 +25,12 @@ func (this *SelectPopupAction) Init() {
func (this *SelectPopupAction) RunGet(params struct {
ViewSize string
SelectedCertIds string
Keyword string
}) {
// TODO 支持关键词搜索
// TODO 列出常用和最新的证书供用户选择
this.Data["keyword"] = params.Keyword
// 已经选择的证书
selectedCertIds := []string{}
if len(params.SelectedCertIds) > 0 {
@@ -40,7 +42,9 @@ func (this *SelectPopupAction) RunGet(params struct {
}
this.Data["viewSize"] = params.ViewSize
countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{})
countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
@@ -50,8 +54,9 @@ func (this *SelectPopupAction) RunGet(params struct {
this.Data["page"] = page.AsHTML()
listResp, err := this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
certConfigs := []*sslconfigs.SSLCertConfig{}

View File

@@ -12,7 +12,7 @@ import (
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
strings "strings"
"strings"
)
type PurgeAction struct {
@@ -53,6 +53,7 @@ func (this *PurgeAction) RunGet(params struct {
func (this *PurgeAction) RunPost(params struct {
CachePolicyId int64
ClusterId int64
Type string
Keys string
Must *actions.Must
}) {
@@ -96,6 +97,11 @@ func (this *PurgeAction) RunPost(params struct {
CachePolicyJSON: cachePolicyJSON,
Keys: realKeys,
}
if params.Type == "prefix" {
msg.Type = messageconfigs.PurgeCacheMessageTypeDir
} else {
msg.Type = messageconfigs.PurgeCacheMessageTypeFile
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodePurgeCache, msg, 10)
if err != nil {
this.ErrorPage(err)

View File

@@ -63,8 +63,13 @@ func (this *UpdateAction) RunPost(params struct {
Description string
IsOn bool
RefsJSON []byte
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改缓存策略:%d", params.CachePolicyId)
params.Must.
Field("name", params.Name).
Require("请输入策略名称")
@@ -104,6 +109,22 @@ func (this *UpdateAction) RunPost(params struct {
this.ErrorPage(err)
return
}
// 校验缓存条件
refs := []*serverconfigs.HTTPCacheRef{}
if len(params.RefsJSON) > 0 {
err = json.Unmarshal(params.RefsJSON, &refs)
if err != nil {
this.Fail("缓存条件解析失败:" + err.Error())
}
for _, ref := range refs {
err = ref.Init()
if err != nil {
this.Fail("缓存条件校验失败:" + err.Error())
}
}
}
_, err = this.RPC().HTTPCachePolicyRPC().UpdateHTTPCachePolicy(this.AdminContext(), &pb.UpdateHTTPCachePolicyRequest{
HttpCachePolicyId: params.CachePolicyId,
IsOn: params.IsOn,
@@ -120,8 +141,15 @@ func (this *UpdateAction) RunPost(params struct {
return
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改缓存策略:%d", params.CachePolicyId)
// 修改缓存条件
_, err = this.RPC().HTTPCachePolicyRPC().UpdateHTTPCachePolicyRefs(this.AdminContext(), &pb.UpdateHTTPCachePolicyRefsRequest{
HttpCachePolicyId: params.CachePolicyId,
RefsJSON: params.RefsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,24 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package servers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type FixLogAction struct {
actionutils.ParentAction
}
func (this *FixLogAction) RunPost(params struct {
LogId int64
}) {
_, err := this.RPC().NodeLogRPC().FixNodeLog(this.AdminContext(), &pb.FixNodeLogRequest{NodeLogId: params.LogId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -4,9 +4,11 @@ import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
@@ -30,10 +32,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,
@@ -231,5 +252,48 @@ func (this *IndexAction) RunGet(params struct {
// 是否有用户管理权限
this.Data["canVisitUser"] = configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeUser)
// 显示服务相关的日志
errorLogsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: 0,
Role: "node",
Offset: 0,
Size: 10,
Level: "error",
FixedState: int32(configutils.BoolStateNo),
AllServers: true,
})
if err != nil {
this.ErrorPage(err)
return
}
errorLogMaps := []maps.Map{}
for _, errorLog := range errorLogsResp.NodeLogs {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: errorLog.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
server := serverResp.Server
if server == nil {
// 设置为已修复
_, err = this.RPC().NodeLogRPC().FixNodeLog(this.AdminContext(), &pb.FixNodeLogRequest{NodeLogId: errorLog.Id})
if err != nil {
this.ErrorPage(err)
return
}
continue
}
errorLogMaps = append(errorLogMaps, maps.Map{
"id": errorLog.Id,
"description": errorLog.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", errorLog.CreatedAt),
"serverId": errorLog.ServerId,
"serverName": server.Name,
})
}
this.Data["errorLogs"] = errorLogMaps
this.Show()
}

View File

@@ -15,6 +15,7 @@ func init() {
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
GetPost("/update", new(UpdateAction)).
Post("/fixLog", new(FixLogAction)).
GetPost("/addPortPopup", new(AddPortPopupAction)).
GetPost("/addServerNamePopup", new(AddServerNamePopupAction)).

View File

@@ -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()
}

View File

@@ -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 {

View File

@@ -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{}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
})
}

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -10,6 +10,8 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
)
type IndexAction struct {
@@ -76,6 +78,24 @@ func (this *IndexAction) RunPost(params struct {
this.Fail("端口地址解析失败:" + err.Error())
}
// 检查端口地址是否正确
for _, addr := range addresses {
err = addr.Init()
if err != nil {
this.Fail("绑定端口校验失败:" + err.Error())
}
if regexp.MustCompile(`^\d+$`).MatchString(addr.PortRange) {
port := types.Int(addr.PortRange)
if port > 65535 {
this.Fail("绑定的端口地址不能大于65535")
}
if port == 443 {
this.Fail("端口443通常是HTTPS的端口不能用在HTTP上")
}
}
}
server, _, isOk := serverutils.FindServer(this.Parent(), params.ServerId)
if !isOk {
return

View File

@@ -12,6 +12,7 @@ import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
)
type IndexAction struct {
@@ -87,7 +88,23 @@ func (this *IndexAction) RunPost(params struct {
this.Fail("端口地址解析失败:" + err.Error())
}
// TODO 校验addresses
// 检查端口地址是否正确
for _, addr := range addresses {
err = addr.Init()
if err != nil {
this.Fail("绑定端口校验失败:" + err.Error())
}
if regexp.MustCompile(`^\d+$`).MatchString(addr.PortRange) {
port := types.Int(addr.PortRange)
if port > 65535 {
this.Fail("绑定的端口地址不能大于65535")
}
if port == 80 {
this.Fail("端口80通常是HTTP的端口不能用在HTTPS上")
}
}
}
// 校验SSL
var sslPolicyId = int64(0)

View File

@@ -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

View File

@@ -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 {

View File

@@ -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()
}

View File

@@ -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()
})
}

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net/url"
"regexp"
)
type CreatePopupAction struct {
@@ -27,6 +28,7 @@ func (this *CreatePopupAction) RunPost(params struct {
BeforeURL string
AfterURL string
MatchPrefix bool
MatchRegexp bool
KeepRequestURI bool
Status int
@@ -39,7 +41,12 @@ func (this *CreatePopupAction) RunPost(params struct {
Require("请填写跳转前的URL")
// 校验格式
{
if params.MatchRegexp {
_, err := regexp.Compile(params.BeforeURL)
if err != nil {
this.Fail("跳转前URL正则表达式错误" + err.Error())
}
} else {
u, err := url.Parse(params.BeforeURL)
if err != nil {
this.FailField("beforeURL", "请输入正确的跳转前URL")
@@ -56,7 +63,9 @@ func (this *CreatePopupAction) RunPost(params struct {
Require("请填写跳转后URL")
// 校验格式
{
if params.MatchRegexp {
// 正则表达式情况下不做校验
} else {
u, err := url.Parse(params.AfterURL)
if err != nil {
this.FailField("afterURL", "请输入正确的跳转后URL")
@@ -76,6 +85,7 @@ func (this *CreatePopupAction) RunPost(params struct {
"beforeURL": params.BeforeURL,
"afterURL": params.AfterURL,
"matchPrefix": params.MatchPrefix,
"matchRegexp": params.MatchRegexp,
"keepRequestURI": params.KeepRequestURI,
"isOn": true,
}

View File

@@ -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()
}

View File

@@ -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",

View File

@@ -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") {
@@ -180,6 +181,11 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
"subtitle": "集群列表",
"icon": "cloud",
"subItems": []maps.Map{
{
"name": "节点日志",
"url": "/clusters/logs",
"code": "log",
},
{
"name": "SSH认证",
"url": "/clusters/grants",
@@ -222,6 +228,7 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
"module": configloaders.AdminModuleCodeFinance,
"name": "财务管理",
"icon": "yen sign",
"isOn": teaconst.IsPlus,
},
{
"code": "admins",

View File

@@ -15,6 +15,7 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/logs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
@@ -48,6 +49,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 +59,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"

View File

@@ -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>&quot;{{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}}&quot; </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>&quot;{{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}}&quot; </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
&nbsp; <a href="" @click.prevent="showLog" title="查看详情"><i class="icon expand"></i></a>
</div>`
})

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,58 @@
Vue.component("http-cache-refs-box", {
props: ["v-cache-refs"],
data: function () {
let refs = this.vCacheRefs
if (refs == null) {
refs = []
}
return {
refs: refs
}
},
methods: {
timeUnitName: function (unit) {
switch (unit) {
case "ms":
return "毫秒"
case "second":
return "秒"
case "minute":
return "分钟"
case "hour":
return "小时"
case "day":
return "天"
case "week":
return "周 "
}
return unit
}
},
template: `<div>
<input type="hidden" name="refsJSON" :value="JSON.stringify(refs)"/>
<p class="comment" v-if="refs.length == 0">暂时还没有缓存条件。</p>
<div v-show="refs.length > 0">
<table class="ui table selectable celled">
<thead>
<tr>
<th>缓存条件</th>
<th class="width10">缓存时间</th>
<th class="two op">操作</th>
</tr>
<tr v-for="(cacheRef, index) in refs">
<td>
<http-request-conds-view :v-conds="cacheRef.conds"></http-request-conds-view>
</td>
<td>{{cacheRef.life.count}} {{timeUnitName(cacheRef.life.unit)}}</td>
<td>
<a href="" @click.prevent="updateRef(index, cacheRef)">修改</a> &nbsp;
<a href="" @click.prevent="removeRef(index)">删除</a>
</td>
</tr>
</thead>
</table>
</div>
<div class="margin"></div>
</div>`
})

View File

@@ -0,0 +1,105 @@
Vue.component("http-cache-refs-config-box", {
props: ["v-cache-refs"],
data: function () {
let refs = this.vCacheRefs
if (refs == null) {
refs = []
}
return {
refs: refs
}
},
methods: {
addRef: function () {
window.UPDATING_CACHE_REF = null
let width = window.innerWidth
if (width > 1024) {
width = 1024
}
let height = window.innerHeight
if (height > 500) {
height = 500
}
let that = this
teaweb.popup("/servers/server/settings/cache/createPopup", {
width: width + "px",
height: height + "px",
callback: function (resp) {
that.refs.push(resp.data.cacheRef)
}
})
},
updateRef: function (index, cacheRef) {
window.UPDATING_CACHE_REF = cacheRef
let width = window.innerWidth
if (width > 1024) {
width = 1024
}
let height = window.innerHeight
if (height > 500) {
height = 500
}
let that = this
teaweb.popup("/servers/server/settings/cache/createPopup", {
width: width + "px",
height: height + "px",
callback: function (resp) {
Vue.set(that.refs, index, resp.data.cacheRef)
}
})
},
removeRef: function (index) {
let that = this
teaweb.confirm("确定要删除此缓存设置吗?", function () {
that.refs.$remove(index)
})
},
timeUnitName: function (unit) {
switch (unit) {
case "ms":
return "毫秒"
case "second":
return "秒"
case "minute":
return "分钟"
case "hour":
return "小时"
case "day":
return "天"
case "week":
return "周 "
}
return unit
}
},
template: `<div>
<input type="hidden" name="refsJSON" :value="JSON.stringify(refs)"/>
<div>
<table class="ui table selectable celled" v-show="refs.length > 0">
<thead>
<tr>
<th>缓存条件</th>
<th class="width10">缓存时间</th>
<th class="two op">操作</th>
</tr>
<tr v-for="(cacheRef, index) in refs">
<td>
<http-request-conds-view :v-conds="cacheRef.conds"></http-request-conds-view>
</td>
<td>{{cacheRef.life.count}} {{timeUnitName(cacheRef.life.unit)}}</td>
<td>
<a href="" @click.prevent="updateRef(index, cacheRef)">修改</a> &nbsp;
<a href="" @click.prevent="removeRef(index)">删除</a>
</td>
</tr>
</thead>
</table>
<button class="ui button tiny" @click.prevent="addRef">+添加缓存设置</button>
</div>
<div class="margin"></div>
</div>`
})

View File

@@ -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"],

View 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}} &nbsp; <a href="" title="修改" @click.prevent="updateFastcgi(fastcgi.id, index)"><i class="ui icon pencil small"></i></a> &nbsp; <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>`
})

View File

@@ -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>

View File

@@ -50,7 +50,10 @@ Vue.component("http-host-redirect-box", {
<!-- TODO 将来支持排序并支持isOn切换 -->
<div v-if="redirects.length > 0">
<div v-for="(redirect, index) in redirects" class="ui label basic small" style="margin-bottom: 0.5em;margin-top: 0.5em">
<span v-if="redirect.status > 0">[{{redirect.status}}]</span><span v-if="redirect.matchPrefix">[prefix]</span> {{redirect.beforeURL}} -&gt; {{redirect.afterURL}} <a href="" @click.prevent="update(index, redirect)" title="修改"><i class="icon pencil small"></i></a> &nbsp; <a href="" @click.prevent="remove(index)" title="删除"><i class="icon remove"></i></a>
<span v-if="redirect.status > 0" class="small grey">[{{redirect.status}}]</span>
<span v-if="redirect.matchPrefix" class="small grey">[匹配前缀]</span>
<span v-if="redirect.matchRegexp" class="small grey">[正则匹配]</span>
{{redirect.beforeURL}} -&gt; {{redirect.afterURL}} <a href="" @click.prevent="update(index, redirect)" title="修改"><i class="icon pencil small"></i></a> &nbsp; <a href="" @click.prevent="remove(index)" title="删除"><i class="icon remove"></i></a>
</div>
<div class="ui divider"></div>
</div>

View File

@@ -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}} &nbsp;</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>`
})

View File

@@ -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) {

View File

@@ -5,7 +5,7 @@ Vue.component("http-websocket-box", {
if (websocketRef == null) {
websocketRef = {
isPrior: false,
isOn: true,
isOn: false,
websocketId: 0
}
}

View File

@@ -11,7 +11,9 @@ Vue.component("origin-list-box", {
teaweb.popup("/servers/server/settings/origins/addPopup?originType=primary&" + this.vParams, {
height: "24em",
callback: function (resp) {
window.location.reload()
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
},
@@ -19,7 +21,9 @@ Vue.component("origin-list-box", {
teaweb.popup("/servers/server/settings/origins/addPopup?originType=backup&" + this.vParams, {
height: "24em",
callback: function (resp) {
window.location.reload()
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
},
@@ -27,7 +31,9 @@ Vue.component("origin-list-box", {
teaweb.popup("/servers/server/settings/origins/updatePopup?originType=" + originType + "&" + this.vParams + "&originId=" + originId, {
height: "24em",
callback: function (resp) {
window.location.reload()
teaweb.success("保存成功", function () {
window.location.reload()
})
}
})
},
@@ -37,7 +43,9 @@ Vue.component("origin-list-box", {
Tea.action("/servers/server/settings/origins/delete?" + that.vParams + "&originId=" + originId + "&originType=" + originType)
.post()
.success(function () {
window.location.reload()
teaweb.success("保存成功", function () {
window.location.reload()
})
})
})
}

View File

@@ -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)}} &nbsp; <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} &nbsp; <a href="" title="删除" @click.prevent="removeCert(index)"><i class="icon remove"></i></a>
</div>
<div class="ui divider" v-if="buttonsVisible()"></div>
</div>

View File

@@ -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)}} &nbsp; <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} &nbsp; <a href="" title="删除" @click.prevent="removeCert(index)"><i class="icon remove"></i></a>
</div>
<div class="ui divider"></div>
</div>

View File

@@ -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
}
]

View File

@@ -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>

View File

@@ -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>

View File

@@ -1,20 +1,58 @@
{$layout}
{$template "node_menu"}
{$template "node_menu"}
{$var "header"}
<!-- datepicker -->
<script type="text/javascript" src="/js/moment.min.js"></script>
<script type="text/javascript" src="/js/pikaday.js"></script>
<link rel="stylesheet" href="/js/pikaday.css"/>
<link rel="stylesheet" href="/js/pikaday.theme.css"/>
<link rel="stylesheet" href="/js/pikaday.triangle.css"/>
{$end}
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
<form method="get" action="/clusters/cluster/node/logs" class="ui form" autocomplete="off">
<input type="hidden" name="clusterId" :value="clusterId"/>
<input type="hidden" name="nodeId" :value="nodeId"/>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" value="" style="width:8em" id="day-from-picker"/>
</div>
<div class="ui field">
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" value="" style="width:8em" id="day-to-picker"/>
</div>
<div class="ui field">
<select class="ui dropdown" name="level" v-model="level">
<option value="">[级别]</option>
<option value="error">错误</option>
<option value="warning">警告</option>
<option value="info">信息</option>
</select>
</div>
<div class="ui field">
<input type="text" name="keyword" style="width:10em" v-model="keyword" placeholder="关键词"/>
</div>
<div class="ui field">
<button type="submit" class="ui button">查询</button>
</div>
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
<a :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId">[清除条件]</a>
</div>
</div>
</form>
<table class="ui table selectable" v-if="logs.length > 0">
<thead>
<tr>
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
</tr>
</thead>
<tr v-for="log in logs">
<td>
<pre class="log-box"><span :class="{red:log.level == 'error', yellow:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span></pre>
</td>
</tr>
</table>
<table class="ui table selectable" v-if="logs.length > 0">
<thead>
<tr>
<div class="page" v-html="page"></div>
</tr>
</thead>
<tr v-for="log in logs">
<td>
<pre class="log-box"><span :class="{red:log.level == 'error', yellow:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span> &nbsp; <span v-if="log.count > 0" class="ui label tiny red">共{{log.count}}条</span></pre>
</td>
</tr>
</table>
<div class="page" v-html="page"></div>

View File

@@ -0,0 +1,6 @@
Tea.context(function () {
this.$delay(function () {
teaweb.datepicker("day-from-picker")
teaweb.datepicker("day-to-picker")
})
})

View File

@@ -0,0 +1,4 @@
.chart-box {
height: 20em;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,YAAA","file":"index.css"}

View 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>

View 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()
}
})

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