Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
245c4374b4 | ||
|
|
ed302370e9 | ||
|
|
b2feb452e0 | ||
|
|
8b7661d82d | ||
|
|
33c9cd0819 | ||
|
|
a0635a97d5 | ||
|
|
e22a9d061c | ||
|
|
1b9d62c7bd | ||
|
|
2ca31ffcb4 | ||
|
|
14636ed82f | ||
|
|
cc5a34c20e | ||
|
|
3ebb47d915 | ||
|
|
3eea58c1fc | ||
|
|
255bf58abf | ||
|
|
b5b324bca0 | ||
|
|
74e654446e | ||
|
|
be01536a09 | ||
|
|
4c6bcc7c19 | ||
|
|
98dceb20db | ||
|
|
25833bc81a | ||
|
|
0d5fc7ad85 | ||
|
|
b9af3d4757 | ||
|
|
f179b0a60e | ||
|
|
875162cfb6 | ||
|
|
2f21effdbf | ||
|
|
f7336235a1 | ||
|
|
6ed0791991 | ||
|
|
045a18fb22 | ||
|
|
dbe598a934 | ||
|
|
2812b30b01 | ||
|
|
386d4957e7 | ||
|
|
ea36e60899 | ||
|
|
bbf7e2898f | ||
|
|
beab50de4c | ||
|
|
a3ac7678d9 |
25
README.md
25
README.md
@@ -1 +1,26 @@
|
|||||||
|
# GoEdge目标
|
||||||
|
做一款人人用得起的CDN & WAF系统。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 特性
|
||||||
|
* `免费` - 开源、免费、自由、开放
|
||||||
|
* `简单` - 架构简单清晰,安装简单,使用简单,运维简单
|
||||||
|
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
[点这里查看文档](https://github.com/TeaOSLab/EdgeDocs)
|
||||||
|
|
||||||
|
## 架构
|
||||||

|

|
||||||
|
|
||||||
|
其中的组件源码地址如下:
|
||||||
|
* [边缘节点](https://github.com/TeaOSLab/EdgeNode)
|
||||||
|
* [API节点](https://github.com/TeaOSLab/EdgeAPI)
|
||||||
|
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
有什么问题和建议都可以加入QQ群 `659832182`。
|
||||||
|
|
||||||
|
## 感谢
|
||||||
|
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
|
||||||
2
build/configs/.gitignore
vendored
2
build/configs/.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
|
api.yaml
|
||||||
server.yaml
|
server.yaml
|
||||||
api_db.yaml
|
api_db.yaml
|
||||||
api-123.yaml
|
|
||||||
*.pem
|
*.pem
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
rpc:
|
|
||||||
endpoints:
|
|
||||||
- http://192.168.2.40:8003
|
|
||||||
nodeId: H6sjDf779jimnVPnBFSgZxvr6Ca0wQ0z
|
|
||||||
secret: hMHjmEng0SIcT3yiA3HIoUjogwAC9cur
|
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ func main() {
|
|||||||
err := nodes.NewAdminNode().InstallSystemService()
|
err := nodes.NewAdminNode().InstallSystemService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("[ERROR]install failed: " + err.Error())
|
fmt.Println("[ERROR]install failed: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("done")
|
fmt.Println("done")
|
||||||
})
|
})
|
||||||
|
|||||||
BIN
doc/screenshot.png
Normal file
BIN
doc/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
@@ -10,15 +10,16 @@ import (
|
|||||||
type AdminModuleCode = string
|
type AdminModuleCode = string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AdminModuleCodeServer AdminModuleCode = "server" // 网站
|
AdminModuleCodeDashboard AdminModuleCode = "dashboard" // 看板
|
||||||
AdminModuleCodeNode AdminModuleCode = "node" // 节点
|
AdminModuleCodeServer AdminModuleCode = "server" // 网站
|
||||||
AdminModuleCodeDNS AdminModuleCode = "dns" // DNS
|
AdminModuleCodeNode AdminModuleCode = "node" // 节点
|
||||||
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
|
AdminModuleCodeDNS AdminModuleCode = "dns" // DNS
|
||||||
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
|
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
|
||||||
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
|
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
|
||||||
AdminModuleCodeLog AdminModuleCode = "log" // 日志
|
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
|
||||||
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
|
AdminModuleCodeLog AdminModuleCode = "log" // 日志
|
||||||
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
|
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
|
||||||
|
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
|
||||||
)
|
)
|
||||||
|
|
||||||
var sharedAdminModuleMapping = map[int64]*AdminModuleList{} // adminId => AdminModuleList
|
var sharedAdminModuleMapping = map[int64]*AdminModuleList{} // adminId => AdminModuleList
|
||||||
@@ -109,7 +110,7 @@ func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
|
|||||||
list, ok2 := sharedAdminModuleMapping[adminId]
|
list, ok2 := sharedAdminModuleMapping[adminId]
|
||||||
if ok2 {
|
if ok2 {
|
||||||
if list.IsSuper {
|
if list.IsSuper {
|
||||||
return AdminModuleCodeServer, true
|
return AdminModuleCodeDashboard, true
|
||||||
} else if len(list.Modules) > 0 {
|
} else if len(list.Modules) > 0 {
|
||||||
return list.Modules[0].Code, true
|
return list.Modules[0].Code, true
|
||||||
}
|
}
|
||||||
@@ -132,6 +133,11 @@ func FindAdminFullname(adminId int64) string {
|
|||||||
// 所有权限列表
|
// 所有权限列表
|
||||||
func AllModuleMaps() []maps.Map {
|
func AllModuleMaps() []maps.Map {
|
||||||
return []maps.Map{
|
return []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "看板",
|
||||||
|
"code": AdminModuleCodeDashboard,
|
||||||
|
"url": "/dashboard",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "网站服务",
|
"name": "网站服务",
|
||||||
"code": AdminModuleCodeServer,
|
"code": AdminModuleCodeServer,
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ func LoadAPIConfig() (*APIConfig, error) {
|
|||||||
localFile := Tea.ConfigFile("api.yaml")
|
localFile := Tea.ConfigFile("api.yaml")
|
||||||
isFromLocal := false
|
isFromLocal := false
|
||||||
paths := []string{localFile}
|
paths := []string{localFile}
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, homeErr := os.UserHomeDir()
|
||||||
if err == nil {
|
if homeErr == nil {
|
||||||
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
|
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
|
||||||
}
|
}
|
||||||
paths = append(paths, "/etc/"+teaconst.ProcessName+"/api.yaml")
|
paths = append(paths, "/etc/"+teaconst.ProcessName+"/api.yaml")
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
|
var err error
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
data, err = ioutil.ReadFile(path)
|
data, err = ioutil.ReadFile(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -69,8 +70,8 @@ func (this *APIConfig) WriteFile(path string) error {
|
|||||||
// 写入 ~/ 和 /etc/ 目录,因为是备份需要,所以不需要提示错误信息
|
// 写入 ~/ 和 /etc/ 目录,因为是备份需要,所以不需要提示错误信息
|
||||||
// 写入 ~/.edge-admin/
|
// 写入 ~/.edge-admin/
|
||||||
filename := filepath.Base(path)
|
filename := filepath.Base(path)
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, homeErr := os.UserHomeDir()
|
||||||
if err == nil {
|
if homeErr == nil {
|
||||||
dir := homeDir + "/." + teaconst.ProcessName
|
dir := homeDir + "/." + teaconst.ProcessName
|
||||||
stat, err := os.Stat(dir)
|
stat, err := os.Stat(dir)
|
||||||
if err == nil && stat.IsDir() {
|
if err == nil && stat.IsDir() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.0.9"
|
Version = "0.0.10"
|
||||||
|
|
||||||
ProductName = "Edge Admin"
|
ProductName = "Edge Admin"
|
||||||
ProcessName = "edge-admin"
|
ProcessName = "edge-admin"
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ func (this *RPCClient) NodeClusterRPC() pb.NodeClusterServiceClient {
|
|||||||
return pb.NewNodeClusterServiceClient(this.pickConn())
|
return pb.NewNodeClusterServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) NodeClusterFirewallActionRPC() pb.NodeClusterFirewallActionServiceClient {
|
||||||
|
return pb.NewNodeClusterFirewallActionServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
func (this *RPCClient) NodeGroupRPC() pb.NodeGroupServiceClient {
|
func (this *RPCClient) NodeGroupRPC() pb.NodeGroupServiceClient {
|
||||||
return pb.NewNodeGroupServiceClient(this.pickConn())
|
return pb.NewNodeGroupServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -91,6 +95,34 @@ func (this *RPCClient) ServerRPC() pb.ServerServiceClient {
|
|||||||
return pb.NewServerServiceClient(this.pickConn())
|
return pb.NewServerServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerClientSystemMonthlyStatRPC() pb.ServerClientSystemMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerClientSystemMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerClientBrowserMonthlyStatRPC() pb.ServerClientBrowserMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerClientBrowserMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerRegionCountryMonthlyStatRPC() pb.ServerRegionCountryMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerRegionCountryMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerRegionProvinceMonthlyStatRPC() pb.ServerRegionProvinceMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerRegionProvinceMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerRegionCityMonthlyStatRPC() pb.ServerRegionCityMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerRegionCityMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerRegionProviderMonthlyStatRPC() pb.ServerRegionProviderMonthlyStatServiceClient {
|
||||||
|
return pb.NewServerRegionProviderMonthlyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerHTTPFirewallDailyStatRPC() pb.ServerHTTPFirewallDailyStatServiceClient {
|
||||||
|
return pb.NewServerHTTPFirewallDailyStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
func (this *RPCClient) ServerGroupRPC() pb.ServerGroupServiceClient {
|
func (this *RPCClient) ServerGroupRPC() pb.ServerGroupServiceClient {
|
||||||
return pb.NewServerGroupServiceClient(this.pickConn())
|
return pb.NewServerGroupServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -236,6 +268,10 @@ func (this *RPCClient) DNSRPC() pb.DNSServiceClient {
|
|||||||
return pb.NewDNSServiceClient(this.pickConn())
|
return pb.NewDNSServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) DNSTaskRPC() pb.DNSTaskServiceClient {
|
||||||
|
return pb.NewDNSTaskServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
func (this *RPCClient) ACMEUserRPC() pb.ACMEUserServiceClient {
|
func (this *RPCClient) ACMEUserRPC() pb.ACMEUserServiceClient {
|
||||||
return pb.NewACMEUserServiceClient(this.pickConn())
|
return pb.NewACMEUserServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,42 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 将IP转换为整型
|
// 将IP转换为整型
|
||||||
func IP2Long(ip string) uint32 {
|
func IP2Long(ip string) uint64 {
|
||||||
s := net.ParseIP(ip)
|
s := net.ParseIP(ip)
|
||||||
if s == nil {
|
if len(s) != 16 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) == 16 {
|
if strings.Contains(ip, ":") { // IPv6
|
||||||
return binary.BigEndian.Uint32(s[12:16])
|
bigInt := big.NewInt(0)
|
||||||
|
bigInt.SetBytes(s.To16())
|
||||||
|
return bigInt.Uint64()
|
||||||
}
|
}
|
||||||
return binary.BigEndian.Uint32(s)
|
return uint64(binary.BigEndian.Uint32(s.To4()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为IPv4
|
||||||
|
func IsIPv4(ip string) bool {
|
||||||
|
if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if IP2Long(ip) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为IPv6
|
||||||
|
func IsIPv6(ip string) bool {
|
||||||
|
if !strings.Contains(ip, ":") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(net.ParseIP(ip)) == net.IPv6len
|
||||||
}
|
}
|
||||||
|
|||||||
85
internal/utils/ip_utils_test.go
Normal file
85
internal/utils/ip_utils_test.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIP2Long(t *testing.T) {
|
||||||
|
for _, ip := range []string{
|
||||||
|
"0.0.0.1",
|
||||||
|
"0.0.0.2",
|
||||||
|
"127.0.0.1",
|
||||||
|
"192.0.0.2",
|
||||||
|
"255.255.255.255",
|
||||||
|
"2001:db8:0:1::101",
|
||||||
|
"2001:db8:0:1::102",
|
||||||
|
"2406:8c00:0:3409:133:18:203:0",
|
||||||
|
"2406:8c00:0:3409:133:18:203:158",
|
||||||
|
"2406:8c00:0:3409:133:18:203:159",
|
||||||
|
"2406:8c00:0:3409:133:18:203:160",
|
||||||
|
} {
|
||||||
|
t.Log(ip, " -> ", IP2Long(ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsIPv4(t *testing.T) {
|
||||||
|
type testIP struct {
|
||||||
|
ip string
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
for _, item := range []testIP{
|
||||||
|
{
|
||||||
|
ip: "1",
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "192.168.0.1",
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "1.1.0.1",
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "255.255.255.255",
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "192.168.0.1233",
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if IsIPv4(item.ip) != item.ok {
|
||||||
|
t.Fatal(item.ip, "should be", item.ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsIPv6(t *testing.T) {
|
||||||
|
type testIP struct {
|
||||||
|
ip string
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
for _, item := range []testIP{
|
||||||
|
{
|
||||||
|
ip: "1",
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "2406:8c00:0:3409:133:18:203:158",
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "2406::8c00:0:3409:133:18:203:158",
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "2001:db8:0:1::101",
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if IsIPv6(item.ip) != item.ok {
|
||||||
|
t.Fatal(item.ip, "should be", item.ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,26 @@ func FormatBytes(bytes int64) string {
|
|||||||
if bytes < 1024 {
|
if bytes < 1024 {
|
||||||
return FormatInt64(bytes) + "B"
|
return FormatInt64(bytes) + "B"
|
||||||
} else if bytes < 1024*1024 {
|
} else if bytes < 1024*1024 {
|
||||||
return fmt.Sprintf("%.2fK", float64(bytes)/1024)
|
return fmt.Sprintf("%.2fKB", float64(bytes)/1024)
|
||||||
} else if bytes < 1024*1024*1024 {
|
} else if bytes < 1024*1024*1024 {
|
||||||
return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
|
return fmt.Sprintf("%.2fMB", float64(bytes)/1024/1024)
|
||||||
} else if bytes < 1024*1024*1024*1024 {
|
} else if bytes < 1024*1024*1024*1024 {
|
||||||
return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
|
return fmt.Sprintf("%.2fGB", float64(bytes)/1024/1024/1024)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%.2fP", float64(bytes)/1024/1024/1024/1024)
|
return fmt.Sprintf("%.2fPB", float64(bytes)/1024/1024/1024/1024)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatBits(bits int64) string {
|
||||||
|
if bits < 1000 {
|
||||||
|
return FormatInt64(bits) + "B"
|
||||||
|
} else if bits < 1000*1000 {
|
||||||
|
return fmt.Sprintf("%.2fKB", float64(bits)/1000)
|
||||||
|
} else if bits < 1000*1000*1000 {
|
||||||
|
return fmt.Sprintf("%.2fMB", float64(bits)/1000/1000)
|
||||||
|
} else if bits < 1000*1000*1000*1000 {
|
||||||
|
return fmt.Sprintf("%.2fGB", float64(bits)/1000/10001000)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%.2fPB", float64(bits)/1000/1000/1000/1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (this *InstallManualAction) Init() {
|
|||||||
func (this *InstallManualAction) RunGet(params struct {
|
func (this *InstallManualAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "manual")
|
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "manual")
|
||||||
|
|
||||||
nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (this *InstallNodesAction) Init() {
|
|||||||
func (this *InstallNodesAction) RunGet(params struct {
|
func (this *InstallNodesAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "register")
|
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "register")
|
||||||
|
|
||||||
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
|
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (this *InstallRemoteAction) Init() {
|
|||||||
func (this *InstallRemoteAction) RunGet(params struct {
|
func (this *InstallRemoteAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "install")
|
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "install")
|
||||||
|
|
||||||
nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ func (this *NodeAction) RunGet(params struct {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(dnsRouteMaps) == 0 {
|
||||||
|
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
|
||||||
|
"name": "",
|
||||||
|
"code": "",
|
||||||
|
})
|
||||||
|
}
|
||||||
this.Data["dnsRoutes"] = dnsRouteMaps
|
this.Data["dnsRoutes"] = dnsRouteMaps
|
||||||
this.Data["dnsRecordName"] = recordName
|
this.Data["dnsRecordName"] = recordName
|
||||||
this.Data["dnsRecordValue"] = recordValue
|
this.Data["dnsRecordValue"] = recordValue
|
||||||
@@ -143,6 +149,24 @@ func (this *NodeAction) RunGet(params struct {
|
|||||||
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否有新版本
|
||||||
|
if len(status.OS) > 0 {
|
||||||
|
checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
|
||||||
|
Os: status.OS,
|
||||||
|
Arch: status.Arch,
|
||||||
|
CurrentVersion: status.BuildVersion,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
|
||||||
|
this.Data["newVersion"] = checkVersionResp.NewVersion
|
||||||
|
} else {
|
||||||
|
this.Data["shouldUpgrade"] = false
|
||||||
|
this.Data["newVersion"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
// 分组
|
// 分组
|
||||||
var groupMap maps.Map = nil
|
var groupMap maps.Map = nil
|
||||||
if node.Group != nil {
|
if node.Group != nil {
|
||||||
@@ -175,14 +199,20 @@ func (this *NodeAction) RunGet(params struct {
|
|||||||
"isOn": node.IsOn,
|
"isOn": node.IsOn,
|
||||||
|
|
||||||
"status": maps.Map{
|
"status": maps.Map{
|
||||||
"isActive": status.IsActive,
|
"isActive": status.IsActive,
|
||||||
"updatedAt": status.UpdatedAt,
|
"updatedAt": status.UpdatedAt,
|
||||||
"hostname": status.Hostname,
|
"hostname": status.Hostname,
|
||||||
"cpuUsage": status.CPUUsage,
|
"cpuUsage": status.CPUUsage,
|
||||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||||
"memUsage": status.MemoryUsage,
|
"memUsage": status.MemoryUsage,
|
||||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||||
"connectionCount": status.ConnectionCount,
|
"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),
|
||||||
},
|
},
|
||||||
|
|
||||||
"group": groupMap,
|
"group": groupMap,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ func (this *StatusAction) RunPost(params struct {
|
|||||||
"isOk": node.InstallStatus.IsOk,
|
"isOk": node.InstallStatus.IsOk,
|
||||||
"updatedAt": node.InstallStatus.UpdatedAt,
|
"updatedAt": node.InstallStatus.UpdatedAt,
|
||||||
"error": node.InstallStatus.Error,
|
"error": node.InstallStatus.Error,
|
||||||
|
"errorCode": node.InstallStatus.ErrorCode,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.Data["installStatus"] = nil
|
this.Data["installStatus"] = nil
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package firewallActions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreatePopupAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreatePopupAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreatePopupAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
this.Data["clusterId"] = params.ClusterId
|
||||||
|
this.Data["actionTypes"] = firewallconfigs.FindAllFirewallActionTypes()
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreatePopupAction) RunPost(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
Name string
|
||||||
|
EventLevel string
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// ipset
|
||||||
|
IpsetWhiteName string
|
||||||
|
IpsetBlackName string
|
||||||
|
IpsetAutoAddToIPTables bool
|
||||||
|
IpsetAutoAddToFirewalld bool
|
||||||
|
|
||||||
|
// script
|
||||||
|
ScriptPath string
|
||||||
|
|
||||||
|
// http api
|
||||||
|
HttpAPIURL string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
CSRF *actionutils.CSRF
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("创建WAF动作")
|
||||||
|
|
||||||
|
params.Must.
|
||||||
|
Field("name", params.Name).
|
||||||
|
Require("请输入动作名称").
|
||||||
|
Field("type", params.Type).
|
||||||
|
Require("请选择动作类型")
|
||||||
|
|
||||||
|
var actionParams interface{} = nil
|
||||||
|
switch params.Type {
|
||||||
|
case firewallconfigs.FirewallActionTypeIPSet:
|
||||||
|
params.Must.
|
||||||
|
Field("ipsetWhiteName", params.IpsetWhiteName).
|
||||||
|
Require("请输入IPSet白名单名称").
|
||||||
|
Match(`^\w+$`, "请输入正确的IPSet白名单名称").
|
||||||
|
Field("ipsetBlackName", params.IpsetBlackName).
|
||||||
|
Require("请输入IPSet黑名单名称").
|
||||||
|
Match(`^\w+$`, "请输入正确的IPSet黑名单名称")
|
||||||
|
|
||||||
|
actionParams = &firewallconfigs.FirewallActionIPSetConfig{
|
||||||
|
WhiteName: params.IpsetWhiteName,
|
||||||
|
BlackName: params.IpsetBlackName,
|
||||||
|
AutoAddToIPTables: params.IpsetAutoAddToIPTables,
|
||||||
|
AutoAddToFirewalld: params.IpsetAutoAddToFirewalld,
|
||||||
|
}
|
||||||
|
case firewallconfigs.FirewallActionTypeIPTables:
|
||||||
|
actionParams = &firewallconfigs.FirewallActionIPTablesConfig{}
|
||||||
|
case firewallconfigs.FirewallActionTypeFirewalld:
|
||||||
|
actionParams = &firewallconfigs.FirewallActionFirewalldConfig{}
|
||||||
|
case firewallconfigs.FirewallActionTypeScript:
|
||||||
|
params.Must.
|
||||||
|
Field("scriptPath", params.ScriptPath).
|
||||||
|
Require("请输入脚本路径")
|
||||||
|
actionParams = &firewallconfigs.FirewallActionScriptConfig{
|
||||||
|
Path: params.ScriptPath,
|
||||||
|
}
|
||||||
|
case firewallconfigs.FirewallActionTypeHTTPAPI:
|
||||||
|
params.Must.
|
||||||
|
Field("httpAPIURL", params.HttpAPIURL).
|
||||||
|
Require("请输入API URL").
|
||||||
|
Match(`^(http|https):`, "API地址必须以http://或https://开头")
|
||||||
|
actionParams = &firewallconfigs.FirewallActionHTTPAPIConfig{
|
||||||
|
URL: params.HttpAPIURL,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
this.Fail("选择的类型'" + params.Type + "'暂时不支持")
|
||||||
|
}
|
||||||
|
|
||||||
|
actionParamsJSON, err := json.Marshal(actionParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = this.RPC().NodeClusterFirewallActionRPC().CreateNodeClusterFirewallAction(this.AdminContext(), &pb.CreateNodeClusterFirewallActionRequest{
|
||||||
|
NodeClusterId: params.ClusterId,
|
||||||
|
Name: params.Name,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
|
Type: params.Type,
|
||||||
|
ParamsJSON: actionParamsJSON,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package firewallActions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) RunPost(params struct {
|
||||||
|
ActionId int64
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("删除WAF动作 %d", params.ActionId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NodeClusterFirewallActionRPC().DeleteNodeClusterFirewallAction(this.AdminContext(), &pb.DeleteNodeClusterFirewallActionRequest{NodeClusterFirewallActionId: params.ActionId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package firewallActions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IndexAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) Init() {
|
||||||
|
this.Nav("", "setting", "")
|
||||||
|
this.SecondMenu("firewallAction")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
actionsResp, err := this.RPC().NodeClusterFirewallActionRPC().FindAllEnabledNodeClusterFirewallActions(this.AdminContext(), &pb.FindAllEnabledNodeClusterFirewallActionsRequest{NodeClusterId: params.ClusterId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
levelMaps := map[string][]maps.Map{} // level => actionMaps
|
||||||
|
for _, action := range actionsResp.NodeClusterFirewallActions {
|
||||||
|
actionMaps, ok := levelMaps[action.EventLevel]
|
||||||
|
if !ok {
|
||||||
|
actionMaps = []maps.Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
actionMaps = append(actionMaps, maps.Map{
|
||||||
|
"id": action.Id,
|
||||||
|
"name": action.Name,
|
||||||
|
"type": action.Type,
|
||||||
|
"typeName": firewallconfigs.FindFirewallActionTypeName(action.Type),
|
||||||
|
})
|
||||||
|
levelMaps[action.EventLevel] = actionMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
levelMaps2 := []maps.Map{} // []levelMap
|
||||||
|
hasActions := false
|
||||||
|
for _, level := range firewallconfigs.FindAllFirewallEventLevels() {
|
||||||
|
actionMaps, ok := levelMaps[level.Code]
|
||||||
|
if !ok {
|
||||||
|
actionMaps = []maps.Map{}
|
||||||
|
} else {
|
||||||
|
hasActions = true
|
||||||
|
}
|
||||||
|
|
||||||
|
levelMaps2 = append(levelMaps2, maps.Map{
|
||||||
|
"name": level.Name,
|
||||||
|
"code": level.Code,
|
||||||
|
"actions": actionMaps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["levels"] = levelMaps2
|
||||||
|
this.Data["hasActions"] = hasActions
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package firewallActions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"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 {
|
||||||
|
ActionId int64
|
||||||
|
}) {
|
||||||
|
actionResp, err := this.RPC().NodeClusterFirewallActionRPC().FindEnabledNodeClusterFirewallAction(this.AdminContext(), &pb.FindEnabledNodeClusterFirewallActionRequest{NodeClusterFirewallActionId: params.ActionId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action := actionResp.NodeClusterFirewallAction
|
||||||
|
if action == nil {
|
||||||
|
this.NotFound("nodeClusterFirewallAction", params.ActionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
actionParams := maps.Map{}
|
||||||
|
if len(action.ParamsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(action.ParamsJSON, &actionParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["action"] = maps.Map{
|
||||||
|
"id": action.Id,
|
||||||
|
"name": action.Name,
|
||||||
|
"eventLevel": action.EventLevel,
|
||||||
|
"params": actionParams,
|
||||||
|
"type": action.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用参数
|
||||||
|
this.Data["actionTypes"] = firewallconfigs.FindAllFirewallActionTypes()
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdatePopupAction) RunPost(params struct {
|
||||||
|
ActionId int64
|
||||||
|
Name string
|
||||||
|
EventLevel string
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// ipset
|
||||||
|
IpsetWhiteName string
|
||||||
|
IpsetBlackName string
|
||||||
|
IpsetAutoAddToIPTables bool
|
||||||
|
IpsetAutoAddToFirewalld bool
|
||||||
|
|
||||||
|
// script
|
||||||
|
ScriptPath string
|
||||||
|
|
||||||
|
// http api
|
||||||
|
HttpAPIURL string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
CSRF *actionutils.CSRF
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("修改WAF动作 %d", params.ActionId)
|
||||||
|
|
||||||
|
params.Must.
|
||||||
|
Field("name", params.Name).
|
||||||
|
Require("请输入动作名称").
|
||||||
|
Field("type", params.Type).
|
||||||
|
Require("请选择动作类型")
|
||||||
|
|
||||||
|
var actionParams interface{} = nil
|
||||||
|
switch params.Type {
|
||||||
|
case firewallconfigs.FirewallActionTypeIPSet:
|
||||||
|
params.Must.
|
||||||
|
Field("ipsetWhiteName", params.IpsetWhiteName).
|
||||||
|
Require("请输入IPSet白名单名称").
|
||||||
|
Match(`^\w+$`, "请输入正确的IPSet白名单名称").
|
||||||
|
Field("ipsetBlackName", params.IpsetBlackName).
|
||||||
|
Require("请输入IPSet黑名单名称").
|
||||||
|
Match(`^\w+$`, "请输入正确的IPSet黑名单名称")
|
||||||
|
|
||||||
|
actionParams = &firewallconfigs.FirewallActionIPSetConfig{
|
||||||
|
WhiteName: params.IpsetWhiteName,
|
||||||
|
BlackName: params.IpsetBlackName,
|
||||||
|
AutoAddToIPTables: params.IpsetAutoAddToIPTables,
|
||||||
|
AutoAddToFirewalld: params.IpsetAutoAddToFirewalld,
|
||||||
|
}
|
||||||
|
case firewallconfigs.FirewallActionTypeIPTables:
|
||||||
|
actionParams = &firewallconfigs.FirewallActionIPTablesConfig{}
|
||||||
|
case firewallconfigs.FirewallActionTypeFirewalld:
|
||||||
|
actionParams = &firewallconfigs.FirewallActionFirewalldConfig{}
|
||||||
|
case firewallconfigs.FirewallActionTypeScript:
|
||||||
|
params.Must.
|
||||||
|
Field("scriptPath", params.ScriptPath).
|
||||||
|
Require("请输入脚本路径")
|
||||||
|
actionParams = &firewallconfigs.FirewallActionScriptConfig{
|
||||||
|
Path: params.ScriptPath,
|
||||||
|
}
|
||||||
|
case firewallconfigs.FirewallActionTypeHTTPAPI:
|
||||||
|
params.Must.
|
||||||
|
Field("httpAPIURL", params.HttpAPIURL).
|
||||||
|
Require("请输入API URL").
|
||||||
|
Match(`^(http|https):`, "API地址必须以http://或https://开头")
|
||||||
|
actionParams = &firewallconfigs.FirewallActionHTTPAPIConfig{
|
||||||
|
URL: params.HttpAPIURL,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
this.Fail("选择的类型'" + params.Type + "'暂时不支持")
|
||||||
|
}
|
||||||
|
|
||||||
|
actionParamsJSON, err := json.Marshal(actionParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = this.RPC().NodeClusterFirewallActionRPC().UpdateNodeClusterFirewallAction(this.AdminContext(), &pb.UpdateNodeClusterFirewallActionRequest{
|
||||||
|
NodeClusterFirewallActionId: params.ActionId,
|
||||||
|
Name: params.Name,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
|
Type: params.Type,
|
||||||
|
ParamsJSON: actionParamsJSON,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/cache"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/cache"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/dns"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/dns"
|
||||||
|
firewallActions "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/firewall-actions"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/services"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/services"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/toa"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/toa"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/waf"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/waf"
|
||||||
@@ -43,6 +44,13 @@ func init() {
|
|||||||
GetPost("", new(services.IndexAction)).
|
GetPost("", new(services.IndexAction)).
|
||||||
GetPost("/status", new(services.StatusAction)).
|
GetPost("/status", new(services.StatusAction)).
|
||||||
|
|
||||||
|
// 防火墙动作
|
||||||
|
Prefix("/clusters/cluster/settings/firewall-actions").
|
||||||
|
Get("", new(firewallActions.IndexAction)).
|
||||||
|
GetPost("/createPopup", new(firewallActions.CreatePopupAction)).
|
||||||
|
GetPost("/updatePopup", new(firewallActions.UpdatePopupAction)).
|
||||||
|
Post("/delete", new(firewallActions.DeleteAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (this *UpgradeRemoteAction) Init() {
|
|||||||
func (this *UpgradeRemoteAction) RunGet(params struct {
|
func (this *UpgradeRemoteAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "upgrade")
|
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "upgrade")
|
||||||
|
|
||||||
nodes := []maps.Map{}
|
nodes := []maps.Map{}
|
||||||
resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||||
|
|||||||
@@ -1,12 +1,34 @@
|
|||||||
package cluster
|
package cluster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 安装升级相关的左侧菜单
|
// 安装升级相关的左侧菜单
|
||||||
func LeftMenuItemsForInstall(clusterId int64, selectedItem string) []maps.Map {
|
func LeftMenuItemsForInstall(ctx context.Context, clusterId int64, selectedItem string) []maps.Map {
|
||||||
|
rpcClient, _ := rpc.SharedRPC()
|
||||||
|
countNotInstalled := int64(0)
|
||||||
|
countUpgrade := int64(0)
|
||||||
|
if rpcClient != nil {
|
||||||
|
{
|
||||||
|
resp, err := rpcClient.NodeRPC().CountAllNotInstalledNodesWithClusterId(ctx, &pb.CountAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: clusterId})
|
||||||
|
if err == nil {
|
||||||
|
countNotInstalled = resp.Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
resp, err := rpcClient.NodeRPC().CountAllUpgradeNodesWithClusterId(ctx, &pb.CountAllUpgradeNodesWithClusterIdRequest{NodeClusterId: clusterId})
|
||||||
|
if err == nil {
|
||||||
|
countUpgrade = resp.Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return []maps.Map{
|
return []maps.Map{
|
||||||
{
|
{
|
||||||
"name": "手动安装",
|
"name": "手动安装",
|
||||||
@@ -19,12 +41,12 @@ func LeftMenuItemsForInstall(clusterId int64, selectedItem string) []maps.Map {
|
|||||||
"isActive": selectedItem == "register",
|
"isActive": selectedItem == "register",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "远程安装",
|
"name": "远程安装(" + strconv.FormatInt(countNotInstalled, 10) + ")",
|
||||||
"url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(clusterId),
|
"url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(clusterId),
|
||||||
"isActive": selectedItem == "install",
|
"isActive": selectedItem == "install",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "远程升级",
|
"name": "远程升级(" + strconv.FormatInt(countUpgrade, 10) + ")",
|
||||||
"url": "/clusters/cluster/upgradeRemote?clusterId=" + numberutils.FormatInt64(clusterId),
|
"url": "/clusters/cluster/upgradeRemote?clusterId=" + numberutils.FormatInt64(clusterId),
|
||||||
"isActive": selectedItem == "upgrade",
|
"isActive": selectedItem == "upgrade",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
|
|||||||
"isActive": selectedItem == "waf",
|
"isActive": selectedItem == "waf",
|
||||||
"isOn": cluster.HttpFirewallPolicyId > 0,
|
"isOn": cluster.HttpFirewallPolicyId > 0,
|
||||||
})
|
})
|
||||||
|
items = append(items, maps.Map{
|
||||||
|
"name": "WAF动作",
|
||||||
|
"url": "/clusters/cluster/settings/firewall-actions?clusterId=" + clusterId,
|
||||||
|
"isActive": selectedItem == "firewallAction",
|
||||||
|
})
|
||||||
items = append(items, maps.Map{
|
items = append(items, maps.Map{
|
||||||
"name": "健康检查",
|
"name": "健康检查",
|
||||||
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
|
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
|
||||||
@@ -95,6 +100,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
|
|||||||
"name": "DNS设置",
|
"name": "DNS设置",
|
||||||
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
|
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
|
||||||
"isActive": selectedItem == "dns",
|
"isActive": selectedItem == "dns",
|
||||||
|
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
|
||||||
})
|
})
|
||||||
items = append(items, maps.Map{
|
items = append(items, maps.Map{
|
||||||
"name": "系统服务",
|
"name": "系统服务",
|
||||||
|
|||||||
24
internal/web/actions/default/clusters/tasks/deleteBatch.go
Normal file
24
internal/web/actions/default/clusters/tasks/deleteBatch.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteBatchAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteBatchAction) RunPost(params struct {
|
||||||
|
TaskIds []int64
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("批量删除节点同步任务")
|
||||||
|
|
||||||
|
_, err := this.RPC().NodeTaskRPC().DeleteNodeTasks(this.AdminContext(), &pb.DeleteNodeTasksRequest{NodeTaskIds: params.TaskIds})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ func init() {
|
|||||||
GetPost("/listPopup", new(ListPopupAction)).
|
GetPost("/listPopup", new(ListPopupAction)).
|
||||||
Post("/check", new(CheckAction)).
|
Post("/check", new(CheckAction)).
|
||||||
Post("/delete", new(DeleteAction)).
|
Post("/delete", new(DeleteAction)).
|
||||||
|
Post("/deleteBatch", new(DeleteBatchAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ package dashboard
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
@@ -17,13 +22,75 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
// 取得用户的权限
|
// 取得用户的权限
|
||||||
module, ok := configloaders.FindFirstAdminModule(this.AdminId())
|
module, ok := configloaders.FindFirstAdminModule(this.AdminId())
|
||||||
if ok {
|
if ok {
|
||||||
for _, m := range configloaders.AllModuleMaps() {
|
if module != "dashboard" {
|
||||||
if m.GetString("code") == module {
|
for _, m := range configloaders.AllModuleMaps() {
|
||||||
this.RedirectURL(m.GetString("url"))
|
if m.GetString("code") == module {
|
||||||
return
|
this.RedirectURL(m.GetString("url"))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取看板数据
|
||||||
|
resp, err := this.RPC().AdminRPC().ComposeAdminDashboard(this.AdminContext(), &pb.ComposeAdminDashboardRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["dashboard"] = maps.Map{
|
||||||
|
"countServers": resp.CountServers,
|
||||||
|
"countNodeClusters": resp.CountNodeClusters,
|
||||||
|
"countNodes": resp.CountNodes,
|
||||||
|
"countUsers": resp.CountUsers,
|
||||||
|
"countAPINodes": resp.CountAPINodes,
|
||||||
|
"countDBNodes": resp.CountDBNodes,
|
||||||
|
"countUserNodes": resp.CountUserNodes,
|
||||||
|
|
||||||
|
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
|
||||||
|
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
|
||||||
|
"canGoSettings": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeSetting),
|
||||||
|
"canGoUsers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeUser),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 今日流量
|
||||||
|
todayTrafficBytes := int64(0)
|
||||||
|
if len(resp.DailyTrafficStats) > 0 {
|
||||||
|
todayTrafficBytes = resp.DailyTrafficStats[len(resp.DailyTrafficStats)-1].Bytes
|
||||||
|
}
|
||||||
|
todayTrafficString := numberutils.FormatBits(todayTrafficBytes * 8)
|
||||||
|
result := regexp.MustCompile(`^(?U)(.+)([a-zA-Z]+)$`).FindStringSubmatch(todayTrafficString)
|
||||||
|
if len(result) > 2 {
|
||||||
|
this.Data["todayTraffic"] = result[1]
|
||||||
|
this.Data["todayTrafficUnit"] = result[2]
|
||||||
|
} else {
|
||||||
|
this.Data["todayTraffic"] = todayTrafficString
|
||||||
|
this.Data["todayTrafficUnit"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24小时流量趋势
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.HourlyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"count": math.Ceil((float64(stat.Bytes)*8/1000/1000/1000)*1000) / 1000,
|
||||||
|
"hour": stat.Hour[8:],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["hourlyTrafficStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15天流量趋势
|
||||||
|
{
|
||||||
|
statMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.DailyTrafficStats {
|
||||||
|
statMaps = append(statMaps, maps.Map{
|
||||||
|
"count": math.Ceil((float64(stat.Bytes)*8/1000/1000/1000)*1000) / 1000,
|
||||||
|
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["dailyTrafficStats"] = statMaps
|
||||||
|
}
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||||
server.Prefix("/dashboard").
|
server.Prefix("/dashboard").
|
||||||
|
Data("teaMenu", "dashboard").
|
||||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
||||||
GetPost("", new(IndexAction)).
|
GetPost("", new(IndexAction)).
|
||||||
EndAll()
|
EndAll()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/rands"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreatePopupAction struct {
|
type CreatePopupAction struct {
|
||||||
@@ -32,6 +33,9 @@ func (this *CreatePopupAction) RunGet(params struct{}) {
|
|||||||
}
|
}
|
||||||
this.Data["types"] = typeMaps
|
this.Data["types"] = typeMaps
|
||||||
|
|
||||||
|
// 自动生成CustomHTTP私钥
|
||||||
|
this.Data["paramCustomHTTPSecret"] = rands.HexString(32)
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +55,10 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
ParamApiKey string
|
ParamApiKey string
|
||||||
ParamApiSecret string
|
ParamApiSecret string
|
||||||
|
|
||||||
|
// CustomHTTP
|
||||||
|
ParamCustomHTTPURL string
|
||||||
|
ParamCustomHTTPSecret string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
@@ -89,6 +97,15 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
|
|
||||||
apiParams["apiKey"] = params.ParamApiKey
|
apiParams["apiKey"] = params.ParamApiKey
|
||||||
apiParams["apiSecret"] = params.ParamApiSecret
|
apiParams["apiSecret"] = params.ParamApiSecret
|
||||||
|
case "customHTTP":
|
||||||
|
params.Must.
|
||||||
|
Field("paramCustomHTTPURL", params.ParamCustomHTTPURL).
|
||||||
|
Require("请输入HTTP URL").
|
||||||
|
Match("^(?i)(http|https):", "URL必须以http://或者https://开头").
|
||||||
|
Field("paramCustomHTTPSecret", params.ParamCustomHTTPSecret).
|
||||||
|
Require("请输入私钥")
|
||||||
|
apiParams["url"] = params.ParamCustomHTTPURL
|
||||||
|
apiParams["secret"] = params.ParamCustomHTTPSecret
|
||||||
default:
|
default:
|
||||||
this.Fail("暂时不支持此服务商'" + params.Type + "'")
|
this.Fail("暂时不支持此服务商'" + params.Type + "'")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
ParamApiKey string
|
ParamApiKey string
|
||||||
ParamApiSecret string
|
ParamApiSecret string
|
||||||
|
|
||||||
|
// CustomHTTP
|
||||||
|
ParamCustomHTTPURL string
|
||||||
|
ParamCustomHTTPSecret string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
@@ -124,6 +128,15 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
|||||||
|
|
||||||
apiParams["apiKey"] = params.ParamApiKey
|
apiParams["apiKey"] = params.ParamApiKey
|
||||||
apiParams["apiSecret"] = params.ParamApiSecret
|
apiParams["apiSecret"] = params.ParamApiSecret
|
||||||
|
case "customHTTP":
|
||||||
|
params.Must.
|
||||||
|
Field("paramCustomHTTPURL", params.ParamCustomHTTPURL).
|
||||||
|
Require("请输入HTTP URL").
|
||||||
|
Match("^(?i)(http|https):", "URL必须以http://或者https://开头").
|
||||||
|
Field("paramCustomHTTPSecret", params.ParamCustomHTTPSecret).
|
||||||
|
Require("请输入私钥")
|
||||||
|
apiParams["url"] = params.ParamCustomHTTPURL
|
||||||
|
apiParams["secret"] = params.ParamCustomHTTPSecret
|
||||||
default:
|
default:
|
||||||
this.Fail("暂时不支持此服务商'" + params.Type + "'")
|
this.Fail("暂时不支持此服务商'" + params.Type + "'")
|
||||||
}
|
}
|
||||||
|
|||||||
23
internal/web/actions/default/dns/tasks/check.go
Normal file
23
internal/web/actions/default/dns/tasks/check.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CheckAction) RunPost(params struct{}) {
|
||||||
|
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["isDoing"] = resp.ExistTasks
|
||||||
|
this.Data["hasError"] = resp.ExistError
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
24
internal/web/actions/default/dns/tasks/delete.go
Normal file
24
internal/web/actions/default/dns/tasks/delete.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) RunPost(params struct {
|
||||||
|
TaskId int64
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("删除DNS同步任务 %d", params.TaskId)
|
||||||
|
|
||||||
|
_, err := this.RPC().DNSTaskRPC().DeleteDNSTask(this.AdminContext(), &pb.DeleteDNSTaskRequest{DnsTaskId: params.TaskId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
22
internal/web/actions/default/dns/tasks/init.go
Normal file
22
internal/web/actions/default/dns/tasks/init.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||||
|
"github.com/iwind/TeaGo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||||
|
server.
|
||||||
|
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeDNS)).
|
||||||
|
Helper(clusterutils.NewClustersHelper()).
|
||||||
|
Prefix("/dns/tasks").
|
||||||
|
GetPost("/listPopup", new(ListPopupAction)).
|
||||||
|
Post("/check", new(CheckAction)).
|
||||||
|
Post("/delete", new(DeleteAction)).
|
||||||
|
|
||||||
|
EndAll()
|
||||||
|
})
|
||||||
|
}
|
||||||
84
internal/web/actions/default/dns/tasks/listPopup.go
Normal file
84
internal/web/actions/default/dns/tasks/listPopup.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListPopupAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ListPopupAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ListPopupAction) RunGet(params struct{}) {
|
||||||
|
this.retrieveTasks()
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ListPopupAction) RunPost(params struct {
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
this.retrieveTasks()
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ListPopupAction) retrieveTasks() {
|
||||||
|
resp, err := this.RPC().DNSTaskRPC().FindAllDoingDNSTasks(this.AdminContext(), &pb.FindAllDoingDNSTasksRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
taskMaps := []maps.Map{}
|
||||||
|
for _, task := range resp.DnsTasks {
|
||||||
|
var clusterMap maps.Map = nil
|
||||||
|
var nodeMap maps.Map = nil
|
||||||
|
var serverMap maps.Map = nil
|
||||||
|
var domainMap maps.Map = nil
|
||||||
|
|
||||||
|
if task.NodeCluster != nil {
|
||||||
|
clusterMap = maps.Map{
|
||||||
|
"id": task.NodeCluster.Id,
|
||||||
|
"name": task.NodeCluster.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if task.Node != nil {
|
||||||
|
nodeMap = maps.Map{
|
||||||
|
"id": task.Node.Id,
|
||||||
|
"name": task.Node.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if task.Server != nil {
|
||||||
|
serverMap = maps.Map{
|
||||||
|
"id": task.Server.Id,
|
||||||
|
"name": task.Server.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if task.DnsDomain != nil {
|
||||||
|
domainMap = maps.Map{
|
||||||
|
"id": task.DnsDomain.Id,
|
||||||
|
"name": task.DnsDomain.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
taskMaps = append(taskMaps, maps.Map{
|
||||||
|
"id": task.Id,
|
||||||
|
"type": task.Type,
|
||||||
|
"isDone": task.IsDone,
|
||||||
|
"isOk": task.IsOk,
|
||||||
|
"error": task.Error,
|
||||||
|
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", task.UpdatedAt),
|
||||||
|
"cluster": clusterMap,
|
||||||
|
"node": nodeMap,
|
||||||
|
"server": serverMap,
|
||||||
|
"domain": domainMap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["tasks"] = taskMaps
|
||||||
|
}
|
||||||
@@ -48,6 +48,7 @@ func init() {
|
|||||||
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
|
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
|
||||||
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
|
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
|
||||||
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
|
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
|
||||||
|
GetPost("/ipadmin/test", new(ipadmin.TestAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (this *CreateIPPopupAction) RunGet(params struct {
|
|||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
Type string
|
Type string
|
||||||
}) {
|
}) {
|
||||||
this.Data["type"] = params.Type
|
this.Data["listType"] = params.Type
|
||||||
|
|
||||||
listId, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type)
|
listId, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,41 +40,57 @@ func (this *CreateIPPopupAction) RunPost(params struct {
|
|||||||
IpTo string
|
IpTo string
|
||||||
ExpiredAt int64
|
ExpiredAt int64
|
||||||
Reason string
|
Reason string
|
||||||
|
Type string
|
||||||
|
EventLevel string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
// TODO 校验ListId所属用户
|
// TODO 校验ListId所属用户
|
||||||
|
|
||||||
params.Must.
|
switch params.Type {
|
||||||
Field("ipFrom", params.IpFrom).
|
case "ipv4":
|
||||||
Require("请输入开始IP")
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入开始IP")
|
||||||
|
|
||||||
// 校验IP格式(ipFrom/ipTo)
|
// 校验IP格式(ipFrom/ipTo)
|
||||||
ipFromLong := utils.IP2Long(params.IpFrom)
|
var ipFromLong uint64
|
||||||
if len(params.IpFrom) > 0 {
|
if !utils.IsIPv4(params.IpFrom) {
|
||||||
if ipFromLong == 0 {
|
|
||||||
this.Fail("请输入正确的开始IP")
|
this.Fail("请输入正确的开始IP")
|
||||||
}
|
}
|
||||||
}
|
ipFromLong = utils.IP2Long(params.IpFrom)
|
||||||
|
|
||||||
ipToLong := utils.IP2Long(params.IpTo)
|
var ipToLong uint64
|
||||||
if len(params.IpTo) > 0 {
|
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
|
||||||
if ipToLong == 0 {
|
ipToLong = utils.IP2Long(params.IpTo)
|
||||||
this.Fail("请输入正确的结束IP")
|
this.Fail("请输入正确的结束IP")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
||||||
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
||||||
|
}
|
||||||
|
case "ipv6":
|
||||||
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入IP")
|
||||||
|
|
||||||
|
// 校验IP格式(ipFrom)
|
||||||
|
if !utils.IsIPv6(params.IpFrom) {
|
||||||
|
this.Fail("请输入正确的IPv6地址")
|
||||||
|
}
|
||||||
|
case "all":
|
||||||
|
params.IpFrom = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
|
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
|
||||||
IpListId: params.ListId,
|
IpListId: params.ListId,
|
||||||
IpFrom: params.IpFrom,
|
IpFrom: params.IpFrom,
|
||||||
IpTo: params.IpTo,
|
IpTo: params.IpTo,
|
||||||
ExpiredAt: params.ExpiredAt,
|
ExpiredAt: params.ExpiredAt,
|
||||||
Reason: params.Reason,
|
Reason: params.Reason,
|
||||||
|
Type: params.Type,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
)
|
)
|
||||||
@@ -58,11 +59,13 @@ func (this *ListsAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemMaps = append(itemMaps, maps.Map{
|
itemMaps = append(itemMaps, maps.Map{
|
||||||
"id": item.Id,
|
"id": item.Id,
|
||||||
"ipFrom": item.IpFrom,
|
"ipFrom": item.IpFrom,
|
||||||
"ipTo": item.IpTo,
|
"ipTo": item.IpTo,
|
||||||
"expiredTime": expiredTime,
|
"expiredTime": expiredTime,
|
||||||
"reason": item.Reason,
|
"reason": item.Reason,
|
||||||
|
"type": item.Type,
|
||||||
|
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(item.EventLevel),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["items"] = itemMaps
|
this.Data["items"] = itemMaps
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package ipadmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) Init() {
|
||||||
|
this.Nav("", "", "ipadmin")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) RunGet(params struct {
|
||||||
|
FirewallPolicyId int64
|
||||||
|
}) {
|
||||||
|
this.Data["subMenuItem"] = "test"
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) RunPost(params struct {
|
||||||
|
FirewallPolicyId int64
|
||||||
|
Ip string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
resp, err := this.RPC().HTTPFirewallPolicyRPC().CheckHTTPFirewallPolicyIPStatus(this.AdminContext(), &pb.CheckHTTPFirewallPolicyIPStatusRequest{
|
||||||
|
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||||
|
Ip: params.Ip,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap := maps.Map{
|
||||||
|
"isDone": true,
|
||||||
|
"isFound": resp.IsFound,
|
||||||
|
"isOk": resp.IsOk,
|
||||||
|
"error": resp.Error,
|
||||||
|
"isAllowed": resp.IsAllowed,
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.IpList != nil {
|
||||||
|
resultMap["list"] = maps.Map{
|
||||||
|
"id": resp.IpList.Id,
|
||||||
|
"name": resp.IpList.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resp.IpItem != nil {
|
||||||
|
resultMap["item"] = maps.Map{
|
||||||
|
"id": resp.IpItem.Id,
|
||||||
|
"ipFrom": resp.IpItem.IpFrom,
|
||||||
|
"ipTo": resp.IpItem.IpTo,
|
||||||
|
"reason": resp.IpItem.Reason,
|
||||||
|
"expiredAt": resp.IpItem.ExpiredAt,
|
||||||
|
"expiredTime": timeutil.FormatTime("Y-m-d H:i:s", resp.IpItem.ExpiredAt),
|
||||||
|
"type": resp.IpItem.Type,
|
||||||
|
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(resp.IpItem.EventLevel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.RegionCountry != nil {
|
||||||
|
resultMap["country"] = maps.Map{
|
||||||
|
"id": resp.RegionCountry.Id,
|
||||||
|
"name": resp.RegionCountry.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.RegionProvince != nil {
|
||||||
|
resultMap["province"] = maps.Map{
|
||||||
|
"id": resp.RegionProvince.Id,
|
||||||
|
"name": resp.RegionProvince.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["result"] = resultMap
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -32,13 +32,17 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.Data["item"] = maps.Map{
|
this.Data["item"] = maps.Map{
|
||||||
"id": item.Id,
|
"id": item.Id,
|
||||||
"ipFrom": item.IpFrom,
|
"ipFrom": item.IpFrom,
|
||||||
"ipTo": item.IpTo,
|
"ipTo": item.IpTo,
|
||||||
"expiredAt": item.ExpiredAt,
|
"expiredAt": item.ExpiredAt,
|
||||||
"reason": item.Reason,
|
"reason": item.Reason,
|
||||||
|
"type": item.Type,
|
||||||
|
"eventLevel": item.EventLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Data["type"] = item.Type
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +50,12 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
|||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
ItemId int64
|
ItemId int64
|
||||||
|
|
||||||
IpFrom string
|
IpFrom string
|
||||||
IpTo string
|
IpTo string
|
||||||
ExpiredAt int64
|
ExpiredAt int64
|
||||||
Reason string
|
Reason string
|
||||||
|
Type string
|
||||||
|
EventLevel string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
@@ -59,35 +65,49 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
|||||||
|
|
||||||
// TODO 校验ItemId所属用户
|
// TODO 校验ItemId所属用户
|
||||||
|
|
||||||
params.Must.
|
switch params.Type {
|
||||||
Field("ipFrom", params.IpFrom).
|
case "ipv4":
|
||||||
Require("请输入开始IP")
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入开始IP")
|
||||||
|
|
||||||
// 校验IP格式(ipFrom/ipTo)
|
// 校验IP格式(ipFrom/ipTo)
|
||||||
ipFromLong := utils.IP2Long(params.IpFrom)
|
var ipFromLong uint64
|
||||||
if len(params.IpFrom) > 0 {
|
if !utils.IsIPv4(params.IpFrom) {
|
||||||
if ipFromLong == 0 {
|
|
||||||
this.Fail("请输入正确的开始IP")
|
this.Fail("请输入正确的开始IP")
|
||||||
}
|
}
|
||||||
}
|
ipFromLong = utils.IP2Long(params.IpFrom)
|
||||||
|
|
||||||
ipToLong := utils.IP2Long(params.IpTo)
|
var ipToLong uint64
|
||||||
if len(params.IpTo) > 0 {
|
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
|
||||||
if ipToLong == 0 {
|
ipToLong = utils.IP2Long(params.IpTo)
|
||||||
this.Fail("请输入正确的结束IP")
|
this.Fail("请输入正确的结束IP")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
||||||
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
||||||
|
}
|
||||||
|
case "ipv6":
|
||||||
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入IP")
|
||||||
|
|
||||||
|
// 校验IP格式(ipFrom)
|
||||||
|
if !utils.IsIPv6(params.IpFrom) {
|
||||||
|
this.Fail("请输入正确的IPv6地址")
|
||||||
|
}
|
||||||
|
case "all":
|
||||||
|
params.IpFrom = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
|
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
|
||||||
IpItemId: params.ItemId,
|
IpItemId: params.ItemId,
|
||||||
IpFrom: params.IpFrom,
|
IpFrom: params.IpFrom,
|
||||||
IpTo: params.IpTo,
|
IpTo: params.IpTo,
|
||||||
ExpiredAt: params.ExpiredAt,
|
ExpiredAt: params.ExpiredAt,
|
||||||
Reason: params.Reason,
|
Reason: params.Reason,
|
||||||
|
Type: params.Type,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ func (this *SettingAction) RunPost(params struct {
|
|||||||
RequestURI: reverseProxyConfig.RequestURI,
|
RequestURI: reverseProxyConfig.RequestURI,
|
||||||
StripPrefix: reverseProxyConfig.StripPrefix,
|
StripPrefix: reverseProxyConfig.StripPrefix,
|
||||||
AutoFlush: reverseProxyConfig.AutoFlush,
|
AutoFlush: reverseProxyConfig.AutoFlush,
|
||||||
|
AddHeaders: reverseProxyConfig.AddHeaders,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ func (this *SettingAction) RunPost(params struct {
|
|||||||
RequestURI: reverseProxyConfig.RequestURI,
|
RequestURI: reverseProxyConfig.RequestURI,
|
||||||
StripPrefix: reverseProxyConfig.StripPrefix,
|
StripPrefix: reverseProxyConfig.StripPrefix,
|
||||||
AutoFlush: reverseProxyConfig.AutoFlush,
|
AutoFlush: reverseProxyConfig.AutoFlush,
|
||||||
|
AddHeaders: reverseProxyConfig.AddHeaders,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ func (this *UpdateSchedulingPopupAction) RunGet(params struct {
|
|||||||
if !types.IsSlice(networks) {
|
if !types.IsSlice(networks) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (serverConfig.IsHTTP() && lists.Contains(networks, "http")) ||
|
if (serverConfig.IsHTTPFamily() && lists.Contains(networks, "http")) ||
|
||||||
(serverConfig.IsTCP() && lists.Contains(networks, "tcp")) ||
|
(serverConfig.IsTCPFamily() && lists.Contains(networks, "tcp")) ||
|
||||||
(serverConfig.IsUDP() && lists.Contains(networks, "udp")) ||
|
(serverConfig.IsUDPFamily() && lists.Contains(networks, "udp")) ||
|
||||||
(serverConfig.IsUnix() && lists.Contains(networks, "unix")) {
|
(serverConfig.IsUnixFamily() && lists.Contains(networks, "unix")) {
|
||||||
schedulingTypes = append(schedulingTypes, m)
|
schedulingTypes = append(schedulingTypes, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func (this *GroupsAction) Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *GroupsAction) RunGet(params struct {
|
func (this *GroupsAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
Type string
|
Type string
|
||||||
}) {
|
}) {
|
||||||
@@ -70,5 +71,13 @@ func (this *GroupsAction) RunGet(params struct {
|
|||||||
|
|
||||||
this.Data["groups"] = groupMaps
|
this.Data["groups"] = groupMaps
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ func init() {
|
|||||||
GetPost("", new(IndexAction)).
|
GetPost("", new(IndexAction)).
|
||||||
Get("/ipadmin/allowList", new(ipadmin.AllowListAction)).
|
Get("/ipadmin/allowList", new(ipadmin.AllowListAction)).
|
||||||
Get("/ipadmin/denyList", new(ipadmin.DenyListAction)).
|
Get("/ipadmin/denyList", new(ipadmin.DenyListAction)).
|
||||||
//GetPost("/ipadmin", new(ipadmin.IndexAction)).
|
GetPost("/ipadmin/countries", new(ipadmin.CountriesAction)).
|
||||||
//GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)).
|
GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)).
|
||||||
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
|
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
|
||||||
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
|
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
|
||||||
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
|
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
|
||||||
|
GetPost("/ipadmin/test", new(ipadmin.TestAction)).
|
||||||
|
|
||||||
// 规则相关
|
// 规则相关
|
||||||
Get("/groups", new(GroupsAction)).
|
Get("/groups", new(GroupsAction)).
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AllowListAction struct {
|
type AllowListAction struct {
|
||||||
@@ -69,14 +71,25 @@ func (this *AllowListAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemMaps = append(itemMaps, maps.Map{
|
itemMaps = append(itemMaps, maps.Map{
|
||||||
"id": item.Id,
|
"id": item.Id,
|
||||||
"ipFrom": item.IpFrom,
|
"ipFrom": item.IpFrom,
|
||||||
"ipTo": item.IpTo,
|
"ipTo": item.IpTo,
|
||||||
"expiredTime": expiredTime,
|
"expiredTime": expiredTime,
|
||||||
"reason": item.Reason,
|
"reason": item.Reason,
|
||||||
|
"type": item.Type,
|
||||||
|
"isExpired": item.ExpiredAt > 0 && item.ExpiredAt < time.Now().Unix(),
|
||||||
|
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(item.EventLevel),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["items"] = itemMaps
|
this.Data["items"] = itemMaps
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,22 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type CountriesAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
func (this *CountriesAction) Init() {
|
||||||
this.Nav("", "", "ipadmin")
|
this.Nav("", "setting", "country")
|
||||||
|
this.SecondMenu("waf")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct {
|
func (this *CountriesAction) RunGet(params struct {
|
||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
|
ServerId int64
|
||||||
}) {
|
}) {
|
||||||
|
this.Data["featureIsOn"] = true
|
||||||
|
this.Data["firewallPolicyId"] = params.FirewallPolicyId
|
||||||
|
|
||||||
this.Data["subMenuItem"] = "region"
|
this.Data["subMenuItem"] = "region"
|
||||||
|
|
||||||
// 当前选中的地区
|
// 当前选中的地区
|
||||||
@@ -57,10 +62,18 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
this.Data["countries"] = countryMaps
|
this.Data["countries"] = countryMaps
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunPost(params struct {
|
func (this *CountriesAction) RunPost(params struct {
|
||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
CountryIds []int64
|
CountryIds []int64
|
||||||
|
|
||||||
@@ -20,53 +20,67 @@ func (this *CreateIPPopupAction) RunGet(params struct {
|
|||||||
ListId int64
|
ListId int64
|
||||||
Type string
|
Type string
|
||||||
}) {
|
}) {
|
||||||
this.Data["type"] = params.Type
|
this.Data["listType"] = params.Type
|
||||||
this.Data["listId"] = params.ListId
|
this.Data["listId"] = params.ListId
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreateIPPopupAction) RunPost(params struct {
|
func (this *CreateIPPopupAction) RunPost(params struct {
|
||||||
ListId int64
|
ListId int64
|
||||||
IpFrom string
|
IpFrom string
|
||||||
IpTo string
|
IpTo string
|
||||||
ExpiredAt int64
|
ExpiredAt int64
|
||||||
Reason string
|
Reason string
|
||||||
|
Type string
|
||||||
|
EventLevel string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
// TODO 校验ListId所属用户
|
switch params.Type {
|
||||||
|
case "ipv4":
|
||||||
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入开始IP")
|
||||||
|
|
||||||
params.Must.
|
// 校验IP格式(ipFrom/ipTo)
|
||||||
Field("ipFrom", params.IpFrom).
|
var ipFromLong uint64
|
||||||
Require("请输入开始IP")
|
if !utils.IsIPv4(params.IpFrom) {
|
||||||
|
|
||||||
// 校验IP格式(ipFrom/ipTo)
|
|
||||||
ipFromLong := utils.IP2Long(params.IpFrom)
|
|
||||||
if len(params.IpFrom) > 0 {
|
|
||||||
if ipFromLong == 0 {
|
|
||||||
this.Fail("请输入正确的开始IP")
|
this.Fail("请输入正确的开始IP")
|
||||||
}
|
}
|
||||||
}
|
ipFromLong = utils.IP2Long(params.IpFrom)
|
||||||
|
|
||||||
ipToLong := utils.IP2Long(params.IpTo)
|
var ipToLong uint64
|
||||||
if len(params.IpTo) > 0 {
|
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
|
||||||
if ipToLong == 0 {
|
ipToLong = utils.IP2Long(params.IpTo)
|
||||||
this.Fail("请输入正确的结束IP")
|
this.Fail("请输入正确的结束IP")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
||||||
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
||||||
|
}
|
||||||
|
case "ipv6":
|
||||||
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入IP")
|
||||||
|
|
||||||
|
// 校验IP格式(ipFrom)
|
||||||
|
if !utils.IsIPv6(params.IpFrom) {
|
||||||
|
this.Fail("请输入正确的IPv6地址")
|
||||||
|
}
|
||||||
|
case "all":
|
||||||
|
params.IpFrom = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
|
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
|
||||||
IpListId: params.ListId,
|
IpListId: params.ListId,
|
||||||
IpFrom: params.IpFrom,
|
IpFrom: params.IpFrom,
|
||||||
IpTo: params.IpTo,
|
IpTo: params.IpTo,
|
||||||
ExpiredAt: params.ExpiredAt,
|
ExpiredAt: params.ExpiredAt,
|
||||||
Reason: params.Reason,
|
Reason: params.Reason,
|
||||||
|
Type: params.Type,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DenyListAction struct {
|
type DenyListAction struct {
|
||||||
@@ -69,14 +71,25 @@ func (this *DenyListAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemMaps = append(itemMaps, maps.Map{
|
itemMaps = append(itemMaps, maps.Map{
|
||||||
"id": item.Id,
|
"id": item.Id,
|
||||||
"ipFrom": item.IpFrom,
|
"ipFrom": item.IpFrom,
|
||||||
"ipTo": item.IpTo,
|
"ipTo": item.IpTo,
|
||||||
"expiredTime": expiredTime,
|
"expiredTime": expiredTime,
|
||||||
"reason": item.Reason,
|
"reason": item.Reason,
|
||||||
|
"type": item.Type,
|
||||||
|
"isExpired": item.ExpiredAt > 0 && item.ExpiredAt < time.Now().Unix(),
|
||||||
|
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(item.EventLevel),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["items"] = itemMaps
|
this.Data["items"] = itemMaps
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ type ProvincesAction struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *ProvincesAction) Init() {
|
func (this *ProvincesAction) Init() {
|
||||||
this.Nav("", "", "ipadmin")
|
this.Nav("", "setting", "province")
|
||||||
|
this.SecondMenu("waf")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ProvincesAction) RunGet(params struct {
|
func (this *ProvincesAction) RunGet(params struct {
|
||||||
FirewallPolicyId int64
|
FirewallPolicyId int64
|
||||||
|
ServerId int64
|
||||||
}) {
|
}) {
|
||||||
|
this.Data["featureIsOn"] = true
|
||||||
|
this.Data["firewallPolicyId"] = params.FirewallPolicyId
|
||||||
this.Data["subMenuItem"] = "province"
|
this.Data["subMenuItem"] = "province"
|
||||||
|
|
||||||
// 当前选中的省份
|
// 当前选中的省份
|
||||||
@@ -59,6 +63,14 @@ func (this *ProvincesAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
this.Data["provinces"] = provinceMaps
|
this.Data["provinces"] = provinceMaps
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package ipadmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/firewallconfigs"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) Init() {
|
||||||
|
this.Nav("", "setting", "test")
|
||||||
|
this.SecondMenu("waf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
|
FirewallPolicyId int64
|
||||||
|
}) {
|
||||||
|
this.Data["featureIsOn"] = true
|
||||||
|
this.Data["firewallPolicyId"] = params.FirewallPolicyId
|
||||||
|
this.Data["subMenuItem"] = "province"
|
||||||
|
|
||||||
|
// WAF是否启用
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TestAction) RunPost(params struct {
|
||||||
|
FirewallPolicyId int64
|
||||||
|
Ip string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
resp, err := this.RPC().HTTPFirewallPolicyRPC().CheckHTTPFirewallPolicyIPStatus(this.AdminContext(), &pb.CheckHTTPFirewallPolicyIPStatusRequest{
|
||||||
|
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||||
|
Ip: params.Ip,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap := maps.Map{
|
||||||
|
"isDone": true,
|
||||||
|
"isFound": resp.IsFound,
|
||||||
|
"isOk": resp.IsOk,
|
||||||
|
"error": resp.Error,
|
||||||
|
"isAllowed": resp.IsAllowed,
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.IpList != nil {
|
||||||
|
resultMap["list"] = maps.Map{
|
||||||
|
"id": resp.IpList.Id,
|
||||||
|
"name": resp.IpList.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resp.IpItem != nil {
|
||||||
|
resultMap["item"] = maps.Map{
|
||||||
|
"id": resp.IpItem.Id,
|
||||||
|
"ipFrom": resp.IpItem.IpFrom,
|
||||||
|
"ipTo": resp.IpItem.IpTo,
|
||||||
|
"reason": resp.IpItem.Reason,
|
||||||
|
"expiredAt": resp.IpItem.ExpiredAt,
|
||||||
|
"expiredTime": timeutil.FormatTime("Y-m-d H:i:s", resp.IpItem.ExpiredAt),
|
||||||
|
"type": resp.IpItem.Type,
|
||||||
|
"eventLevelName": firewallconfigs.FindFirewallEventLevelName(resp.IpItem.EventLevel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.RegionCountry != nil {
|
||||||
|
resultMap["country"] = maps.Map{
|
||||||
|
"id": resp.RegionCountry.Id,
|
||||||
|
"name": resp.RegionCountry.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.RegionProvince != nil {
|
||||||
|
resultMap["province"] = maps.Map{
|
||||||
|
"id": resp.RegionProvince.Id,
|
||||||
|
"name": resp.RegionProvince.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["result"] = resultMap
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -32,23 +32,29 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.Data["item"] = maps.Map{
|
this.Data["item"] = maps.Map{
|
||||||
"id": item.Id,
|
"id": item.Id,
|
||||||
"ipFrom": item.IpFrom,
|
"ipFrom": item.IpFrom,
|
||||||
"ipTo": item.IpTo,
|
"ipTo": item.IpTo,
|
||||||
"expiredAt": item.ExpiredAt,
|
"expiredAt": item.ExpiredAt,
|
||||||
"reason": item.Reason,
|
"reason": item.Reason,
|
||||||
|
"type": item.Type,
|
||||||
|
"eventLevel": item.EventLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Data["type"] = item.Type
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UpdateIPPopupAction) RunPost(params struct {
|
func (this *UpdateIPPopupAction) RunPost(params struct {
|
||||||
ItemId int64
|
ItemId int64
|
||||||
|
|
||||||
IpFrom string
|
IpFrom string
|
||||||
IpTo string
|
IpTo string
|
||||||
ExpiredAt int64
|
ExpiredAt int64
|
||||||
Reason string
|
Reason string
|
||||||
|
Type string
|
||||||
|
EventLevel string
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
@@ -58,35 +64,49 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
|||||||
|
|
||||||
// TODO 校验ItemId所属用户
|
// TODO 校验ItemId所属用户
|
||||||
|
|
||||||
params.Must.
|
switch params.Type {
|
||||||
Field("ipFrom", params.IpFrom).
|
case "ipv4":
|
||||||
Require("请输入开始IP")
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入开始IP")
|
||||||
|
|
||||||
// 校验IP格式(ipFrom/ipTo)
|
// 校验IP格式(ipFrom/ipTo)
|
||||||
ipFromLong := utils.IP2Long(params.IpFrom)
|
var ipFromLong uint64
|
||||||
if len(params.IpFrom) > 0 {
|
if !utils.IsIPv4(params.IpFrom) {
|
||||||
if ipFromLong == 0 {
|
|
||||||
this.Fail("请输入正确的开始IP")
|
this.Fail("请输入正确的开始IP")
|
||||||
}
|
}
|
||||||
}
|
ipFromLong = utils.IP2Long(params.IpFrom)
|
||||||
|
|
||||||
ipToLong := utils.IP2Long(params.IpTo)
|
var ipToLong uint64
|
||||||
if len(params.IpTo) > 0 {
|
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
|
||||||
if ipToLong == 0 {
|
ipToLong = utils.IP2Long(params.IpTo)
|
||||||
this.Fail("请输入正确的结束IP")
|
this.Fail("请输入正确的结束IP")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
||||||
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
||||||
|
}
|
||||||
|
case "ipv6":
|
||||||
|
params.Must.
|
||||||
|
Field("ipFrom", params.IpFrom).
|
||||||
|
Require("请输入IP")
|
||||||
|
|
||||||
|
// 校验IP格式(ipFrom)
|
||||||
|
if !utils.IsIPv6(params.IpFrom) {
|
||||||
|
this.Fail("请输入正确的IPv6地址")
|
||||||
|
}
|
||||||
|
case "all":
|
||||||
|
params.IpFrom = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
|
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
|
||||||
IpItemId: params.ItemId,
|
IpItemId: params.ItemId,
|
||||||
IpFrom: params.IpFrom,
|
IpFrom: params.IpFrom,
|
||||||
IpTo: params.IpTo,
|
IpTo: params.IpTo,
|
||||||
ExpiredAt: params.ExpiredAt,
|
ExpiredAt: params.ExpiredAt,
|
||||||
Reason: params.Reason,
|
Reason: params.Reason,
|
||||||
|
Type: params.Type,
|
||||||
|
EventLevel: params.EventLevel,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
110
internal/web/actions/default/servers/server/stat/clients.go
Normal file
110
internal/web/actions/default/servers/server/stat/clients.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package stat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientsAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientsAction) Init() {
|
||||||
|
this.Nav("", "stat", "")
|
||||||
|
this.SecondMenu("client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientsAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
|
Month string
|
||||||
|
}) {
|
||||||
|
month := params.Month
|
||||||
|
if len(month) != 6 {
|
||||||
|
month = timeutil.Format("Ym")
|
||||||
|
}
|
||||||
|
this.Data["month"] = month
|
||||||
|
|
||||||
|
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.AdminContext(), &pb.FindEnabledServerTypeRequest{ServerId: params.ServerId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverType := serverTypeResp.Type
|
||||||
|
|
||||||
|
statIsOn := false
|
||||||
|
|
||||||
|
// 是否已开启
|
||||||
|
if serverconfigs.IsHTTPServerType(serverType) {
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if webConfig != nil && webConfig.StatRef != nil {
|
||||||
|
statIsOn = webConfig.StatRef.IsOn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.WriteString("此类型服务暂不支持统计")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["statIsOn"] = statIsOn
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
systemMaps := []maps.Map{}
|
||||||
|
browserMaps := []maps.Map{}
|
||||||
|
|
||||||
|
if statIsOn {
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerClientSystemMonthlyStatRPC().FindTopServerClientSystemMonthlyStats(this.AdminContext(), &pb.FindTopServerClientSystemMonthlyStatsRequest{
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Month: month,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
systemMaps = append(systemMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"system": maps.Map{
|
||||||
|
"id": stat.ClientSystem.Id,
|
||||||
|
"name": stat.ClientSystem.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerClientBrowserMonthlyStatRPC().FindTopServerClientBrowserMonthlyStats(this.AdminContext(), &pb.FindTopServerClientBrowserMonthlyStatsRequest{
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Month: month,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
browserMaps = append(browserMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"browser": maps.Map{
|
||||||
|
"id": stat.ClientBrowser.Id,
|
||||||
|
"name": stat.ClientBrowser.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["systemStats"] = systemMaps
|
||||||
|
this.Data["browserStats"] = browserMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
package stat
|
package stat
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
import (
|
||||||
|
"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/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
@@ -11,6 +18,133 @@ func (this *IndexAction) Init() {
|
|||||||
this.SecondMenu("index")
|
this.SecondMenu("index")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
|
Month string
|
||||||
|
}) {
|
||||||
|
month := params.Month
|
||||||
|
if len(month) != 6 {
|
||||||
|
month = timeutil.Format("Ym")
|
||||||
|
}
|
||||||
|
this.Data["month"] = month
|
||||||
|
|
||||||
|
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.AdminContext(), &pb.FindEnabledServerTypeRequest{ServerId: params.ServerId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverType := serverTypeResp.Type
|
||||||
|
|
||||||
|
statIsOn := false
|
||||||
|
|
||||||
|
// 是否已开启
|
||||||
|
if serverconfigs.IsHTTPServerType(serverType) {
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if webConfig != nil && webConfig.StatRef != nil {
|
||||||
|
statIsOn = webConfig.StatRef.IsOn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.WriteString("此类型服务暂不支持统计")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["statIsOn"] = statIsOn
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
countryStatMaps := []maps.Map{}
|
||||||
|
provinceStatMaps := []maps.Map{}
|
||||||
|
cityStatMaps := []maps.Map{}
|
||||||
|
|
||||||
|
if statIsOn {
|
||||||
|
// 地区
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerRegionCountryMonthlyStatRPC().FindTopServerRegionCountryMonthlyStats(this.AdminContext(), &pb.FindTopServerRegionCountryMonthlyStatsRequest{
|
||||||
|
Month: month,
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
countryStatMaps = append(countryStatMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"country": maps.Map{
|
||||||
|
"id": stat.RegionCountry.Id,
|
||||||
|
"name": stat.RegionCountry.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 省份
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerRegionProvinceMonthlyStatRPC().FindTopServerRegionProvinceMonthlyStats(this.AdminContext(), &pb.FindTopServerRegionProvinceMonthlyStatsRequest{
|
||||||
|
Month: month,
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
provinceStatMaps = append(provinceStatMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"country": maps.Map{
|
||||||
|
"id": stat.RegionCountry.Id,
|
||||||
|
"name": stat.RegionCountry.Name,
|
||||||
|
},
|
||||||
|
"province": maps.Map{
|
||||||
|
"id": stat.RegionProvince.Id,
|
||||||
|
"name": stat.RegionProvince.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 城市
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerRegionCityMonthlyStatRPC().FindTopServerRegionCityMonthlyStats(this.AdminContext(), &pb.FindTopServerRegionCityMonthlyStatsRequest{
|
||||||
|
Month: month,
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
cityStatMaps = append(cityStatMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"country": maps.Map{
|
||||||
|
"id": stat.RegionCountry.Id,
|
||||||
|
"name": stat.RegionCountry.Name,
|
||||||
|
},
|
||||||
|
"province": maps.Map{
|
||||||
|
"id": stat.RegionProvince.Id,
|
||||||
|
"name": stat.RegionProvince.Name,
|
||||||
|
},
|
||||||
|
"city": maps.Map{
|
||||||
|
"id": stat.RegionCity.Id,
|
||||||
|
"name": stat.RegionCity.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["countryStats"] = countryStatMaps
|
||||||
|
this.Data["provinceStats"] = provinceStatMaps
|
||||||
|
this.Data["cityStats"] = cityStatMaps
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ func init() {
|
|||||||
Helper(serverutils.NewServerHelper()).
|
Helper(serverutils.NewServerHelper()).
|
||||||
Prefix("/servers/server/stat").
|
Prefix("/servers/server/stat").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
|
Get("/providers", new(ProvidersAction)).
|
||||||
|
Get("/clients", new(ClientsAction)).
|
||||||
|
Get("/waf", new(WafAction)).
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package stat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProvidersAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ProvidersAction) Init() {
|
||||||
|
this.Nav("", "stat", "")
|
||||||
|
this.SecondMenu("provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ProvidersAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
|
Month string
|
||||||
|
}) {
|
||||||
|
month := params.Month
|
||||||
|
if len(month) != 6 {
|
||||||
|
month = timeutil.Format("Ym")
|
||||||
|
}
|
||||||
|
this.Data["month"] = month
|
||||||
|
|
||||||
|
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.AdminContext(), &pb.FindEnabledServerTypeRequest{ServerId: params.ServerId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverType := serverTypeResp.Type
|
||||||
|
|
||||||
|
statIsOn := false
|
||||||
|
|
||||||
|
// 是否已开启
|
||||||
|
if serverconfigs.IsHTTPServerType(serverType) {
|
||||||
|
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if webConfig != nil && webConfig.StatRef != nil {
|
||||||
|
statIsOn = webConfig.StatRef.IsOn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.WriteString("此类型服务暂不支持统计")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["statIsOn"] = statIsOn
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
providerMaps := []maps.Map{}
|
||||||
|
|
||||||
|
if statIsOn {
|
||||||
|
{
|
||||||
|
resp, err := this.RPC().ServerRegionProviderMonthlyStatRPC().FindTopServerRegionProviderMonthlyStats(this.AdminContext(), &pb.FindTopServerRegionProviderMonthlyStatsRequest{
|
||||||
|
Month: month,
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
Offset: 0,
|
||||||
|
Size: 10,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, stat := range resp.Stats {
|
||||||
|
providerMaps = append(providerMaps, maps.Map{
|
||||||
|
"count": stat.Count,
|
||||||
|
"provider": maps.Map{
|
||||||
|
"id": stat.RegionProvider.Id,
|
||||||
|
"name": stat.RegionProvider.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["providerStats"] = providerMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
78
internal/web/actions/default/servers/server/stat/waf.go
Normal file
78
internal/web/actions/default/servers/server/stat/waf.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package stat
|
||||||
|
|
||||||
|
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 WafAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *WafAction) Init() {
|
||||||
|
this.Nav("", "stat", "")
|
||||||
|
this.SecondMenu("waf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *WafAction) RunGet(params struct {
|
||||||
|
ServerId int64
|
||||||
|
}) {
|
||||||
|
// 统计数据
|
||||||
|
resp, err := this.RPC().ServerHTTPFirewallDailyStatRPC().ComposeServerHTTPFirewallDashboard(this.AdminContext(), &pb.ComposeServerHTTPFirewallDashboardRequest{
|
||||||
|
Day: timeutil.Format("Ymd"),
|
||||||
|
ServerId: params.ServerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["countDailyLog"] = resp.CountDailyLog
|
||||||
|
this.Data["countDailyBlock"] = resp.CountDailyBlock
|
||||||
|
this.Data["countDailyCaptcha"] = resp.CountDailyCaptcha
|
||||||
|
this.Data["countWeeklyBlock"] = resp.CountWeeklyBlock
|
||||||
|
this.Data["countMonthlyBlock"] = resp.CountMonthlyBlock
|
||||||
|
|
||||||
|
// 分组
|
||||||
|
groupStatMaps := []maps.Map{}
|
||||||
|
for _, group := range resp.HttpFirewallRuleGroups {
|
||||||
|
groupStatMaps = append(groupStatMaps, maps.Map{
|
||||||
|
"group": maps.Map{
|
||||||
|
"id": group.HttpFirewallRuleGroup.Id,
|
||||||
|
"name": group.HttpFirewallRuleGroup.Name,
|
||||||
|
},
|
||||||
|
"count": group.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["groupStats"] = groupStatMaps
|
||||||
|
|
||||||
|
// 每日趋势
|
||||||
|
logStatMaps := []maps.Map{}
|
||||||
|
blockStatMaps := []maps.Map{}
|
||||||
|
captchaStatMaps := []maps.Map{}
|
||||||
|
for _, stat := range resp.LogDailyStats {
|
||||||
|
logStatMaps = append(logStatMaps, maps.Map{
|
||||||
|
"day": stat.Day,
|
||||||
|
"count": stat.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, stat := range resp.BlockDailyStats {
|
||||||
|
blockStatMaps = append(blockStatMaps, maps.Map{
|
||||||
|
"day": stat.Day,
|
||||||
|
"count": stat.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, stat := range resp.CaptchaDailyStats {
|
||||||
|
captchaStatMaps = append(captchaStatMaps, maps.Map{
|
||||||
|
"day": stat.Day,
|
||||||
|
"count": stat.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["logDailyStats"] = logStatMaps
|
||||||
|
this.Data["blockDailyStats"] = blockStatMaps
|
||||||
|
this.Data["captchaDailyStats"] = captchaStatMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -77,13 +77,13 @@ func (this *ServerHelper) createLeftMenu(action *actions.ActionObject) {
|
|||||||
|
|
||||||
// 协议簇
|
// 协议簇
|
||||||
family := ""
|
family := ""
|
||||||
if serverConfig.IsHTTP() {
|
if serverConfig.IsHTTPFamily() {
|
||||||
family = "http"
|
family = "http"
|
||||||
} else if serverConfig.IsTCP() {
|
} else if serverConfig.IsTCPFamily() {
|
||||||
family = "tcp"
|
family = "tcp"
|
||||||
} else if serverConfig.IsUnix() {
|
} else if serverConfig.IsUnixFamily() {
|
||||||
family = "unix"
|
family = "unix"
|
||||||
} else if serverConfig.IsUDP() {
|
} else if serverConfig.IsUDPFamily() {
|
||||||
family = "udp"
|
family = "udp"
|
||||||
}
|
}
|
||||||
action.Data["serverFamily"] = family
|
action.Data["serverFamily"] = family
|
||||||
@@ -94,7 +94,9 @@ func (this *ServerHelper) createLeftMenu(action *actions.ActionObject) {
|
|||||||
tabbar.Add("服务列表", "", "/servers", "", false)
|
tabbar.Add("服务列表", "", "/servers", "", false)
|
||||||
//tabbar.Add("看板", "", "/servers/server/board?serverId="+serverIdString, "dashboard", selectedTabbar == "board")
|
//tabbar.Add("看板", "", "/servers/server/board?serverId="+serverIdString, "dashboard", selectedTabbar == "board")
|
||||||
tabbar.Add("日志", "", "/servers/server/log?serverId="+serverIdString, "history", selectedTabbar == "log")
|
tabbar.Add("日志", "", "/servers/server/log?serverId="+serverIdString, "history", selectedTabbar == "log")
|
||||||
//tabbar.Add("统计", "", "/servers/server/stat?serverId="+serverIdString, "chart area", selectedTabbar == "stat")
|
if family == "http" {
|
||||||
|
tabbar.Add("统计", "", "/servers/server/stat?serverId="+serverIdString, "chart area", selectedTabbar == "stat")
|
||||||
|
}
|
||||||
tabbar.Add("设置", "", "/servers/server/settings?serverId="+serverIdString, "setting", selectedTabbar == "setting")
|
tabbar.Add("设置", "", "/servers/server/settings?serverId="+serverIdString, "setting", selectedTabbar == "setting")
|
||||||
tabbar.Add("删除", "", "/servers/server/delete?serverId="+serverIdString, "trash", selectedTabbar == "delete")
|
tabbar.Add("删除", "", "/servers/server/delete?serverId="+serverIdString, "trash", selectedTabbar == "delete")
|
||||||
{
|
{
|
||||||
@@ -155,10 +157,25 @@ func (this *ServerHelper) createLogMenu(secondMenuItem string, serverIdString st
|
|||||||
func (this *ServerHelper) createStatMenu(secondMenuItem string, serverIdString string, serverConfig *serverconfigs.ServerConfig) []maps.Map {
|
func (this *ServerHelper) createStatMenu(secondMenuItem string, serverIdString string, serverConfig *serverconfigs.ServerConfig) []maps.Map {
|
||||||
menuItems := []maps.Map{}
|
menuItems := []maps.Map{}
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "统计",
|
"name": "地域分布",
|
||||||
"url": "/servers/server/stat?serverId=" + serverIdString,
|
"url": "/servers/server/stat?serverId=" + serverIdString,
|
||||||
"isActive": secondMenuItem == "index",
|
"isActive": secondMenuItem == "index",
|
||||||
})
|
})
|
||||||
|
menuItems = append(menuItems, maps.Map{
|
||||||
|
"name": "运营商",
|
||||||
|
"url": "/servers/server/stat/providers?serverId=" + serverIdString,
|
||||||
|
"isActive": secondMenuItem == "provider",
|
||||||
|
})
|
||||||
|
menuItems = append(menuItems, maps.Map{
|
||||||
|
"name": "终端",
|
||||||
|
"url": "/servers/server/stat/clients?serverId=" + serverIdString,
|
||||||
|
"isActive": secondMenuItem == "client",
|
||||||
|
})
|
||||||
|
menuItems = append(menuItems, maps.Map{
|
||||||
|
"name": "WAF",
|
||||||
|
"url": "/servers/server/stat/waf?serverId=" + serverIdString,
|
||||||
|
"isActive": secondMenuItem == "waf",
|
||||||
|
})
|
||||||
return menuItems
|
return menuItems
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +196,7 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
if serverConfig.IsHTTP() {
|
if serverConfig.IsHTTPFamily() {
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "域名",
|
"name": "域名",
|
||||||
"url": "/servers/server/settings/serverNames?serverId=" + serverIdString,
|
"url": "/servers/server/settings/serverNames?serverId=" + serverIdString,
|
||||||
@@ -294,7 +311,7 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
|||||||
"isActive": secondMenuItem == "websocket",
|
"isActive": secondMenuItem == "websocket",
|
||||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebsocketRef != nil && serverConfig.Web.WebsocketRef.IsOn,
|
"isOn": serverConfig.Web != nil && serverConfig.Web.WebsocketRef != nil && serverConfig.Web.WebsocketRef.IsOn,
|
||||||
})
|
})
|
||||||
} else if serverConfig.IsTCP() {
|
} else if serverConfig.IsTCPFamily() {
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "TCP",
|
"name": "TCP",
|
||||||
"url": "/servers/server/settings/tcp?serverId=" + serverIdString,
|
"url": "/servers/server/settings/tcp?serverId=" + serverIdString,
|
||||||
@@ -313,14 +330,14 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
|||||||
"isActive": secondMenuItem == "reverseProxy",
|
"isActive": secondMenuItem == "reverseProxy",
|
||||||
"isOn": serverConfig.ReverseProxyRef != nil && serverConfig.ReverseProxyRef.IsOn,
|
"isOn": serverConfig.ReverseProxyRef != nil && serverConfig.ReverseProxyRef.IsOn,
|
||||||
})
|
})
|
||||||
} else if serverConfig.IsUnix() {
|
} else if serverConfig.IsUnixFamily() {
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "Unix",
|
"name": "Unix",
|
||||||
"url": "/servers/server/settings/unix?serverId=" + serverIdString,
|
"url": "/servers/server/settings/unix?serverId=" + serverIdString,
|
||||||
"isActive": secondMenuItem == "unix",
|
"isActive": secondMenuItem == "unix",
|
||||||
"isOn": serverConfig.Unix != nil && serverConfig.Unix.IsOn && len(serverConfig.Unix.Listen) > 0,
|
"isOn": serverConfig.Unix != nil && serverConfig.Unix.IsOn && len(serverConfig.Unix.Listen) > 0,
|
||||||
})
|
})
|
||||||
} else if serverConfig.IsUDP() {
|
} else if serverConfig.IsUDPFamily() {
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "UDP",
|
"name": "UDP",
|
||||||
"url": "/servers/server/settings/udp?serverId=" + serverIdString,
|
"url": "/servers/server/settings/udp?serverId=" + serverIdString,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -11,9 +12,19 @@ import (
|
|||||||
|
|
||||||
type ComponentsAction actions.Action
|
type ComponentsAction actions.Action
|
||||||
|
|
||||||
|
var componentsData = []byte{}
|
||||||
|
|
||||||
func (this *ComponentsAction) RunGet(params struct{}) {
|
func (this *ComponentsAction) RunGet(params struct{}) {
|
||||||
this.AddHeader("Content-Type", "text/javascript; charset=utf-8")
|
this.AddHeader("Content-Type", "text/javascript; charset=utf-8")
|
||||||
|
|
||||||
|
if !Tea.IsTesting() && len(componentsData) > 0 {
|
||||||
|
this.AddHeader("Last-Modified", "Fri, 06 Sep 2019 08:29:50 GMT")
|
||||||
|
this.Write(componentsData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
var webRoot string
|
var webRoot string
|
||||||
if Tea.IsTesting() {
|
if Tea.IsTesting() {
|
||||||
webRoot = Tea.Root + "/../web/public/js/components/"
|
webRoot = Tea.Root + "/../web/public/js/components/"
|
||||||
@@ -34,8 +45,8 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
|||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.Write(data)
|
buffer.Write(data)
|
||||||
this.Write([]byte{'\n', '\n'})
|
buffer.Write([]byte{'\n', '\n'})
|
||||||
})
|
})
|
||||||
|
|
||||||
// 条件组件
|
// 条件组件
|
||||||
@@ -43,8 +54,11 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("ComponentsAction: " + err.Error())
|
logs.Println("ComponentsAction: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
this.WriteString("window.REQUEST_COND_COMPONENTS = ")
|
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
|
||||||
this.Write(typesJSON)
|
buffer.Write(typesJSON)
|
||||||
this.Write([]byte{'\n', '\n'})
|
buffer.Write([]byte{'\n', '\n'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentsData = buffer.Bytes()
|
||||||
|
this.Write(componentsData)
|
||||||
}
|
}
|
||||||
|
|||||||
16
internal/web/actions/default/ui/eventLevelOptions.go
Normal file
16
internal/web/actions/default/ui/eventLevelOptions.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventLevelOptionsAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *EventLevelOptionsAction) RunPost(params struct{}) {
|
||||||
|
this.Data["eventLevels"] = firewallconfigs.FindAllFirewallEventLevels()
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ func init() {
|
|||||||
Get("/download", new(DownloadAction)).
|
Get("/download", new(DownloadAction)).
|
||||||
GetPost("/selectProvincesPopup", new(SelectProvincesPopupAction)).
|
GetPost("/selectProvincesPopup", new(SelectProvincesPopupAction)).
|
||||||
GetPost("/selectCountriesPopup", new(SelectCountriesPopupAction)).
|
GetPost("/selectCountriesPopup", new(SelectCountriesPopupAction)).
|
||||||
|
Post("/eventLevelOptions", new(EventLevelOptionsAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
if !action.Data.Has("teaSubMenu") {
|
if !action.Data.Has("teaSubMenu") {
|
||||||
action.Data["teaSubMenu"] = ""
|
action.Data["teaSubMenu"] = ""
|
||||||
}
|
}
|
||||||
action.Data["teaCheckClusterTask"] = configloaders.AllowModule(adminId, configloaders.AdminModuleCodeNode)
|
action.Data["teaCheckNodeTasks"] = configloaders.AllowModule(adminId, configloaders.AdminModuleCodeNode)
|
||||||
|
action.Data["teaCheckDNSTasks"] = configloaders.AllowModule(adminId, configloaders.AdminModuleCodeDNS)
|
||||||
|
|
||||||
// 菜单
|
// 菜单
|
||||||
action.Data["firstMenuItem"] = ""
|
action.Data["firstMenuItem"] = ""
|
||||||
@@ -130,6 +131,12 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
// 菜单配置
|
// 菜单配置
|
||||||
func (this *userMustAuth) modules(adminId int64) []maps.Map {
|
func (this *userMustAuth) modules(adminId int64) []maps.Map {
|
||||||
allMaps := []maps.Map{
|
allMaps := []maps.Map{
|
||||||
|
{
|
||||||
|
"code": "dashboard",
|
||||||
|
"module": configloaders.AdminModuleCodeDashboard,
|
||||||
|
"name": "数据看板",
|
||||||
|
"icon": "dashboard",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "servers",
|
"code": "servers",
|
||||||
"module": configloaders.AdminModuleCodeServer,
|
"module": configloaders.AdminModuleCodeServer,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns"
|
||||||
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/tasks"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/finance"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/finance"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/finance/bills"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/finance/bills"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index"
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ Vue.component("checkbox", {
|
|||||||
this.$emit("input", this.newValue)
|
this.$emit("input", this.newValue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
value: function (v) {
|
||||||
|
if (typeof v == "boolean") {
|
||||||
|
this.newValue = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
template: `<div class="ui checkbox">
|
template: `<div class="ui checkbox">
|
||||||
<input type="checkbox" :name="name" :value="elementValue" :id="elementId" @change="change" v-model="newValue"/>
|
<input type="checkbox" :name="name" :value="elementValue" :id="elementId" @change="change" v-model="newValue"/>
|
||||||
<label :for="elementId" style="font-size: 0.85em!important;"><slot></slot></label>
|
<label :for="elementId" style="font-size: 0.85em!important;"><slot></slot></label>
|
||||||
|
|||||||
@@ -1,144 +1,154 @@
|
|||||||
Vue.component("datetime-input", {
|
Vue.component("datetime-input", {
|
||||||
props: ["v-name", "v-timestamp"],
|
props: ["v-name", "v-timestamp"],
|
||||||
data: function () {
|
mounted: function () {
|
||||||
let timestamp = this.vTimestamp
|
let that = this
|
||||||
if (timestamp != null) {
|
teaweb.datepicker(this.$refs.dayInput, function (v) {
|
||||||
timestamp = parseInt(timestamp)
|
that.day = v
|
||||||
if (isNaN(timestamp)) {
|
that.hour = "23"
|
||||||
timestamp = 0
|
that.minute = "59"
|
||||||
}
|
that.second = "59"
|
||||||
} else {
|
that.change()
|
||||||
timestamp = 0
|
})
|
||||||
}
|
},
|
||||||
|
data: function () {
|
||||||
|
let timestamp = this.vTimestamp
|
||||||
|
if (timestamp != null) {
|
||||||
|
timestamp = parseInt(timestamp)
|
||||||
|
if (isNaN(timestamp)) {
|
||||||
|
timestamp = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timestamp = 0
|
||||||
|
}
|
||||||
|
|
||||||
let day = ""
|
let day = ""
|
||||||
let hour = ""
|
let hour = ""
|
||||||
let minute = ""
|
let minute = ""
|
||||||
let second = ""
|
let second = ""
|
||||||
|
|
||||||
if (timestamp > 0) {
|
if (timestamp > 0) {
|
||||||
let date = new Date()
|
let date = new Date()
|
||||||
date.setTime(timestamp * 1000)
|
date.setTime(timestamp * 1000)
|
||||||
|
|
||||||
let year = date.getFullYear().toString()
|
let year = date.getFullYear().toString()
|
||||||
let month = this.leadingZero((date.getMonth() + 1).toString(), 2)
|
let month = this.leadingZero((date.getMonth() + 1).toString(), 2)
|
||||||
day = year + "-" + month + "-" + this.leadingZero(date.getDate().toString(), 2)
|
day = year + "-" + month + "-" + this.leadingZero(date.getDate().toString(), 2)
|
||||||
|
|
||||||
hour = this.leadingZero(date.getHours().toString(), 2)
|
hour = this.leadingZero(date.getHours().toString(), 2)
|
||||||
minute = this.leadingZero(date.getMinutes().toString(), 2)
|
minute = this.leadingZero(date.getMinutes().toString(), 2)
|
||||||
second = this.leadingZero(date.getSeconds().toString(), 2)
|
second = this.leadingZero(date.getSeconds().toString(), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
day: day,
|
day: day,
|
||||||
hour: hour,
|
hour: hour,
|
||||||
minute: minute,
|
minute: minute,
|
||||||
second: second,
|
second: second,
|
||||||
|
|
||||||
hasDayError: false,
|
hasDayError: false,
|
||||||
hasHourError: false,
|
hasHourError: false,
|
||||||
hasMinuteError: false,
|
hasMinuteError: false,
|
||||||
hasSecondError: false
|
hasSecondError: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change: function () {
|
change: function () {
|
||||||
let date = new Date()
|
let date = new Date()
|
||||||
|
|
||||||
// day
|
// day
|
||||||
if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
|
if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let pieces = this.day.split("-")
|
let pieces = this.day.split("-")
|
||||||
let year = parseInt(pieces[0])
|
let year = parseInt(pieces[0])
|
||||||
date.setFullYear(year)
|
date.setFullYear(year)
|
||||||
|
|
||||||
let month = parseInt(pieces[1])
|
let month = parseInt(pieces[1])
|
||||||
if (month < 1 || month > 12) {
|
if (month < 1 || month > 12) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
date.setMonth(month - 1)
|
date.setMonth(month - 1)
|
||||||
|
|
||||||
let day = parseInt(pieces[2])
|
let day = parseInt(pieces[2])
|
||||||
if (day < 1 || day > 32) {
|
if (day < 1 || day > 32) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
date.setDate(day)
|
date.setDate(day)
|
||||||
|
|
||||||
this.hasDayError = false
|
this.hasDayError = false
|
||||||
|
|
||||||
// hour
|
// hour
|
||||||
if (!/^\d+$/.test(this.hour)) {
|
if (!/^\d+$/.test(this.hour)) {
|
||||||
this.hasHourError = true
|
this.hasHourError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let hour = parseInt(this.hour)
|
let hour = parseInt(this.hour)
|
||||||
if (isNaN(hour)) {
|
if (isNaN(hour)) {
|
||||||
this.hasHourError = true
|
this.hasHourError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (hour < 0 || hour >= 24) {
|
if (hour < 0 || hour >= 24) {
|
||||||
this.hasHourError = true
|
this.hasHourError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasHourError = false
|
this.hasHourError = false
|
||||||
date.setHours(hour)
|
date.setHours(hour)
|
||||||
|
|
||||||
// minute
|
// minute
|
||||||
if (!/^\d+$/.test(this.minute)) {
|
if (!/^\d+$/.test(this.minute)) {
|
||||||
this.hasMinuteError = true
|
this.hasMinuteError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let minute = parseInt(this.minute)
|
let minute = parseInt(this.minute)
|
||||||
if (isNaN(minute)) {
|
if (isNaN(minute)) {
|
||||||
this.hasMinuteError = true
|
this.hasMinuteError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (minute < 0 || minute >= 60) {
|
if (minute < 0 || minute >= 60) {
|
||||||
this.hasMinuteError = true
|
this.hasMinuteError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasMinuteError = false
|
this.hasMinuteError = false
|
||||||
date.setMinutes(minute)
|
date.setMinutes(minute)
|
||||||
|
|
||||||
// second
|
// second
|
||||||
if (!/^\d+$/.test(this.second)) {
|
if (!/^\d+$/.test(this.second)) {
|
||||||
this.hasSecondError = true
|
this.hasSecondError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let second = parseInt(this.second)
|
let second = parseInt(this.second)
|
||||||
if (isNaN(second)) {
|
if (isNaN(second)) {
|
||||||
this.hasSecondError = true
|
this.hasSecondError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (second < 0 || second >= 60) {
|
if (second < 0 || second >= 60) {
|
||||||
this.hasSecondError = true
|
this.hasSecondError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasSecondError = false
|
this.hasSecondError = false
|
||||||
date.setSeconds(second)
|
date.setSeconds(second)
|
||||||
|
|
||||||
this.timestamp = Math.ceil(date.getTime() / 1000)
|
this.timestamp = parseInt(date.getTime() / 1000)
|
||||||
},
|
},
|
||||||
leadingZero: function (s, l) {
|
leadingZero: function (s, l) {
|
||||||
if (l <= s.length) {
|
if (l <= s.length) {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
for (let i = 0; i < l - s.length; i++) {
|
for (let i = 0; i < l - s.length; i++) {
|
||||||
s = "0" + s
|
s = "0" + s
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<input type="hidden" :name="vName" :value="timestamp"/>
|
<input type="hidden" :name="vName" :value="timestamp"/>
|
||||||
<div class="ui fields inline" style="padding: 0; margin:0">
|
<div class="ui fields inline" style="padding: 0; margin:0">
|
||||||
<div class="ui field" :class="{error: hasDayError}">
|
<div class="ui field" :class="{error: hasDayError}">
|
||||||
<input type="text" v-model="day" placeholder="YYYY-mm-dd" style="width:8em" maxlength="10" @input="change"/>
|
<input type="text" v-model="day" placeholder="YYYY-MM-DD" style="width:8.6em" maxlength="10" @input="change" ref="dayInput"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field" :class="{error: hasHourError}"><input type="text" v-model="hour" maxlength="2" style="width:4em" placeholder="时" @input="change"/></div>
|
<div class="ui field" :class="{error: hasHourError}"><input type="text" v-model="hour" maxlength="2" style="width:4em" placeholder="时" @input="change"/></div>
|
||||||
<div class="ui field">:</div>
|
<div class="ui field">:</div>
|
||||||
|
|||||||
12
web/public/js/components/iplist/ip-item-text.js
Normal file
12
web/public/js/components/iplist/ip-item-text.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Vue.component("ip-item-text", {
|
||||||
|
props: ["v-item"],
|
||||||
|
template: `<span>
|
||||||
|
<span v-if="vItem.type == 'all'">*</span>
|
||||||
|
<span v-if="vItem.type == 'ipv4' || vItem.type.length == 0">
|
||||||
|
{{vItem.ipFrom}}
|
||||||
|
<span v-if="vItem.ipTo.length > 0">- {{vItem.ipTo}}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="vItem.type == 'ipv6'">{{vItem.ipFrom}}</span>
|
||||||
|
<span v-if="vItem.eventLevelName != null && vItem.eventLevelName.length > 0"> 级别:{{vItem.eventLevelName}}</span>
|
||||||
|
</span>`
|
||||||
|
})
|
||||||
63
web/public/js/components/iplist/ip-list-table.js
Normal file
63
web/public/js/components/iplist/ip-list-table.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
Vue.component("ip-list-table", {
|
||||||
|
props: ["v-items"],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
items: this.vItems
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateItem: function (itemId) {
|
||||||
|
this.$emit("update-item", itemId)
|
||||||
|
},
|
||||||
|
deleteItem: function (itemId) {
|
||||||
|
this.$emit("delete-item", itemId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<table class="ui table selectable celled" v-if="items.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:18em">IP</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>级别</th>
|
||||||
|
<th>过期时间</th>
|
||||||
|
<th>备注</th>
|
||||||
|
<th class="two op">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="item in items">
|
||||||
|
<td>
|
||||||
|
<span v-if="item.type != 'all'">{{item.ipFrom}}<span v-if="item.ipTo.length > 0"> - {{item.ipTo}}</span></span>
|
||||||
|
<span v-else class="disabled">*</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="item.type.length == 0">IPv4</span>
|
||||||
|
<span v-else-if="item.type == 'ipv4'">IPv4</span>
|
||||||
|
<span v-else-if="item.type == 'ipv6'">IPv6</span>
|
||||||
|
<span v-else-if="item.type == 'all'"><strong>所有IP</strong></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="item.eventLevelName != null && item.eventLevelName.length > 0">{{item.eventLevelName}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="item.expiredTime.length > 0">
|
||||||
|
{{item.expiredTime}}
|
||||||
|
<div v-if="item.isExpired" style="margin-top: 0.5em">
|
||||||
|
<span class="ui label tiny basic red">已过期</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span v-else class="disabled">不过期</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="item.reason.length > 0">{{item.reason}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="" @click.prevent="updateItem(item.id)">修改</a>
|
||||||
|
<a href="" @click.prevent="deleteItem(item.id)">删除</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
@@ -1,38 +1,53 @@
|
|||||||
Vue.component("message-row", {
|
Vue.component("message-row", {
|
||||||
props: ["v-message"],
|
props: ["v-message"],
|
||||||
data: function () {
|
data: function () {
|
||||||
let paramsJSON = this.vMessage.params
|
let paramsJSON = this.vMessage.params
|
||||||
let params = null
|
let params = null
|
||||||
if (paramsJSON != null && paramsJSON.length > 0) {
|
if (paramsJSON != null && paramsJSON.length > 0) {
|
||||||
params = JSON.parse(paramsJSON)
|
params = JSON.parse(paramsJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: this.vMessage,
|
message: this.vMessage,
|
||||||
params: params
|
params: params
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
viewCert: function (certId) {
|
viewCert: function (certId) {
|
||||||
teaweb.popup("/servers/certs/certPopup?certId=" + certId, {
|
teaweb.popup("/servers/certs/certPopup?certId=" + certId, {
|
||||||
height: "28em",
|
height: "28em",
|
||||||
width: "48em"
|
width: "48em"
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
readMessage: function (messageId) {
|
||||||
template: `<div>
|
Tea.action("/messages/readPage")
|
||||||
|
.params({"messageIds": [messageId]})
|
||||||
|
.post()
|
||||||
|
.success(function () {
|
||||||
|
// 刷新父级页面Badge
|
||||||
|
if (window.parent.Tea != null && window.parent.Tea.Vue != null) {
|
||||||
|
window.parent.Tea.Vue.checkMessagesOnce()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新当前页面
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
<table class="ui table selectable">
|
<table class="ui table selectable">
|
||||||
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
|
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
|
||||||
<td>
|
<td style="position: relative">
|
||||||
<strong>{{message.datetime}}</strong>
|
<strong>{{message.datetime}}</strong>
|
||||||
<span v-if="message.cluster != null && message.cluster.id != null">
|
<span v-if="message.cluster != null && message.cluster.id != null">
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a :href="'/clusters/cluster?clusterId=' + message.cluster.id">集群:{{message.cluster.name}}</a>
|
<a :href="'/clusters/cluster?clusterId=' + message.cluster.id" target="_top">集群:{{message.cluster.name}}</a>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="message.node != null && message.node.id != null">
|
<span v-if="message.node != null && message.node.id != null">
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id">节点:{{message.node.name}}</a>
|
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id" target="_top">节点:{{message.node.name}}</a>
|
||||||
</span>
|
</span>
|
||||||
|
<a href="" style="position: absolute; right: 1em" @click.prevent="readMessage(message.id)" title="标为已读"><i class="icon check"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
|
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
|
||||||
@@ -41,25 +56,25 @@ Vue.component("message-row", {
|
|||||||
|
|
||||||
<!-- 健康检查 -->
|
<!-- 健康检查 -->
|
||||||
<div v-if="message.type == 'HealthCheckFailed'" style="margin-top: 0.8em">
|
<div v-if="message.type == 'HealthCheckFailed'" style="margin-top: 0.8em">
|
||||||
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + param.node.id" v-for="param in params" class="ui label small basic" style="margin-bottom: 0.5em">{{param.node.name}}: {{param.error}}</a>
|
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + param.node.id" v-for="param in params" class="ui label small basic" style="margin-bottom: 0.5em" target="_top">{{param.node.name}}: {{param.error}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 集群DNS设置 -->
|
<!-- 集群DNS设置 -->
|
||||||
<div v-if="message.type == 'ClusterDNSSyncFailed'" style="margin-top: 0.8em">
|
<div v-if="message.type == 'ClusterDNSSyncFailed'" style="margin-top: 0.8em">
|
||||||
<a :href="'/dns/clusters/cluster?clusterId=' + message.cluster.id">查看问题 »</a>
|
<a :href="'/dns/clusters/cluster?clusterId=' + message.cluster.id" target="_top">查看问题 »</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 证书即将过期 -->
|
<!-- 证书即将过期 -->
|
||||||
<div v-if="message.type == 'SSLCertExpiring'" style="margin-top: 0.8em">
|
<div v-if="message.type == 'SSLCertExpiring'" style="margin-top: 0.8em">
|
||||||
<a href="" @click.prevent="viewCert(params.certId)">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0">查看任务»</a>
|
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务»</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 证书续期成功 -->
|
<!-- 证书续期成功 -->
|
||||||
<div v-if="message.type == 'SSLCertACMETaskSuccess'" style="margin-top: 0.8em">
|
<div v-if="message.type == 'SSLCertACMETaskSuccess'" style="margin-top: 0.8em">
|
||||||
<a href="" @click.prevent="viewCert(params.certId)">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0">查看任务»</a>
|
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务»</a>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="message.type == 'SSLCertACMETaskFailed'" style="margin-top: 0.8em">
|
<div v-if="message.type == 'SSLCertACMETaskFailed'" style="margin-top: 0.8em">
|
||||||
<a href="" @click.prevent="viewCert(params.certId)">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0">查看任务»</a>
|
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务»</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
45
web/public/js/components/server/firewall-event-levels.js
Normal file
45
web/public/js/components/server/firewall-event-levels.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Vue.component("firewall-event-level-options", {
|
||||||
|
props: ["v-value"],
|
||||||
|
mounted: function () {
|
||||||
|
let that = this
|
||||||
|
Tea.action("/ui/eventLevelOptions")
|
||||||
|
.post()
|
||||||
|
.success(function (resp) {
|
||||||
|
that.levels = resp.data.eventLevels
|
||||||
|
that.change()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
let value = this.vValue
|
||||||
|
if (value == null || value.length == 0) {
|
||||||
|
value = "" // 不要给默认值,因为黑白名单等默认值均有不同
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
levels: [],
|
||||||
|
description: "",
|
||||||
|
level: value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
change: function () {
|
||||||
|
this.$emit("change")
|
||||||
|
|
||||||
|
let that = this
|
||||||
|
let l = this.levels.$find(function (k, v) {
|
||||||
|
return v.code == that.level
|
||||||
|
})
|
||||||
|
if (l != null) {
|
||||||
|
this.description = l.description
|
||||||
|
} else {
|
||||||
|
this.description = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<select class="ui dropdown auto-width" name="eventLevel" v-model="level" @change="change">
|
||||||
|
<option v-for="level in levels" :value="level.code">{{level.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment">{{description}}</p>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
@@ -20,7 +20,9 @@ Vue.component("http-access-log-config-box", {
|
|||||||
status5: true,
|
status5: true,
|
||||||
|
|
||||||
storageOnly: false,
|
storageOnly: false,
|
||||||
storagePolicies: []
|
storagePolicies: [],
|
||||||
|
|
||||||
|
firewallOnly: false
|
||||||
}
|
}
|
||||||
if (this.vAccessLogConfig != null) {
|
if (this.vAccessLogConfig != null) {
|
||||||
accessLog = this.vAccessLogConfig
|
accessLog = this.vAccessLogConfig
|
||||||
@@ -130,6 +132,13 @@ Vue.component("http-access-log-config-box", {
|
|||||||
<p class="comment">选中表示只输出日志到日志策略,而停止默认的日志存储。</p>
|
<p class="comment">选中表示只输出日志到日志策略,而停止默认的日志存储。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>是否只记录WAF相关日志</td>
|
||||||
|
<td>
|
||||||
|
<checkbox v-model="accessLog.firewallOnly"></checkbox>
|
||||||
|
<p class="comment">选中后只记录WAF相关的日志。通过此选项可有效减少访问日志数量,降低网络带宽和存储压力。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="margin"></div>
|
<div class="margin"></div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Vue.component("http-stat-config-box", {
|
|||||||
if (stat == null) {
|
if (stat == null) {
|
||||||
stat = {
|
stat = {
|
||||||
isPrior: false,
|
isPrior: false,
|
||||||
isOn: true
|
isOn: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Vue.component("http-websocket-box", {
|
|||||||
if (websocketConfig == null) {
|
if (websocketConfig == null) {
|
||||||
websocketConfig = {
|
websocketConfig = {
|
||||||
id: 0,
|
id: 0,
|
||||||
isOn: true,
|
isOn: false,
|
||||||
handshakeTimeout: {
|
handshakeTimeout: {
|
||||||
count: 30,
|
count: 30,
|
||||||
unit: "second"
|
unit: "second"
|
||||||
|
|||||||
@@ -1,50 +1,86 @@
|
|||||||
Vue.component("reverse-proxy-box", {
|
Vue.component("reverse-proxy-box", {
|
||||||
props: ["v-reverse-proxy-ref", "v-reverse-proxy-config", "v-is-location", "v-family"],
|
props: ["v-reverse-proxy-ref", "v-reverse-proxy-config", "v-is-location", "v-family"],
|
||||||
data: function () {
|
data: function () {
|
||||||
let reverseProxyRef = this.vReverseProxyRef
|
let reverseProxyRef = this.vReverseProxyRef
|
||||||
if (reverseProxyRef == null) {
|
if (reverseProxyRef == null) {
|
||||||
reverseProxyRef = {
|
reverseProxyRef = {
|
||||||
isPrior: false,
|
isPrior: false,
|
||||||
isOn: false,
|
isOn: false,
|
||||||
reverseProxyId: 0
|
reverseProxyId: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reverseProxyConfig = this.vReverseProxyConfig
|
let reverseProxyConfig = this.vReverseProxyConfig
|
||||||
if (reverseProxyConfig == null) {
|
if (reverseProxyConfig == null) {
|
||||||
reverseProxyConfig = {
|
reverseProxyConfig = {
|
||||||
requestPath: "",
|
requestPath: "",
|
||||||
stripPrefix: "",
|
stripPrefix: "",
|
||||||
requestURI: "",
|
requestURI: "",
|
||||||
requestHost: "",
|
requestHost: "",
|
||||||
requestHostType: 0
|
requestHostType: 0,
|
||||||
}
|
addHeaders: []
|
||||||
}
|
}
|
||||||
return {
|
}
|
||||||
reverseProxyRef: reverseProxyRef,
|
|
||||||
reverseProxyConfig: reverseProxyConfig,
|
let forwardHeaders = [
|
||||||
advancedVisible: false,
|
{
|
||||||
family: this.vFamily
|
name: "X-Real-IP",
|
||||||
}
|
isChecked: false
|
||||||
},
|
},
|
||||||
watch: {
|
{
|
||||||
"reverseProxyConfig.requestHostType": function (v) {
|
name: "X-Forwarded-For",
|
||||||
let requestHostType = parseInt(v)
|
isChecked: false
|
||||||
if (isNaN(requestHostType)) {
|
},
|
||||||
requestHostType = 0
|
{
|
||||||
}
|
name: "X-Forwarded-By",
|
||||||
this.reverseProxyConfig.requestHostType = requestHostType
|
isChecked: false
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
methods: {
|
name: "X-Forwarded-Host",
|
||||||
isOn: function () {
|
isChecked: false
|
||||||
return (!this.vIsLocation || this.reverseProxyRef.isPrior) && this.reverseProxyRef.isOn
|
},
|
||||||
},
|
{
|
||||||
changeAdvancedVisible: function (v) {
|
name: "X-Forwarded-Proto",
|
||||||
this.advancedVisible = v
|
isChecked: false
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
template: `<div>
|
forwardHeaders.forEach(function (v) {
|
||||||
|
v.isChecked = reverseProxyConfig.addHeaders.$contains(v.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
reverseProxyRef: reverseProxyRef,
|
||||||
|
reverseProxyConfig: reverseProxyConfig,
|
||||||
|
advancedVisible: false,
|
||||||
|
family: this.vFamily,
|
||||||
|
forwardHeaders: forwardHeaders
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
"reverseProxyConfig.requestHostType": function (v) {
|
||||||
|
let requestHostType = parseInt(v)
|
||||||
|
if (isNaN(requestHostType)) {
|
||||||
|
requestHostType = 0
|
||||||
|
}
|
||||||
|
this.reverseProxyConfig.requestHostType = requestHostType
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isOn: function () {
|
||||||
|
return (!this.vIsLocation || this.reverseProxyRef.isPrior) && this.reverseProxyRef.isOn
|
||||||
|
},
|
||||||
|
changeAdvancedVisible: function (v) {
|
||||||
|
this.advancedVisible = v
|
||||||
|
},
|
||||||
|
changeAddHeader: function () {
|
||||||
|
this.reverseProxyConfig.addHeaders = this.forwardHeaders.filter(function (v) {
|
||||||
|
return v.isChecked
|
||||||
|
}).map(function (v) {
|
||||||
|
return v.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
<input type="hidden" name="reverseProxyRefJSON" :value="JSON.stringify(reverseProxyRef)"/>
|
<input type="hidden" name="reverseProxyRefJSON" :value="JSON.stringify(reverseProxyRef)"/>
|
||||||
<input type="hidden" name="reverseProxyJSON" :value="JSON.stringify(reverseProxyConfig)"/>
|
<input type="hidden" name="reverseProxyJSON" :value="JSON.stringify(reverseProxyConfig)"/>
|
||||||
<table class="ui table selectable definition">
|
<table class="ui table selectable definition">
|
||||||
@@ -77,6 +113,18 @@ Vue.component("reverse-proxy-box", {
|
|||||||
</tbody>
|
</tbody>
|
||||||
<more-options-tbody @change="changeAdvancedVisible" v-if="isOn()"></more-options-tbody>
|
<more-options-tbody @change="changeAdvancedVisible" v-if="isOn()"></more-options-tbody>
|
||||||
<tbody v-show="isOn() && advancedVisible">
|
<tbody v-show="isOn() && advancedVisible">
|
||||||
|
<tr v-show="family == null || family == 'http'">
|
||||||
|
<td>自动添加的Header</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<div style="width: 14em; float: left; margin-bottom: 1em" v-for="header in forwardHeaders" :key="header.name">
|
||||||
|
<checkbox v-model="header.isChecked" @input="changeAddHeader">{{header.name}}</checkbox>
|
||||||
|
</div>
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</div>
|
||||||
|
<p class="comment">选中后,会自动向源站请求添加这些Header。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr v-show="family == null || family == 'http'">
|
<tr v-show="family == null || family == 'http'">
|
||||||
<td>请求URI<em>(RequestURI)</em></td>
|
<td>请求URI<em>(RequestURI)</em></td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
16
web/public/js/echarts/echarts.min.js
vendored
Normal file
16
web/public/js/echarts/echarts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,261 +1,297 @@
|
|||||||
window.teaweb = {
|
window.teaweb = {
|
||||||
set: function (key, value) {
|
set: function (key, value) {
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
localStorage.setItem(key, JSON.stringify(value));
|
||||||
},
|
},
|
||||||
get: function (key) {
|
get: function (key) {
|
||||||
var item = localStorage.getItem(key);
|
var item = localStorage.getItem(key);
|
||||||
if (item == null || item.length == 0) {
|
if (item == null || item.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.parse(item);
|
return JSON.parse(item);
|
||||||
},
|
},
|
||||||
getString: function (key) {
|
getString: function (key) {
|
||||||
var value = this.get(key);
|
var value = this.get(key);
|
||||||
if (typeof (value) == "string") {
|
if (typeof (value) == "string") {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
getBool: function (key) {
|
getBool: function (key) {
|
||||||
return Boolean(this.get(key));
|
return Boolean(this.get(key));
|
||||||
},
|
},
|
||||||
remove: function (key) {
|
remove: function (key) {
|
||||||
localStorage.removeItem(key)
|
localStorage.removeItem(key)
|
||||||
},
|
},
|
||||||
match: function (source, keyword) {
|
match: function (source, keyword) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (keyword == null) {
|
if (keyword == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
source = source.trim();
|
source = source.trim();
|
||||||
keyword = keyword.trim();
|
keyword = keyword.trim();
|
||||||
if (keyword.length == 0) {
|
if (keyword.length == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (source.length == 0) {
|
if (source.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var pieces = keyword.split(/\s+/);
|
var pieces = keyword.split(/\s+/);
|
||||||
for (var i = 0; i < pieces.length; i++) {
|
for (var i = 0; i < pieces.length; i++) {
|
||||||
var pattern = pieces[i];
|
var pattern = pieces[i];
|
||||||
pattern = pattern.replace(/(\+|\*|\?|[|]|{|}|\||\\|\(|\)|\.)/g, "\\$1");
|
pattern = pattern.replace(/(\+|\*|\?|[|]|{|}|\||\\|\(|\)|\.)/g, "\\$1");
|
||||||
var reg = new RegExp(pattern, "i");
|
var reg = new RegExp(pattern, "i");
|
||||||
if (!reg.test(source)) {
|
if (!reg.test(source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
datepicker: function (element, callback) {
|
loadJS: function (file, callback) {
|
||||||
if (typeof (element) == "string") {
|
let element = document.createElement("script")
|
||||||
element = document.getElementById(element);
|
element.setAttribute("type", "text/javascript")
|
||||||
}
|
element.setAttribute("src", file)
|
||||||
var year = new Date().getFullYear();
|
if (typeof callback == "function") {
|
||||||
var picker = new Pikaday({
|
element.addEventListener("load", callback)
|
||||||
field: element,
|
}
|
||||||
firstDay: 1,
|
document.head.append(element)
|
||||||
minDate: new Date(year - 1, 0, 1),
|
},
|
||||||
maxDate: new Date(year + 10, 11, 31),
|
loadCSS: function (file, callback) {
|
||||||
yearRange: [year - 1, year + 10],
|
let element = document.createElement("link")
|
||||||
format: "YYYY-MM-DD",
|
element.setAttribute("rel", "stylesheet")
|
||||||
i18n: {
|
element.setAttribute("type", "text/css")
|
||||||
previousMonth: '上月',
|
element.setAttribute("href", file)
|
||||||
nextMonth: '下月',
|
if (typeof callback == "function") {
|
||||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
element.addEventListener("load", callback)
|
||||||
weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
}
|
||||||
weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
document.head.append(element)
|
||||||
},
|
},
|
||||||
theme: 'triangle-theme',
|
datepicker: function (element, callback) {
|
||||||
onSelect: function () {
|
// 加载
|
||||||
if (typeof (callback) == "function") {
|
if (typeof Pikaday == "undefined") {
|
||||||
callback.call(Tea.Vue, picker.toString());
|
let that = this
|
||||||
}
|
this.loadJS("/js/moment.min.js")
|
||||||
}
|
this.loadJS("/js/pikaday.js", function () {
|
||||||
});
|
that.datepicker(element, callback)
|
||||||
},
|
})
|
||||||
|
this.loadCSS("/js/pikaday.css")
|
||||||
|
this.loadCSS("/js/pikaday.theme.css")
|
||||||
|
this.loadCSS("/js/pikaday.triangle.css")
|
||||||
|
|
||||||
formatBytes: function (bytes) {
|
return
|
||||||
bytes = Math.ceil(bytes);
|
}
|
||||||
if (bytes < 1024) {
|
|
||||||
return bytes + " bytes";
|
|
||||||
}
|
|
||||||
if (bytes < 1024 * 1024) {
|
|
||||||
return (Math.ceil(bytes * 100 / 1024) / 100) + " k";
|
|
||||||
}
|
|
||||||
return (Math.ceil(bytes * 100 / 1024 / 1024) / 100) + " m";
|
|
||||||
},
|
|
||||||
|
|
||||||
popup: function (url, options) {
|
|
||||||
if (options == null) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
var width = "40em";
|
|
||||||
var height = "20em";
|
|
||||||
window.POPUP_CALLBACK = function () {
|
|
||||||
Swal.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options["width"] != null) {
|
if (typeof (element) == "string") {
|
||||||
width = options["width"];
|
element = document.getElementById(element);
|
||||||
}
|
}
|
||||||
if (options["height"] != null) {
|
var year = new Date().getFullYear();
|
||||||
height = options["height"];
|
var picker = new Pikaday({
|
||||||
}
|
field: element,
|
||||||
if (typeof (options["callback"]) == "function") {
|
firstDay: 1,
|
||||||
window.POPUP_CALLBACK = function () {
|
minDate: new Date(year - 1, 0, 1),
|
||||||
Swal.close();
|
maxDate: new Date(year + 10, 11, 31),
|
||||||
options["callback"].apply(Tea.Vue, arguments);
|
yearRange: [year - 1, year + 10],
|
||||||
};
|
format: "YYYY-MM-DD",
|
||||||
}
|
i18n: {
|
||||||
|
previousMonth: '上月',
|
||||||
|
nextMonth: '下月',
|
||||||
|
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||||
|
weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||||
|
weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||||
|
},
|
||||||
|
theme: 'triangle-theme',
|
||||||
|
onSelect: function () {
|
||||||
|
if (typeof (callback) == "function") {
|
||||||
|
callback.call(Tea.Vue, picker.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
Swal.fire({
|
formatBytes: function (bytes) {
|
||||||
html: '<iframe src="' + url + '#popup-' + width + '" style="border:0; width: 100%; height:' + height + '"></iframe>',
|
bytes = Math.ceil(bytes);
|
||||||
width: width,
|
if (bytes < 1024) {
|
||||||
padding: "0.5em",
|
return bytes + " bytes";
|
||||||
showConfirmButton: false,
|
}
|
||||||
showCloseButton: true,
|
if (bytes < 1024 * 1024) {
|
||||||
focusConfirm: false,
|
return (Math.ceil(bytes * 100 / 1024) / 100) + " k";
|
||||||
onClose: function (popup) {
|
}
|
||||||
if (typeof (options["onClose"]) == "function") {
|
return (Math.ceil(bytes * 100 / 1024 / 1024) / 100) + " m";
|
||||||
options["onClose"].apply(Tea.Vue, arguments)
|
},
|
||||||
}
|
formatNumber: function (x) {
|
||||||
}
|
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ", ");
|
||||||
});
|
},
|
||||||
},
|
popup: function (url, options) {
|
||||||
popupFinish: function () {
|
if (options == null) {
|
||||||
if (window.POPUP_CALLBACK != null) {
|
options = {};
|
||||||
window.POPUP_CALLBACK.apply(window, arguments);
|
}
|
||||||
}
|
var width = "40em";
|
||||||
},
|
var height = "20em";
|
||||||
popupTip: function (html) {
|
window.POPUP_CALLBACK = function () {
|
||||||
Swal.fire({
|
Swal.close();
|
||||||
html: '<i class="icon question circle"></i><span style="line-height: 1.7">' + html + "</span>",
|
};
|
||||||
width: "30em",
|
|
||||||
padding: "5em",
|
|
||||||
showConfirmButton: false,
|
|
||||||
showCloseButton: true,
|
|
||||||
focusConfirm: false
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isPopup: function () {
|
|
||||||
var hash = window.location.hash;
|
|
||||||
return hash != null && hash.startsWith("#popup");
|
|
||||||
},
|
|
||||||
Swal: function () {
|
|
||||||
return this.isPopup() ? window.parent.Swal : window.Swal;
|
|
||||||
},
|
|
||||||
success: function (message, callback) {
|
|
||||||
var width = "20em";
|
|
||||||
if (message.length > 30) {
|
|
||||||
width = "30em";
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = {
|
if (options["width"] != null) {
|
||||||
confirmButtonText: "确定",
|
width = options["width"];
|
||||||
buttonsStyling: false,
|
}
|
||||||
icon: "success",
|
if (options["height"] != null) {
|
||||||
customClass: {
|
height = options["height"];
|
||||||
closeButton: "ui button",
|
}
|
||||||
cancelButton: "ui button",
|
if (typeof (options["callback"]) == "function") {
|
||||||
confirmButton: "ui button primary"
|
window.POPUP_CALLBACK = function () {
|
||||||
},
|
Swal.close();
|
||||||
width: width,
|
options["callback"].apply(Tea.Vue, arguments);
|
||||||
onAfterClose: function () {
|
};
|
||||||
if (typeof (callback) == "function") {
|
}
|
||||||
setTimeout(function () {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
} else if (typeof (callback) == "string") {
|
|
||||||
window.location = callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.startsWith("html:")) {
|
Swal.fire({
|
||||||
config.html = message.substring(5)
|
html: '<iframe src="' + url + '#popup-' + width + '" style="border:0; width: 100%; height:' + height + '"></iframe>',
|
||||||
} else {
|
width: width,
|
||||||
config.text = message
|
padding: "0.5em",
|
||||||
}
|
showConfirmButton: false,
|
||||||
|
showCloseButton: true,
|
||||||
|
focusConfirm: false,
|
||||||
|
onClose: function (popup) {
|
||||||
|
if (typeof (options["onClose"]) == "function") {
|
||||||
|
options["onClose"].apply(Tea.Vue, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
popupFinish: function () {
|
||||||
|
if (window.POPUP_CALLBACK != null) {
|
||||||
|
window.POPUP_CALLBACK.apply(window, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
popupTip: function (html) {
|
||||||
|
Swal.fire({
|
||||||
|
html: '<i class="icon question circle"></i><span style="line-height: 1.7">' + html + "</span>",
|
||||||
|
width: "30em",
|
||||||
|
padding: "5em",
|
||||||
|
showConfirmButton: false,
|
||||||
|
showCloseButton: true,
|
||||||
|
focusConfirm: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isPopup: function () {
|
||||||
|
var hash = window.location.hash;
|
||||||
|
return hash != null && hash.startsWith("#popup");
|
||||||
|
},
|
||||||
|
Swal: function () {
|
||||||
|
return this.isPopup() ? window.parent.Swal : window.Swal;
|
||||||
|
},
|
||||||
|
success: function (message, callback) {
|
||||||
|
var width = "20em";
|
||||||
|
if (message.length > 30) {
|
||||||
|
width = "30em";
|
||||||
|
}
|
||||||
|
|
||||||
Swal.fire(config);
|
let config = {
|
||||||
},
|
confirmButtonText: "确定",
|
||||||
successToast: function (message, timeout) {
|
buttonsStyling: false,
|
||||||
if (timeout == null) {
|
icon: "success",
|
||||||
timeout = 2000
|
customClass: {
|
||||||
}
|
closeButton: "ui button",
|
||||||
var width = "20em";
|
cancelButton: "ui button",
|
||||||
if (message.length > 30) {
|
confirmButton: "ui button primary"
|
||||||
width = "30em";
|
},
|
||||||
}
|
width: width,
|
||||||
Swal.fire({
|
onAfterClose: function () {
|
||||||
text: message,
|
if (typeof (callback) == "function") {
|
||||||
icon: "success",
|
setTimeout(function () {
|
||||||
width: width,
|
callback();
|
||||||
timer: timeout,
|
});
|
||||||
showConfirmButton: false
|
} else if (typeof (callback) == "string") {
|
||||||
});
|
window.location = callback
|
||||||
},
|
}
|
||||||
warn: function (message, callback) {
|
}
|
||||||
var width = "20em";
|
}
|
||||||
if (message.length > 30) {
|
|
||||||
width = "30em";
|
if (message.startsWith("html:")) {
|
||||||
}
|
config.html = message.substring(5)
|
||||||
Swal.fire({
|
} else {
|
||||||
text: message,
|
config.text = message
|
||||||
confirmButtonText: "确定",
|
}
|
||||||
buttonsStyling: false,
|
|
||||||
customClass: {
|
Swal.fire(config);
|
||||||
closeButton: "ui button",
|
},
|
||||||
cancelButton: "ui button",
|
successToast: function (message, timeout) {
|
||||||
confirmButton: "ui button primary"
|
if (timeout == null) {
|
||||||
},
|
timeout = 2000
|
||||||
icon: "warning",
|
}
|
||||||
width: width,
|
var width = "20em";
|
||||||
onAfterClose: function () {
|
if (message.length > 30) {
|
||||||
if (typeof (callback) == "function") {
|
width = "30em";
|
||||||
setTimeout(function () {
|
}
|
||||||
callback();
|
Swal.fire({
|
||||||
});
|
text: message,
|
||||||
}
|
icon: "success",
|
||||||
}
|
width: width,
|
||||||
});
|
timer: timeout,
|
||||||
},
|
showConfirmButton: false
|
||||||
confirm: function (message, callback) {
|
});
|
||||||
let width = "20em";
|
},
|
||||||
if (message.length > 30) {
|
warn: function (message, callback) {
|
||||||
width = "30em";
|
var width = "20em";
|
||||||
}
|
if (message.length > 30) {
|
||||||
let config = {
|
width = "30em";
|
||||||
confirmButtonText: "确定",
|
}
|
||||||
cancelButtonText: "取消",
|
Swal.fire({
|
||||||
showCancelButton: true,
|
text: message,
|
||||||
showCloseButton: false,
|
confirmButtonText: "确定",
|
||||||
buttonsStyling: false,
|
buttonsStyling: false,
|
||||||
customClass: {
|
customClass: {
|
||||||
closeButton: "ui button",
|
closeButton: "ui button",
|
||||||
cancelButton: "ui button",
|
cancelButton: "ui button",
|
||||||
confirmButton: "ui button primary"
|
confirmButton: "ui button primary"
|
||||||
},
|
},
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
width: width,
|
width: width,
|
||||||
preConfirm: function () {
|
onAfterClose: function () {
|
||||||
if (typeof (callback) == "function") {
|
if (typeof (callback) == "function") {
|
||||||
callback.call(Tea.Vue);
|
setTimeout(function () {
|
||||||
}
|
callback();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
if (message.startsWith("html:")) {
|
}
|
||||||
config.html = message.substring(5)
|
});
|
||||||
} else {
|
},
|
||||||
config.text = message
|
confirm: function (message, callback) {
|
||||||
}
|
let width = "20em";
|
||||||
Swal.fire(config);
|
if (message.length > 30) {
|
||||||
},
|
width = "30em";
|
||||||
reload: function () {
|
}
|
||||||
window.location.reload()
|
let config = {
|
||||||
}
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
showCancelButton: true,
|
||||||
|
showCloseButton: false,
|
||||||
|
buttonsStyling: false,
|
||||||
|
customClass: {
|
||||||
|
closeButton: "ui button",
|
||||||
|
cancelButton: "ui button",
|
||||||
|
confirmButton: "ui button primary"
|
||||||
|
},
|
||||||
|
icon: "warning",
|
||||||
|
width: width,
|
||||||
|
preConfirm: function () {
|
||||||
|
if (typeof (callback) == "function") {
|
||||||
|
callback.call(Tea.Vue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.startsWith("html:")) {
|
||||||
|
config.html = message.substring(5)
|
||||||
|
} else {
|
||||||
|
config.text = message
|
||||||
|
}
|
||||||
|
Swal.fire(config);
|
||||||
|
},
|
||||||
|
reload: function () {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
width: 8em;
|
width: 8em;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 7.5em;
|
top: 7.5em;
|
||||||
bottom: 0.5em;
|
bottom: 2.4em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
border-right: 1px #ddd solid;
|
border-right: 1px #ddd solid;
|
||||||
@@ -77,6 +77,9 @@
|
|||||||
.right-box.without-tabbar {
|
.right-box.without-tabbar {
|
||||||
top: 3em;
|
top: 3em;
|
||||||
}
|
}
|
||||||
|
.main.without-footer .left-box {
|
||||||
|
bottom: 0.2em;
|
||||||
|
}
|
||||||
/** 通用 **/
|
/** 通用 **/
|
||||||
.clear {
|
.clear {
|
||||||
clear: both;
|
clear: both;
|
||||||
@@ -217,9 +220,20 @@ p.margin {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@keyframes rotation {
|
||||||
|
from {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
body .ui.menu .item .blink {
|
body .ui.menu .item .blink {
|
||||||
animation: blink 1s infinite;
|
animation: blink 1s infinite;
|
||||||
}
|
}
|
||||||
|
body .ui.menu .item:not(:hover) span.rotate {
|
||||||
|
animation: rotation 3s infinite;
|
||||||
|
}
|
||||||
body.expanded .main-menu {
|
body.expanded .main-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -234,10 +248,7 @@ body.expanded .main {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
}
|
|
||||||
.top-nav::-webkit-scrollbar {
|
|
||||||
height: 2px;
|
|
||||||
}
|
}
|
||||||
.top-nav img.avatar {
|
.top-nav img.avatar {
|
||||||
width: 1.6em !important;
|
width: 1.6em !important;
|
||||||
@@ -252,9 +263,18 @@ body.expanded .main {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding-left: 0.2em;
|
padding-left: 0.2em;
|
||||||
}
|
}
|
||||||
|
.top-nav .item .hover-span span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.top-nav .item:hover .hover-span span {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
.top-nav .item.red {
|
.top-nav .item.red {
|
||||||
color: red !important;
|
color: red !important;
|
||||||
}
|
}
|
||||||
|
.top-nav::-webkit-scrollbar {
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
/** 顶部菜单 **/
|
/** 顶部菜单 **/
|
||||||
.top-secondary-menu {
|
.top-secondary-menu {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -365,11 +385,11 @@ body.expanded .main {
|
|||||||
top: 2em;
|
top: 2em;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
.main-menu .menu {
|
.main-menu .menu {
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -28,14 +28,24 @@
|
|||||||
|
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
<!-- 集群同步 -->
|
<!-- 集群同步 -->
|
||||||
<a href="" class="item" v-if="teaCheckClusterTask && doingNodeTasks.isUpdated" @click.prevent="showNodeTasks()">
|
<a href="" class="item" v-if="teaCheckNodeTasks && doingNodeTasks.isUpdated" @click.prevent="showNodeTasks()">
|
||||||
<span v-if="!doingNodeTasks.isDoing && !doingNodeTasks.hasError"><i class="icon sync"></i>已同步</span>
|
<span v-if="!doingNodeTasks.isDoing && !doingNodeTasks.hasError" class="hover-span"><i class="icon cloud disabled"></i><span class="disabled">已同步节点</span></span>
|
||||||
<span v-if="doingNodeTasks.isDoing && !doingNodeTasks.hasError"><i class="icon sync"></i>正在同步...</span>
|
<span v-if="doingNodeTasks.isDoing && !doingNodeTasks.hasError" class="hover-span rotate"><i class="icon cloud"></i><span>正在同步节点...</span></span>
|
||||||
<span v-if="doingNodeTasks.hasError" class="red"><i class="icon sync"></i>同步失败</span>
|
<span v-if="doingNodeTasks.hasError" class="red"><i class="icon cloud"></i>节点同步失败</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- DNS同步 -->
|
||||||
|
<a href="" class="item" v-if="teaCheckDNSTasks && doingDNSTasks.isUpdated" @click.prevent="showDNSTasks()">
|
||||||
|
<span v-if="!doingDNSTasks.isDoing && !doingDNSTasks.hasError" class="hover-span"><i class="icon globe disabled"></i><span class="disabled">已同步DNS</span></span>
|
||||||
|
<span v-if="doingDNSTasks.isDoing && !doingDNSTasks.hasError" class="hover-span rotate"><i class="icon globe"></i><span>正在同步DNS...</span></span>
|
||||||
|
<span v-if="doingDNSTasks.hasError" class="red"><i class="icon globe"></i>DNS同步失败</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- 消息 -->
|
<!-- 消息 -->
|
||||||
<a href="/messages" class="item" :class="{active:teaMenu == 'message'}"><span :class="{'blink':globalMessageBadge > 0}"><i class="icon bell"></i>消息({{globalMessageBadge}}) </span></a>
|
<a href="" class="item" :class="{active:teaMenu == 'message'}" @click.prevent="showMessages()">
|
||||||
|
<span v-if="globalMessageBadge > 0" class="blink hover-span"><i class="icon bell"></i><span>消息({{globalMessageBadge}}) </span></span>
|
||||||
|
<span v-if="globalMessageBadge == 0" class="hover-span"><i class="icon bell disabled"></i><span class="disabled">消息(0)</span></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- 用户信息 -->
|
<!-- 用户信息 -->
|
||||||
<a href="/settings/profile" class="item">
|
<a href="/settings/profile" class="item">
|
||||||
@@ -45,7 +55,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- 退出登录 -->
|
<!-- 退出登录 -->
|
||||||
<a :href="Tea.url('logout')" class="item" title="安全退出登录"><i class="icon sign out"></i>退出</a>
|
<a :href="Tea.url('logout')" class="item" title="安全退出登录"><i class="icon sign out"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -75,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧主操作栏 -->
|
<!-- 右侧主操作栏 -->
|
||||||
<div class="main" :class="{'without-menu':teaSubMenus.menus == null || teaSubMenus.menus.length == 0 || (teaSubMenus.menus.length == 1 && teaSubMenus.menus[0].alwaysActive), 'without-secondary-menu':teaSubMenus.alwaysMenu == null || teaSubMenus.alwaysMenu.items.length <= 1}" v-cloak="">
|
<div class="main" :class="{'without-menu':teaSubMenus.menus == null || teaSubMenus.menus.length == 0 || (teaSubMenus.menus.length == 1 && teaSubMenus.menus[0].alwaysActive), 'without-secondary-menu':teaSubMenus.alwaysMenu == null || teaSubMenus.alwaysMenu.items.length <= 1, 'without-footer':!teaShowOpenSourceInfo}" v-cloak="">
|
||||||
<!-- 操作菜单 -->
|
<!-- 操作菜单 -->
|
||||||
<div class="ui top menu tabular tab-menu small" v-if="teaTabbar.length > 0">
|
<div class="ui top menu tabular tab-menu small" v-if="teaTabbar.length > 0">
|
||||||
<a class="item" v-for="item in teaTabbar" :class="{'active':item.active,right:item.right}" :href="item.url">
|
<a class="item" v-for="item in teaTabbar" :class="{'active':item.active,right:item.right}" :href="item.url">
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ Tea.context(function () {
|
|||||||
|
|
||||||
// 检查集群节点同步
|
// 检查集群节点同步
|
||||||
this.loadNodeTasks();
|
this.loadNodeTasks();
|
||||||
|
|
||||||
|
// 检查DNS同步
|
||||||
|
this.loadDNSTasks()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +59,21 @@ Tea.context(function () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkMessagesOnce = function () {
|
||||||
|
this.$post("/messages/badge")
|
||||||
|
.params({})
|
||||||
|
.success(function (resp) {
|
||||||
|
this.globalMessageBadge = resp.data.count
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showMessages = function () {
|
||||||
|
teaweb.popup("/messages", {
|
||||||
|
height: "24em",
|
||||||
|
width: "50em"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 底部伸展框
|
* 底部伸展框
|
||||||
*/
|
*/
|
||||||
@@ -83,6 +101,9 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.loadNodeTasks = function () {
|
this.loadNodeTasks = function () {
|
||||||
|
if (!Tea.Vue.teaCheckNodeTasks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$post("/clusters/tasks/check")
|
this.$post("/clusters/tasks/check")
|
||||||
.success(function (resp) {
|
.success(function (resp) {
|
||||||
this.doingNodeTasks.isDoing = resp.data.isDoing
|
this.doingNodeTasks.isDoing = resp.data.isDoing
|
||||||
@@ -92,7 +113,7 @@ Tea.context(function () {
|
|||||||
.done(function () {
|
.done(function () {
|
||||||
this.$delay(function () {
|
this.$delay(function () {
|
||||||
this.loadNodeTasks()
|
this.loadNodeTasks()
|
||||||
}, 5000)
|
}, 3000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +123,39 @@ Tea.context(function () {
|
|||||||
width: "50em"
|
width: "50em"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DNS同步任务
|
||||||
|
*/
|
||||||
|
this.doingDNSTasks = {
|
||||||
|
isDoing: false,
|
||||||
|
hasError: false,
|
||||||
|
isUpdated: false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadDNSTasks = function () {
|
||||||
|
if (!Tea.Vue.teaCheckDNSTasks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$post("/dns/tasks/check")
|
||||||
|
.success(function (resp) {
|
||||||
|
this.doingDNSTasks.isDoing = resp.data.isDoing
|
||||||
|
this.doingDNSTasks.hasError = resp.data.hasError
|
||||||
|
this.doingDNSTasks.isUpdated = true
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.loadDNSTasks()
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showDNSTasks = function () {
|
||||||
|
teaweb.popup("/dns/tasks/listPopup", {
|
||||||
|
height: "24em",
|
||||||
|
width: "50em"
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.NotifySuccess = function (message, url, params) {
|
window.NotifySuccess = function (message, url, params) {
|
||||||
|
|||||||
@@ -176,10 +176,23 @@ div.margin, p.margin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes rotation {
|
||||||
|
from {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
body .ui.menu .item .blink {
|
body .ui.menu .item .blink {
|
||||||
animation: blink 1s infinite;
|
animation: blink 1s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body .ui.menu .item:not(:hover) span.rotate {
|
||||||
|
animation: rotation 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
body.expanded .main-menu {
|
body.expanded .main-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -196,31 +209,48 @@ body.expanded .main {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
|
|
||||||
|
img.avatar {
|
||||||
|
width: 1.6em !important;
|
||||||
|
height: 1.6em !important;
|
||||||
|
padding: 0.2em;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 0.9em;
|
||||||
|
margin-right: 0.5em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
.hover-span {
|
||||||
|
span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover {
|
||||||
|
.hover-span {
|
||||||
|
span {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item.red {
|
||||||
|
color: red !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-nav::-webkit-scrollbar {
|
.top-nav::-webkit-scrollbar {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-nav img.avatar {
|
|
||||||
width: 1.6em !important;
|
|
||||||
height: 1.6em !important;
|
|
||||||
padding: 0.2em;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 0.9em;
|
|
||||||
margin-right: 0.5em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-nav em {
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding-left: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-nav .item.red {
|
|
||||||
color: red !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 顶部菜单 **/
|
/** 顶部菜单 **/
|
||||||
.top-secondary-menu {
|
.top-secondary-menu {
|
||||||
@@ -357,11 +387,11 @@ body.expanded .main {
|
|||||||
top: 2em;
|
top: 2em;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
background: #276ac6 !important;
|
background: #14539A !important;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
width: 8em;
|
width: 8em;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 7.5em;
|
top: 7.5em;
|
||||||
bottom: 0.5em;
|
bottom: 2.4em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
border-right: 1px #ddd solid;
|
border-right: 1px #ddd solid;
|
||||||
@@ -102,3 +102,9 @@
|
|||||||
.right-box.without-tabbar {
|
.right-box.without-tabbar {
|
||||||
top: 3em;
|
top: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// main
|
||||||
|
.main.without-footer .left-box {
|
||||||
|
bottom: 0.2em;
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
||||||
<dns-route-selector :v-all-routes="dnsRoutes"></dns-route-selector>
|
<dns-route-selector :v-all-routes="dnsRoutes"></dns-route-selector>
|
||||||
<p class="comment">可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有所限制。</p>
|
<p class="comment">当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<p class="comment">点击可已选中要使用的分组。</p>
|
<p class="comment">点击可已选中要使用的分组。</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<p class="comment">暂时还没有可以使用的分组。</p>
|
<p class="comment">当前集群下暂时还没有可以使用的分组。</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -4,4 +4,12 @@
|
|||||||
.right-box {
|
.right-box {
|
||||||
top: 10em;
|
top: 10em;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
h3 button {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: -0.2em;
|
||||||
|
}
|
||||||
/*# sourceMappingURL=installRemote.css.map */
|
/*# sourceMappingURL=installRemote.css.map */
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"sources":["installRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"installRemote.css"}
|
{"version":3,"sources":["installRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA;;AAGD;EACC,kBAAA;;AAGD,EAAG;EACF,kBAAA;EACA,UAAA;EACA,WAAA","file":"installRemote.css"}
|
||||||
@@ -6,10 +6,15 @@
|
|||||||
<p class="comment" v-if="nodes.length == 0">暂时没有需要远程安装的节点。</p>
|
<p class="comment" v-if="nodes.length == 0">暂时没有需要远程安装的节点。</p>
|
||||||
|
|
||||||
<div v-if="nodes.length > 0">
|
<div v-if="nodes.length > 0">
|
||||||
<h3>所有未安装节点</h3>
|
<h3>所有未安装节点
|
||||||
|
<button class="ui button primary tiny" v-if="countCheckedNodes() > 0" @click.prevent="installBatch()">批量安装({{countCheckedNodes()}})</button>
|
||||||
|
</h3>
|
||||||
<table class="ui table selectable celled">
|
<table class="ui table selectable celled">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="width:3em">
|
||||||
|
<checkbox @input="checkNodes"></checkbox>
|
||||||
|
</th>
|
||||||
<th>节点名</th>
|
<th>节点名</th>
|
||||||
<th>访问IP</th>
|
<th>访问IP</th>
|
||||||
<th>SSH地址</th>
|
<th>SSH地址</th>
|
||||||
@@ -18,11 +23,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr v-for="node in nodes">
|
<tr v-for="node in nodes">
|
||||||
|
<td>
|
||||||
|
<checkbox v-model="node.isChecked" v-if="node.installStatus == null || !node.installStatus.isOk"></checkbox>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</a>
|
<link-icon :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-for="addr in node.addresses" v-if="addr.canAccess" class="ui label tiny">{{addr.ip}}</span>
|
<span v-for="addr in node.addresses" v-if="addr.canAccess" class="ui label tiny basic">{{addr.ip}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="node.login != null && node.login.type == 'ssh' && node.loginParams != null && node.loginParams.host != null && node.loginParams.host.length > 0">
|
<span v-if="node.login != null && node.login.type == 'ssh' && node.loginParams != null && node.loginParams.host != null && node.loginParams.host.length > 0">
|
||||||
|
|||||||
@@ -1,87 +1,160 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.isInstalling = false
|
this.isInstalling = false
|
||||||
let installingNode = null
|
this.isBatch = false
|
||||||
|
let installingNode = null
|
||||||
|
|
||||||
this.$delay(function () {
|
this.nodes.forEach(function (v) {
|
||||||
this.reload()
|
v.isChecked = false
|
||||||
})
|
})
|
||||||
|
|
||||||
this.installNode = function (node) {
|
this.$delay(function () {
|
||||||
let that = this
|
this.reload()
|
||||||
teaweb.confirm("确定要开始安装此节点吗?", function () {
|
})
|
||||||
installingNode = node
|
|
||||||
that.isInstalling = true
|
|
||||||
node.isInstalling = true
|
|
||||||
|
|
||||||
that.$post("$")
|
let that = this
|
||||||
.params({
|
|
||||||
nodeId: node.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.reload = function () {
|
this.checkNodes = function (isChecked) {
|
||||||
let that = this
|
this.nodes.forEach(function (v) {
|
||||||
if (installingNode != null) {
|
v.isChecked = isChecked
|
||||||
this.$post("/clusters/cluster/installStatus")
|
})
|
||||||
.params({
|
}
|
||||||
nodeId: installingNode.id
|
|
||||||
})
|
|
||||||
.success(function (resp) {
|
|
||||||
if (resp.data.status != null) {
|
|
||||||
installingNode.installStatus = resp.data.status
|
|
||||||
if (installingNode.installStatus.isFinished) {
|
|
||||||
if (installingNode.installStatus.isOk) {
|
|
||||||
installingNode = null
|
|
||||||
teaweb.success("安装成功", function () {
|
|
||||||
window.location.reload()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let nodeId = installingNode.id
|
|
||||||
let errMsg = installingNode.installStatus.error
|
|
||||||
that.isInstalling = false
|
|
||||||
installingNode.isInstalling = false
|
|
||||||
installingNode = null
|
|
||||||
|
|
||||||
switch (resp.data.status.errorCode) {
|
this.countCheckedNodes = function () {
|
||||||
case "EMPTY_LOGIN":
|
return that.nodes.$count(function (k, v) {
|
||||||
case "EMPTY_SSH_HOST":
|
return v.isChecked
|
||||||
case "EMPTY_SSH_PORT":
|
})
|
||||||
case "EMPTY_GRANT":
|
}
|
||||||
teaweb.warn("需要填写SSH登录信息", function () {
|
|
||||||
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
this.installNode = function (node) {
|
||||||
callback: function () {
|
let that = this
|
||||||
teaweb.reload()
|
if (this.isBatch) {
|
||||||
}
|
installingNode = node
|
||||||
})
|
that.isInstalling = true
|
||||||
})
|
node.isInstalling = true
|
||||||
return
|
|
||||||
case "CREATE_ROOT_DIRECTORY_FAILED":
|
that.$post("$")
|
||||||
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg)
|
.params({
|
||||||
return
|
nodeId: node.id
|
||||||
case "INSTALL_HELPER_FAILED":
|
})
|
||||||
teaweb.warn("安装助手失败:" + errMsg)
|
} else {
|
||||||
return
|
teaweb.confirm("确定要开始安装此节点吗?", function () {
|
||||||
case "TEST_FAILED":
|
installingNode = node
|
||||||
teaweb.warn("环境测试失败:" + errMsg)
|
that.isInstalling = true
|
||||||
return
|
node.isInstalling = true
|
||||||
case "RPC_TEST_FAILED":
|
|
||||||
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
that.$post("$")
|
||||||
window.location = "/api"
|
.params({
|
||||||
})
|
nodeId: node.id
|
||||||
return
|
})
|
||||||
default:
|
})
|
||||||
teaweb.warn("安装失败:" + errMsg)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
this.installBatch = function () {
|
||||||
}
|
let that = this
|
||||||
})
|
this.isBatch = true
|
||||||
.done(function () {
|
teaweb.confirm("确定要批量安装选中的节点吗?", function () {
|
||||||
setTimeout(this.reload, 3000)
|
that.installNext()
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
setTimeout(this.reload, 3000)
|
|
||||||
}
|
/**
|
||||||
}
|
* 安装下一个
|
||||||
|
*/
|
||||||
|
this.installNext = function () {
|
||||||
|
let nextNode = this.nodes.$find(function (k, v) {
|
||||||
|
return v.isChecked
|
||||||
|
})
|
||||||
|
|
||||||
|
if (nextNode == null) {
|
||||||
|
teaweb.success("全部安装成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.installNode(nextNode)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载状态
|
||||||
|
*/
|
||||||
|
this.reload = function () {
|
||||||
|
let that = this
|
||||||
|
if (installingNode != null) {
|
||||||
|
this.$post("/clusters/cluster/installStatus")
|
||||||
|
.params({
|
||||||
|
nodeId: installingNode.id
|
||||||
|
})
|
||||||
|
.success(function (resp) {
|
||||||
|
if (resp.data.status != null) {
|
||||||
|
installingNode.installStatus = resp.data.status
|
||||||
|
if (installingNode.installStatus.isFinished) {
|
||||||
|
if (installingNode.installStatus.isOk) {
|
||||||
|
installingNode.isChecked = false // 取消选中
|
||||||
|
installingNode = null
|
||||||
|
if (that.isBatch) {
|
||||||
|
that.installNext()
|
||||||
|
} else {
|
||||||
|
teaweb.success("安装成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let nodeId = installingNode.id
|
||||||
|
let errMsg = installingNode.installStatus.error
|
||||||
|
that.isInstalling = false
|
||||||
|
installingNode.isInstalling = false
|
||||||
|
installingNode = null
|
||||||
|
|
||||||
|
switch (resp.data.status.errorCode) {
|
||||||
|
case "EMPTY_LOGIN":
|
||||||
|
case "EMPTY_SSH_HOST":
|
||||||
|
case "EMPTY_SSH_PORT":
|
||||||
|
case "EMPTY_GRANT":
|
||||||
|
teaweb.warn("需要填写SSH登录信息", function () {
|
||||||
|
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
teaweb.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "SSH登录失败,请检查设置":
|
||||||
|
teaweb.warn("需要填写SSH登录信息", function () {
|
||||||
|
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
teaweb.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "CREATE_ROOT_DIRECTORY_FAILED":
|
||||||
|
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg)
|
||||||
|
return
|
||||||
|
case "INSTALL_HELPER_FAILED":
|
||||||
|
teaweb.warn("安装助手失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "TEST_FAILED":
|
||||||
|
teaweb.warn("环境测试失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "RPC_TEST_FAILED":
|
||||||
|
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||||
|
window.location = "/api"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
teaweb.warn("安装失败:" + errMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
setTimeout(this.reload, 3000)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setTimeout(this.reload, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@@ -5,3 +5,13 @@
|
|||||||
.right-box {
|
.right-box {
|
||||||
top: 10em;
|
top: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 button {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: -0.2em;
|
||||||
|
}
|
||||||
@@ -1,45 +1,103 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.$delay(function () {
|
let isInstalling = false
|
||||||
this.reloadStatus(this.nodeId)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 开始安装
|
this.$delay(function () {
|
||||||
this.install = function () {
|
this.reloadStatus(this.nodeId)
|
||||||
this.$post("$")
|
})
|
||||||
.params({
|
|
||||||
nodeId: this.nodeId
|
|
||||||
})
|
|
||||||
.success(function () {
|
|
||||||
|
|
||||||
})
|
// 开始安装
|
||||||
}
|
this.install = function () {
|
||||||
|
isInstalling = true
|
||||||
|
|
||||||
// 设置节点安装状态
|
this.$post("$")
|
||||||
this.updateNodeIsInstalled = function (isInstalled) {
|
.params({
|
||||||
teaweb.confirm("确定要将当前节点修改为未安装状态?", function () {
|
nodeId: this.nodeId
|
||||||
this.$post("/clusters/cluster/node/updateInstallStatus")
|
})
|
||||||
.params({
|
.success(function () {
|
||||||
nodeId: this.nodeId,
|
|
||||||
isInstalled: isInstalled ? 1 : 0
|
|
||||||
})
|
|
||||||
.refresh()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新状态
|
})
|
||||||
this.reloadStatus = function (nodeId) {
|
}
|
||||||
this.$post("/clusters/cluster/node/status")
|
|
||||||
.params({
|
// 设置节点安装状态
|
||||||
nodeId: nodeId
|
this.updateNodeIsInstalled = function (isInstalled) {
|
||||||
})
|
teaweb.confirm("确定要将当前节点修改为未安装状态?", function () {
|
||||||
.success(function (resp) {
|
this.$post("/clusters/cluster/node/updateInstallStatus")
|
||||||
this.installStatus = resp.data.installStatus
|
.params({
|
||||||
this.node.isInstalled = resp.data.isInstalled
|
nodeId: this.nodeId,
|
||||||
})
|
isInstalled: isInstalled ? 1 : 0
|
||||||
.done(function () {
|
})
|
||||||
this.$delay(function () {
|
.refresh()
|
||||||
this.reloadStatus(nodeId)
|
})
|
||||||
}, 1000)
|
}
|
||||||
});
|
|
||||||
}
|
// 刷新状态
|
||||||
|
this.reloadStatus = function (nodeId) {
|
||||||
|
let that = this
|
||||||
|
|
||||||
|
this.$post("/clusters/cluster/node/status")
|
||||||
|
.params({
|
||||||
|
nodeId: nodeId
|
||||||
|
})
|
||||||
|
.success(function (resp) {
|
||||||
|
this.installStatus = resp.data.installStatus
|
||||||
|
this.node.isInstalled = resp.data.isInstalled
|
||||||
|
|
||||||
|
if (!isInstalling) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodeId = this.node.id
|
||||||
|
let errMsg = this.installStatus.error
|
||||||
|
|
||||||
|
if (this.installStatus.errorCode.length > 0) {
|
||||||
|
isInstalling = false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.installStatus.errorCode) {
|
||||||
|
case "EMPTY_LOGIN":
|
||||||
|
case "EMPTY_SSH_HOST":
|
||||||
|
case "EMPTY_SSH_PORT":
|
||||||
|
case "EMPTY_GRANT":
|
||||||
|
teaweb.warn("需要填写SSH登录信息", function () {
|
||||||
|
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
that.install()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "SSH_LOGIN_FAILED":
|
||||||
|
teaweb.warn("SSH登录失败,请检查设置", function () {
|
||||||
|
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
that.install()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "CREATE_ROOT_DIRECTORY_FAILED":
|
||||||
|
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg)
|
||||||
|
return
|
||||||
|
case "INSTALL_HELPER_FAILED":
|
||||||
|
teaweb.warn("安装助手失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "TEST_FAILED":
|
||||||
|
teaweb.warn("环境测试失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "RPC_TEST_FAILED":
|
||||||
|
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||||
|
window.location = "/api"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
shouldReload = true
|
||||||
|
//teaweb.warn("安装失败:" + errMsg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadStatus(nodeId)
|
||||||
|
}, 1000)
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="dnsRoutes.length > 0">
|
<tr v-if="dnsRoutes.length > 0 && dnsRoutes[0].name.length > 0">
|
||||||
<td>DNS线路</td>
|
<td>DNS线路</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="ui label tiny basic" v-for="route in dnsRoutes">{{route.name}}</span>
|
<span class="ui label tiny basic" v-for="route in dnsRoutes">{{route.name}}</span>
|
||||||
@@ -37,7 +37,28 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr v-if="dnsRecordName.length > 0 && dnsRecordValue.length > 0">
|
<tr v-if="dnsRecordName.length > 0 && dnsRecordValue.length > 0">
|
||||||
<td>DNS记录</td>
|
<td>DNS记录</td>
|
||||||
<td>{{dnsRecordName}} -> {{dnsRecordValue}}
|
<td>
|
||||||
|
<table class="ui table celled">
|
||||||
|
<thead class="full-width">
|
||||||
|
<tr>
|
||||||
|
<th>记录名</th>
|
||||||
|
<th>记录类型</th>
|
||||||
|
<th>线路</th>
|
||||||
|
<th>记录值</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-for="address in node.ipAddresses" v-if="address.canAccess">
|
||||||
|
<tr v-for="route in dnsRoutes">
|
||||||
|
<td>{{dnsRecordName}}</td>
|
||||||
|
<td>A</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="route.name.length > 0">{{route.name}}</span>
|
||||||
|
<span v-else class="disabled">默认</span>
|
||||||
|
</td>
|
||||||
|
<td>{{address.ip}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<p class="comment">通过设置A记录可以将集群上的服务请求转发到不同线路的节点上。</p>
|
<p class="comment">通过设置A记录可以将集群上的服务请求转发到不同线路的节点上。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -125,17 +146,34 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tbody v-show="node.status.isActive">
|
<tbody v-show="node.status.isActive">
|
||||||
<tr>
|
<tr>
|
||||||
<td>CPU</td>
|
<td>CPU用量</td>
|
||||||
<td>{{node.status.cpuUsageText}}</td>
|
<td>{{node.status.cpuUsageText}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>内存</td>
|
<td>内存用量</td>
|
||||||
<td>{{node.status.memUsageText}}</td>
|
<td>{{node.status.memUsageText}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>连接数</td>
|
<td>连接数</td>
|
||||||
<td>{{node.status.connectionCount}}</td>
|
<td>{{node.status.connectionCount}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>版本</td>
|
||||||
|
<td>v{{node.status.buildVersion}}
|
||||||
|
<a :href="'/clusters/cluster/upgradeRemote?clusterId=' + clusterId" v-if="shouldUpgrade"><span class="red">发现新版本v{{newVersion}} »</span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CPU</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="node.status.cpuPhysicalCount > 0">{{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程</span>
|
||||||
|
<span v-else class="disabled">-/-</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>负载</td>
|
||||||
|
<td>{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}} <tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="comment" v-if="node.status.isActive">每隔30秒钟更新一次运行状态。</p>
|
<p class="comment" v-if="node.status.isActive">每隔30秒钟更新一次运行状态。</p>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
|
||||||
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
|
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
|
||||||
|
<p class="comment">当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3>添加动作</h3>
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||||
|
<csrf-token></csrf-token>
|
||||||
|
|
||||||
|
<table class="ui table selectable definition">
|
||||||
|
<tr>
|
||||||
|
<td class="title">名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="name" maxlength="50" ref="focus"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>级别</td>
|
||||||
|
<td>
|
||||||
|
<firewall-event-level-options :v-value="'critical'"></firewall-event-level-options>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>类型</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown auto-width" name="type" v-model="type">
|
||||||
|
<option value="">[请选择]</option>
|
||||||
|
<option v-for="type in actionTypes" :value="type.code">{{type.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment">{{typeDescription}}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- IPSet -->
|
||||||
|
<tbody v-if="type == 'ipset'">
|
||||||
|
<tr>
|
||||||
|
<td>IPSet白名单名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="ipsetWhiteName" value="edge_white_list" maxlength="64"/>
|
||||||
|
<p class="comment">只能是英文、数字、下划线的组合。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IPSet黑名单名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="ipsetBlackName" value="edge_black_list" maxlength="64"/>
|
||||||
|
<p class="comment">只能是英文、数字、下划线的组合。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>创建IPTables规则</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="ipsetAutoAddToIPTables" :value="true"></checkbox>
|
||||||
|
<p class="comment">是否尝试自动创建包含有此IPSet的IPTables规则。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>创建Firewalld规则</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="ipsetAutoAddToFirewalld" :value="true"></checkbox>
|
||||||
|
<p class="comment">是否尝试自动创建包含有此IPSet的Firewalld规则。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- Firewalld -->
|
||||||
|
<tbody v-if="type == 'firewalld'">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- IPTables -->
|
||||||
|
<tbody v-if="type == 'iptables'">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- 脚本 -->
|
||||||
|
<tbody v-if="type == 'script'">
|
||||||
|
<tr>
|
||||||
|
<td>脚本路径 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="scriptPath" maxlength="200"/>
|
||||||
|
<p class="comment">可执行脚本文件的完整路径。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- HTTP API -->
|
||||||
|
<tbody v-if="type == 'httpAPI'">
|
||||||
|
<tr>
|
||||||
|
<td>API URL *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="httpAPIURL" maxlength="200" placeholder="http|https://..."/>
|
||||||
|
<p class="comment">完整的API地址。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
let that = this
|
||||||
|
|
||||||
|
// 类型
|
||||||
|
this.$watch("type", function () {
|
||||||
|
that.changeType()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.changeType()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
this.type = ""
|
||||||
|
this.typeDescription = ""
|
||||||
|
|
||||||
|
this.changeType = function () {
|
||||||
|
let that = this
|
||||||
|
let t = this.actionTypes.$find(function (k, v) {
|
||||||
|
return v.code == that.type
|
||||||
|
})
|
||||||
|
if (t != null) {
|
||||||
|
this.typeDescription = t.description
|
||||||
|
} else {
|
||||||
|
this.typeDescription = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "/left_menu"}
|
||||||
|
|
||||||
|
<div class="right-box">
|
||||||
|
<first-menu>
|
||||||
|
<menu-item @click.prevent="createAction">添加动作</menu-item>
|
||||||
|
<span class="item disabled">|</span>
|
||||||
|
<span class="item"><tip-icon content="可以设置WAF中的规则集和IP名单中的IP级别,从而在匹配时触发对应的动作。"></tip-icon></span>
|
||||||
|
</first-menu>
|
||||||
|
|
||||||
|
<div class="margin"></div>
|
||||||
|
<div v-if="!hasActions">
|
||||||
|
<p class="comment">暂时还没有自定义动作。</p>
|
||||||
|
</div>
|
||||||
|
<div v-for="level in levels" v-if="level.actions.length > 0">
|
||||||
|
<h4 style="margin-bottom: 0">{{level.name}}级别</h4>
|
||||||
|
<p class="comment" v-if="level.actions.length == 0">暂时还没有定义动作。</p>
|
||||||
|
<table class="ui table selectable" v-if="level.actions.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>名称</th>
|
||||||
|
<th class="four wide">类型</th>
|
||||||
|
<th class="two op">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="action in level.actions">
|
||||||
|
<td>{{action.name}}</td>
|
||||||
|
<td>{{action.typeName}}</td>
|
||||||
|
<td>
|
||||||
|
<a href="" @click.prevent="updateAction(action.id)">修改</a> <a href="" @click.prevent="deleteAction(action.id)">删除</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.createAction = function () {
|
||||||
|
teaweb.popup(Tea.url(".createPopup", {clusterId: this.clusterId}), {
|
||||||
|
callback: function () {
|
||||||
|
teaweb.success("保存成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateAction = function (actionId) {
|
||||||
|
teaweb.popup(Tea.url(".updatePopup", {actionId: actionId}), {
|
||||||
|
callback: function () {
|
||||||
|
teaweb.success("保存成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteAction = function (actionId) {
|
||||||
|
let that = this
|
||||||
|
teaweb.confirm("确定要删除此动作吗?", function () {
|
||||||
|
that.$post(".delete")
|
||||||
|
.params({
|
||||||
|
actionId: actionId
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
teaweb.success("删除成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3>修改动作</h3>
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="actionId" :value="action.id"/>
|
||||||
|
<csrf-token></csrf-token>
|
||||||
|
|
||||||
|
<table class="ui table selectable definition">
|
||||||
|
<tr>
|
||||||
|
<td class="title">名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="name" maxlength="50" ref="focus" v-model="action.name"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>级别</td>
|
||||||
|
<td>
|
||||||
|
<firewall-event-level-options :v-value="eventLevel"></firewall-event-level-options>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>类型</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown auto-width" name="type" v-model="type">
|
||||||
|
<option value="">[请选择]</option>
|
||||||
|
<option v-for="type in actionTypes" :value="type.code">{{type.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment">{{typeDescription}}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- IPSet -->
|
||||||
|
<tbody v-if="type == 'ipset'">
|
||||||
|
<tr>
|
||||||
|
<td>IPSet白名单名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="ipsetWhiteName" value="edge_white_list" maxlength="64" v-model="action.params.whiteName"/>
|
||||||
|
<p class="comment">只能是英文、数字、下划线的组合。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IPSet黑名单名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="ipsetBlackName" value="edge_black_list" maxlength="64" v-model="action.params.blackName"/>
|
||||||
|
<p class="comment">只能是英文、数字、下划线的组合。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>创建IPTables规则</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="ipsetAutoAddToIPTables" v-model="action.params.autoAddToIPTables"></checkbox>
|
||||||
|
<p class="comment">是否尝试自动创建包含有此IPSet的IPTables规则。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>创建Firewalld规则</td>
|
||||||
|
<td>
|
||||||
|
<checkbox name="ipsetAutoAddToFirewalld" v-model="action.params.autoAddToFirewalld"></checkbox>
|
||||||
|
<p class="comment">是否尝试自动创建包含有此IPSet的Firewalld规则。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- Firewalld -->
|
||||||
|
<tbody v-if="type == 'firewalld'">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- IPTables -->
|
||||||
|
<tbody v-if="type == 'iptables'">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- 脚本 -->
|
||||||
|
<tbody v-if="type == 'script'">
|
||||||
|
<tr>
|
||||||
|
<td>脚本路径 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="scriptPath" maxlength="200" v-model="action.params.path"/>
|
||||||
|
<p class="comment">可执行脚本文件的完整路径。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<!-- HTTP API -->
|
||||||
|
<tbody v-if="type == 'httpAPI'">
|
||||||
|
<tr>
|
||||||
|
<td>API URL *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="httpAPIURL" maxlength="200" placeholder="http|https://..." v-model="action.params.url"/>
|
||||||
|
<p class="comment">完整的API地址。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
let that = this
|
||||||
|
|
||||||
|
// 类型
|
||||||
|
this.$watch("type", function () {
|
||||||
|
that.changeType()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.changeType()
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 级别
|
||||||
|
*/
|
||||||
|
this.eventLevel = this.action.eventLevel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
this.type = this.action.type
|
||||||
|
this.typeDescription = ""
|
||||||
|
|
||||||
|
this.changeType = function () {
|
||||||
|
let that = this
|
||||||
|
let t = this.actionTypes.$find(function (k, v) {
|
||||||
|
return v.code == that.type
|
||||||
|
})
|
||||||
|
if (t != null) {
|
||||||
|
this.typeDescription = t.description
|
||||||
|
} else {
|
||||||
|
this.typeDescription = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>SSH主机端口 &</td>
|
<td>SSH主机端口 *</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
|
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
|
||||||
<p class="comment">比如22。</p>
|
<p class="comment">比如22。</p>
|
||||||
|
|||||||
@@ -4,4 +4,12 @@
|
|||||||
.right-box {
|
.right-box {
|
||||||
top: 10em;
|
top: 10em;
|
||||||
}
|
}
|
||||||
|
h3 {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
h3 button {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: -0.2em;
|
||||||
|
}
|
||||||
/*# sourceMappingURL=upgradeRemote.css.map */
|
/*# sourceMappingURL=upgradeRemote.css.map */
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"sources":["upgradeRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"upgradeRemote.css"}
|
{"version":3,"sources":["upgradeRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA;;AAGD;EACC,kBAAA;;AAGD,EAAG;EACF,kBAAA;EACA,UAAA;EACA,WAAA","file":"upgradeRemote.css"}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user