mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-05 14:20:25 +08:00
增加节点同步状态提示和任务列表
This commit is contained in:
@@ -252,6 +252,10 @@ func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
|
|||||||
return pb.NewLoginServiceClient(this.pickConn())
|
return pb.NewLoginServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) NodeTaskRPC() pb.NodeTaskServiceClient {
|
||||||
|
return pb.NewNodeTaskServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
// 构造Admin上下文
|
// 构造Admin上下文
|
||||||
func (this *RPCClient) Context(adminId int64) context.Context {
|
func (this *RPCClient) Context(adminId int64) context.Context {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -48,24 +49,32 @@ func (this *SyncClusterTask) loop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx := rpcClient.Context(0)
|
ctx := rpcClient.Context(0)
|
||||||
resp, err := rpcClient.NodeClusterRPC().FindAllChangedNodeClusters(ctx, &pb.FindAllChangedNodeClustersRequest{})
|
|
||||||
|
tasksResp, err := rpcClient.NodeTaskRPC().FindNotifyingNodeTasks(ctx, &pb.FindNotifyingNodeTasksRequest{Size: 100})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodeIds := []int64{}
|
||||||
|
taskIds := []int64{}
|
||||||
|
for _, task := range tasksResp.NodeTasks {
|
||||||
|
if !lists.ContainsInt64(nodeIds, task.Node.Id) {
|
||||||
|
nodeIds = append(nodeIds, task.Node.Id)
|
||||||
|
}
|
||||||
|
taskIds = append(taskIds, task.Id)
|
||||||
|
}
|
||||||
|
if len(nodeIds) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err = nodeutils.SendMessageToNodeIds(ctx, nodeIds, messageconfigs.MessageCodeNewNodeTask, &messageconfigs.NewNodeTaskMessage{}, 3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cluster := range resp.NodeClusters {
|
// 设置已通知
|
||||||
_, err := rpcClient.NodeRPC().SyncNodesVersionWithCluster(ctx, &pb.SyncNodesVersionWithClusterRequest{
|
_, err = rpcClient.NodeTaskRPC().UpdateNodeTasksNotified(ctx, &pb.UpdateNodeTasksNotifiedRequest{NodeTaskIds: taskIds})
|
||||||
NodeClusterId: cluster.Id,
|
if err != nil {
|
||||||
})
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
_, err = nodeutils.SendMessageToCluster(ctx, cluster.Id, messageconfigs.MessageCodeConfigChanged, &messageconfigs.ConfigChangedMessage{}, 10)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package clusters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 检查变更的集群列表
|
|
||||||
type CheckChangeAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CheckChangeAction) Init() {
|
|
||||||
this.Nav("", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CheckChangeAction) RunPost(params struct {
|
|
||||||
IsNotifying bool
|
|
||||||
}) {
|
|
||||||
resp, err := this.RPC().NodeClusterRPC().FindAllChangedNodeClusters(this.AdminContext(), &pb.FindAllChangedNodeClustersRequest{})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result := []maps.Map{}
|
|
||||||
for _, cluster := range resp.NodeClusters {
|
|
||||||
result = append(result, maps.Map{
|
|
||||||
"id": cluster.Id,
|
|
||||||
"name": cluster.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Data["clusters"] = result
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -15,14 +15,12 @@ func init() {
|
|||||||
Prefix("/clusters").
|
Prefix("/clusters").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
GetPost("/create", new(CreateAction)).
|
GetPost("/create", new(CreateAction)).
|
||||||
Post("/sync", new(SyncAction)).
|
|
||||||
Post("/checkChange", new(CheckChangeAction)).
|
|
||||||
|
|
||||||
// 只要登录即可访问的Action
|
// 只要登录即可访问的Action
|
||||||
EndHelpers().
|
EndHelpers().
|
||||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
||||||
Post("/options", new(OptionsAction)).
|
Post("/options", new(OptionsAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package clusters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 同步集群
|
|
||||||
type SyncAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *SyncAction) RunPost(params struct{}) {
|
|
||||||
// TODO 将来可以单独选择某一个集群进行单独的同步
|
|
||||||
|
|
||||||
// 所有有变化的集群
|
|
||||||
clustersResp, err := this.RPC().NodeClusterRPC().FindAllChangedNodeClusters(this.AdminContext(), &pb.FindAllChangedNodeClustersRequest{})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clusters := clustersResp.NodeClusters
|
|
||||||
|
|
||||||
for _, cluster := range clusters {
|
|
||||||
_, err := this.RPC().NodeRPC().SyncNodesVersionWithCluster(this.AdminContext(), &pb.SyncNodesVersionWithClusterRequest{
|
|
||||||
NodeClusterId: cluster.Id,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
_, err = nodeutils.SendMessageToCluster(this.AdminContext(), cluster.Id, messageconfigs.MessageCodeConfigChanged, &messageconfigs.ConfigChangedMessage{}, 10)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
23
internal/web/actions/default/clusters/tasks/check.go
Normal file
23
internal/web/actions/default/clusters/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().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["isDoing"] = resp.ExistTasks
|
||||||
|
this.Data["hasError"] = resp.ExistError
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
24
internal/web/actions/default/clusters/tasks/delete.go
Normal file
24
internal/web/actions/default/clusters/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("删除同步任务 %d", params.TaskId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NodeTaskRPC().DeleteNodeTask(this.AdminContext(), &pb.DeleteNodeTaskRequest{NodeTaskId: params.TaskId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
22
internal/web/actions/default/clusters/tasks/init.go
Normal file
22
internal/web/actions/default/clusters/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.AdminModuleCodeNode)).
|
||||||
|
Helper(clusterutils.NewClustersHelper()).
|
||||||
|
Prefix("/clusters/tasks").
|
||||||
|
GetPost("/listPopup", new(ListPopupAction)).
|
||||||
|
Post("/check", new(CheckAction)).
|
||||||
|
Post("/delete", new(DeleteAction)).
|
||||||
|
|
||||||
|
EndAll()
|
||||||
|
})
|
||||||
|
}
|
||||||
67
internal/web/actions/default/clusters/tasks/listPopup.go
Normal file
67
internal/web/actions/default/clusters/tasks/listPopup.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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().NodeTaskRPC().FindNodeClusterTasks(this.AdminContext(), &pb.FindNodeClusterTasksRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
countTasks := 0
|
||||||
|
clusterMaps := []maps.Map{}
|
||||||
|
for _, cluster := range resp.ClusterTasks {
|
||||||
|
taskMaps := []maps.Map{}
|
||||||
|
for _, task := range cluster.NodeTasks {
|
||||||
|
countTasks++
|
||||||
|
taskMaps = append(taskMaps, maps.Map{
|
||||||
|
"id": task.Id,
|
||||||
|
"type": task.Type,
|
||||||
|
"node": maps.Map{
|
||||||
|
"id": task.Node.Id,
|
||||||
|
"name": task.Node.Name,
|
||||||
|
},
|
||||||
|
"isOk": task.IsOk,
|
||||||
|
"error": task.Error,
|
||||||
|
"isDone": task.IsDone,
|
||||||
|
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", task.UpdatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterMaps = append(clusterMaps, maps.Map{
|
||||||
|
"id": cluster.ClusterId,
|
||||||
|
"name": cluster.ClusterName,
|
||||||
|
"tasks": taskMaps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["clusters"] = clusterMaps
|
||||||
|
this.Data["countTasks"] = countTasks
|
||||||
|
}
|
||||||
@@ -47,6 +47,176 @@ func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg
|
|||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(len(nodes))
|
wg.Add(len(nodes))
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
|
// TODO 检查是否在线
|
||||||
|
|
||||||
|
if len(node.ConnectedAPINodeIds) == 0 {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "节点尚未连接到API",
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取API节点信息
|
||||||
|
apiNodeId := node.ConnectedAPINodeIds[0]
|
||||||
|
rpcClient, ok := rpcMap[apiNodeId]
|
||||||
|
if !ok {
|
||||||
|
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{NodeId: apiNodeId})
|
||||||
|
if err != nil {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "无法读取对应的API节点信息:" + err.Error(),
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiNodeResp.Node == nil {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "无法读取对应的API节点信息:API节点ID:" + strconv.FormatInt(apiNodeId, 10),
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
apiNode := apiNodeResp.Node
|
||||||
|
|
||||||
|
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
|
||||||
|
RPC: struct {
|
||||||
|
Endpoints []string `yaml:"endpoints"`
|
||||||
|
}{
|
||||||
|
Endpoints: apiNode.AccessAddrs,
|
||||||
|
},
|
||||||
|
NodeId: apiNode.UniqueId,
|
||||||
|
Secret: apiNode.Secret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "初始化API节点错误:API节点ID:" + strconv.FormatInt(apiNodeId, 10) + ":" + err.Error(),
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rpcMap[apiNodeId] = apiRPCClient
|
||||||
|
rpcClient = apiRPCClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
go func(node *pb.Node) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
result, err := rpcClient.NodeRPC().SendCommandToNode(ctx, &pb.NodeStreamMessage{
|
||||||
|
NodeId: node.Id,
|
||||||
|
TimeoutSeconds: timeoutSeconds,
|
||||||
|
Code: code,
|
||||||
|
DataJSON: msgJSON,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "API返回错误:" + err.Error(),
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: result.IsOk,
|
||||||
|
Message: result.Message,
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
}(node)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向一组节点发送命令消息
|
||||||
|
func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg interface{}, timeoutSeconds int32) (results []*MessageResult, err error) {
|
||||||
|
results = []*MessageResult{}
|
||||||
|
if len(nodeIds) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgJSON, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultRPCClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有节点
|
||||||
|
nodesResp, err := defaultRPCClient.NodeRPC().FindEnabledNodesWithIds(ctx, &pb.FindEnabledNodesWithIdsRequest{NodeIds: nodeIds})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodes := nodesResp.Nodes
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcMap := map[int64]*rpc.RPCClient{} // apiNodeId => RPCClient
|
||||||
|
locker := &sync.Mutex{}
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
wg.Add(len(nodes))
|
||||||
|
for _, node := range nodes {
|
||||||
|
if !node.IsActive {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "节点不在线",
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !node.IsOn {
|
||||||
|
if !node.IsActive {
|
||||||
|
locker.Lock()
|
||||||
|
results = append(results, &MessageResult{
|
||||||
|
NodeId: node.Id,
|
||||||
|
NodeName: node.Name,
|
||||||
|
IsOK: false,
|
||||||
|
Message: "节点未启用",
|
||||||
|
})
|
||||||
|
locker.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(node.ConnectedAPINodeIds) == 0 {
|
if len(node.ConnectedAPINodeIds) == 0 {
|
||||||
locker.Lock()
|
locker.Lock()
|
||||||
results = append(results, &MessageResult{
|
results = append(results, &MessageResult{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/waf/ipadmin/ipadminutils"
|
|
||||||
"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/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
@@ -83,13 +82,6 @@ func (this *CreateIPPopupAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
itemId := createResp.IpItemId
|
itemId := createResp.IpItemId
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
err = ipadminutils.NotifyUpdateToClustersWithFirewallPolicyId(this.AdminContext(), params.FirewallPolicyId)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 日志
|
// 日志
|
||||||
defer this.CreateLog(oplogs.LevelInfo, "在WAF策略 %d 名单中添加IP %d", params.FirewallPolicyId, itemId)
|
defer this.CreateLog(oplogs.LevelInfo, "在WAF策略 %d 名单中添加IP %d", params.FirewallPolicyId, itemId)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package ipadmin
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/waf/ipadmin/ipadminutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,12 +25,5 @@ func (this *DeleteIPAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
err = ipadminutils.NotifyUpdateToClustersWithFirewallPolicyId(this.AdminContext(), params.FirewallPolicyId)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package ipadminutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 通知使用此WAF策略的集群更新
|
|
||||||
func NotifyUpdateToClustersWithFirewallPolicyId(ctx context.Context, firewallPolicyId int64) error {
|
|
||||||
client, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := client.NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPFirewallPolicyId(ctx, &pb.FindAllEnabledNodeClustersWithHTTPFirewallPolicyIdRequest{HttpFirewallPolicyId: firewallPolicyId})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, cluster := range resp.NodeClusters {
|
|
||||||
_, err = nodeutils.SendMessageToCluster(ctx, cluster.Id, messageconfigs.MessageCodeIPListChanged, &messageconfigs.IPListChangedMessage{}, 3)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/waf/ipadmin/ipadminutils"
|
|
||||||
"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"
|
||||||
@@ -95,12 +94,5 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
err = ipadminutils.NotifyUpdateToClustersWithFirewallPolicyId(this.AdminContext(), params.FirewallPolicyId)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ 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["firstMenuItem"] = ""
|
action.Data["firstMenuItem"] = ""
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
|
||||||
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/csrf"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/csrf"
|
||||||
_ "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"
|
||||||
|
|||||||
@@ -27,12 +27,24 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
|
<!-- 集群同步 -->
|
||||||
|
<a href="" class="item" v-if="teaCheckClusterTask && 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"><i class="icon sync"></i>正在同步...</span>
|
||||||
|
<span v-if="doingNodeTasks.hasError" class="red"><i class="icon sync"></i>同步失败</span>
|
||||||
|
</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="/messages" class="item" :class="{active:teaMenu == 'message'}"><span :class="{'blink':globalMessageBadge > 0}"><i class="icon bell"></i>消息({{globalMessageBadge}}) </span></a>
|
||||||
|
|
||||||
|
<!-- 用户信息 -->
|
||||||
<a href="/settings/profile" class="item">
|
<a href="/settings/profile" class="item">
|
||||||
<i class="icon user" v-if="teaUserAvatar.length == 0"></i>
|
<i class="icon user" v-if="teaUserAvatar.length == 0"></i>
|
||||||
<img class="avatar" alt="" :src="teaUserAvatar" v-if="teaUserAvatar.length > 0"/>
|
<img class="avatar" alt="" :src="teaUserAvatar" v-if="teaUserAvatar.length > 0"/>
|
||||||
{{teaUsername}}
|
{{teaUsername}}
|
||||||
</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>
|
||||||
|
|||||||
@@ -1,119 +1,152 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.moreOptionsVisible = false
|
this.moreOptionsVisible = false
|
||||||
this.globalMessageBadge = 0
|
this.globalMessageBadge = 0
|
||||||
|
|
||||||
if (typeof this.leftMenuItemIsDisabled == "undefined") {
|
if (typeof this.leftMenuItemIsDisabled == "undefined") {
|
||||||
this.leftMenuItemIsDisabled = false
|
this.leftMenuItemIsDisabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$delay(function () {
|
this.$delay(function () {
|
||||||
if (this.$refs.focus != null) {
|
if (this.$refs.focus != null) {
|
||||||
this.$refs.focus.focus()
|
this.$refs.focus.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查消息
|
// 检查消息
|
||||||
this.checkMessages()
|
this.checkMessages()
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
// 检查集群节点同步
|
||||||
* 左侧子菜单
|
this.loadNodeTasks();
|
||||||
*/
|
})
|
||||||
this.showSubMenu = function (menu) {
|
|
||||||
if (menu.alwaysActive) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.teaSubMenus.menus != null && this.teaSubMenus.menus.length > 0) {
|
|
||||||
this.teaSubMenus.menus.$each(function (k, v) {
|
|
||||||
if (menu.id == v.id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v.isActive = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
menu.isActive = !menu.isActive
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查消息
|
* 左侧子菜单
|
||||||
*/
|
*/
|
||||||
this.checkMessages = function () {
|
this.showSubMenu = function (menu) {
|
||||||
this.$post("/messages/badge")
|
if (menu.alwaysActive) {
|
||||||
.params({})
|
return
|
||||||
.success(function (resp) {
|
}
|
||||||
this.globalMessageBadge = resp.data.count
|
if (this.teaSubMenus.menus != null && this.teaSubMenus.menus.length > 0) {
|
||||||
})
|
this.teaSubMenus.menus.$each(function (k, v) {
|
||||||
.done(function () {
|
if (menu.id == v.id) {
|
||||||
let delay = 6000
|
return
|
||||||
if (this.globalMessageBadge > 0) {
|
}
|
||||||
delay = 30000
|
v.isActive = false
|
||||||
}
|
})
|
||||||
this.$delay(function () {
|
}
|
||||||
this.checkMessages()
|
menu.isActive = !menu.isActive
|
||||||
}, delay)
|
};
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 底部伸展框
|
* 检查消息
|
||||||
*/
|
*/
|
||||||
this.showQQGroupQrcode = function () {
|
this.checkMessages = function () {
|
||||||
teaweb.popup("/about/qq", {
|
this.$post("/messages/badge")
|
||||||
width: "21em",
|
.params({})
|
||||||
height: "24em"
|
.success(function (resp) {
|
||||||
})
|
this.globalMessageBadge = resp.data.count
|
||||||
}
|
})
|
||||||
|
.done(function () {
|
||||||
|
let delay = 6000
|
||||||
|
if (this.globalMessageBadge > 0) {
|
||||||
|
delay = 30000
|
||||||
|
}
|
||||||
|
this.$delay(function () {
|
||||||
|
this.checkMessages()
|
||||||
|
}, delay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹窗中默认成功回调
|
* 底部伸展框
|
||||||
*/
|
*/
|
||||||
if (window.IS_POPUP === true) {
|
this.showQQGroupQrcode = function () {
|
||||||
this.success = window.NotifyPopup
|
teaweb.popup("/about/qq", {
|
||||||
}
|
width: "21em",
|
||||||
|
height: "24em"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹窗中默认成功回调
|
||||||
|
*/
|
||||||
|
if (window.IS_POPUP === true) {
|
||||||
|
this.success = window.NotifyPopup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点同步任务
|
||||||
|
*/
|
||||||
|
this.doingNodeTasks = {
|
||||||
|
isDoing: false,
|
||||||
|
hasError: false,
|
||||||
|
isUpdated: false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadNodeTasks = function () {
|
||||||
|
this.$post("/clusters/tasks/check")
|
||||||
|
.success(function (resp) {
|
||||||
|
this.doingNodeTasks.isDoing = resp.data.isDoing
|
||||||
|
this.doingNodeTasks.hasError = resp.data.hasError
|
||||||
|
this.doingNodeTasks.isUpdated = true
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.loadNodeTasks()
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showNodeTasks = function () {
|
||||||
|
teaweb.popup("/clusters/tasks/listPopup", {
|
||||||
|
height: "24em",
|
||||||
|
width: "50em"
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.NotifySuccess = function (message, url, params) {
|
window.NotifySuccess = function (message, url, params) {
|
||||||
if (typeof (url) == "string" && url.length > 0) {
|
if (typeof (url) == "string" && url.length > 0) {
|
||||||
if (url[0] != "/") {
|
if (url[0] != "/") {
|
||||||
url = Tea.url(url, params);
|
url = Tea.url(url, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return function () {
|
return function () {
|
||||||
teaweb.success(message, function () {
|
teaweb.success(message, function () {
|
||||||
window.location = url;
|
window.location = url;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
window.NotifyReloadSuccess = function (message) {
|
window.NotifyReloadSuccess = function (message) {
|
||||||
return function () {
|
return function () {
|
||||||
teaweb.success(message, function () {
|
teaweb.success(message, function () {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.NotifyDelete = function (message, url, params) {
|
window.NotifyDelete = function (message, url, params) {
|
||||||
teaweb.confirm(message, function () {
|
teaweb.confirm(message, function () {
|
||||||
Tea.Vue.$post(url)
|
Tea.Vue.$post(url)
|
||||||
.params(params)
|
.params(params)
|
||||||
.refresh();
|
.refresh();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.NotifyPopup = function (resp) {
|
window.NotifyPopup = function (resp) {
|
||||||
window.parent.teaweb.popupFinish(resp);
|
window.parent.teaweb.popupFinish(resp);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.ChangePageSize = function (size) {
|
window.ChangePageSize = function (size) {
|
||||||
let url = window.location.toString();
|
let url = window.location.toString();
|
||||||
if (url.indexOf("pageSize") > 0) {
|
if (url.indexOf("pageSize") > 0) {
|
||||||
url = url.replace(/pageSize=\d+/g, "pageSize=" + size);
|
url = url.replace(/pageSize=\d+/g, "pageSize=" + size);
|
||||||
} else {
|
} else {
|
||||||
if (url.indexOf("?") > 0) {
|
if (url.indexOf("?") > 0) {
|
||||||
url += "&pageSize=" + size;
|
url += "&pageSize=" + size;
|
||||||
} else {
|
} else {
|
||||||
url += "?pageSize=" + size;
|
url += "?pageSize=" + size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.location = url;
|
window.location = url;
|
||||||
};
|
};
|
||||||
6
web/views/@default/clusters/tasks/listPopup.css
Normal file
6
web/views/@default/clusters/tasks/listPopup.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
h3 span {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
color: grey;
|
||||||
|
font-size: 0.6em !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=listPopup.css.map */
|
||||||
1
web/views/@default/clusters/tasks/listPopup.css.map
Normal file
1
web/views/@default/clusters/tasks/listPopup.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["listPopup.less"],"names":[],"mappings":"AAAA,EAAG;EACF,kBAAA;EACA,WAAA;EACA,2BAAA","file":"listPopup.css"}
|
||||||
38
web/views/@default/clusters/tasks/listPopup.html
Normal file
38
web/views/@default/clusters/tasks/listPopup.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3>正在同步的任务<span v-if="countTasks > 0">(共{{countTasks}}个)</span></h3>
|
||||||
|
<p class="comment" v-if="clusters.length == 0">暂时没有同步的集群。</p>
|
||||||
|
<div v-if="clusters.length > 0">
|
||||||
|
<table class="ui table selectable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>集群</th>
|
||||||
|
<th>节点</th>
|
||||||
|
<th>任务</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>触发时间</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-for="cluster in clusters">
|
||||||
|
<tr v-for="task in cluster.tasks">
|
||||||
|
<td>{{cluster.name}}</td>
|
||||||
|
<td>
|
||||||
|
{{task.node.name}} <a :href="'/clusters/cluster/node?clusterId=' + cluster.id + '&nodeId=' + task.node.id" target="_blank"><i class="icon linkify small grey"></i></a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="task.type == 'configChanged'">同步配置</span>
|
||||||
|
<span v-if="task.type == 'ipItemChanged'">同步IP名单</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="task.isDone" class="red">{{task.error}}</span>
|
||||||
|
<span v-else>正在同步...</span>
|
||||||
|
</td>
|
||||||
|
<td>{{task.updatedTime}}</td>
|
||||||
|
<td>
|
||||||
|
<a href="" title="删除" class="remove-btn" @click.prevent="deleteTask(task.id)"><i class="icon remove small grey"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
31
web/views/@default/clusters/tasks/listPopup.js
Normal file
31
web/views/@default/clusters/tasks/listPopup.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reload()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.reload = function () {
|
||||||
|
this.$post("$")
|
||||||
|
.success(function (resp) {
|
||||||
|
this.countTasks = resp.data.countTasks
|
||||||
|
this.clusters = resp.data.clusters
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reload()
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteTask = function (taskId) {
|
||||||
|
let that = this
|
||||||
|
teaweb.confirm("确定要删除这个任务吗?", function () {
|
||||||
|
that.$post(".delete")
|
||||||
|
.params({
|
||||||
|
taskId: taskId
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
5
web/views/@default/clusters/tasks/listPopup.less
Normal file
5
web/views/@default/clusters/tasks/listPopup.less
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
h3 span {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
color: grey;
|
||||||
|
font-size: 0.6em !important;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user