diff --git a/internal/web/actions/default/admins/recipients/createPopup.go b/internal/web/actions/default/admins/recipients/createPopup.go
deleted file mode 100644
index f9156fa3..00000000
--- a/internal/web/actions/default/admins/recipients/createPopup.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package recipients
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/utils"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"regexp"
-)
-
-type CreatePopupAction struct {
-	actionutils.ParentAction
-}
-
-func (this *CreatePopupAction) Init() {
-	this.Nav("", "", "")
-}
-
-func (this *CreatePopupAction) RunGet(params struct{}) {
-	this.Show()
-}
-
-func (this *CreatePopupAction) RunPost(params struct {
-	AdminId    int64
-	InstanceId int64
-	User       string
-
-	TelegramToken string
-
-	GroupIds    string
-	Description string
-
-	TimeFromHour   string
-	TimeFromMinute string
-	TimeFromSecond string
-
-	TimeToHour   string
-	TimeToMinute string
-	TimeToSecond string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("adminId", params.AdminId).
-		Gt(0, "请选择系统用户").
-		Field("instanceId", params.InstanceId).
-		Gt(0, "请选择媒介")
-
-	groupIds := utils.SplitNumbers(params.GroupIds)
-
-	var digitReg = regexp.MustCompile(`^\d+$`)
-
-	var timeFrom = ""
-	if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
-		timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
-	}
-
-	var timeTo = ""
-	if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
-		timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
-	}
-
-	resp, err := this.RPC().MessageRecipientRPC().CreateMessageRecipient(this.AdminContext(), &pb.CreateMessageRecipientRequest{
-		AdminId:                  params.AdminId,
-		MessageMediaInstanceId:   params.InstanceId,
-		User:                     params.User,
-		MessageRecipientGroupIds: groupIds,
-		Description:              params.Description,
-		TimeFrom:                 timeFrom,
-		TimeTo:                   timeTo,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	defer this.CreateLogInfo(codes.MessageRecipient_LogCreateMessageRecipient, resp.MessageRecipientId)
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/delete.go b/internal/web/actions/default/admins/recipients/delete.go
deleted file mode 100644
index d4e80e85..00000000
--- a/internal/web/actions/default/admins/recipients/delete.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package recipients
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-)
-
-type DeleteAction struct {
-	actionutils.ParentAction
-}
-
-func (this *DeleteAction) RunPost(params struct {
-	RecipientId int64
-}) {
-	defer this.CreateLogInfo(codes.MessageRecipient_LogDeleteMessageRecipient, params.RecipientId)
-
-	_, err := this.RPC().MessageRecipientRPC().DeleteMessageRecipient(this.AdminContext(), &pb.DeleteMessageRecipientRequest{MessageRecipientId: params.RecipientId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/createPopup.go b/internal/web/actions/default/admins/recipients/groups/createPopup.go
deleted file mode 100644
index 4cf733f5..00000000
--- a/internal/web/actions/default/admins/recipients/groups/createPopup.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package groups
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-)
-
-type CreatePopupAction struct {
-	actionutils.ParentAction
-}
-
-func (this *CreatePopupAction) Init() {
-	this.Nav("", "", "")
-}
-
-func (this *CreatePopupAction) RunGet(params struct{}) {
-	this.Show()
-}
-
-func (this *CreatePopupAction) RunPost(params struct {
-	Name string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("name", params.Name).
-		Require("请输入分组名称")
-
-	_, err := this.RPC().MessageRecipientGroupRPC().CreateMessageRecipientGroup(this.AdminContext(), &pb.CreateMessageRecipientGroupRequest{Name: params.Name})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/delete.go b/internal/web/actions/default/admins/recipients/groups/delete.go
deleted file mode 100644
index f6384f0a..00000000
--- a/internal/web/actions/default/admins/recipients/groups/delete.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package groups
-
-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 {
-	GroupId int64
-}) {
-	_, err := this.RPC().MessageRecipientGroupRPC().DeleteMessageRecipientGroup(this.AdminContext(), &pb.DeleteMessageRecipientGroupRequest{MessageRecipientGroupId: params.GroupId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/index.go b/internal/web/actions/default/admins/recipients/groups/index.go
deleted file mode 100644
index e311d6b0..00000000
--- a/internal/web/actions/default/admins/recipients/groups/index.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package groups
-
-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("", "", "group")
-}
-
-func (this *IndexAction) RunGet(params struct{}) {
-	groupsResp, err := this.RPC().MessageRecipientGroupRPC().FindAllEnabledMessageRecipientGroups(this.AdminContext(), &pb.FindAllEnabledMessageRecipientGroupsRequest{})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	groupMaps := []maps.Map{}
-	for _, group := range groupsResp.MessageRecipientGroups {
-		groupMaps = append(groupMaps, maps.Map{
-			"id":   group.Id,
-			"name": group.Name,
-			"isOn": group.IsOn,
-		})
-	}
-	this.Data["groups"] = groupMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/init.go b/internal/web/actions/default/admins/recipients/groups/init.go
deleted file mode 100644
index f8b4199f..00000000
--- a/internal/web/actions/default/admins/recipients/groups/init.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package groups
-
-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.AdminModuleCodeAdmin)).
-			Data("teaMenu", "admins").
-			Data("teaSubMenu", "recipients").
-			Prefix("/admins/recipients/groups").
-			Get("", new(IndexAction)).
-			GetPost("/createPopup", new(CreatePopupAction)).
-			GetPost("/updatePopup", new(UpdatePopupAction)).
-			Post("/delete", new(DeleteAction)).
-			Get("/selectPopup", new(SelectPopupAction)).
-			EndAll()
-	})
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/selectPopup.go b/internal/web/actions/default/admins/recipients/groups/selectPopup.go
deleted file mode 100644
index de37d6d6..00000000
--- a/internal/web/actions/default/admins/recipients/groups/selectPopup.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package groups
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/utils"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/lists"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type SelectPopupAction struct {
-	actionutils.ParentAction
-}
-
-func (this *SelectPopupAction) RunGet(params struct {
-	GroupIds string
-}) {
-	selectedGroupIds := utils.SplitNumbers(params.GroupIds)
-
-	groupsResp, err := this.RPC().MessageRecipientGroupRPC().FindAllEnabledMessageRecipientGroups(this.AdminContext(), &pb.FindAllEnabledMessageRecipientGroupsRequest{})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	groupMaps := []maps.Map{}
-	for _, group := range groupsResp.MessageRecipientGroups {
-		if lists.ContainsInt64(selectedGroupIds, group.Id) {
-			continue
-		}
-		groupMaps = append(groupMaps, maps.Map{
-			"id":   group.Id,
-			"name": group.Name,
-			"isOn": group.IsOn,
-		})
-	}
-	this.Data["groups"] = groupMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/groups/updatePopup.go b/internal/web/actions/default/admins/recipients/groups/updatePopup.go
deleted file mode 100644
index 04dbad62..00000000
--- a/internal/web/actions/default/admins/recipients/groups/updatePopup.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package groups
-
-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 UpdatePopupAction struct {
-	actionutils.ParentAction
-}
-
-func (this *UpdatePopupAction) Init() {
-	this.Nav("", "", "")
-}
-
-func (this *UpdatePopupAction) RunGet(params struct {
-	GroupId int64
-}) {
-	groupResp, err := this.RPC().MessageRecipientGroupRPC().FindEnabledMessageRecipientGroup(this.AdminContext(), &pb.FindEnabledMessageRecipientGroupRequest{MessageRecipientGroupId: params.GroupId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	group := groupResp.MessageRecipientGroup
-	if group == nil {
-		this.NotFound("messageRecipientGroup", params.GroupId)
-		return
-	}
-
-	this.Data["group"] = maps.Map{
-		"id":   group.Id,
-		"name": group.Name,
-		"isOn": group.IsOn,
-	}
-
-	this.Show()
-}
-
-func (this *UpdatePopupAction) RunPost(params struct {
-	GroupId int64
-	Name    string
-	IsOn    bool
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("name", params.Name).
-		Require("请输入分组名称")
-
-	_, err := this.RPC().MessageRecipientGroupRPC().UpdateMessageRecipientGroup(this.AdminContext(), &pb.UpdateMessageRecipientGroupRequest{
-		MessageRecipientGroupId: params.GroupId,
-		Name:                    params.Name,
-		IsOn:                    params.IsOn,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/index.go b/internal/web/actions/default/admins/recipients/index.go
deleted file mode 100644
index 39c6b106..00000000
--- a/internal/web/actions/default/admins/recipients/index.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package recipients
-
-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("", "", "recipient")
-}
-
-func (this *IndexAction) RunGet(params struct {
-}) {
-	// TODO 增加系统用户、媒介类型等条件搜索
-	countResp, err := this.RPC().MessageRecipientRPC().CountAllEnabledMessageRecipients(this.AdminContext(), &pb.CountAllEnabledMessageRecipientsRequest{
-		AdminId:                 0,
-		MediaType:               "",
-		MessageRecipientGroupId: 0,
-		Keyword:                 "",
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	count := countResp.Count
-	page := this.NewPage(count)
-	this.Data["page"] = page.AsHTML()
-
-	recipientsResp, err := this.RPC().MessageRecipientRPC().ListEnabledMessageRecipients(this.AdminContext(), &pb.ListEnabledMessageRecipientsRequest{
-		AdminId:                 0,
-		MediaType:               "",
-		MessageRecipientGroupId: 0,
-		Keyword:                 "",
-		Offset:                  page.Offset,
-		Size:                    page.Size,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	recipientMaps := []maps.Map{}
-	for _, recipient := range recipientsResp.MessageRecipients {
-		if recipient.Admin == nil {
-			continue
-		}
-		if recipient.MessageMediaInstance == nil {
-			continue
-		}
-		recipientMaps = append(recipientMaps, maps.Map{
-			"id": recipient.Id,
-			"admin": maps.Map{
-				"id":       recipient.Admin.Id,
-				"fullname": recipient.Admin.Fullname,
-				"username": recipient.Admin.Username,
-			},
-			"groups": recipient.MessageRecipientGroups,
-			"isOn":   recipient.IsOn,
-			"instance": maps.Map{
-				"name": recipient.MessageMediaInstance.Name,
-			},
-			"user":        recipient.User,
-			"description": recipient.Description,
-		})
-	}
-	this.Data["recipients"] = recipientMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/init.go b/internal/web/actions/default/admins/recipients/init.go
deleted file mode 100644
index 268a9ae1..00000000
--- a/internal/web/actions/default/admins/recipients/init.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package recipients
-
-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.AdminModuleCodeAdmin)).
-			Data("teaMenu", "admins").
-			Data("teaSubMenu", "recipients").
-			Prefix("/admins/recipients").
-			Get("", new(IndexAction)).
-			GetPost("/createPopup", new(CreatePopupAction)).
-			GetPost("/update", new(UpdateAction)).
-			Post("/delete", new(DeleteAction)).
-			Post("/mediaOptions", new(MediaOptionsAction)).
-			Get("/recipient", new(RecipientAction)).
-			GetPost("/test", new(TestAction)).
-			EndAll()
-	})
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/createPopup.go b/internal/web/actions/default/admins/recipients/instances/createPopup.go
deleted file mode 100644
index f7b7caa6..00000000
--- a/internal/web/actions/default/admins/recipients/instances/createPopup.go
+++ /dev/null
@@ -1,267 +0,0 @@
-package instances
-
-import (
-	"encoding/json"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type CreatePopupAction struct {
-	actionutils.ParentAction
-}
-
-func (this *CreatePopupAction) Init() {
-	this.Nav("", "", "")
-}
-
-func (this *CreatePopupAction) RunGet(params struct{}) {
-	this.Show()
-}
-
-func (this *CreatePopupAction) RunPost(params struct {
-	Name      string
-	MediaType string
-
-	EmailSmtp     string
-	EmailUsername string
-	EmailPassword string
-	EmailFrom     string
-
-	WebHookURL          string
-	WebHookMethod       string
-	WebHookHeaderNames  []string
-	WebHookHeaderValues []string
-	WebHookContentType  string
-	WebHookParamNames   []string
-	WebHookParamValues  []string
-	WebHookBody         string
-
-	ScriptType      string
-	ScriptPath      string
-	ScriptLang      string
-	ScriptCode      string
-	ScriptCwd       string
-	ScriptEnvNames  []string
-	ScriptEnvValues []string
-
-	DingTalkWebHookURL string
-
-	QyWeixinCorporateId string
-	QyWeixinAgentId     string
-	QyWeixinAppSecret   string
-	QyWeixinTextFormat  string
-
-	QyWeixinRobotWebHookURL string
-	QyWeixinRobotTextFormat string
-
-	AliyunSmsSign              string
-	AliyunSmsTemplateCode      string
-	AliyunSmsTemplateVarNames  []string
-	AliyunSmsTemplateVarValues []string
-	AliyunSmsAccessKeyId       string
-	AliyunSmsAccessKeySecret   string
-
-	TelegramToken string
-
-	RateMinutes int32
-	RateCount   int32
-
-	HashLife int32
-
-	Description string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("name", params.Name).
-		Require("请输入媒介名称").
-		Field("mediaType", params.MediaType).
-		Require("请选择媒介类型")
-
-	options := maps.Map{}
-
-	switch params.MediaType {
-	case "email":
-		params.Must.
-			Field("emailSmtp", params.EmailSmtp).
-			Require("请输入SMTP地址").
-			Field("emailUsername", params.EmailUsername).
-			Require("请输入邮箱账号").
-			Field("emailPassword", params.EmailPassword).
-			Require("请输入密码或授权码")
-
-		options["smtp"] = params.EmailSmtp
-		options["username"] = params.EmailUsername
-		options["password"] = params.EmailPassword
-		options["from"] = params.EmailFrom
-	case "webHook":
-		params.Must.
-			Field("webHookURL", params.WebHookURL).
-			Require("请输入URL地址").
-			Match("(?i)^(http|https)://", "URL地址必须以http或https开头").
-			Field("webHookMethod", params.WebHookMethod).
-			Require("请选择请求方法")
-
-		options["url"] = params.WebHookURL
-		options["method"] = params.WebHookMethod
-		options["contentType"] = params.WebHookContentType
-
-		headers := []maps.Map{}
-		if len(params.WebHookHeaderNames) > 0 {
-			for index, name := range params.WebHookHeaderNames {
-				if index < len(params.WebHookHeaderValues) {
-					headers = append(headers, maps.Map{
-						"name":  name,
-						"value": params.WebHookHeaderValues[index],
-					})
-				}
-			}
-		}
-		options["headers"] = headers
-
-		if params.WebHookContentType == "params" {
-			webHookParams := []maps.Map{}
-			for index, name := range params.WebHookParamNames {
-				if index < len(params.WebHookParamValues) {
-					webHookParams = append(webHookParams, maps.Map{
-						"name":  name,
-						"value": params.WebHookParamValues[index],
-					})
-				}
-			}
-			options["params"] = webHookParams
-		} else if params.WebHookContentType == "body" {
-			options["body"] = params.WebHookBody
-		}
-	case "script":
-		if params.ScriptType == "path" {
-			params.Must.
-				Field("scriptPath", params.ScriptPath).
-				Require("请输入脚本路径")
-		} else if params.ScriptType == "code" {
-			params.Must.
-				Field("scriptCode", params.ScriptCode).
-				Require("请输入脚本代码")
-		} else {
-			params.Must.
-				Field("scriptPath", params.ScriptPath).
-				Require("请输入脚本路径")
-		}
-
-		options["scriptType"] = params.ScriptType
-		options["path"] = params.ScriptPath
-		options["scriptLang"] = params.ScriptLang
-		options["script"] = params.ScriptCode
-		options["cwd"] = params.ScriptCwd
-
-		env := []maps.Map{}
-		for index, envName := range params.ScriptEnvNames {
-			if index < len(params.ScriptEnvValues) {
-				env = append(env, maps.Map{
-					"name":  envName,
-					"value": params.ScriptEnvValues[index],
-				})
-			}
-		}
-		options["env"] = env
-	case "dingTalk":
-		params.Must.
-			Field("dingTalkWebHookURL", params.DingTalkWebHookURL).
-			Require("请输入Hook地址").
-			Match("^https:", "Hook地址必须以https://开头")
-
-		options["webHookURL"] = params.DingTalkWebHookURL
-	case "qyWeixin":
-		params.Must.
-			Field("qyWeixinCorporateId", params.QyWeixinCorporateId).
-			Require("请输入企业ID").
-			Field("qyWeixinAgentId", params.QyWeixinAgentId).
-			Require("请输入应用AgentId").
-			Field("qyWeixinSecret", params.QyWeixinAppSecret).
-			Require("请输入应用Secret")
-
-		options["corporateId"] = params.QyWeixinCorporateId
-		options["agentId"] = params.QyWeixinAgentId
-		options["appSecret"] = params.QyWeixinAppSecret
-		options["textFormat"] = params.QyWeixinTextFormat
-	case "qyWeixinRobot":
-		params.Must.
-			Field("qyWeixinRobotWebHookURL", params.QyWeixinRobotWebHookURL).
-			Require("请输入WebHook地址").
-			Match("^https:", "WebHook地址必须以https://开头")
-
-		options["webHookURL"] = params.QyWeixinRobotWebHookURL
-		options["textFormat"] = params.QyWeixinRobotTextFormat
-	case "aliyunSms":
-		params.Must.
-			Field("aliyunSmsSign", params.AliyunSmsSign).
-			Require("请输入签名名称").
-			Field("aliyunSmsTemplateCode", params.AliyunSmsTemplateCode).
-			Require("请输入模板CODE").
-			Field("aliyunSmsAccessKeyId", params.AliyunSmsAccessKeyId).
-			Require("请输入AccessKey ID").
-			Field("aliyunSmsAccessKeySecret", params.AliyunSmsAccessKeySecret).
-			Require("请输入AccessKey Secret")
-
-		options["sign"] = params.AliyunSmsSign
-		options["templateCode"] = params.AliyunSmsTemplateCode
-		options["accessKeyId"] = params.AliyunSmsAccessKeyId
-		options["accessKeySecret"] = params.AliyunSmsAccessKeySecret
-
-		variables := []maps.Map{}
-		for index, name := range params.AliyunSmsTemplateVarNames {
-			if index < len(params.AliyunSmsTemplateVarValues) {
-				variables = append(variables, maps.Map{
-					"name":  name,
-					"value": params.AliyunSmsTemplateVarValues[index],
-				})
-			}
-		}
-		options["variables"] = variables
-	case "telegram":
-		params.Must.
-			Field("telegramToken", params.TelegramToken).
-			Require("请输入机器人Token")
-		options["token"] = params.TelegramToken
-	default:
-		this.Fail("错误的媒介类型")
-	}
-
-	optionsJSON, err := json.Marshal(options)
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	var rateConfig = &monitorconfigs.RateConfig{
-		Minutes: params.RateMinutes,
-		Count:   params.RateCount,
-	}
-	rateJSON, err := json.Marshal(rateConfig)
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	resp, err := this.RPC().MessageMediaInstanceRPC().CreateMessageMediaInstance(this.AdminContext(), &pb.CreateMessageMediaInstanceRequest{
-		Name:        params.Name,
-		MediaType:   params.MediaType,
-		ParamsJSON:  optionsJSON,
-		Description: params.Description,
-		RateJSON:    rateJSON,
-		HashLife:    params.HashLife,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	defer this.CreateLogInfo(codes.MessageMediaInstance_LogCreateMessageMediaInstance, resp.MessageMediaInstanceId)
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/delete.go b/internal/web/actions/default/admins/recipients/instances/delete.go
deleted file mode 100644
index 4a50b73d..00000000
--- a/internal/web/actions/default/admins/recipients/instances/delete.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package instances
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-)
-
-type DeleteAction struct {
-	actionutils.ParentAction
-}
-
-func (this *DeleteAction) RunPost(params struct {
-	InstanceId int64
-}) {
-	defer this.CreateLogInfo(codes.MessageMediaInstance_LogDeleteMessageMediaInstance, params.InstanceId)
-
-	_, err := this.RPC().MessageMediaInstanceRPC().DeleteMessageMediaInstance(this.AdminContext(), &pb.DeleteMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/index.go b/internal/web/actions/default/admins/recipients/instances/index.go
deleted file mode 100644
index 604f9632..00000000
--- a/internal/web/actions/default/admins/recipients/instances/index.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package instances
-
-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("", "", "instance")
-}
-
-func (this *IndexAction) RunGet(params struct {
-}) {
-	// TODO 增加系统用户、媒介类型等条件搜索
-	countResp, err := this.RPC().MessageMediaInstanceRPC().CountAllEnabledMessageMediaInstances(this.AdminContext(), &pb.CountAllEnabledMessageMediaInstancesRequest{
-		MediaType: "",
-		Keyword:   "",
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	count := countResp.Count
-	page := this.NewPage(count)
-	this.Data["page"] = page.AsHTML()
-
-	instancesResp, err := this.RPC().MessageMediaInstanceRPC().ListEnabledMessageMediaInstances(this.AdminContext(), &pb.ListEnabledMessageMediaInstancesRequest{
-		MediaType: "",
-		Keyword:   "",
-		Offset:    page.Offset,
-		Size:      page.Size,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	instanceMaps := []maps.Map{}
-	for _, instance := range instancesResp.MessageMediaInstances {
-		if instance.MessageMedia == nil {
-			continue
-		}
-		instanceMaps = append(instanceMaps, maps.Map{
-			"id":   instance.Id,
-			"name": instance.Name,
-			"isOn": instance.IsOn,
-			"media": maps.Map{
-				"name": instance.MessageMedia.Name,
-			},
-			"description": instance.Description,
-		})
-	}
-	this.Data["instances"] = instanceMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/init.go b/internal/web/actions/default/admins/recipients/instances/init.go
deleted file mode 100644
index 88c0c6fe..00000000
--- a/internal/web/actions/default/admins/recipients/instances/init.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package instances
-
-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.AdminModuleCodeAdmin)).
-			Data("teaMenu", "admins").
-			Data("teaSubMenu", "instances").
-			Prefix("/admins/recipients/instances").
-			Get("", new(IndexAction)).
-			GetPost("/createPopup", new(CreatePopupAction)).
-			GetPost("/update", new(UpdateAction)).
-			Post("/delete", new(DeleteAction)).
-			Get("/instance", new(InstanceAction)).
-			GetPost("/test", new(TestAction)).
-			Post("/options", new(OptionsAction)).
-			EndAll()
-	})
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/instance.go b/internal/web/actions/default/admins/recipients/instances/instance.go
deleted file mode 100644
index 4a7c0061..00000000
--- a/internal/web/actions/default/admins/recipients/instances/instance.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package instances
-
-import (
-	"encoding/json"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type InstanceAction struct {
-	actionutils.ParentAction
-}
-
-func (this *InstanceAction) Init() {
-	this.Nav("", "", "instance")
-}
-
-func (this *InstanceAction) RunGet(params struct {
-	InstanceId int64
-}) {
-	instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	instance := instanceResp.MessageMediaInstance
-	if instance == nil || instance.MessageMedia == nil {
-		this.NotFound("messageMediaInstance", params.InstanceId)
-		return
-	}
-
-	mediaParams := maps.Map{}
-	if len(instance.ParamsJSON) > 0 {
-		err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	// 频率
-	var rateConfig = &monitorconfigs.RateConfig{}
-	if len(instance.RateJSON) > 0 {
-		err = json.Unmarshal(instance.RateJSON, rateConfig)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	this.Data["instance"] = maps.Map{
-		"id":   instance.Id,
-		"name": instance.Name,
-		"isOn": instance.IsOn,
-		"media": maps.Map{
-			"type": instance.MessageMedia.Type,
-			"name": instance.MessageMedia.Name,
-		},
-		"description": instance.Description,
-		"params":      mediaParams,
-		"rate":        rateConfig,
-		"hashLife":    instance.HashLife,
-	}
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/options.go b/internal/web/actions/default/admins/recipients/instances/options.go
deleted file mode 100644
index 0031a9c3..00000000
--- a/internal/web/actions/default/admins/recipients/instances/options.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package instances
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-)
-
-// 媒介类型选项
-type OptionsAction struct {
-	actionutils.ParentAction
-}
-
-func (this *OptionsAction) RunPost(params struct{}) {
-	resp, err := this.RPC().MessageMediaInstanceRPC().ListEnabledMessageMediaInstances(this.AdminContext(), &pb.ListEnabledMessageMediaInstancesRequest{
-		Offset: 0,
-		Size:   1000,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	instanceMaps := []maps.Map{}
-	for _, instance := range resp.MessageMediaInstances {
-		instanceMaps = append(instanceMaps, maps.Map{
-			"id":          instance.Id,
-			"name":        instance.Name,
-			"description": instance.Description,
-			"media": maps.Map{
-				"type":            instance.MessageMedia.Type,
-				"name":            instance.MessageMedia.Name,
-				"userDescription": instance.MessageMedia.UserDescription,
-			},
-		})
-	}
-	this.Data["instances"] = instanceMaps
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/test.go b/internal/web/actions/default/admins/recipients/instances/test.go
deleted file mode 100644
index 892362bb..00000000
--- a/internal/web/actions/default/admins/recipients/instances/test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package instances
-
-import (
-	"encoding/json"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type TestAction struct {
-	actionutils.ParentAction
-}
-
-func (this *TestAction) Init() {
-	this.Nav("", "", "test")
-}
-
-func (this *TestAction) RunGet(params struct {
-	InstanceId int64
-}) {
-	instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	instance := instanceResp.MessageMediaInstance
-	if instance == nil || instance.MessageMedia == nil {
-		this.NotFound("messageMediaInstance", params.InstanceId)
-		return
-	}
-
-	mediaParams := maps.Map{}
-	if len(instance.ParamsJSON) > 0 {
-		err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	this.Data["instance"] = maps.Map{
-		"id":   instance.Id,
-		"isOn": instance.IsOn,
-		"media": maps.Map{
-			"type":            instance.MessageMedia.Type,
-			"name":            instance.MessageMedia.Name,
-			"userDescription": instance.MessageMedia.UserDescription,
-		},
-		"description": instance.Description,
-		"params":      mediaParams,
-	}
-
-	this.Show()
-}
-
-func (this *TestAction) RunPost(params struct {
-	InstanceId int64
-	Subject    string
-	Body       string
-	User       string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("instanceId", params.InstanceId).
-		Gt(0, "请选择正确的媒介")
-
-	resp, err := this.RPC().MessageTaskRPC().CreateMessageTask(this.AdminContext(), &pb.CreateMessageTaskRequest{
-		RecipientId: 0,
-		InstanceId:  params.InstanceId,
-		User:        params.User,
-		Subject:     params.Subject,
-		Body:        params.Body,
-		IsPrimary:   true,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	this.Data["taskId"] = resp.MessageTaskId
-
-	defer this.CreateLogInfo(codes.MessageTask_LogCreateTestingMessageTask, resp.MessageTaskId)
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/instances/update.go b/internal/web/actions/default/admins/recipients/instances/update.go
deleted file mode 100644
index 8e0429b7..00000000
--- a/internal/web/actions/default/admins/recipients/instances/update.go
+++ /dev/null
@@ -1,317 +0,0 @@
-package instances
-
-import (
-	"encoding/json"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type UpdateAction struct {
-	actionutils.ParentAction
-}
-
-func (this *UpdateAction) Init() {
-	this.Nav("", "", "update")
-}
-
-func (this *UpdateAction) RunGet(params struct {
-	InstanceId int64
-}) {
-	instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	instance := instanceResp.MessageMediaInstance
-	if instance == nil || instance.MessageMedia == nil {
-		this.NotFound("messageMediaInstance", params.InstanceId)
-		return
-	}
-
-	mediaParams := maps.Map{}
-	if len(instance.ParamsJSON) > 0 {
-		err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	var rateConfig = &monitorconfigs.RateConfig{}
-	if len(instance.RateJSON) > 0 {
-		err = json.Unmarshal(instance.RateJSON, rateConfig)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	this.Data["instance"] = maps.Map{
-		"id":   instance.Id,
-		"name": instance.Name,
-		"isOn": instance.IsOn,
-		"media": maps.Map{
-			"type": instance.MessageMedia.Type,
-			"name": instance.MessageMedia.Name,
-		},
-		"description": instance.Description,
-		"params":      mediaParams,
-		"rate":        rateConfig,
-		"hashLife":    instance.HashLife,
-	}
-
-	this.Show()
-}
-
-func (this *UpdateAction) RunPost(params struct {
-	InstanceId int64
-	Name       string
-	MediaType  string
-
-	EmailSmtp     string
-	EmailUsername string
-	EmailPassword string
-	EmailFrom     string
-
-	WebHookURL          string
-	WebHookMethod       string
-	WebHookHeaderNames  []string
-	WebHookHeaderValues []string
-	WebHookContentType  string
-	WebHookParamNames   []string
-	WebHookParamValues  []string
-	WebHookBody         string
-
-	ScriptType      string
-	ScriptPath      string
-	ScriptLang      string
-	ScriptCode      string
-	ScriptCwd       string
-	ScriptEnvNames  []string
-	ScriptEnvValues []string
-
-	DingTalkWebHookURL string
-
-	QyWeixinCorporateId string
-	QyWeixinAgentId     string
-	QyWeixinAppSecret   string
-	QyWeixinTextFormat  string
-
-	QyWeixinRobotWebHookURL string
-	QyWeixinRobotTextFormat string
-
-	AliyunSmsSign              string
-	AliyunSmsTemplateCode      string
-	AliyunSmsTemplateVarNames  []string
-	AliyunSmsTemplateVarValues []string
-	AliyunSmsAccessKeyId       string
-	AliyunSmsAccessKeySecret   string
-
-	TelegramToken string
-
-	GroupIds    string
-	Description string
-	IsOn        bool
-
-	RateMinutes int32
-	RateCount   int32
-
-	HashLife int32
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	defer this.CreateLogInfo(codes.MessageMediaInstance_LogUpdateMessageMediaInstance, params.InstanceId)
-
-	params.Must.
-		Field("name", params.Name).
-		Require("请输入媒介名称").
-		Field("mediaType", params.MediaType).
-		Require("请选择媒介类型")
-
-	options := maps.Map{}
-
-	switch params.MediaType {
-	case "email":
-		params.Must.
-			Field("emailSmtp", params.EmailSmtp).
-			Require("请输入SMTP地址").
-			Field("emailUsername", params.EmailUsername).
-			Require("请输入邮箱账号").
-			Field("emailPassword", params.EmailPassword).
-			Require("请输入密码或授权码")
-
-		options["smtp"] = params.EmailSmtp
-		options["username"] = params.EmailUsername
-		options["password"] = params.EmailPassword
-		options["from"] = params.EmailFrom
-	case "webHook":
-		params.Must.
-			Field("webHookURL", params.WebHookURL).
-			Require("请输入URL地址").
-			Match("(?i)^(http|https)://", "URL地址必须以http或https开头").
-			Field("webHookMethod", params.WebHookMethod).
-			Require("请选择请求方法")
-
-		options["url"] = params.WebHookURL
-		options["method"] = params.WebHookMethod
-		options["contentType"] = params.WebHookContentType
-
-		headers := []maps.Map{}
-		if len(params.WebHookHeaderNames) > 0 {
-			for index, name := range params.WebHookHeaderNames {
-				if index < len(params.WebHookHeaderValues) {
-					headers = append(headers, maps.Map{
-						"name":  name,
-						"value": params.WebHookHeaderValues[index],
-					})
-				}
-			}
-		}
-		options["headers"] = headers
-
-		if params.WebHookContentType == "params" {
-			webHookParams := []maps.Map{}
-			for index, name := range params.WebHookParamNames {
-				if index < len(params.WebHookParamValues) {
-					webHookParams = append(webHookParams, maps.Map{
-						"name":  name,
-						"value": params.WebHookParamValues[index],
-					})
-				}
-			}
-			options["params"] = webHookParams
-		} else if params.WebHookContentType == "body" {
-			options["body"] = params.WebHookBody
-		}
-	case "script":
-		if params.ScriptType == "path" {
-			params.Must.
-				Field("scriptPath", params.ScriptPath).
-				Require("请输入脚本路径")
-		} else if params.ScriptType == "code" {
-			params.Must.
-				Field("scriptCode", params.ScriptCode).
-				Require("请输入脚本代码")
-		} else {
-			params.Must.
-				Field("scriptPath", params.ScriptPath).
-				Require("请输入脚本路径")
-		}
-
-		options["scriptType"] = params.ScriptType
-		options["path"] = params.ScriptPath
-		options["scriptLang"] = params.ScriptLang
-		options["script"] = params.ScriptCode
-		options["cwd"] = params.ScriptCwd
-
-		env := []maps.Map{}
-		for index, envName := range params.ScriptEnvNames {
-			if index < len(params.ScriptEnvValues) {
-				env = append(env, maps.Map{
-					"name":  envName,
-					"value": params.ScriptEnvValues[index],
-				})
-			}
-		}
-		options["env"] = env
-	case "dingTalk":
-		params.Must.
-			Field("dingTalkWebHookURL", params.DingTalkWebHookURL).
-			Require("请输入Hook地址").
-			Match("^https:", "Hook地址必须以https://开头")
-
-		options["webHookURL"] = params.DingTalkWebHookURL
-	case "qyWeixin":
-		params.Must.
-			Field("qyWeixinCorporateId", params.QyWeixinCorporateId).
-			Require("请输入企业ID").
-			Field("qyWeixinAgentId", params.QyWeixinAgentId).
-			Require("请输入应用AgentId").
-			Field("qyWeixinSecret", params.QyWeixinAppSecret).
-			Require("请输入应用Secret")
-
-		options["corporateId"] = params.QyWeixinCorporateId
-		options["agentId"] = params.QyWeixinAgentId
-		options["appSecret"] = params.QyWeixinAppSecret
-		options["textFormat"] = params.QyWeixinTextFormat
-	case "qyWeixinRobot":
-		params.Must.
-			Field("qyWeixinRobotWebHookURL", params.QyWeixinRobotWebHookURL).
-			Require("请输入WebHook地址").
-			Match("^https:", "WebHook地址必须以https://开头")
-
-		options["webHookURL"] = params.QyWeixinRobotWebHookURL
-		options["textFormat"] = params.QyWeixinRobotTextFormat
-	case "aliyunSms":
-		params.Must.
-			Field("aliyunSmsSign", params.AliyunSmsSign).
-			Require("请输入签名名称").
-			Field("aliyunSmsTemplateCode", params.AliyunSmsTemplateCode).
-			Require("请输入模板CODE").
-			Field("aliyunSmsAccessKeyId", params.AliyunSmsAccessKeyId).
-			Require("请输入AccessKey ID").
-			Field("aliyunSmsAccessKeySecret", params.AliyunSmsAccessKeySecret).
-			Require("请输入AccessKey Secret")
-
-		options["sign"] = params.AliyunSmsSign
-		options["templateCode"] = params.AliyunSmsTemplateCode
-		options["accessKeyId"] = params.AliyunSmsAccessKeyId
-		options["accessKeySecret"] = params.AliyunSmsAccessKeySecret
-
-		variables := []maps.Map{}
-		for index, name := range params.AliyunSmsTemplateVarNames {
-			if index < len(params.AliyunSmsTemplateVarValues) {
-				variables = append(variables, maps.Map{
-					"name":  name,
-					"value": params.AliyunSmsTemplateVarValues[index],
-				})
-			}
-		}
-		options["variables"] = variables
-	case "telegram":
-		params.Must.
-			Field("telegramToken", params.TelegramToken).
-			Require("请输入机器人Token")
-		options["token"] = params.TelegramToken
-	default:
-		this.Fail("错误的媒介类型")
-	}
-
-	optionsJSON, err := json.Marshal(options)
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	var rateConfig = &monitorconfigs.RateConfig{
-		Minutes: params.RateMinutes,
-		Count:   params.RateCount,
-	}
-	rateJSON, err := json.Marshal(rateConfig)
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	_, err = this.RPC().MessageMediaInstanceRPC().UpdateMessageMediaInstance(this.AdminContext(), &pb.UpdateMessageMediaInstanceRequest{
-		MessageMediaInstanceId: params.InstanceId,
-		Name:                   params.Name,
-		MediaType:              params.MediaType,
-		ParamsJSON:             optionsJSON,
-		Description:            params.Description,
-		RateJSON:               rateJSON,
-		HashLife:               params.HashLife,
-		IsOn:                   params.IsOn,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/logs/index.go b/internal/web/actions/default/admins/recipients/logs/index.go
deleted file mode 100644
index 67bf04c2..00000000
--- a/internal/web/actions/default/admins/recipients/logs/index.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package logs
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-	timeutil "github.com/iwind/TeaGo/utils/time"
-)
-
-type IndexAction struct {
-	actionutils.ParentAction
-}
-
-func (this *IndexAction) Init() {
-	this.Nav("", "", "log")
-}
-
-func (this *IndexAction) RunGet(params struct{}) {
-	countResp, err := this.RPC().MessageTaskLogRPC().CountMessageTaskLogs(this.AdminContext(), &pb.CountMessageTaskLogsRequest{})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	page := this.NewPage(countResp.Count)
-	this.Data["page"] = page.AsHTML()
-
-	logsResp, err := this.RPC().MessageTaskLogRPC().ListMessageTaskLogs(this.AdminContext(), &pb.ListMessageTaskLogsRequest{
-		Offset: page.Offset,
-		Size:   page.Size,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	logMaps := []maps.Map{}
-	for _, log := range logsResp.MessageTaskLogs {
-		if log.MessageTask.MessageRecipient != nil {
-			log.MessageTask.User = log.MessageTask.MessageRecipient.User
-		}
-		logMaps = append(logMaps, maps.Map{
-			"task": maps.Map{
-				"id":      log.MessageTask.Id,
-				"user":    log.MessageTask.User,
-				"subject": log.MessageTask.Subject,
-				"body":    log.MessageTask.Body,
-				"instance": maps.Map{
-					"id":   log.MessageTask.MessageMediaInstance.Id,
-					"name": log.MessageTask.MessageMediaInstance.Name,
-				},
-			},
-			"isOk":        log.IsOk,
-			"error":       log.Error,
-			"response":    log.Response,
-			"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
-		})
-	}
-	this.Data["logs"] = logMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/logs/init.go b/internal/web/actions/default/admins/recipients/logs/init.go
deleted file mode 100644
index 4646c24d..00000000
--- a/internal/web/actions/default/admins/recipients/logs/init.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package logs
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
-	"github.com/iwind/TeaGo"
-)
-
-func init() {
-	TeaGo.BeforeStart(func(server *TeaGo.Server) {
-		server.
-			Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
-			Data("teaMenu", "admins").
-			Data("teaSubMenu", "recipients").
-			Prefix("/admins/recipients/logs").
-			Get("", new(IndexAction)).
-			EndAll()
-	})
-}
diff --git a/internal/web/actions/default/admins/recipients/mediaOptions.go b/internal/web/actions/default/admins/recipients/mediaOptions.go
deleted file mode 100644
index e3cad461..00000000
--- a/internal/web/actions/default/admins/recipients/mediaOptions.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package recipients
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-)
-
-// 媒介类型选项
-type MediaOptionsAction struct {
-	actionutils.ParentAction
-}
-
-func (this *MediaOptionsAction) RunPost(params struct{}) {
-	resp, err := this.RPC().MessageMediaRPC().FindAllMessageMedias(this.AdminContext(), &pb.FindAllMessageMediasRequest{})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	mediaMaps := []maps.Map{}
-	for _, media := range resp.MessageMedias {
-		mediaMaps = append(mediaMaps, maps.Map{
-			"id":          media.Id,
-			"type":        media.Type,
-			"name":        media.Name,
-			"description": media.Description,
-		})
-	}
-	this.Data["medias"] = mediaMaps
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/recipient.go b/internal/web/actions/default/admins/recipients/recipient.go
deleted file mode 100644
index 0ef64cd4..00000000
--- a/internal/web/actions/default/admins/recipients/recipient.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package recipients
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type RecipientAction struct {
-	actionutils.ParentAction
-}
-
-func (this *RecipientAction) Init() {
-	this.Nav("", "", "recipient")
-}
-
-func (this *RecipientAction) RunGet(params struct {
-	RecipientId int64
-}) {
-	recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	recipient := recipientResp.MessageRecipient
-	if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
-		this.NotFound("messageRecipient", params.RecipientId)
-		return
-	}
-
-	this.Data["recipient"] = maps.Map{
-		"id": recipient.Id,
-		"admin": maps.Map{
-			"id":       recipient.Admin.Id,
-			"fullname": recipient.Admin.Fullname,
-			"username": recipient.Admin.Username,
-		},
-		"groups": recipient.MessageRecipientGroups,
-		"isOn":   recipient.IsOn,
-		"instance": maps.Map{
-			"id":          recipient.MessageMediaInstance.Id,
-			"name":        recipient.MessageMediaInstance.Name,
-			"description": recipient.MessageMediaInstance.Description,
-		},
-		"user":        recipient.User,
-		"description": recipient.Description,
-		"timeFrom":    recipient.TimeFrom,
-		"timeTo":      recipient.TimeTo,
-	}
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/tasks/delete.go b/internal/web/actions/default/admins/recipients/tasks/delete.go
deleted file mode 100644
index bfc85513..00000000
--- a/internal/web/actions/default/admins/recipients/tasks/delete.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
-
-package tasks
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-)
-
-type DeleteAction struct {
-	actionutils.ParentAction
-}
-
-func (this *DeleteAction) RunPost(params struct {
-	TaskId int64
-}) {
-	defer this.CreateLogInfo(codes.MessageTask_LogDeleteMessageTask, params.TaskId)
-
-	_, err := this.RPC().MessageTaskRPC().DeleteMessageTask(this.AdminContext(), &pb.DeleteMessageTaskRequest{MessageTaskId: params.TaskId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/tasks/index.go b/internal/web/actions/default/admins/recipients/tasks/index.go
deleted file mode 100644
index 6038a3c0..00000000
--- a/internal/web/actions/default/admins/recipients/tasks/index.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
-
-package tasks
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-	timeutil "github.com/iwind/TeaGo/utils/time"
-)
-
-type IndexAction struct {
-	actionutils.ParentAction
-}
-
-func (this *IndexAction) Init() {
-	this.Nav("", "", "task")
-}
-
-func (this *IndexAction) RunGet(params struct {
-	Status int32
-}) {
-	this.Data["status"] = params.Status
-	if params.Status > 3 {
-		params.Status = 0
-	}
-
-	countWaitingResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusNone})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	var countWaiting = countWaitingResp.Count
-	this.Data["countWaiting"] = countWaiting
-
-	countFailedResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusFailed})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	var countFailed = countFailedResp.Count
-	this.Data["countFailed"] = countFailed
-
-	// 列表
-	var total = int64(0)
-	switch params.Status {
-	case 0:
-		total = countWaiting
-	case 3:
-		total = countFailed
-	}
-	page := this.NewPage(total)
-	this.Data["page"] = page.AsHTML()
-
-	var taskMaps = []maps.Map{}
-	tasksResp, err := this.RPC().MessageTaskRPC().ListMessageTasksWithStatus(this.AdminContext(), &pb.ListMessageTasksWithStatusRequest{
-		Status: pb.ListMessageTasksWithStatusRequest_Status(params.Status),
-		Offset: page.Offset,
-		Size:   page.Size,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	for _, task := range tasksResp.MessageTasks {
-		var resultMap = maps.Map{}
-		var result = task.Result
-		if result != nil {
-			resultMap = maps.Map{
-				"isOk":     result.IsOk,
-				"error":    result.Error,
-				"response": result.Response,
-			}
-		}
-
-		//var recipients = []string{}
-		var user = ""
-		var instanceMap maps.Map
-		if task.MessageRecipient != nil {
-			user = task.MessageRecipient.User
-			if task.MessageRecipient.MessageMediaInstance != nil {
-				instanceMap = maps.Map{
-					"id":   task.MessageRecipient.MessageMediaInstance.Id,
-					"name": task.MessageRecipient.MessageMediaInstance.Name,
-				}
-			}
-		}
-
-		taskMaps = append(taskMaps, maps.Map{
-			"id":          task.Id,
-			"subject":     task.Subject,
-			"body":        task.Body,
-			"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
-			"result":      resultMap,
-			"status":      task.Status,
-			"user":        user,
-			"instance":    instanceMap,
-		})
-	}
-	this.Data["tasks"] = taskMaps
-
-	this.Show()
-}
diff --git a/internal/web/actions/default/admins/recipients/tasks/init.go b/internal/web/actions/default/admins/recipients/tasks/init.go
deleted file mode 100644
index 2a8f7499..00000000
--- a/internal/web/actions/default/admins/recipients/tasks/init.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package tasks
-
-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.AdminModuleCodeAdmin)).
-			Data("teaMenu", "admins").
-			Data("teaSubMenu", "recipients").
-			Prefix("/admins/recipients/tasks").
-			Get("", new(IndexAction)).
-			Post("/taskInfo", new(TaskInfoAction)).
-			Post("/delete", new(DeleteAction)).
-			EndAll()
-	})
-}
diff --git a/internal/web/actions/default/admins/recipients/tasks/taskInfo.go b/internal/web/actions/default/admins/recipients/tasks/taskInfo.go
deleted file mode 100644
index cfcfbc1a..00000000
--- a/internal/web/actions/default/admins/recipients/tasks/taskInfo.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package tasks
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type TaskInfoAction struct {
-	actionutils.ParentAction
-}
-
-func (this *TaskInfoAction) Init() {
-	this.Nav("", "", "")
-}
-
-func (this *TaskInfoAction) RunPost(params struct {
-	TaskId int64
-}) {
-	resp, err := this.RPC().MessageTaskRPC().FindEnabledMessageTask(this.AdminContext(), &pb.FindEnabledMessageTaskRequest{MessageTaskId: params.TaskId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	if resp.MessageTask == nil {
-		this.NotFound("messageTask", params.TaskId)
-		return
-	}
-
-	result := resp.MessageTask.Result
-	this.Data["status"] = resp.MessageTask.Status
-	this.Data["result"] = maps.Map{
-		"isOk":     result.IsOk,
-		"response": result.Response,
-		"error":    result.Error,
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/test.go b/internal/web/actions/default/admins/recipients/test.go
deleted file mode 100644
index 6623219f..00000000
--- a/internal/web/actions/default/admins/recipients/test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package recipients
-
-import (
-	"encoding/json"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"github.com/iwind/TeaGo/maps"
-)
-
-type TestAction struct {
-	actionutils.ParentAction
-}
-
-func (this *TestAction) Init() {
-	this.Nav("", "", "test")
-}
-
-func (this *TestAction) RunGet(params struct {
-	RecipientId int64
-}) {
-	recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	recipient := recipientResp.MessageRecipient
-	if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
-		this.NotFound("messageRecipient", params.RecipientId)
-		return
-	}
-
-	this.Data["recipient"] = maps.Map{
-		"id": recipient.Id,
-		"admin": maps.Map{
-			"id":       recipient.Admin.Id,
-			"fullname": recipient.Admin.Fullname,
-			"username": recipient.Admin.Username,
-		},
-		"instance": maps.Map{
-			"id":          recipient.MessageMediaInstance.Id,
-			"name":        recipient.MessageMediaInstance.Name,
-			"description": recipient.MessageMediaInstance.Description,
-		},
-		"user": recipient.User,
-	}
-
-	instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: recipient.MessageMediaInstance.Id})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	instance := instanceResp.MessageMediaInstance
-	if instance == nil || instance.MessageMedia == nil {
-		this.NotFound("messageMediaInstance", recipient.MessageMediaInstance.Id)
-		return
-	}
-
-	mediaParams := maps.Map{}
-	if len(instance.ParamsJSON) > 0 {
-		err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
-		if err != nil {
-			this.ErrorPage(err)
-			return
-		}
-	}
-
-	this.Data["instance"] = maps.Map{
-		"id":   instance.Id,
-		"isOn": instance.IsOn,
-		"media": maps.Map{
-			"type":            instance.MessageMedia.Type,
-			"name":            instance.MessageMedia.Name,
-			"userDescription": instance.MessageMedia.UserDescription,
-		},
-		"description": instance.Description,
-		"params":      mediaParams,
-	}
-
-	this.Show()
-}
-
-func (this *TestAction) RunPost(params struct {
-	InstanceId int64
-	Subject    string
-	Body       string
-	User       string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	params.Must.
-		Field("instanceId", params.InstanceId).
-		Gt(0, "请选择正确的媒介")
-
-	resp, err := this.RPC().MessageTaskRPC().CreateMessageTask(this.AdminContext(), &pb.CreateMessageTaskRequest{
-		RecipientId: 0,
-		InstanceId:  params.InstanceId,
-		User:        params.User,
-		Subject:     params.Subject,
-		Body:        params.Body,
-		IsPrimary:   true,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	this.Data["taskId"] = resp.MessageTaskId
-
-	defer this.CreateLogInfo(codes.MessageTask_LogCreateTestingMessageTask, resp.MessageTaskId)
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/admins/recipients/update.go b/internal/web/actions/default/admins/recipients/update.go
deleted file mode 100644
index 010b5bcf..00000000
--- a/internal/web/actions/default/admins/recipients/update.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package recipients
-
-import (
-	"github.com/TeaOSLab/EdgeAdmin/internal/utils"
-	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
-	"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
-	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
-	"github.com/iwind/TeaGo/actions"
-	"github.com/iwind/TeaGo/maps"
-	"regexp"
-	"strings"
-)
-
-type UpdateAction struct {
-	actionutils.ParentAction
-}
-
-func (this *UpdateAction) Init() {
-	this.Nav("", "", "update")
-}
-
-func (this *UpdateAction) RunGet(params struct {
-	RecipientId int64
-}) {
-	recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-	recipient := recipientResp.MessageRecipient
-	if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
-		this.NotFound("messageRecipient", params.RecipientId)
-		return
-	}
-
-	var timeFromHour = ""
-	var timeFromMinute = ""
-	var timeFromSecond = ""
-
-	if len(recipient.TimeFrom) > 0 {
-		var pieces = strings.Split(recipient.TimeFrom, ":")
-		timeFromHour = pieces[0]
-		timeFromMinute = pieces[1]
-		timeFromSecond = pieces[2]
-	}
-
-	var timeToHour = ""
-	var timeToMinute = ""
-	var timeToSecond = ""
-	if len(recipient.TimeTo) > 0 {
-		var pieces = strings.Split(recipient.TimeTo, ":")
-		timeToHour = pieces[0]
-		timeToMinute = pieces[1]
-		timeToSecond = pieces[2]
-	}
-
-	this.Data["recipient"] = maps.Map{
-		"id": recipient.Id,
-		"admin": maps.Map{
-			"id":       recipient.Admin.Id,
-			"fullname": recipient.Admin.Fullname,
-			"username": recipient.Admin.Username,
-		},
-		"groups": recipient.MessageRecipientGroups,
-		"isOn":   recipient.IsOn,
-		"instance": maps.Map{
-			"id":   recipient.MessageMediaInstance.Id,
-			"name": recipient.MessageMediaInstance.Name,
-		},
-		"user":           recipient.User,
-		"description":    recipient.Description,
-		"timeFromHour":   timeFromHour,
-		"timeFromMinute": timeFromMinute,
-		"timeFromSecond": timeFromSecond,
-		"timeToHour":     timeToHour,
-		"timeToMinute":   timeToMinute,
-		"timeToSecond":   timeToSecond,
-	}
-
-	this.Show()
-}
-
-func (this *UpdateAction) RunPost(params struct {
-	RecipientId int64
-
-	AdminId    int64
-	User       string
-	InstanceId int64
-
-	GroupIds    string
-	Description string
-	IsOn        bool
-
-	TimeFromHour   string
-	TimeFromMinute string
-	TimeFromSecond string
-
-	TimeToHour   string
-	TimeToMinute string
-	TimeToSecond string
-
-	Must *actions.Must
-	CSRF *actionutils.CSRF
-}) {
-	defer this.CreateLogInfo(codes.MessageRecipient_LogUpdateMessageRecipient, params.RecipientId)
-
-	params.Must.
-		Field("adminId", params.AdminId).
-		Gt(0, "请选择系统用户").
-		Field("instanceId", params.InstanceId).
-		Gt(0, "请选择媒介")
-
-	groupIds := utils.SplitNumbers(params.GroupIds)
-
-	var digitReg = regexp.MustCompile(`^\d+$`)
-
-	var timeFrom = ""
-	if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
-		timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
-	}
-
-	var timeTo = ""
-	if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
-		timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
-	}
-
-	_, err := this.RPC().MessageRecipientRPC().UpdateMessageRecipient(this.AdminContext(), &pb.UpdateMessageRecipientRequest{
-		MessageRecipientId:       params.RecipientId,
-		AdminId:                  params.AdminId,
-		MessageMediaInstanceId:   params.InstanceId,
-		User:                     params.User,
-		MessageRecipientGroupIds: groupIds,
-		Description:              params.Description,
-		IsOn:                     params.IsOn,
-		TimeFrom:                 timeFrom,
-		TimeTo:                   timeTo,
-	})
-	if err != nil {
-		this.ErrorPage(err)
-		return
-	}
-
-	this.Success()
-}
diff --git a/internal/web/actions/default/dashboard/index.go b/internal/web/actions/default/dashboard/index.go
index 7cdfa99a..bb38e73c 100644
--- a/internal/web/actions/default/dashboard/index.go
+++ b/internal/web/actions/default/dashboard/index.go
@@ -154,17 +154,6 @@ func (this *IndexAction) RunPost(params struct{}) {
 			"version": "",
 		}
 	}
-	if resp.MonitorNodeUpgradeInfo != nil {
-		this.Data["monitorNodeUpgradeInfo"] = maps.Map{
-			"count":   resp.MonitorNodeUpgradeInfo.CountNodes,
-			"version": resp.MonitorNodeUpgradeInfo.NewVersion,
-		}
-	} else {
-		this.Data["monitorNodeUpgradeInfo"] = maps.Map{
-			"count":   0,
-			"version": "",
-		}
-	}
 	if resp.ApiNodeUpgradeInfo != nil {
 		this.Data["apiNodeUpgradeInfo"] = maps.Map{
 			"count":   resp.ApiNodeUpgradeInfo.CountNodes,
diff --git a/web/views/@default/settings/monitor-nodes/index.html b/web/views/@default/settings/monitor-nodes/index.html
deleted file mode 100644
index efe75642..00000000
--- a/web/views/@default/settings/monitor-nodes/index.html
+++ /dev/null
@@ -1,49 +0,0 @@
-{$layout}
-
-
暂时还没有节点。
- -| 节点名称 | -版本号 | -CPU | -内存 | -状态 | -操作 | -
|---|---|---|---|---|---|
| {{node.name}} | -- v{{node.status.buildVersion}} - - - | -- {{node.status.cpuUsageText}} - - - | -- {{node.status.memUsageText}} - - - | -
-             
-                运行中
-             
-            已断开
-            未连接
-         | 
-		- 详情 - 删除 - | -
| 节点名称 | -- {{node.name}} - | -
| 状态 | -
-			 | 
-	
| 描述 | -- {{node.description}} - 暂时还没有描述。 - | -
| 配置文件 | -
-            configs/api.yaml    | 
-    
| 配置内容 | -
-			 | 
-	
暂时还没有日志。
- -| 
-             | 
-