diff --git a/internal/configloaders/admin_module.go b/internal/configloaders/admin_module.go
index b372dabe..8e7abb4c 100644
--- a/internal/configloaders/admin_module.go
+++ b/internal/configloaders/admin_module.go
@@ -1,6 +1,7 @@
package configloaders
import (
+ teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
@@ -14,6 +15,7 @@ const (
AdminModuleCodeServer AdminModuleCode = "server" // 网站
AdminModuleCodeNode AdminModuleCode = "node" // 节点
AdminModuleCodeDNS AdminModuleCode = "dns" // DNS
+ AdminModuleCodeNS AdminModuleCode = "ns" // 域名服务
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
@@ -68,7 +70,7 @@ func NotifyAdminModuleMappingChange() error {
return err
}
-// 检查用户是否存在
+// CheckAdmin 检查用户是否存在
func CheckAdmin(adminId int64) bool {
locker.Lock()
defer locker.Unlock()
@@ -82,7 +84,7 @@ func CheckAdmin(adminId int64) bool {
return ok
}
-// 检查模块是否允许访问
+// AllowModule 检查模块是否允许访问
func AllowModule(adminId int64, module string) bool {
locker.Lock()
defer locker.Unlock()
@@ -103,7 +105,7 @@ func AllowModule(adminId int64, module string) bool {
return false
}
-// 获取管理员第一个可访问模块
+// FindFirstAdminModule 获取管理员第一个可访问模块
func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
locker.Lock()
defer locker.Unlock()
@@ -118,7 +120,7 @@ func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
return
}
-// 查找某个管理员名称
+// FindAdminFullname 查找某个管理员名称
func FindAdminFullname(adminId int64) string {
locker.Lock()
defer locker.Unlock()
@@ -130,9 +132,9 @@ func FindAdminFullname(adminId int64) string {
return ""
}
-// 所有权限列表
+// AllModuleMaps 所有权限列表
func AllModuleMaps() []maps.Map {
- return []maps.Map{
+ m := []maps.Map{
{
"name": "看板",
"code": AdminModuleCodeDashboard,
@@ -153,6 +155,15 @@ func AllModuleMaps() []maps.Map {
"code": AdminModuleCodeDNS,
"url": "/dns",
},
+ }
+ if teaconst.IsPlus {
+ m = append(m, maps.Map{
+ "name": "域名服务器",
+ "code": AdminModuleCodeNS,
+ "url": "/ns",
+ })
+ }
+ m = append(m, []maps.Map{
{
"name": "平台用户",
"code": AdminModuleCodeUser,
@@ -178,5 +189,6 @@ func AllModuleMaps() []maps.Map {
"code": AdminModuleCodeSetting,
"url": "/settings",
},
- }
+ }...)
+ return m
}
diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go
index b47c7067..ba18db89 100644
--- a/internal/rpc/rpc_client.go
+++ b/internal/rpc/rpc_client.go
@@ -352,6 +352,10 @@ func (this *RPCClient) LatestItemRPC() pb.LatestItemServiceClient {
return pb.NewLatestItemServiceClient(this.pickConn())
}
+func (this *RPCClient) NSClusterRPC() pb.NSClusterServiceClient {
+ return pb.NewNSClusterServiceClient(this.pickConn())
+}
+
// Context 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
diff --git a/internal/web/actions/default/ns/clusters/cluster/delete.go b/internal/web/actions/default/ns/clusters/cluster/delete.go
new file mode 100644
index 00000000..c29e6b33
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/cluster/delete.go
@@ -0,0 +1,38 @@
+package cluster
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+)
+
+type DeleteAction struct {
+ actionutils.ParentAction
+}
+
+func (this *DeleteAction) Init() {
+ this.Nav("", "delete", "index")
+ this.SecondMenu("nodes")
+}
+
+func (this *DeleteAction) RunGet(params struct{}) {
+ this.Show()
+}
+
+func (this *DeleteAction) RunPost(params struct {
+ ClusterId int64
+}) {
+ // 创建日志
+ defer this.CreateLog(oplogs.LevelInfo, "删除域名服务集群 %d", params.ClusterId)
+
+ // TODO 如果有用户在使用此集群,就不能删除
+
+ // 删除
+ _, err := this.RPC().NSClusterRPC().DeleteNSCluster(this.AdminContext(), &pb.DeleteNSCluster{NsClusterId: params.ClusterId})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/ns/clusters/cluster/index.go b/internal/web/actions/default/ns/clusters/cluster/index.go
new file mode 100644
index 00000000..c9fcf0f5
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/cluster/index.go
@@ -0,0 +1,17 @@
+// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
+
+package cluster
+
+import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+
+type IndexAction struct {
+ actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+ this.Nav("", "node", "")
+}
+
+func (this *IndexAction) RunGet(params struct{}) {
+ this.Show()
+}
diff --git a/internal/web/actions/default/ns/clusters/cluster/init.go b/internal/web/actions/default/ns/clusters/cluster/init.go
new file mode 100644
index 00000000..ab9a0f0e
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/cluster/init.go
@@ -0,0 +1,22 @@
+package cluster
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+ "github.com/iwind/TeaGo"
+)
+
+func init() {
+ TeaGo.BeforeStart(func(server *TeaGo.Server) {
+ server.
+ Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
+ Helper(new(clusterutils.ClusterHelper)).
+ Data("teaMenu", "ns").
+ Data("teaSubMenu", "cluster").
+ Prefix("/ns/clusters/cluster").
+ Get("", new(IndexAction)).
+ GetPost("/delete", new(DeleteAction)).
+ EndAll()
+ })
+}
diff --git a/internal/web/actions/default/ns/clusters/cluster/settings/index.go b/internal/web/actions/default/ns/clusters/cluster/settings/index.go
new file mode 100644
index 00000000..9dfa36c6
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/cluster/settings/index.go
@@ -0,0 +1,69 @@
+// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
+
+package cluster
+
+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"
+)
+
+type IndexAction struct {
+ actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+ this.Nav("", "setting", "")
+ this.SecondMenu("basic")
+}
+
+func (this *IndexAction) RunGet(params struct {
+ ClusterId int64
+}) {
+ clusterResp, err := this.RPC().NSClusterRPC().FindEnabledNSCluster(this.AdminContext(), &pb.FindEnabledNSClusterRequest{NsClusterId: params.ClusterId})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ cluster := clusterResp.NsCluster
+ if cluster == nil {
+ this.NotFound("nsCluster", params.ClusterId)
+ return
+ }
+
+ this.Data["cluster"] = maps.Map{
+ "id": cluster.Id,
+ "name": cluster.Name,
+ "isOn": cluster.IsOn,
+ }
+
+ this.Show()
+}
+
+func (this *IndexAction) RunPost(params struct {
+ ClusterId int64
+ Name string
+ IsOn bool
+
+ Must *actions.Must
+ CSRF *actionutils.CSRF
+}) {
+ defer this.CreateLogInfo("修改域名服务集群基本信息 %d", params.ClusterId)
+
+ params.Must.
+ Field("name", params.Name).
+ Require("请输入集群名称")
+
+ _, err := this.RPC().NSClusterRPC().UpdateNSCluster(this.AdminContext(), &pb.UpdateNSClusterRequest{
+ NsClusterId: params.ClusterId,
+ Name: params.Name,
+ IsOn: params.IsOn,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/ns/clusters/cluster/settings/init.go b/internal/web/actions/default/ns/clusters/cluster/settings/init.go
new file mode 100644
index 00000000..45b32a53
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/cluster/settings/init.go
@@ -0,0 +1,21 @@
+package cluster
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+ "github.com/iwind/TeaGo"
+)
+
+func init() {
+ TeaGo.BeforeStart(func(server *TeaGo.Server) {
+ server.
+ Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
+ Helper(new(clusterutils.ClusterHelper)).
+ Data("teaMenu", "ns").
+ Data("teaSubMenu", "cluster").
+ Prefix("/ns/clusters/cluster/settings").
+ GetPost("", new(IndexAction)).
+ EndAll()
+ })
+}
diff --git a/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go
new file mode 100644
index 00000000..f4a0e45f
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go
@@ -0,0 +1,87 @@
+package clusterutils
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/rpc"
+ "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/actions"
+ "github.com/iwind/TeaGo/logs"
+ "github.com/iwind/TeaGo/maps"
+ "net/http"
+ "strconv"
+)
+
+// ClusterHelper 单个集群的帮助
+type ClusterHelper struct {
+}
+
+func NewClusterHelper() *ClusterHelper {
+ return &ClusterHelper{}
+}
+
+func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
+ action := actionPtr.Object()
+ if action.Request.Method != http.MethodGet {
+ return true
+ }
+
+ action.Data["teaMenu"] = "ns"
+
+ selectedTabbar := action.Data.GetString("mainTab")
+ clusterId := action.ParamInt64("clusterId")
+ clusterIdString := strconv.FormatInt(clusterId, 10)
+ action.Data["clusterId"] = clusterId
+
+ if clusterId > 0 {
+ rpcClient, err := rpc.SharedRPC()
+ if err != nil {
+ logs.Error(err)
+ return
+ }
+ clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(rpc.ContextInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{
+ NsClusterId: clusterId,
+ })
+ if err != nil {
+ logs.Error(err)
+ return
+ }
+ cluster := clusterResp.NsCluster
+ if cluster == nil {
+ action.WriteString("can not find ns cluster")
+ return
+ }
+
+ tabbar := actionutils.NewTabbar()
+ tabbar.Add("集群列表", "", "/ns/clusters", "", false)
+ tabbar.Add("集群节点", "", "/ns/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
+ tabbar.Add("集群设置", "", "/ns/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
+ tabbar.Add("删除集群", "", "/ns/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
+
+ {
+ m := tabbar.Add("当前集群:"+cluster.Name, "", "/ns/clusters/cluster?clusterId="+clusterIdString, "", false)
+ m["right"] = true
+ }
+ actionutils.SetTabbar(action, tabbar)
+
+ // 左侧菜单
+ secondMenuItem := action.Data.GetString("secondMenuItem")
+ switch selectedTabbar {
+ case "setting":
+ action.Data["leftMenuItems"] = this.createSettingMenu(cluster, secondMenuItem)
+ }
+ }
+
+ return true
+}
+
+// 设置菜单
+func (this *ClusterHelper) createSettingMenu(cluster *pb.NSCluster, selectedItem string) (items []maps.Map) {
+ clusterId := numberutils.FormatInt64(cluster.Id)
+ items = append(items, maps.Map{
+ "name": "基础设置",
+ "url": "/ns/clusters/cluster/settings?clusterId=" + clusterId,
+ "isActive": selectedItem == "basic",
+ })
+ return
+}
diff --git a/internal/web/actions/default/ns/clusters/create.go b/internal/web/actions/default/ns/clusters/create.go
new file mode 100644
index 00000000..9db92d49
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/create.go
@@ -0,0 +1,46 @@
+// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
+
+package clusters
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/actions"
+)
+
+type CreateAction struct {
+ actionutils.ParentAction
+}
+
+func (this *CreateAction) Init() {
+ this.Nav("", "", "create")
+}
+
+func (this *CreateAction) RunGet(params struct{}) {
+ this.Show()
+}
+
+func (this *CreateAction) RunPost(params struct {
+ Name string
+
+ Must *actions.Must
+ CSRF *actionutils.CSRF
+}) {
+ var clusterId int64
+ defer this.CreateLogInfo("创建域名服务集群 %d", clusterId)
+
+ params.Must.
+ Field("name", params.Name).
+ Require("请输入集群名称")
+
+ resp, err := this.RPC().NSClusterRPC().CreateNSCluster(this.AdminContext(), &pb.CreateNSClusterRequest{
+ Name: params.Name,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ clusterId = resp.NsClusterId
+
+ this.Success()
+}
diff --git a/internal/web/actions/default/ns/clusters/index.go b/internal/web/actions/default/ns/clusters/index.go
new file mode 100644
index 00000000..1cd6e9a4
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/index.go
@@ -0,0 +1,48 @@
+// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
+
+package clusters
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+ "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+ "github.com/iwind/TeaGo/maps"
+)
+
+type IndexAction struct {
+ actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+ this.Nav("", "", "index")
+}
+
+func (this *IndexAction) RunGet(params struct{}) {
+ countResp, err := this.RPC().NSClusterRPC().CountAllEnabledNSClusters(this.AdminContext(), &pb.CountAllEnabledNSClustersRequest{})
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ count := countResp.Count
+ page := this.NewPage(count)
+ this.Data["page"] = page.AsHTML()
+
+ clustersResp, err := this.RPC().NSClusterRPC().ListEnabledNSClusters(this.AdminContext(), &pb.ListEnabledNSClustersRequest{
+ Offset: page.Offset,
+ Size: page.Size,
+ })
+ if err != nil {
+ this.ErrorPage(err)
+ return
+ }
+ clusterMaps := []maps.Map{}
+ for _, cluster := range clustersResp.NsClusters {
+ clusterMaps = append(clusterMaps, maps.Map{
+ "id": cluster.Id,
+ "name": cluster.Name,
+ "isOn": cluster.IsOn,
+ })
+ }
+ this.Data["clusters"] = clusterMaps
+
+ this.Show()
+}
diff --git a/internal/web/actions/default/ns/clusters/init.go b/internal/web/actions/default/ns/clusters/init.go
new file mode 100644
index 00000000..77f5445b
--- /dev/null
+++ b/internal/web/actions/default/ns/clusters/init.go
@@ -0,0 +1,20 @@
+package clusters
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+ "github.com/iwind/TeaGo"
+)
+
+func init() {
+ TeaGo.BeforeStart(func(server *TeaGo.Server) {
+ server.
+ Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
+ Data("teaMenu", "ns").
+ Data("teaSubMenu", "cluster").
+ Prefix("/ns/clusters").
+ Get("", new(IndexAction)).
+ GetPost("/create", new(CreateAction)).
+ EndAll()
+ })
+}
diff --git a/internal/web/actions/default/ns/index.go b/internal/web/actions/default/ns/index.go
new file mode 100644
index 00000000..ee04d239
--- /dev/null
+++ b/internal/web/actions/default/ns/index.go
@@ -0,0 +1,17 @@
+package ns
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+)
+
+type IndexAction struct {
+ actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+}
+
+func (this *IndexAction) RunGet(params struct{}) {
+
+ this.Show()
+}
diff --git a/internal/web/actions/default/ns/init.go b/internal/web/actions/default/ns/init.go
new file mode 100644
index 00000000..0abc2e3f
--- /dev/null
+++ b/internal/web/actions/default/ns/init.go
@@ -0,0 +1,19 @@
+package ns
+
+import (
+ "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+ "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+ "github.com/iwind/TeaGo"
+)
+
+func init() {
+ TeaGo.BeforeStart(func(server *TeaGo.Server) {
+ server.
+ Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
+ Data("teaMenu", "ns").
+ Prefix("/ns").
+ Get("", new(IndexAction)).
+
+ EndAll()
+ })
+}
diff --git a/internal/web/helpers/user_must_auth.go b/internal/web/helpers/user_must_auth.go
index 20d84568..551c658b 100644
--- a/internal/web/helpers/user_must_auth.go
+++ b/internal/web/helpers/user_must_auth.go
@@ -217,6 +217,26 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
},
},
},
+ {
+ "code": "ns",
+ "module": configloaders.AdminModuleCodeNS,
+ "name": "域名服务器",
+ "subtitle": "域名列表",
+ "icon": "cubes",
+ "isOn": teaconst.IsPlus,
+ "subItems": []maps.Map{
+ {
+ "name": "集群管理",
+ "url": "/ns/clusters",
+ "code": "cluster",
+ },
+ {
+ "name": "节点日志",
+ "url": "/ns/logs",
+ "code": "log",
+ },
+ },
+ },
{
"code": "users",
"module": configloaders.AdminModuleCodeUser,
diff --git a/internal/web/import.go b/internal/web/import.go
index 1d8b9c52..92d35a43 100644
--- a/internal/web/import.go
+++ b/internal/web/import.go
@@ -31,6 +31,13 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/logout"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/messages"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes"
+
+ // 域名服务
+ _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns"
+ _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters"
+ _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster"
+ _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/settings"
+
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components"
diff --git a/web/views/@default/clusters/cluster/delete.css b/web/views/@default/clusters/cluster/delete.css
index c44e4d4f..125938fb 100644
--- a/web/views/@default/clusters/cluster/delete.css
+++ b/web/views/@default/clusters/cluster/delete.css
@@ -2,4 +2,7 @@
text-align: center;
margin-top: 2em;
}
+.buttons-box button {
+ width: 20em;
+}
/*# sourceMappingURL=delete.css.map */
\ No newline at end of file
diff --git a/web/views/@default/clusters/cluster/delete.css.map b/web/views/@default/clusters/cluster/delete.css.map
index c266be7d..343ddfdf 100644
--- a/web/views/@default/clusters/cluster/delete.css.map
+++ b/web/views/@default/clusters/cluster/delete.css.map
@@ -1 +1 @@
-{"version":3,"sources":["delete.less"],"names":[],"mappings":"AAAA;EACC,kBAAA;EACA,eAAA","file":"delete.css"}
\ No newline at end of file
+{"version":3,"sources":["delete.less"],"names":[],"mappings":"AAAA;EACC,kBAAA;EACA,eAAA;;AAFD,YAIC;EACC,WAAA","file":"delete.css"}
\ No newline at end of file
diff --git a/web/views/@default/clusters/cluster/delete.less b/web/views/@default/clusters/cluster/delete.less
index a8c4d597..33cfd4bc 100644
--- a/web/views/@default/clusters/cluster/delete.less
+++ b/web/views/@default/clusters/cluster/delete.less
@@ -1,4 +1,8 @@
.buttons-box {
text-align: center;
margin-top: 2em;
+
+ button {
+ width: 20em;
+ }
}
\ No newline at end of file
diff --git a/web/views/@default/ns/clusters/@menu.html b/web/views/@default/ns/clusters/@menu.html
new file mode 100644
index 00000000..85331361
--- /dev/null
+++ b/web/views/@default/ns/clusters/@menu.html
@@ -0,0 +1,4 @@
+
暂时还没有集群。
+ +| 集群名称 | +集群状态 | +操作 | +
|---|---|---|
| {{cluster.name}} | +
+ |
+ + 详情 + | +