fix: add success alert message for cron jobs and enhance alert handling (#13919)

This commit is contained in:
蘭
2026-09-24 18:23:39 +08:00
committed by GitHub
parent 4712ceaf2c
commit cac73e6d45
42 changed files with 726 additions and 114 deletions
+2
View File
@@ -21,6 +21,7 @@ type AlertBase struct {
}
type PushAlert struct {
Result string `json:"result,omitempty"`
TaskName string `json:"taskName"`
AlertType string `json:"alertType"`
EntryID uint `json:"entryID"`
@@ -53,6 +54,7 @@ type AlertDTO struct {
Method string `json:"method"`
Title string `json:"title"`
Project string `json:"project"`
TaskName string `json:"taskName,omitempty"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
AdvancedParams string `json:"advancedParams"`
+10 -7
View File
@@ -51,9 +51,10 @@ type CronjobOperate struct {
Secret string `json:"secret"`
Args string `json:"args"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
AlertTriggerMode string `json:"alertTriggerMode" validate:"omitempty,oneof=failed success both"`
CleanLogConfig
}
@@ -126,7 +127,8 @@ type CronjobInfo struct {
Secret string `json:"secret"`
Args string `json:"args"`
AlertCount uint `json:"alertCount"`
AlertCount uint `json:"alertCount"`
AlertTriggerMode string `json:"alertTriggerMode"`
}
type CronjobImport struct {
@@ -169,9 +171,10 @@ type CronjobTrans struct {
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
AlertTriggerMode string `json:"alertTriggerMode" validate:"omitempty,oneof=failed success both"`
}
type TransHelper struct {
Name string `json:"name"`
+98
View File
@@ -17,6 +17,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
alertconfig "github.com/1Panel-dev/1Panel/agent/utils/alert_config"
alertwebhook "github.com/1Panel-dev/1Panel/agent/utils/alert_webhook"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
@@ -109,7 +110,38 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
return 0, nil, err
}
cronjobProjects := make(map[string]uint)
var cronjobIDs []uint
for _, item := range alerts {
if alertUtil.GetCronJobType(item.Type) != "cronJob" {
continue
}
if _, exists := cronjobProjects[item.Project]; exists {
continue
}
id, parseErr := strconv.ParseUint(item.Project, 10, strconv.IntSize)
if parseErr != nil || id == 0 {
continue
}
cronjobProjects[item.Project] = uint(id)
cronjobIDs = append(cronjobIDs, uint(id))
}
cronjobsByID := make(map[uint]model.Cronjob)
if len(cronjobIDs) > 0 {
cronjobs, err := cronjobRepo.List(repo.WithByIDs(cronjobIDs))
if err != nil {
return 0, nil, err
}
for _, cronjob := range cronjobs {
cronjobsByID[cronjob.ID] = cronjob
}
}
for _, item := range alerts {
var taskName string
if cronjob, exists := cronjobsByID[cronjobProjects[item.Project]]; exists && cronjob.Type == item.Type {
taskName = cronjob.Name
}
result = append(result, dto.AlertDTO{
ID: item.ID,
@@ -119,6 +151,7 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
Method: item.Method,
Title: item.Title,
Project: item.Project,
TaskName: taskName,
Status: item.Status,
SendCount: item.SendCount,
AdvancedParams: item.AdvancedParams,
@@ -190,6 +223,16 @@ func (a AlertService) CreateAlert(create dto.AlertCreate, operator string) error
return err
}
} else {
advanced, err := prepareCronJobAlertParams(create.Type, "", create.AdvancedParams)
if err != nil {
return err
}
create.AdvancedParams = advanced
if create.Status != constant.AlertDisable {
if err := a.validateCronJobAlertChannels(create.Type, advanced, create.Method); err != nil {
return err
}
}
alertInfo.Status = constant.AlertEnable
if err := copier.Copy(&alertInfo, &create); err != nil {
return buserr.WithErr("ErrStructTransform", err)
@@ -207,11 +250,24 @@ func (a AlertService) CreateAlert(create dto.AlertCreate, operator string) error
}
func (a AlertService) UpdateAlert(req dto.AlertUpdate, operator string) error {
if alertUtil.GetCronJobType(req.Type) == "cronJob" {
previous, err := alertRepo.Get(repo.WithByID(req.ID))
if err != nil {
return err
}
req.AdvancedParams, err = prepareCronJobAlertParams(req.Type, previous.AdvancedParams, req.AdvancedParams)
if err != nil {
return err
}
}
methodTypes, err := a.validateAlertMethodReferences(req.Method)
if err != nil {
return err
}
if req.Status != constant.AlertDisable {
if err := a.validateCronJobAlertChannels(req.Type, req.AdvancedParams, req.Method); err != nil {
return err
}
if err := a.validateAlertMethodEntitlement(methodTypes); err != nil {
return err
}
@@ -278,6 +334,9 @@ func (a AlertService) UpdateStatus(id uint, status string) error {
return err
}
if status == constant.AlertEnable {
if err := a.validateCronJobAlertChannels(alertInfo.Type, alertInfo.AdvancedParams, alertInfo.Method); err != nil {
return err
}
if err := a.validateAlertMethodEntitlement(methodTypes); err != nil {
return err
}
@@ -1021,6 +1080,23 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
alertRepo.WithByType(updateAlert.Type),
alertRepo.WithByProject(updateAlert.Project),
)
advanced, err := prepareCronJobAlertParams(updateAlert.Type, alertInfo.AdvancedParams, updateAlert.AdvancedParams)
if err != nil {
return err
}
updateAlert.AdvancedParams = advanced
if alertUtil.GetCronJobType(updateAlert.Type) == "cronJob" {
upMap["advanced_params"] = advanced
}
if newStatus == constant.AlertEnable {
method := updateAlert.Method
if method == "" {
method = alertInfo.Method
}
if err := a.validateCronJobAlertChannels(updateAlert.Type, advanced, method); err != nil {
return err
}
}
if alertInfo.ID > 0 {
shouldUpdate := false
@@ -1034,6 +1110,9 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
if val, ok := upMap["method"]; ok && val != "" && val != alertInfo.Method {
shouldUpdate = true
}
if val, ok := upMap["advanced_params"]; ok && val != alertInfo.AdvancedParams {
shouldUpdate = true
}
if shouldUpdate {
if err := alertRepo.Update(
@@ -1055,3 +1134,22 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
return nil
}
func prepareCronJobAlertParams(alertType, previous, incoming string) (string, error) {
if alertUtil.GetCronJobType(alertType) != "cronJob" {
return incoming, nil
}
return alertUtil.MergeCronJobAlertParams(previous, incoming)
}
func (a AlertService) validateCronJobAlertChannels(alertType, advanced, method string) error {
if alertUtil.GetCronJobType(alertType) != "cronJob" {
return nil
}
mode, err := alertUtil.CronJobAlertTriggerMode(advanced)
if err != nil || mode != alertUtil.CronJobAlertSuccess {
return err
}
_, err = a.validateAlertMethodReferences(method)
return err
}
+92 -26
View File
@@ -16,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@@ -75,6 +76,7 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
EntryID: cronjob.ID,
}
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
if alertInfo.SendCount != 0 {
item.AlertCount = alertInfo.SendCount
} else {
@@ -98,9 +100,11 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
AlertType: cronjob.Type,
EntryID: cronjob.ID,
}
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))))
item.AlertMethod = alertInfo.Method
if alertInfo.SendCount != 0 {
item.AlertTitle = alertInfo.Title
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
if alertInfo.Status == constant.AlertEnable {
item.AlertCount = alertInfo.SendCount
} else {
item.AlertCount = 0
@@ -195,11 +199,12 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
}
}
item.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), repo.WithByStatus(constant.AlertEnable))
if alertInfo.SendCount != 0 {
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))))
item.AlertTitle = alertInfo.Title
item.AlertMethod = alertInfo.Method
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
if alertInfo.Status == constant.AlertEnable {
item.AlertCount = alertInfo.SendCount
item.AlertTitle = alertInfo.Title
item.AlertMethod = alertInfo.Method
} else {
item.AlertCount = 0
}
@@ -213,6 +218,17 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
}
func (u *CronjobService) Import(req []dto.CronjobTrans, operator string) error {
for _, item := range req {
advanced, err := cronJobAlertAdvancedParams(item.AlertTriggerMode)
if err != nil {
return err
}
if item.AlertCount != 0 {
if err := (AlertService{}).validateCronJobAlertChannels(item.Type, advanced, item.AlertMethod); err != nil {
return err
}
}
}
for _, item := range req {
cronjobItem, _ := cronjobRepo.Get(repo.WithByName(item.Name))
if cronjobItem.ID != 0 {
@@ -395,17 +411,27 @@ func (u *CronjobService) Import(req []dto.CronjobTrans, operator string) error {
} else {
cronjob.Status = constant.StatusDisable
}
_ = cronjobRepo.Create(&cronjob)
if item.AlertCount != 0 && item.AlertTitle != "" && item.AlertMethod != "" {
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
if item.AlertTitle != "" && item.AlertMethod != "" {
advanced, _ := cronJobAlertAdvancedParams(item.AlertTriggerMode)
status := constant.AlertEnable
if item.AlertCount == 0 {
status = constant.AlertDisable
}
createAlert := dto.AlertCreate{
Title: item.AlertTitle,
SendCount: item.AlertCount,
Method: item.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronjob.ID)),
Status: constant.AlertEnable,
Title: item.AlertTitle,
SendCount: item.AlertCount,
Method: item.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronjob.ID)),
Status: status,
AdvancedParams: advanced,
}
if err := NewIAlertService().CreateAlert(createAlert, operator); err != nil {
return err
}
_ = NewIAlertService().CreateAlert(createAlert, operator)
}
}
return nil
@@ -562,6 +588,15 @@ func (u *CronjobService) HandleOnce(id uint) error {
}
func (u *CronjobService) Create(req dto.CronjobOperate, operator string) error {
advanced, err := cronJobAlertAdvancedParams(req.AlertTriggerMode)
if err != nil {
return err
}
if req.AlertCount != 0 {
if err := (AlertService{}).validateCronJobAlertChannels(req.Type, advanced, req.AlertMethod); err != nil {
return err
}
}
cronjob, _ := cronjobRepo.Get(repo.WithByName(req.Name))
if cronjob.ID != 0 {
return buserr.New("ErrRecordExist")
@@ -603,12 +638,13 @@ func (u *CronjobService) Create(req dto.CronjobOperate, operator string) error {
}
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
createAlert := dto.AlertCreate{
Title: req.AlertTitle,
SendCount: req.AlertCount,
Method: req.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronjob.ID)),
Status: constant.AlertEnable,
Title: req.AlertTitle,
SendCount: req.AlertCount,
Method: req.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronjob.ID)),
Status: constant.AlertEnable,
AdvancedParams: advanced,
}
err := NewIAlertService().CreateAlert(createAlert, operator)
if err != nil {
@@ -682,6 +718,10 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
}
func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string) error {
advanced, err := cronJobAlertAdvancedParams(req.AlertTriggerMode)
if err != nil {
return err
}
var cronjob model.Cronjob
if err := copier.Copy(&cronjob, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
@@ -697,6 +737,20 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
if err != nil {
return buserr.New("ErrRecordNotFound")
}
if req.AlertCount != 0 {
previous, _ := alertRepo.Get(alertRepo.WithByType(cronModel.Type), alertRepo.WithByProject(strconv.Itoa(int(id))))
merged, err := prepareCronJobAlertParams(cronModel.Type, previous.AdvancedParams, advanced)
if err != nil {
return err
}
method := req.AlertMethod
if method == "" {
method = previous.Method
}
if err := (AlertService{}).validateCronJobAlertChannels(cronModel.Type, merged, method); err != nil {
return err
}
}
upMap := make(map[string]interface{})
cronjob.EntryIDs = cronModel.EntryIDs
cronjob.Type = cronModel.Type
@@ -753,11 +807,12 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
return err
}
updateAlert := dto.AlertCreate{
Title: req.AlertTitle,
SendCount: req.AlertCount,
Method: req.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronModel.ID)),
Title: req.AlertTitle,
SendCount: req.AlertCount,
Method: req.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronModel.ID)),
AdvancedParams: advanced,
}
err = NewIAlertService().ExternalUpdateAlert(updateAlert, operator)
if err != nil {
@@ -766,6 +821,17 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
return nil
}
func cronJobAlertAdvancedParams(mode string) (string, error) {
if mode == "" {
return "", nil
}
data, err := json.Marshal(map[string]string{"alertTriggerMode": mode})
if err != nil {
return "", err
}
return alertUtil.MergeCronJobAlertParams("", string(data))
}
func (u *CronjobService) UpdateStatus(id uint, status string) error {
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
if cronjob.ID == 0 {
+1
View File
@@ -412,6 +412,7 @@ func addSkipTask(source string, taskItem *task.Task) {
taskItem.Log(i18n.GetMsgByKey("NoSuchResource"))
return nil
}, nil)
taskItem.SubTasks[len(taskItem.SubTasks)-1].StepAlias = cronJobSkippedStep
}
func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
+35 -6
View File
@@ -4,6 +4,7 @@ import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@@ -23,6 +24,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
@@ -56,10 +58,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
_ = taskRepo.Save(context.Background(), taskItem.Task)
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
return
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, nil))
}()
return
}
@@ -70,19 +73,20 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
record.TaskID = ""
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
return
}
go func() {
if err := taskItem.Execute(); err != nil {
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(taskItem.ID) == 0 {
storedTask, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(storedTask.ID) == 0 {
record.TaskID = ""
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
} else {
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, nil))
}
}()
}
@@ -482,8 +486,33 @@ func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log" || cronjobType == "cutWebsiteLog"
}
func handleCronJobAlert(cronjob *model.Cronjob) {
const cronJobSkippedStep = "cronjob-skipped"
func cronJobAlertResult(taskItem *task.Task, err error) string {
if errors.Is(err, context.Canceled) || taskItem.Task.Status == constant.StatusCanceled ||
(taskItem.TaskCtx != nil && taskItem.TaskCtx.Err() != nil) {
return ""
}
if err != nil {
return alertUtil.CronJobAlertFailed
}
if taskItem.Task.Status != constant.StatusSuccess {
return ""
}
for _, subTask := range taskItem.SubTasks {
if subTask.StepAlias != cronJobSkippedStep {
return alertUtil.CronJobAlertSuccess
}
}
return ""
}
func handleCronJobAlert(cronjob *model.Cronjob, result string) {
if result == "" {
return
}
pushAlert := dto.PushAlert{
Result: result,
TaskName: cronjob.Name,
AlertType: cronjob.Type,
EntryID: cronjob.ID,
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: "Panel {{ .node }}{{ .ip }}: {{ .msg }}. Log in to view details."
NodeExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} nodes are abnormal. Log in to view details."
LicenseExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} licenses are abnormal. Log in to view details."
SSHAndPanelLoginAlert: "Panel {{ .node }}{{ .ip }}: abnormal {{ .name }} login from {{ .loginIp }}. Log in to view details."
CronJobSuccessAlert: "Panel {{ .node }}{{ .ip }}: scheduled task {{ .name }} completed successfully. Log in to view details."
# disk
DeviceNotFound: "Device {{ .name }} not found"
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el pa
NodeExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles.'
LicenseExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles.'
SSHAndPanelLoginAlert: 'Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles.'
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: la tarea programada {{ .name }} se completó correctamente. Inicie sesión para ver los detalles.'
# disco
DeviceNotFound: 'Dispositivo {{ .name }} no encontrado'
+2
View File
@@ -649,6 +649,8 @@ CommonAlert: "پنل {{ .node }}{{ .ip }}: {{ .msg }}. برای مشاهده ج
NodeExceptionAlert: "پنل {{ .node }}{{ .ip }}: {{ .num }} گره غیرعادی هستند. برای مشاهده جزئیات وارد شوید."
LicenseExceptionAlert: "پنل {{ .node }}{{ .ip }}: {{ .num }} مجوز غیرعادی است. برای مشاهده جزئیات وارد شوید."
SSHAndPanelLoginAlert: "پنل {{ .node }}{{ .ip }}: ورود غیرعادی {{ .name }} از {{ .loginIp }}. برای مشاهده جزئیات وارد شوید."
CronJobSuccessAlert: "پنل {{ .node }}{{ .ip }}: وظیفه زمان‌بندی‌شده {{ .name }} با موفقیت تکمیل شد. برای مشاهده جزئیات وارد شوید."
# دیسک
DeviceNotFound: "دستگاه {{ .name }} یافت نشد"
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細
NodeExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインして'
LicenseExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインして'
SSHAndPanelLoginAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインして'
CronJobSuccessAlert: 'パネル {{ .node }}{{ .ip }}: スケジュールタスク {{ .name }} が正常に完了しました。詳細はパネルにログインして確認してください。'
# ディスク
DeviceNotFound: 'デバイス {{ .name }} が見つかりません'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내
NodeExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
LicenseExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
SSHAndPanelLoginAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
CronJobSuccessAlert: '패널 {{ .node }}{{ .ip }}: 예약 작업 {{ .name }}이(가) 성공적으로 완료되었습니다. 로그인하여 자세한 내용을 확인하십시오.'
# 디스크
DeviceNotFound: '장치 {{ .name }} 을(를) 찾을 수 없습니다'
+1
View File
@@ -639,6 +639,7 @@ CommonAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: {{ .msg }}. ເ
NodeExceptionAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ໂນດ {{ .num }} ແຫ່ງຜິດປົກກະຕິ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
LicenseExceptionAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ໃບອະນຸຍາດ {{ .num }} ສະບັບຜິດປົກກະຕິ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
SSHAndPanelLoginAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ມີການເຂົ້າສູ່ລະບົບ {{ .name }} ທີ່ຜິດປົກກະຕິຈາກ {{ .loginIp }}. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
CronJobSuccessAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ວຽກທີ່ຕັ້ງເວລາ {{ .name }} ສຳເລັດແລ້ວ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
#disk
DeviceNotFound: "ບໍ່ພົບອຸປະກອນ {{ .name }}"
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke pan
NodeExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
LicenseExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
SSHAndPanelLoginAlert: 'Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: tugas berjadual {{ .name }} berjaya diselesaikan. Log masuk untuk melihat butiran.'
# cakera
DeviceNotFound: 'Peranti {{ .name }} tidak ditemui'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel
NodeExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes.'
LicenseExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes.'
SSHAndPanelLoginAlert: 'Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes.'
CronJobSuccessAlert: 'Painel {{ .node }}{{ .ip }}: a tarefa agendada {{ .name }} foi concluída com sucesso. Faça login para ver os detalhes.'
# disco
DeviceNotFound: 'Dispositivo {{ .name }} não encontrado'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдит
NodeExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей.'
LicenseExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей.'
SSHAndPanelLoginAlert: 'Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей.'
CronJobSuccessAlert: 'Панель {{ .node }}{{ .ip }}: запланированная задача {{ .name }} успешно завершена. Войдите для просмотра подробностей.'
# диск
DeviceNotFound: 'Устройство {{ .name }} не найдено'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için panelin
NodeExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın.'
LicenseExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın.'
SSHAndPanelLoginAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın.'
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: zamanlanmış görev {{ .name }} başarıyla tamamlandı. Ayrıntıları görmek için giriş yapın.'
# disk
DeviceNotFound: 'Cihaz {{ .name }} bulunamadı'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},詳情請登入
NodeExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個節點出現異常,詳情請登入面板檢視。'
LicenseExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個授權出現異常,詳情請登入面板檢視。'
SSHAndPanelLoginAlert: '您的 {{ .node }}{{ .ip }} 面板,來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常,詳情請登入面板檢視。'
CronJobSuccessAlert: '您的 {{ .node }}{{ .ip }} 面板,排程任務 {{ .name }} 執行成功,詳情請登入面板檢視。'
# 磁碟
DeviceNotFound: '裝置 {{ .name }} 未找到'
+1
View File
@@ -649,6 +649,7 @@ CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},请登录面板
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个节点存在异常,请登录面板查看详情。"
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个许可证存在异常,请登录面板查看详情。"
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,面板 {{ .name }} 登录 {{ .loginIp }} 异常,请登录面板查看详情。"
CronJobSuccessAlert: "您的 {{ .node }}{{ .ip }} 面板,计划任务-{{ .name }}执行成功,请登录面板查看详情。"
# 磁盘
DeviceNotFound: "设备 {{ .name }} 未找到"
+48 -7
View File
@@ -24,10 +24,10 @@ import (
"github.com/jinzhu/copier"
)
var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp"}
var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp", "syncIpGroup", "cleanLog"}
func CreateTaskScanEmailAlertLog(alert dto.AlertDTO, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo, emailConfig model.AlertConfig) error {
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
params := CreateTaskAlertParams(pushAlert)
alertDetail := ProcessAlertDetail(alert, pushAlert.TaskName, params, method)
alertRule := ProcessAlertRule(alert)
create.AlertRule = alertRule
@@ -76,14 +76,14 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
Encryption: emailInfo.Encryption,
Recipient: emailInfo.Recipient,
}
content := GetSendContent(alert.Type, params, agentInfo)
content := GetAlertLogContent(create, alert, params, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
}
msg := email.EmailMessage{
Subject: i18n.GetMsgByKey("PanelAlertTitle"),
Body: content,
IsHTML: true,
IsHTML: GetCronJobType(alert.Type) != "cronJob",
}
if err = email.SendMail(smtpConfig, msg, transport); err != nil {
@@ -110,7 +110,7 @@ func CreateBarkAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []
return SaveAlertLog(create, &alertLog)
}
content := GetSendContent(alert.Type, params, agentInfo)
content := GetAlertLogContent(create, alert, params, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
}
@@ -268,7 +268,7 @@ func ProcessAlertDetail(alert dto.AlertDTO, project string, params []dto.Param,
alertDetail := dto.AlertDetail{
Type: GetCronJobType(alert.Type),
SubType: alert.Type,
Title: alert.Title,
Title: TaskAlertTitle(alert.Type, alert.Title, project, params),
Method: method,
Project: project,
Params: params,
@@ -281,6 +281,14 @@ func ProcessAlertDetail(alert dto.AlertDTO, project string, params []dto.Param,
return string(marshal)
}
func TaskAlertTitle(alertType, title, project string, params []dto.Param) string {
if GetCronJobType(alertType) != "cronJob" || CronJobAlertResultFromParams(params) != CronJobAlertSuccess {
return title
}
name := cronJobTaskName(project, params)
return i18n.GetMsgWithMap("TaskSuccess", map[string]interface{}{"name": name})
}
func ProcessAlertRule(alert dto.AlertDTO) string {
marshal, err := json.Marshal(alert)
if err != nil {
@@ -324,6 +332,10 @@ func GetCronJobTypeName(cronJobType string) string {
module = "系统快照"
case "ntp":
module = "同步服务器时间"
case "syncIpGroup":
module = "同步 IP 组"
case "cleanLog":
module = "清理日志"
default:
}
return module
@@ -444,6 +456,30 @@ func isWithinTimeRange(savedTimeString string) bool {
}
func GetSendContent(alertType string, params []dto.Param, agentInfo *dto.AgentInfo) string {
return GetAlertDetailContent(dto.AlertDetail{Type: alertType, Params: params}, agentInfo)
}
func GetAlertLogContent(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, agentInfo *dto.AgentInfo) string {
detail := dto.AlertDetail{Type: alert.Type, Params: params}
var stored dto.AlertDetail
if json.Unmarshal([]byte(create.AlertDetail), &stored) == nil {
detail = stored
if detail.Type == "" {
detail.Type = alert.Type
}
if detail.Params == nil {
detail.Params = params
}
}
return GetAlertDetailContent(detail, agentInfo)
}
func GetAlertDetailContent(detail dto.AlertDetail, agentInfo *dto.AgentInfo) string {
alertType := detail.SubType
if alertType == "" {
alertType = detail.Type
}
params := detail.Params
switch GetCronJobType(alertType) {
case "ssl":
return i18n.GetMsgWithMap("SSLAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
@@ -464,7 +500,12 @@ func GetSendContent(alertType string, params []dto.Param, agentInfo *dto.AgentIn
case "disk":
return i18n.GetMsgWithMap("DiskUsedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "used": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "cronJob":
return i18n.GetMsgWithMap("CronJobFailedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
messageKey := "CronJobFailedAlert"
if CronJobAlertResultFromParams(params) == CronJobAlertSuccess {
messageKey = "CronJobSuccessAlert"
}
name := cronJobTaskName(detail.Project, params)
return i18n.GetMsgWithMap(messageKey, map[string]interface{}{"name": name, "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "clams":
return i18n.GetMsgWithMap("ClamAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "panelLogin":
+130
View File
@@ -0,0 +1,130 @@
package alert
import (
"encoding/json"
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
)
const (
CronJobAlertFailed = "failed"
CronJobAlertSuccess = "success"
CronJobAlertBoth = "both"
)
func cronJobAlertParams(advanced string) (map[string]json.RawMessage, error) {
params := make(map[string]json.RawMessage)
if strings.TrimSpace(advanced) != "" {
if err := json.Unmarshal([]byte(advanced), &params); err != nil {
return nil, fmt.Errorf("invalid cronjob alert advanced parameters: %w", err)
}
}
if params == nil {
params = make(map[string]json.RawMessage)
}
return params, nil
}
func CronJobAlertTriggerMode(advanced string) (string, error) {
params, err := cronJobAlertParams(advanced)
if err != nil {
return CronJobAlertFailed, err
}
mode := CronJobAlertFailed
if raw, ok := params["alertTriggerMode"]; ok {
if err := json.Unmarshal(raw, &mode); err != nil {
return CronJobAlertFailed, fmt.Errorf("invalid cronjob alert trigger mode: %w", err)
}
}
switch mode {
case CronJobAlertFailed, CronJobAlertSuccess, CronJobAlertBoth:
return mode, nil
default:
return CronJobAlertFailed, fmt.Errorf("invalid cronjob alert trigger mode %q", mode)
}
}
func MergeCronJobAlertParams(previous, incoming string) (string, error) {
params, err := cronJobAlertParams(previous)
if err != nil {
return "", err
}
updates, err := cronJobAlertParams(incoming)
if err != nil {
return "", err
}
for key, value := range updates {
params[key] = value
}
if _, ok := params["alertTriggerMode"]; !ok {
params["alertTriggerMode"] = json.RawMessage(`"failed"`)
}
data, err := json.Marshal(params)
if err != nil {
return "", err
}
if _, err := CronJobAlertTriggerMode(string(data)); err != nil {
return "", err
}
return string(data), nil
}
func MatchCronJobAlertResult(advanced, result string) bool {
mode, err := CronJobAlertTriggerMode(advanced)
if err != nil {
return false
}
if result == "" {
result = CronJobAlertFailed
}
if result != CronJobAlertFailed && result != CronJobAlertSuccess {
return false
}
return mode == CronJobAlertBoth || mode == result
}
func CreateTaskAlertParams(pushAlert dto.PushAlert) []dto.Param {
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
if GetCronJobType(pushAlert.AlertType) != "cronJob" {
return params
}
params = append(params, CreateCronJobResultParam(pushAlert.Result))
return params
}
func cronJobTaskName(project string, params []dto.Param) string {
if project != "" {
return project
}
if name := getValueByIndex(params, "taskName"); name != "" {
return name
}
return getValueByIndex(params, "1")
}
func CreateCronJobResultParam(result string) dto.Param {
value := "失败"
if result == CronJobAlertSuccess {
value = "成功"
}
return dto.Param{Index: "2", Key: "result", Value: value}
}
func CronJobAlertResultFromParams(params []dto.Param) string {
for _, param := range params {
if param.Index == "result" {
if param.Value == CronJobAlertSuccess {
return CronJobAlertSuccess
}
return CronJobAlertFailed
}
}
for _, param := range params {
if param.Index == "2" && param.Key == "result" && param.Value == "成功" {
return CronJobAlertSuccess
}
}
return CronJobAlertFailed
}
+2 -2
View File
@@ -46,7 +46,7 @@ func CreateTaskScanCustomWebhookAlertLog(
transport *http.Transport,
agentInfo *dto.AgentInfo,
) error {
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
params := CreateTaskAlertParams(pushAlert)
alertInfo := info
alertInfo.Type = alertType
create.Type = GetCronJobType(alertType)
@@ -99,7 +99,7 @@ func customWebhookTemplateData(rawDetail string, agentInfo *dto.AgentInfo, occur
if businessType == "" {
return webhook_sender.TemplateData{}, errors.New("resolve custom webhook alert detail failed")
}
content := GetSendContent(businessType, detail.Params, agentInfo)
content := GetAlertDetailContent(detail, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": detail.Title})
}
+4 -1
View File
@@ -26,6 +26,9 @@ func PushAlert(pushAlert dto.PushAlert) error {
}
var alert dto.AlertDTO
_ = copier.Copy(&alert, &alertInfo)
if alertUtil.GetCronJobType(pushAlert.AlertType) == "cronJob" && !alertUtil.MatchCronJobAlertResult(alert.AdvancedParams, pushAlert.Result) {
return nil
}
methods := strings.Split(alert.Method, ",")
for _, m := range methods {
@@ -126,7 +129,7 @@ func sendAlert(alertRepo repo.IAlertRepo, alert dto.AlertDTO, pushAlert dto.Push
}
transport := xpack.MultiNodeProvider.LoadRequestTransport()
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
params := alertUtil.CreateTaskAlertParams(pushAlert)
alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
alertRule := alertUtil.ProcessAlertRule(alert)
create.AlertRule = alertRule
+7 -1
View File
@@ -1,4 +1,5 @@
import { CommonModel, ReqPage } from '@/api/interface';
import type { CronjobAlertTriggerMode } from '@/utils/cronjob-alert';
export namespace Alert {
export interface AlertInfo extends CommonModel {
@@ -9,22 +10,25 @@ export namespace Alert {
interval: number;
method: string;
title: string;
taskName?: string;
project: string;
status: string;
sendCount: number;
sendMethod: string[];
advancedParams: string;
alertTriggerMode?: CronjobAlertTriggerMode;
createUser?: string;
updateUser?: string;
}
export interface AlertDetail {
type: string;
subType?: string;
licenseId: string;
title: string;
project: string;
method: string;
params: string;
params: { index: string; key: string; value: string }[] | string;
}
export interface AlertUpdateStatusReq {
@@ -74,6 +78,7 @@ export namespace Alert {
project: string;
status: string;
sendCount: number;
advancedParams?: string;
}
export interface AlertUpdateReq extends CommonModel {
@@ -86,6 +91,7 @@ export namespace Alert {
project: string;
status: string;
sendCount: number;
advancedParams?: string;
}
export interface DelReq {
+4
View File
@@ -1,4 +1,5 @@
import { ReqPage } from '.';
import type { CronjobAlertTriggerMode } from '@/utils/cronjob-alert';
export namespace Cronjob {
export interface Search extends ReqPage {
@@ -62,6 +63,7 @@ export namespace Cronjob {
secret: string;
hasAlert: boolean;
alertCount: number;
alertTriggerMode?: CronjobAlertTriggerMode;
alertTitle: string;
alertMethod: string;
alertMethodItems: Array<string>;
@@ -111,6 +113,7 @@ export namespace Cronjob {
secret: string;
alertCount: number;
alertTriggerMode?: CronjobAlertTriggerMode;
alertTitle: string;
alertMethod: string;
@@ -154,6 +157,7 @@ export namespace Cronjob {
downloadAccount: string;
alertCount: number;
alertTriggerMode?: CronjobAlertTriggerMode;
}
export interface TransHelper {
name: string;
+9
View File
@@ -6723,6 +6723,15 @@ const message = {
snapshot: 'System Snapshot',
clamsRulesHelper: 'Virus scanning tasks that require alerts',
cronJobRulesHelper: 'This type of scheduled task needs to be configured',
alertTriggerMode: 'Notify on',
alertTriggerFailed: 'Failure only',
alertTriggerSuccess: 'Success only',
alertTriggerBoth: 'Success or failure',
alertNotificationHelper:
'Notify after the final execution result, not during retries. Success and failure notifications share the daily limit and sending hours.',
notificationTitle: 'Scheduled task - {0} 「{1}」 execution notification',
notificationSuccessTitle: 'Scheduled task - {0} 「{1}」 completed successfully',
notificationRule: '{0}: {1}, up to {2} notifications per day',
clamsTitle: 'Virus scan task 「 {0} 」 detected infected file alert',
cronJobAppTitle: 'Cronjob - Backup App 「 {0} 」 Task Failure Alert',
cronJobWebsiteTitle: 'Cronjob - Backup Website「 {0} 」Task Failure Alert',
+9
View File
@@ -6835,6 +6835,15 @@ const message = {
snapshot: 'Snapshot del sistema',
clamsRulesHelper: 'Tareas de escaneo antivirus que requieren alerta',
cronJobRulesHelper: 'Este tipo de tareas programadas necesita configuración',
alertTriggerMode: 'Notificar cuando',
alertTriggerFailed: 'Solo falle',
alertTriggerSuccess: 'Solo tenga éxito',
alertTriggerBoth: 'Tenga éxito o falle',
alertNotificationHelper:
'Notificar al finalizar la ejecución, no durante los reintentos. Las notificaciones de éxito y fallo comparten el límite diario y el horario de envío.',
notificationTitle: 'Tarea programada - {0} 「{1}」 notificación de ejecución',
notificationSuccessTitle: 'Tarea programada - {0} 「{1}」 completada correctamente',
notificationRule: '{0}: {1}, hasta {2} notificaciones al día',
clamsTitle: 'Tarea antivirus 「 {0} 」 detectó archivo infectado',
cronJobAppTitle: 'Cronjob - Backup de app 「 {0} 」 falló',
cronJobWebsiteTitle: 'Cronjob - Backup de sitio 「 {0} 」 falló',
+9
View File
@@ -6677,6 +6677,15 @@ const message = {
snapshot: 'تصویر لحظه‌ای سیستم',
clamsRulesHelper: 'وظایف اسکن ویروس که نیاز به هشدار دارند',
cronJobRulesHelper: 'این نوع وظیفه زمان‌بندی باید پیکربندی شود',
alertTriggerMode: 'شرط اعلان',
alertTriggerFailed: 'فقط شکست',
alertTriggerSuccess: 'فقط موفقیت',
alertTriggerBoth: 'موفقیت یا شکست',
alertNotificationHelper:
'اعلان پس از نتیجه نهایی اجرا ارسال می‌شود، نه هنگام تلاش مجدد. اعلان‌های موفقیت و شکست سقف روزانه و ساعات ارسال مشترک دارند.',
notificationTitle: 'وظیفه زمان‌بندی - {0} «{1}» اعلان اجرا',
notificationSuccessTitle: 'وظیفه زمان‌بندی - {0} «{1}» با موفقیت انجام شد',
notificationRule: '{0}: {1}، حداکثر {2} اعلان در روز',
clamsTitle: 'وظیفه اسکن ویروس « {0} » هشدار فایل آلوده شناسایی شد',
cronJobAppTitle: 'وظیفه زمان‌بندی - پشتیبان‌گیری از برنامه « {0} » هشدار شکست وظیفه',
cronJobWebsiteTitle: 'وظیفه زمان‌بندی - پشتیبان‌گیری از وب‌سایت « {0} » هشدار شکست وظیفه',
+9
View File
@@ -6717,6 +6717,15 @@ const message = {
snapshot: 'システムスナップショット',
clamsRulesHelper: 'アラートが必要なウイルススキャンタスク',
cronJobRulesHelper: 'このタイプのスケジュールタスクには設定が必要です',
alertTriggerMode: '通知条件',
alertTriggerFailed: '失敗時のみ',
alertTriggerSuccess: '成功時のみ',
alertTriggerBoth: '成功時または失敗時',
alertNotificationHelper:
'再試行中ではなく、最終的な実行結果に基づいて通知します。成功と失敗の通知は、1日の上限と送信時間帯を共有します。',
notificationTitle: '定期タスク - {0}「{1}」実行通知',
notificationSuccessTitle: '定期タスク - {0}「{1}」が正常に完了しました',
notificationRule: '{0}:{1}、1日最大 {2} 回通知',
clamsTitle: 'ウイルススキャンタスク「{0}」が感染ファイルを検出したアラート',
cronJobAppTitle: 'Cronジョブ - アプリバックアップ「{0}」タスク失敗アラート',
cronJobWebsiteTitle: 'Cronジョブ - ウェブサイトバックアップ「{0}」タスク失敗アラート',
+9
View File
@@ -6582,6 +6582,15 @@ const message = {
snapshot: '시스템 스냅샷',
clamsRulesHelper: '알림이 필요한 바이러스 검사 작업',
cronJobRulesHelper: '이 유형의 예약된 작업은 구성해야 합니다',
alertTriggerMode: '알림 조건',
alertTriggerFailed: '실패 시에만',
alertTriggerSuccess: '성공 시에만',
alertTriggerBoth: '성공 또는 실패 시',
alertNotificationHelper:
'재시도 중이 아닌 최종 실행 결과에 따라 알립니다. 성공 및 실패 알림은 일일 한도와 전송 시간대를 공유합니다.',
notificationTitle: '예약 작업 - {0} 「{1}」 실행 알림',
notificationSuccessTitle: '예약 작업 - {0} 「{1}」 실행 성공',
notificationRule: '{0}: {1}, 하루 최대 {2}회 알림',
clamsTitle: '바이러스 검사 작업 「 {0} 」 감염된 파일 알림',
cronJobAppTitle: '크론 작업 - 백업 애플리케이션 「 {0} 」 작업 실패 알림',
cronJobWebsiteTitle: '크론 작업 - 백업 웹사이트「 {0} 」작업 실패 알림',
+9
View File
@@ -6493,6 +6493,15 @@ const message = {
snapshot: 'System Snapshot',
clamsRulesHelper: 'ງານສະແກນໄວຣັສທີ່ຕ້ອງການແຈ້ງເຕືອນ',
cronJobRulesHelper: 'ຕ້ອງຕັ້ງຄ່າງານ Cronjob ປະເພດນີ້',
alertTriggerMode: 'ເງື່ອນໄຂແຈ້ງເຕືອນ',
alertTriggerFailed: 'ເມື່ອລົ້ມເຫຼວເທົ່ານັ້ນ',
alertTriggerSuccess: 'ເມື່ອສຳເລັດເທົ່ານັ້ນ',
alertTriggerBoth: 'ເມື່ອສຳເລັດ ຫຼື ລົ້ມເຫຼວ',
alertNotificationHelper:
'ແຈ້ງເຕືອນຕາມຜົນສຸດທ້າຍ ບໍ່ແມ່ນລະຫວ່າງລອງໃໝ່. ການແຈ້ງເຕືອນສຳເລັດ ແລະ ລົ້ມເຫຼວໃຊ້ຂີດຈຳກັດລາຍວັນ ແລະ ເວລາສົ່ງຮ່ວມກັນ.',
notificationTitle: 'ວຽກຕາມກຳນົດເວລາ - {0} 「{1}」 ແຈ້ງເຕືອນການເຮັດວຽກ',
notificationSuccessTitle: 'ວຽກຕາມກຳນົດເວລາ - {0} 「{1}」 ສຳເລັດແລ້ວ',
notificationRule: '{0}: {1}, ສູງສຸດ {2} ຄັ້ງຕໍ່ມື້',
clamsTitle: 'ງານສະແກນໄວຣັສ 「 {0} 」 ກວດພົບໄຟລ໌ອັນຕະລາຍ',
cronJobAppTitle: 'ແຈ້ງເຕືອນງານສຳຮອງແອັບ 「 {0} 」 ລົ້ມເຫຼວ',
cronJobWebsiteTitle: 'ແຈ້ງເຕືອນງານສຳຮອງເວັບໄຊ 「 {0} 」 ລົ້ມເຫຼວ',
+9
View File
@@ -6833,6 +6833,15 @@ const message = {
snapshot: 'Snapshot Sistem',
clamsRulesHelper: 'Tugas imbasan virus yang memerlukan amaran',
cronJobRulesHelper: 'Jenis tugas berjadual ini perlu dikonfigurasikan',
alertTriggerMode: 'Syarat pemberitahuan',
alertTriggerFailed: 'Gagal sahaja',
alertTriggerSuccess: 'Berjaya sahaja',
alertTriggerBoth: 'Berjaya atau gagal',
alertNotificationHelper:
'Maklumkan selepas keputusan akhir pelaksanaan, bukan semasa percubaan semula. Pemberitahuan berjaya dan gagal berkongsi had harian dan waktu penghantaran.',
notificationTitle: 'Tugas berjadual - {0} 「{1}」 pemberitahuan pelaksanaan',
notificationSuccessTitle: 'Tugas berjadual - {0} 「{1}」 berjaya diselesaikan',
notificationRule: '{0}: {1}, sehingga {2} pemberitahuan sehari',
clamsTitle: 'Tugas imbasan virus 「 {0} 」 mengesan amaran fail dijangkiti',
cronJobAppTitle: 'Cronjob - Sandaran Aplikasi 「 {0} 」 Amaran Kegagalan Tugas',
cronJobWebsiteTitle: 'Cronjob - Sandaran Laman Web 「 {0} 」 Amaran Kegagalan Tugas',
+9
View File
@@ -6867,6 +6867,15 @@ const message = {
snapshot: 'Instantâneo do Sistema',
clamsRulesHelper: 'Tarefas de varredura de vírus que exigem alertas por',
cronJobRulesHelper: 'Este tipo de tarefa cron precisa ser configurado',
alertTriggerMode: 'Notificar em caso de',
alertTriggerFailed: 'Apenas falha',
alertTriggerSuccess: 'Apenas sucesso',
alertTriggerBoth: 'Sucesso ou falha',
alertNotificationHelper:
'Notificar após o resultado final, não durante as novas tentativas. As notificações de sucesso e falha compartilham o limite diário e o horário de envio.',
notificationTitle: 'Tarefa agendada - {0} 「{1}」 notificação de execução',
notificationSuccessTitle: 'Tarefa agendada - {0} 「{1}」 concluída com sucesso',
notificationRule: '{0}: {1}, até {2} notificações por dia',
clamsTitle: 'Tarefa de Varredura de Vírus 「 {0} 」 detectou arquivo infectado',
cronJobAppTitle: 'Tarefa Cron - Falha no Backup do Aplicativo 「 {0} 」',
cronJobWebsiteTitle: 'Tarefa Cron - Falha no Backup do Site 「 {0} 」',
+9
View File
@@ -6835,6 +6835,15 @@ const message = {
snapshot: 'Системный снимок',
clamsRulesHelper: 'Задачи сканирования на вирусы, требующие уведомлений',
cronJobRulesHelper: 'Этот тип планируемой задачи требует конфигурации',
alertTriggerMode: 'Условие уведомления',
alertTriggerFailed: 'Только при ошибке',
alertTriggerSuccess: 'Только при успехе',
alertTriggerBoth: 'При успехе или ошибке',
alertNotificationHelper:
'Уведомление отправляется по окончательному результату, а не во время повторных попыток. Уведомления об успехе и ошибке используют общий дневной лимит и часы отправки.',
notificationTitle: 'Плановая задача - {0} «{1}»: уведомление о выполнении',
notificationSuccessTitle: 'Плановая задача - {0} «{1}» успешно завершена',
notificationRule: '{0}: {1}, до {2} уведомлений в день',
clamsTitle: 'Задача сканирования на вирусы 「 {0} 」 обнаружила заражённый файл',
cronJobAppTitle: 'Задача Cron - ошибка задачи резервного копирования приложения 「 {0} 」',
cronJobWebsiteTitle: 'Задача Cron - ошибка задачи резервного копирования сайта 「 {0} 」',
+9
View File
@@ -6841,6 +6841,15 @@ const message = {
snapshot: 'Sistem Anlık Görüntüsü',
clamsRulesHelper: 'Uyarısı gerektiren virüs tarama görevleri',
cronJobRulesHelper: 'Bu tür zamanlanmış görevlerin yapılandırılması gerekir',
alertTriggerMode: 'Bildirim koşulu',
alertTriggerFailed: 'Yalnızca başarısızlık',
alertTriggerSuccess: 'Yalnızca başarı',
alertTriggerBoth: 'Başarı veya başarısızlık',
alertNotificationHelper:
'Yeniden denemeler sırasında değil, son yürütme sonucuna göre bildirim gönderilir. Başarı ve başarısızlık bildirimleri günlük sınırı ve gönderim saatlerini paylaşır.',
notificationTitle: 'Zamanlanmış görev - {0} 「{1}」 yürütme bildirimi',
notificationSuccessTitle: 'Zamanlanmış görev - {0} 「{1}」 başarıyla tamamlandı',
notificationRule: '{0}: {1}, günde en fazla {2} bildirim',
clamsTitle: 'Virüs tarama görevi 「 {0} 」 enfekte dosya algıladı uyarısı',
cronJobAppTitle: 'Zamanlanmış Görev - Yedekleme Uygulaması 「 {0} 」 Görev Başarısızlık Uyarısı',
cronJobWebsiteTitle: 'Zamanlanmış Görev - Yedekleme Web Sitesi 「 {0} 」 Görev Başarısızlık Uyarısı',
+9
View File
@@ -6238,6 +6238,15 @@ const message = {
snapshot: '系統快照',
clamsRulesHelper: '需要開啟告警的病毒掃描任務',
cronJobRulesHelper: '需要設定此類型的計劃任務',
alertTriggerMode: '通知條件',
alertTriggerFailed: '僅失敗',
alertTriggerSuccess: '僅成功',
alertTriggerBoth: '成功或失敗',
alertNotificationHelper:
'任務最終執行完成後按結果通知,重試過程中不發送;成功與失敗共用每日次數及發送時段。',
notificationTitle: '計劃任務-{0}「{1}」執行告警',
notificationSuccessTitle: '計劃任務-{0}「{1}」執行成功通知',
notificationRule: '{0}:{1},每天最多發送 {2} 次',
clamsTitle: '病毒掃描「{0}」任務偵測到感染檔案告警',
cronJobAppTitle: '計劃任務-備份應用「{0}」任務失敗告警',
cronJobWebsiteTitle: '計劃任務-備份網站「{0}」任務失敗告警',
+9
View File
@@ -6294,6 +6294,15 @@ const message = {
snapshot: '系统快照',
clamsRulesHelper: '需要开启告警通知的病毒扫描任务',
cronJobRulesHelper: '需要配置此类型的计划任务',
alertTriggerMode: '通知条件',
alertTriggerFailed: '仅失败',
alertTriggerSuccess: '仅成功',
alertTriggerBoth: '成功或失败',
alertNotificationHelper:
'任务最终执行完成后按结果通知,重试过程中不发送;成功与失败共用每日次数及发送时段。',
notificationTitle: '计划任务-{0}「{1}」执行告警',
notificationSuccessTitle: '计划任务-{0}「{1}」执行成功通知',
notificationRule: '{0}:{1},每天最多发送 {2} 次',
clamsTitle: '病毒扫描「 {0} 」任务检测到感染文件告警',
cronJobAppTitle: '计划任务-备份应用「 {0} 」任务失败告警',
cronJobWebsiteTitle: '计划任务-备份网站「 {0} 」任务失败告警',
+74
View File
@@ -0,0 +1,74 @@
export type CronjobAlertTriggerMode = 'failed' | 'success' | 'both';
export const cronjobAlertTypes = [
'shell',
'app',
'website',
'database',
'directory',
'log',
'snapshot',
'curl',
'cutWebsiteLog',
'clean',
'ntp',
'syncIpGroup',
'cleanLog',
];
export const cronjobAlertModes = [
{ value: 'failed', label: 'xpack.alert.alertTriggerFailed' },
{ value: 'success', label: 'xpack.alert.alertTriggerSuccess' },
{ value: 'both', label: 'xpack.alert.alertTriggerBoth' },
] as const;
export const normalizeCronjobAlertMode = (value: unknown): CronjobAlertTriggerMode => {
return value === 'success' || value === 'both' ? value : 'failed';
};
const parseAdvancedParams = (value?: string): Record<string, unknown> => {
try {
const params = JSON.parse(value || '{}');
return params && typeof params === 'object' && !Array.isArray(params) ? params : {};
} catch {
return {};
}
};
export const getCronjobAlertMode = (advancedParams?: string): CronjobAlertTriggerMode => {
return normalizeCronjobAlertMode(parseAdvancedParams(advancedParams).alertTriggerMode);
};
export const setCronjobAlertMode = (advancedParams: string | undefined, mode: CronjobAlertTriggerMode): string => {
return JSON.stringify({ ...parseAdvancedParams(advancedParams), alertTriggerMode: mode });
};
export const getCronjobAlertModeLabel = (mode: unknown): string => {
return cronjobAlertModes.find((item) => item.value === normalizeCronjobAlertMode(mode))!.label;
};
export const getCronjobAlertTitle = (
row: { type: string; subType?: string; taskName?: string; title: string },
renderTitle: (type: string, taskName: string) => string,
): string => {
const type = row.type === 'cronJob' ? row.subType : row.type;
if (type && cronjobAlertTypes.includes(type) && typeof row.taskName === 'string' && row.taskName.length > 0) {
return renderTitle(type, row.taskName);
}
return row.title;
};
export const getCronjobAlertResult = (params: unknown): 'success' | 'failed' => {
if (typeof params === 'string') {
try {
params = JSON.parse(params);
} catch {
return 'failed';
}
}
if (!Array.isArray(params)) return 'failed';
const result = params.find((item) => item?.index === 'result');
if (result) return result.value === 'success' ? 'success' : 'failed';
const numberedResult = params.find((item) => item?.index === '2' && item?.key === 'result');
return numberedResult?.value === '成功' ? 'success' : 'failed';
};
@@ -689,10 +689,10 @@
<el-card class="mt-5">
<el-row :gutter="20">
<LayoutCol :span="8">
<LayoutCol :span="20">
<el-form-item prop="hasAlert">
<el-checkbox v-model="form.hasAlert" :label="$t('xpack.alert.isAlert')" />
<span class="input-help">{{ $t('xpack.alert.cronJobHelper') }}</span>
<span class="input-help">{{ $t('xpack.alert.alertNotificationHelper') }}</span>
<span class="input-help logText" v-if="form.hasAlert && !isProductPro">
{{ $t('xpack.alert.licenseHelper') }}
@@ -703,6 +703,20 @@
</el-form-item>
</LayoutCol>
</el-row>
<el-row :gutter="20" v-if="form.hasAlert">
<LayoutCol>
<el-form-item :label="$t('xpack.alert.alertTriggerMode')" prop="alertTriggerMode">
<el-select class="selectClass" v-model="form.alertTriggerMode">
<el-option
v-for="item in cronjobAlertModes"
:key="item.value"
:value="item.value"
:label="$t(item.label)"
/>
</el-select>
</el-form-item>
</LayoutCol>
</el-row>
<el-row :gutter="20">
<LayoutCol>
<el-form-item
@@ -871,6 +885,7 @@ import { getGroupList } from '@/api/modules/group';
import { routerToName, routerToPath } from '@/utils/router';
import { loadBaseDir } from '@/api/modules/setting';
import { getAlertConfigDisplayName } from '@/views/setting/alert/setting/drawer/secret-field';
import { cronjobAlertModes, normalizeCronjobAlertMode } from '@/utils/cronjob-alert';
const router = useRouter();
const { docsUrl, isFxplay, isProductPro } = useGlobalStore();
@@ -1034,6 +1049,7 @@ const form = reactive<Cronjob.CronjobInfo>({
secret: '',
hasAlert: false,
alertCount: 3,
alertTriggerMode: 'failed',
alertTitle: '',
alertMethod: '',
alertMethodItems: [],
@@ -1133,6 +1149,7 @@ const search = async () => {
form.secret = res.data.secret;
form.hasAlert = res.data.alertCount > 0;
form.alertCount = res.data.alertCount || 3;
form.alertTriggerMode = normalizeCronjobAlertMode(res.data.alertTriggerMode);
form.alertTitle = res.data.alertTitle;
if (res.data.alertMethod) {
form.alertMethodItems = normalizeAlertMethodItems(res.data.alertMethod.split(',') || []);
@@ -1671,7 +1688,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
form.alertCount = form.hasAlert ? form.alertCount : 0;
form.alertMethod = form.alertMethodItems.join(',');
form.alertTitle = form.hasAlert
? i18n.global.t('cronjob.alertTitle', [i18n.global.t('cronjob.' + form.type), form.name])
? i18n.global.t('xpack.alert.notificationTitle', [i18n.global.t('cronjob.' + form.type), form.name])
: '';
if (!form) return;
+21 -11
View File
@@ -84,6 +84,7 @@
<el-table-column
:label="$t('commons.table.title')"
prop="title"
:formatter="formatTitle"
min-width="300px"
show-overflow-tooltip
></el-table-column>
@@ -167,6 +168,12 @@ import { ElMessageBox } from 'element-plus';
import AddTask from '@/views/setting/alert/dash/task/index.vue';
import { Alert } from '@/api/interface/alert';
import { UpdateAlertStatus, SearchAlerts, DeleteAlert, PageAlertConfigs } from '@/api/modules/alert';
import {
cronjobAlertTypes,
getCronjobAlertMode,
getCronjobAlertModeLabel,
getCronjobAlertTitle,
} from '@/utils/cronjob-alert';
const { isMobile, isMaster, isProductPro, isEE } = useGlobalStore();
@@ -238,7 +245,21 @@ const changeSort = ({ prop, order }) => {
search();
};
const formatTitle = (row: Alert.AlertInfo) => {
return getCronjobAlertTitle(row, (type, taskName) =>
t('xpack.alert.notificationTitle', [t('cronjob.' + type), taskName]),
);
};
const formatRule = (row: Alert.AlertInfo) => {
const type = row.type === 'cronJob' ? row.subType : row.type;
if (cronjobAlertTypes.includes(type)) {
return t('xpack.alert.notificationRule', [
t('cronjob.' + type),
t(getCronjobAlertModeLabel(getCronjobAlertMode(row.advancedParams))),
row.sendCount,
]);
}
const ruleTemplates = {
ssl: () => t('xpack.alert.timeRule', [row.cycle, row.sendCount]),
siteEndTime: () => t('xpack.alert.timeRule', [row.cycle, row.sendCount]),
@@ -253,17 +274,6 @@ const formatRule = (row: Alert.AlertInfo) => {
: t('xpack.alert.diskRule', [row.project, row.count, row.cycle === 1 ? 'G' : '%', row.sendCount]);
},
clams: () => t('xpack.alert.clamsRule', [row.sendCount]),
app: () => t('xpack.alert.cronJobAppRule', [row.sendCount]),
website: () => t('xpack.alert.cronJobWebsiteRule', [row.sendCount]),
database: () => t('xpack.alert.cronJobDatabaseRule', [row.sendCount]),
directory: () => t('xpack.alert.cronJobDirectoryRule', [row.sendCount]),
log: () => t('xpack.alert.cronJobLogRule', [row.sendCount]),
snapshot: () => t('xpack.alert.cronJobSnapshotRule', [row.sendCount]),
shell: () => t('xpack.alert.cronJobShellRule', [row.sendCount]),
curl: () => t('xpack.alert.cronJobCurlRule', [row.sendCount]),
cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogRule', [row.sendCount]),
clean: () => t('xpack.alert.cronJobCleanRule', [row.sendCount]),
ntp: () => t('xpack.alert.cronJobNtpRule', [row.sendCount]),
nodeException: () => t('xpack.alert.nodeExceptionRule', [row.sendCount]),
licenseException: () => t('xpack.alert.licenseExceptionRule', [row.sendCount]),
panelLogin: () => t('xpack.alert.panelLoginRule', [row.sendCount]),
@@ -60,6 +60,8 @@
<el-option value="clean" :label="$t('setting.diskClean')" />
<el-option value="snapshot" :label="$t('cronjob.snapshot')" />
<el-option value="ntp" :label="$t('cronjob.ntp')" />
<el-option value="syncIpGroup" :label="$t('cronjob.syncIpGroup')" />
<el-option value="cleanLog" :label="$t('cronjob.cleanLog')" />
</el-select>
</el-form-item>
@@ -305,6 +307,22 @@
<span class="input-help">{{ $t('xpack.alert.ipWhiteListHelper') }}</span>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'cronJob'"
:label="$t('xpack.alert.alertTriggerMode')"
prop="alertTriggerMode"
>
<el-select class="selectClass" v-model="dialogData.rowData!.alertTriggerMode">
<el-option
v-for="item in cronjobAlertModes"
:key="item.value"
:value="item.value"
:label="$t(item.label)"
/>
</el-select>
<span class="input-help">{{ $t('xpack.alert.alertNotificationHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('xpack.alert.sendCount')" prop="sendCount">
<el-input v-model.number="dialogData.rowData!.sendCount" />
<span class="input-help">
@@ -398,6 +416,7 @@ import { routerToName } from '@/utils/router';
import { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/validate';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { getAlertConfigDisplayName } from '@/views/setting/alert/setting/drawer/secret-field';
import { cronjobAlertTypes, cronjobAlertModes, getCronjobAlertMode, setCronjobAlertMode } from '@/utils/cronjob-alert';
const { isMaster, isProductPro, isEE, isIntl } = useGlobalStore();
@@ -524,19 +543,7 @@ const noParamTypes = ['panelUpdate'];
const intervalTypes = ['cpu', 'memory', 'load', 'disk', 'sshLogin', 'panelLogin', 'nodeException', 'licenseException'];
const diskTypes = ['disk'];
const cronjobTypes = [
'shell',
'app',
'website',
'database',
'directory',
'log',
'snapshot',
'curl',
'cutWebsiteLog',
'clean',
'ntp',
];
const cronjobTypes = cronjobAlertTypes;
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
@@ -556,6 +563,7 @@ const acceptParams = (params: DialogProps): void => {
dialogData.value.rowData.subType = dialogData.value.rowData.type;
dialogData.value.rowData.type = 'cronJob';
}
dialogData.value.rowData.alertTriggerMode = getCronjobAlertMode(dialogData.value.rowData.advancedParams);
initOptions(dialogData.value.rowData.type, dialogData.value.rowData.subType);
visible.value = true;
};
@@ -655,6 +663,7 @@ function checkSendCount(rule: any, value: any, callback: any) {
}
function checkIPs(rule: any, value: any, callback: any) {
if (!ipTypes.includes(dialogData.value.rowData.type)) return callback();
if (typeof value === 'string' && value.trim() !== '') {
let addr = value.split('\n');
for (const item of addr) {
@@ -793,6 +802,12 @@ const changeType = () => {
type = subType;
rowData.subType = subType;
}
if (cronjobTypes.includes(type)) {
rowData.project = '';
rowData.cycle = 0;
rowData.count = 0;
rowData.alertTriggerMode = rowData.alertTriggerMode || 'failed';
}
rowData.project = typeof typeToProjectMap[type] !== 'undefined' ? typeToProjectMap[type] : rowData.project;
rowData.cycle = typeof typeToCycleMap[type] !== 'undefined' ? typeToCycleMap[type] : rowData.cycle;
rowData.count = typeof typeToCountMap[type] !== 'undefined' ? typeToCountMap[type] : rowData.count;
@@ -838,12 +853,13 @@ const loadCronJob = async (jobType: string) => {
status: '',
});
cronJobOptions.value = res.data || [];
dialogData.value.rowData.project = dialogData.value.rowData.project || String(cronJobOptions.value[0].id);
dialogData.value.rowData.project = dialogData.value.rowData.project || String(cronJobOptions.value[0]?.id || '');
};
const formatTitle = (row: Alert.AlertInfo) => {
if (row.type === 'cronJob') {
row.type = row.subType;
const type = row.type === 'cronJob' ? row.subType : row.type;
if (cronjobTypes.includes(type)) {
return t('xpack.alert.notificationTitle', [t('cronjob.' + type), formatCronJobName(Number(row.project))]);
}
const titleTemplates = {
ssl: () => {
@@ -865,17 +881,6 @@ const formatTitle = (row: Alert.AlertInfo) => {
return row.project === 'all' ? t('xpack.alert.allDiskTitle') : t('xpack.alert.diskTitle', [row.project]);
},
clams: () => t('xpack.alert.clamsTitle', [formatClamName(Number(row.project))]),
app: () => t('xpack.alert.cronJobAppTitle', [formatCronJobName(Number(row.project))]),
website: () => t('xpack.alert.cronJobWebsiteTitle', [formatCronJobName(Number(row.project))]),
database: () => t('xpack.alert.cronJobDatabaseTitle', [formatCronJobName(Number(row.project))]),
directory: () => t('xpack.alert.cronJobDirectoryTitle', [formatCronJobName(Number(row.project))]),
log: () => t('xpack.alert.cronJobLogTitle', [formatCronJobName(Number(row.project))]),
snapshot: () => t('xpack.alert.cronJobSnapshotTitle', [formatCronJobName(Number(row.project))]),
shell: () => t('xpack.alert.cronJobShellTitle', [formatCronJobName(Number(row.project))]),
curl: () => t('xpack.alert.cronJobCurlTitle', [formatCronJobName(Number(row.project))]),
cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogTitle', [formatCronJobName(Number(row.project))]),
clean: () => t('xpack.alert.cronJobCleanTitle', [formatCronJobName(Number(row.project))]),
ntp: () => t('xpack.alert.cronJobNtpTitle', [formatCronJobName(Number(row.project))]),
nodeException: () => t('xpack.alert.nodeException'),
licenseException: () => t('xpack.alert.licenseException'),
panelLogin: () => t('xpack.alert.panelLogin'),
@@ -909,25 +914,31 @@ const emit = defineEmits<{ (e: 'search'): void }>();
const onSubmit = async (formEl: FormInstance | undefined) => {
if (loading.value) return;
loading.value = true;
if (!formEl) return;
loading.value = true;
await formEl.validate(async (valid) => {
if (!valid) return;
if (!dialogData.value.rowData) return;
if (!valid || !dialogData.value.rowData) {
loading.value = false;
return;
}
const sendMethods = dialogData.value.rowData.sendMethod.includes(ALL_SEND_METHOD)
? allConfigValues.value
: dialogData.value.rowData.sendMethod;
dialogData.value.rowData.method = sendMethods.join(',');
dialogData.value.rowData.title = formatTitle(dialogData.value.rowData);
if (dialogData.value.rowData.type === 'cronJob') {
dialogData.value.rowData.type = dialogData.value.rowData.subType;
const payload = {
...dialogData.value.rowData,
method: sendMethods.join(','),
title: formatTitle(dialogData.value.rowData),
};
if (payload.type === 'cronJob') {
payload.type = payload.subType;
payload.advancedParams = setCronjobAlertMode(payload.advancedParams, payload.alertTriggerMode || 'failed');
}
try {
if (dialogData.value.title === 'create') {
await CreateAlert(dialogData.value.rowData);
await CreateAlert(payload);
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
} else if (dialogData.value.title === 'edit') {
await UpdateAlert(dialogData.value.rowData);
await UpdateAlert(payload);
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
}
emit('search');
+10 -13
View File
@@ -99,6 +99,7 @@ import {
import { ElMessageBox } from 'element-plus';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { getAlertConfigDisplayName } from '@/views/setting/alert/setting/drawer/secret-field';
import { cronjobAlertTypes, getCronjobAlertResult } from '@/utils/cronjob-alert';
const { isMobile, isProductPro, isIntl, isMaster } = useGlobalStore();
const { t } = i18n.global;
@@ -193,7 +194,15 @@ const syncAlert = (row: Alert.AlertLog) => {
});
};
const formatMessage = (row: Alert.AlertInfo) => {
const formatMessage = (row: Alert.AlertDetail) => {
const type = row.type === 'cronJob' ? row.subType : row.type;
if (cronjobAlertTypes.includes(type)) {
const title =
getCronjobAlertResult(row.params) === 'success'
? 'xpack.alert.notificationSuccessTitle'
: 'cronjob.alertTitle';
return t(title, [t('cronjob.' + type), row.project]);
}
const messageTemplates = {
ssl: () => {
return row.project === 'all' ? t('xpack.alert.allSslTitle') : t('xpack.alert.sslTitle', [row.project]);
@@ -212,17 +221,6 @@ const formatMessage = (row: Alert.AlertInfo) => {
return row.project === 'all' ? t('xpack.alert.allDiskTitle') : t('xpack.alert.diskTitle', [row.project]);
},
clams: () => t('xpack.alert.clamsTitle', [row.project]),
app: () => t('xpack.alert.cronJobAppTitle', [row.project]),
website: () => t('xpack.alert.cronJobWebsiteTitle', [row.project]),
database: () => t('xpack.alert.cronJobDatabaseTitle', [row.project]),
directory: () => t('xpack.alert.cronJobDirectoryTitle', [row.project]),
log: () => t('xpack.alert.cronJobLogTitle', [row.project]),
snapshot: () => t('xpack.alert.cronJobSnapshotTitle', [row.project]),
shell: () => t('xpack.alert.cronJobShellTitle', [row.project]),
curl: () => t('xpack.alert.cronJobCurlTitle', [row.project]),
cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogTitle', [row.project]),
clean: () => t('xpack.alert.cronJobCleanTitle', [row.project]),
ntp: () => t('xpack.alert.cronJobNtpTitle', [row.project]),
nodeException: () => t('xpack.alert.nodeException'),
licenseException: () => t('xpack.alert.licenseException'),
panelLogin: () => t('xpack.alert.panelLogin'),
@@ -230,7 +228,6 @@ const formatMessage = (row: Alert.AlertInfo) => {
panelIpLogin: () => t('xpack.alert.panelIpLogin'),
sshIpLogin: () => t('xpack.alert.sshIpLogin'),
};
let type = row.type === 'cronJob' ? row.subType : row.type;
return messageTemplates[type] ? messageTemplates[type]() : '';
};
@@ -78,7 +78,6 @@
<template v-else-if="form.type === 'sms'">
<el-form-item :label="$t('xpack.alert.displayName')" prop="smsDisplayName">
<el-input v-model.trim="form.smsDisplayName" />
<span class="input-help">{{ $t('xpack.alert.displayNameHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('xpack.alert.phone')" prop="smsPhone">
<el-input clearable v-model.trim="form.smsPhone" />