From acf94b052bb43596201938d3c3648c41a39b8ec6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=98=AD?= <81747598+lan-yonghui@users.noreply.github.com>
Date: Mon, 14 Oct 2024 17:51:17 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=97=85=E6=AF=92?=
=?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=92=8C=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1?=
=?UTF-8?q?=E5=91=8A=E8=AD=A6=20(#6709)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
backend/app/dto/alert.go | 20 ++++++
backend/app/dto/clam.go | 5 ++
backend/app/dto/cronjob.go | 6 ++
backend/app/service/clam.go | 69 +++++++++++++++++++
backend/app/service/cronjob.go | 48 ++++++++++++-
backend/app/service/cronjob_helper.go | 22 +++++-
backend/utils/xpack/xpack.go | 21 ++++++
frontend/src/api/interface/cronjob.ts | 3 +
frontend/src/api/interface/toolbox.ts | 3 +
frontend/src/lang/modules/en.ts | 11 +++
frontend/src/lang/modules/tw.ts | 11 +++
frontend/src/lang/modules/zh.ts | 11 +++
frontend/src/views/cronjob/operate/index.vue | 64 +++++++++++++++++
frontend/src/views/toolbox/clam/index.vue | 2 +-
.../src/views/toolbox/clam/operate/index.vue | 50 ++++++++++++++
15 files changed, 343 insertions(+), 3 deletions(-)
create mode 100644 backend/app/dto/alert.go
diff --git a/backend/app/dto/alert.go b/backend/app/dto/alert.go
new file mode 100644
index 000000000..6cf8d7829
--- /dev/null
+++ b/backend/app/dto/alert.go
@@ -0,0 +1,20 @@
+package dto
+
+type CreateOrUpdateAlert struct {
+ AlertTitle string `json:"alertTitle"`
+ AlertType string `json:"alertType"`
+ AlertCount uint `json:"alertCount"`
+ EntryID uint `json:"entryID"`
+}
+
+type AlertBase struct {
+ AlertType string `json:"alertType"`
+ EntryID uint `json:"entryID"`
+}
+
+type PushAlert struct {
+ TaskName string `json:"taskName"`
+ AlertType string `json:"alertType"`
+ EntryID uint `json:"entryID"`
+ Param string `json:"param"`
+}
diff --git a/backend/app/dto/clam.go b/backend/app/dto/clam.go
index 964db15ee..86c01d643 100644
--- a/backend/app/dto/clam.go
+++ b/backend/app/dto/clam.go
@@ -33,6 +33,7 @@ type ClamInfo struct {
LastHandleDate string `json:"lastHandleDate"`
Spec string `json:"spec"`
Description string `json:"description"`
+ AlertCount uint `json:"alertCount"`
}
type ClamLogSearch struct {
@@ -71,6 +72,8 @@ type ClamCreate struct {
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Description string `json:"description"`
+ AlertCount uint `json:"alertCount"`
+ AlertTitle string `json:"alertTitle"`
}
type ClamUpdate struct {
@@ -82,6 +85,8 @@ type ClamUpdate struct {
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Description string `json:"description"`
+ AlertCount uint `json:"alertCount"`
+ AlertTitle string `json:"alertTitle"`
}
type ClamUpdateStatus struct {
diff --git a/backend/app/dto/cronjob.go b/backend/app/dto/cronjob.go
index 9189a7e09..49e7cd5da 100644
--- a/backend/app/dto/cronjob.go
+++ b/backend/app/dto/cronjob.go
@@ -31,10 +31,13 @@ type CronjobCreate struct {
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
+ AlertCount uint `json:"alertCount"`
+ AlertTitle string `json:"alertTitle"`
}
type CronjobUpdate struct {
ID uint `json:"id" validate:"required"`
+ Type string `json:"type" validate:"required"`
Name string `json:"name" validate:"required"`
Spec string `json:"spec" validate:"required"`
@@ -53,6 +56,8 @@ type CronjobUpdate struct {
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
+ AlertCount uint `json:"alertCount"`
+ AlertTitle string `json:"alertTitle"`
}
type CronjobUpdateStatus struct {
@@ -99,6 +104,7 @@ type CronjobInfo struct {
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
+ AlertCount uint `json:"alertCount"`
}
type SearchRecord struct {
diff --git a/backend/app/service/clam.go b/backend/app/service/clam.go
index d96c0dc0b..5abee657a 100644
--- a/backend/app/service/clam.go
+++ b/backend/app/service/clam.go
@@ -8,6 +8,7 @@ import (
"path"
"path/filepath"
"sort"
+ "strconv"
"strings"
"time"
@@ -20,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
+ "github.com/1Panel-dev/1Panel/backend/xpack/utils/alert"
"github.com/jinzhu/copier"
"github.com/robfig/cron/v3"
@@ -154,6 +156,16 @@ func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interfa
}
datas[i].LastHandleDate = t1.Format(constant.DateTimeLayout)
}
+ alertBase := dto.AlertBase{
+ AlertType: "clams",
+ EntryID: datas[i].ID,
+ }
+ alertCount := xpack.GetAlert(alertBase)
+ if alertCount != 0 {
+ datas[i].AlertCount = alertCount
+ } else {
+ datas[i].AlertCount = 0
+ }
}
return total, datas, err
}
@@ -180,6 +192,19 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
if err := clamRepo.Create(&clam); err != nil {
return err
}
+
+ if req.AlertCount != 0 {
+ createAlert := dto.CreateOrUpdateAlert{
+ AlertTitle: req.AlertTitle,
+ AlertCount: req.AlertCount,
+ AlertType: "clams",
+ EntryID: clam.ID,
+ }
+ err := xpack.CreateAlert(createAlert)
+ if err != nil {
+ return err
+ }
+ }
return nil
}
@@ -225,6 +250,16 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
if err := clamRepo.Update(req.ID, upMap); err != nil {
return err
}
+ updateAlert := dto.CreateOrUpdateAlert{
+ AlertTitle: req.AlertTitle,
+ AlertType: "clams",
+ AlertCount: req.AlertCount,
+ EntryID: clam.ID,
+ }
+ err := xpack.UpdateAlert(updateAlert)
+ if err != nil {
+ return err
+ }
return nil
}
@@ -265,6 +300,14 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
if err := clamRepo.Delete(commonRepo.WithByID(id)); err != nil {
return err
}
+ alertBase := dto.AlertBase{
+ AlertType: "clams",
+ EntryID: clam.ID,
+ }
+ err := xpack.DeleteAlert(alertBase)
+ if err != nil {
+ return err
+ }
}
return nil
}
@@ -305,6 +348,7 @@ func (c *ClamService) HandleOnce(req dto.OperateByID) error {
}
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
stdout, err := cmd.Execf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
+ handleAlert(stdout, clam.Name, clam.ID)
if err != nil {
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
}
@@ -585,3 +629,28 @@ func (c *ClamService) loadLogPath(name string) string {
return ""
}
+
+func handleAlert(stdout, clamName string, clamId uint) {
+ if strings.Contains(stdout, "- SCAN SUMMARY -") {
+ lines := strings.Split(stdout, "\n")
+ for _, line := range lines {
+ if strings.HasPrefix(line, "Infected files:") {
+ var infectedFiles = 0
+ infectedFiles, _ = strconv.Atoi(strings.TrimPrefix(line, "Infected files:"))
+ if infectedFiles > 0 {
+ pushAlert := dto.PushAlert{
+ TaskName: clamName,
+ AlertType: "clams",
+ EntryID: clamId,
+ Param: strconv.Itoa(infectedFiles),
+ }
+ err := alert.PushAlert(pushAlert)
+ if err != nil {
+ global.LOG.Errorf("clamdscan push failed, err: %v", err)
+ }
+ break
+ }
+ }
+ }
+ }
+}
diff --git a/backend/app/service/cronjob.go b/backend/app/service/cronjob.go
index 41accecf4..81dee96fa 100644
--- a/backend/app/service/cronjob.go
+++ b/backend/app/service/cronjob.go
@@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
+ "github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
@@ -53,6 +54,16 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
} else {
item.LastRecordTime = "-"
}
+ alertBase := dto.AlertBase{
+ AlertType: cronjob.Type,
+ EntryID: cronjob.ID,
+ }
+ alertCount := xpack.GetAlert(alertBase)
+ if alertCount != 0 {
+ item.AlertCount = alertCount
+ } else {
+ item.AlertCount = 0
+ }
dtoCronjobs = append(dtoCronjobs, item)
}
return total, dtoCronjobs, err
@@ -190,6 +201,18 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
+ if cronjobDto.AlertCount != 0 {
+ createAlert := dto.CreateOrUpdateAlert{
+ AlertTitle: cronjobDto.AlertTitle,
+ AlertCount: cronjobDto.AlertCount,
+ AlertType: cronjob.Type,
+ EntryID: cronjob.ID,
+ }
+ err := xpack.CreateAlert(createAlert)
+ if err != nil {
+ return err
+ }
+ }
return nil
}
@@ -232,6 +255,14 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
return err
}
+ alertBase := dto.AlertBase{
+ AlertType: cronjob.Type,
+ EntryID: cronjob.ID,
+ }
+ err := xpack.DeleteAlert(alertBase)
+ if err != nil {
+ return err
+ }
}
return nil
@@ -281,7 +312,21 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["default_download"] = req.DefaultDownload
upMap["retain_copies"] = req.RetainCopies
upMap["secret"] = req.Secret
- return cronjobRepo.Update(id, upMap)
+ err = cronjobRepo.Update(id, upMap)
+ if err != nil {
+ return err
+ }
+ updateAlert := dto.CreateOrUpdateAlert{
+ AlertTitle: req.AlertTitle,
+ AlertType: cronModel.Type,
+ AlertCount: req.AlertCount,
+ EntryID: cronModel.ID,
+ }
+ err = xpack.UpdateAlert(updateAlert)
+ if err != nil {
+ return err
+ }
+ return nil
}
func (u *CronjobService) UpdateStatus(id uint, status string) error {
@@ -293,6 +338,7 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
entryIDs string
err error
)
+
if status == constant.StatusEnable {
entryIDs, err = u.StartJob(&cronjob, false)
if err != nil {
diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go
index e882bf16f..cf31acfe8 100644
--- a/backend/app/service/cronjob_helper.go
+++ b/backend/app/service/cronjob_helper.go
@@ -12,6 +12,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/i18n"
+ "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -20,9 +21,12 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
+ "github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/pkg/errors"
)
+var alertTypes = map[string]bool{"app": true, "website": true, "database": true, "directory": true, "log": true, "snapshot": true}
+
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
var (
message []byte
@@ -85,12 +89,12 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
}
-
if err != nil {
if len(message) != 0 {
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
+ handleCronJobAlert(cronjob)
return
}
if len(message) != 0 {
@@ -392,3 +396,19 @@ func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.
func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log"
}
+
+func handleCronJobAlert(cronjob *model.Cronjob) {
+ if alertTypes[cronjob.Type] {
+ pushAlert := dto.PushAlert{
+ TaskName: cronjob.Name,
+ AlertType: cronjob.Type,
+ EntryID: cronjob.ID,
+ Param: cronjob.Type,
+ }
+ err := xpack.PushAlert(pushAlert)
+ if err != nil {
+ global.LOG.Errorf("cronjob alert push failed, err: %v", err)
+ return
+ }
+ }
+}
diff --git a/backend/utils/xpack/xpack.go b/backend/utils/xpack/xpack.go
index b8161d86a..29afab3e6 100644
--- a/backend/utils/xpack/xpack.go
+++ b/backend/utils/xpack/xpack.go
@@ -8,6 +8,7 @@ import (
"net/http"
"time"
+ "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -39,3 +40,23 @@ func LoadXpuInfo() []interface{} {
func StartClam(startClam model.Clam, isUpdate bool) (int, error) {
return 0, buserr.New(constant.ErrXpackNotFound)
}
+
+func CreateAlert(createAlert dto.CreateOrUpdateAlert) error {
+ return nil
+}
+
+func UpdateAlert(updateAlert dto.CreateOrUpdateAlert) error {
+ return nil
+}
+
+func DeleteAlert(alertBase dto.AlertBase) error {
+ return nil
+}
+
+func GetAlert(alertBase dto.AlertBase) uint {
+ return 0
+}
+
+func PushAlert(pushAlert dto.PushAlert) error {
+ return nil
+}
diff --git a/frontend/src/api/interface/cronjob.ts b/frontend/src/api/interface/cronjob.ts
index bcd7c44f7..0da286476 100644
--- a/frontend/src/api/interface/cronjob.ts
+++ b/frontend/src/api/interface/cronjob.ts
@@ -30,6 +30,9 @@ export namespace Cronjob {
retainCopies: number;
status: string;
secret: string;
+ hasAlert: boolean;
+ alertCount: number;
+ alertTitle: string;
}
export interface CronjobCreate {
name: string;
diff --git a/frontend/src/api/interface/toolbox.ts b/frontend/src/api/interface/toolbox.ts
index dd94a17ba..3d8535477 100644
--- a/frontend/src/api/interface/toolbox.ts
+++ b/frontend/src/api/interface/toolbox.ts
@@ -139,6 +139,9 @@ export namespace Toolbox {
spec: string;
specObj: Cronjob.SpecObj;
description: string;
+ hasAlert: boolean;
+ alertCount: number;
+ alertTitle: string;
}
export interface ClamCreate {
name: string;
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index c49578dd8..0218ef995 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -935,6 +935,7 @@ const message = {
requestExpirationTime: 'Upload Request Expiration Time(Hours)',
unitHours: 'Unit: Hours',
+ alertTitle: 'Planned Task - {0} 「{1}」 Task Failure Alert',
},
monitor: {
monitor: 'Monitor',
@@ -1132,6 +1133,8 @@ const message = {
clamLog: 'Scan Logs',
freshClam: 'Update Virus Definitions',
freshClamLog: 'Update Virus Definitions Logs',
+ alertHelper: 'Professional version supports SMS alert',
+ alertTitle: 'Virus scanning 「{0}」 task failed alert',
},
},
logs: {
@@ -2461,6 +2464,14 @@ const message = {
manage: 'Management',
},
},
+ alert: {
+ isAlert: 'Alert',
+ alertCount: 'Alert Count',
+ clamHelper: 'Trigger SMS alert when scanning infected files',
+ cronJobHelper: 'Trigger SMS alert when scheduled task execution fails',
+ licenseHelper: 'Professional version supports SMS alert',
+ alertCountHelper: 'Maximum daily alarm frequency',
+ },
};
export default {
diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts
index 61849797c..9827c26a1 100644
--- a/frontend/src/lang/modules/tw.ts
+++ b/frontend/src/lang/modules/tw.ts
@@ -889,6 +889,7 @@ const message = {
requestExpirationTime: '上傳請求過期時間(小時)',
unitHours: '單位:小時',
+ alertTitle: '計畫任務-{0}「{1}」任務失敗告警',
},
monitor: {
monitor: '監控',
@@ -1069,6 +1070,8 @@ const message = {
clamLog: '掃描日誌',
freshClam: '病毒庫刷新配置',
freshClamLog: '病毒庫刷新日誌',
+ alertHelper: '專業版支持短信告警功能',
+ alertTitle: '病毒掃描「{0}」任務失敗告警',
},
},
logs: {
@@ -2285,6 +2288,14 @@ const message = {
manage: '管理',
},
},
+ alert: {
+ isAlert: '是否告警',
+ alertCount: '告警次數',
+ clamHelper: '掃描到感染檔案時觸發簡訊告警',
+ cronJobHelper: '定時執行計畫任務失敗時觸發簡訊告警',
+ licenseHelper: '專業版支持簡訊告警功能',
+ alertCountHelper: '每日最大告警次數',
+ },
};
export default {
...fit2cloudTwLocale,
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index aa40b9b54..246ba89fb 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -890,6 +890,7 @@ const message = {
requestExpirationTime: '上传请求过期时间(小时)',
unitHours: '单位:小时',
+ alertTitle: '计划任务-{0}「 {1} 」任务失败告警',
},
monitor: {
monitor: '监控',
@@ -1071,6 +1072,8 @@ const message = {
clamLog: '扫描日志',
freshClam: '病毒库刷新配置',
freshClamLog: '病毒库刷新日志',
+ alertHelper: '专业版支持短信告警功能',
+ alertTitle: '病毒扫描「 {0} 」任务失败告警',
},
},
logs: {
@@ -2288,6 +2291,14 @@ const message = {
manage: '管理',
},
},
+ alert: {
+ isAlert: '是否告警',
+ alertCount: '告警次数',
+ clamHelper: '扫描到感染文件时触发短信告警',
+ cronJobHelper: '定时执行计划任务失败时触发短信告警',
+ licenseHelper: '专业版支持短信告警功能',
+ alertCountHelper: '每日最大告警次数',
+ },
};
export default {
...fit2cloudZhLocale,
diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue
index 2b5a811d4..8f194e60f 100644
--- a/frontend/src/views/cronjob/operate/index.vue
+++ b/frontend/src/views/cronjob/operate/index.vue
@@ -332,6 +332,31 @@
+
+
+ {{ $t('alert.cronJobHelper') }}
+
+
+
+ {{ $t('alert.alertCountHelper') }}
+
+
+ {{ $t('alert.licenseHelper') }}
+
+ {{ $t('license.levelUpPro') }}
+
+
+
+
@@ -378,6 +404,9 @@ import { listContainer } from '@/api/modules/container';
import { Database } from '@/api/interface/database';
import { ListAppInstalled } from '@/api/modules/app';
import { loadDefaultSpec, specOptions, transObjToSpec, transSpecToObj, weekOptions } from './../helper';
+import { storeToRefs } from 'pinia';
+import { GlobalStore } from '@/store';
+import LicenseImport from '@/components/license-import/index.vue';
const router = useRouter();
@@ -393,6 +422,11 @@ const dialogData = ref({
title: '',
});
+const globalStore = GlobalStore();
+const licenseRef = ref();
+const { isProductPro } = storeToRefs(globalStore);
+const alertTypes = ['app', 'website', 'database', 'directory', 'log', 'snapshot'];
+
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
if (dialogData.value.rowData?.spec) {
@@ -419,6 +453,8 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value.rowData.dbName) {
dialogData.value.rowData.dbNameList = dialogData.value.rowData.dbName.split(',');
}
+ dialogData.value.rowData.hasAlert = dialogData.value.rowData!.alertCount > 0;
+ dialogData.value.rowData!.alertCount = dialogData.value.rowData!.alertCount || 3;
dialogData.value.rowData!.command = dialogData.value.rowData!.command || 'sh';
dialogData.value.rowData!.isCustom =
dialogData.value.rowData!.command !== 'sh' &&
@@ -556,6 +592,18 @@ const verifySpec = (rule: any, value: any, callback: any) => {
callback();
};
+function checkSendCount(rule: any, value: any, callback: any) {
+ if (value === '') {
+ callback();
+ }
+ const regex = /^(?:[1-9]|[12][0-9]|30)$/;
+ if (!regex.test(value)) {
+ return callback(new Error(i18n.global.t('commons.rule.numberRange', [1, 30])));
+ }
+
+ callback();
+}
+
const rules = reactive({
name: [Rules.requiredInput, Rules.noSpace],
type: [Rules.requiredSelect],
@@ -574,6 +622,7 @@ const rules = reactive({
backupAccounts: [Rules.requiredSelect],
defaultDownload: [Rules.requiredSelect],
retainCopies: [Rules.number],
+ alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],
});
type FormInstance = InstanceType;
@@ -752,6 +801,17 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (dialogData.value?.rowData?.exclusionRules) {
dialogData.value.rowData.exclusionRules = dialogData.value.rowData.exclusionRules.replaceAll('\n', ',');
}
+ if (alertTypes.includes(dialogData.value.rowData.type)) {
+ dialogData.value.rowData.alertCount =
+ dialogData.value.rowData!.hasAlert && isProductPro.value ? dialogData.value.rowData.alertCount : 0;
+ dialogData.value.rowData.alertTitle =
+ dialogData.value.rowData!.hasAlert && isProductPro.value
+ ? i18n.global.t('cronjob.alertTitle', [
+ i18n.global.t('cronjob.' + dialogData.value.rowData.type),
+ dialogData.value.rowData.name,
+ ])
+ : '';
+ }
if (!dialogData.value.rowData) return;
if (dialogData.value.title === 'create') {
await addCronjob(dialogData.value.rowData);
@@ -766,6 +826,10 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
});
};
+const toUpload = () => {
+ licenseRef.value.acceptParams();
+};
+
defineExpose({
acceptParams,
});
diff --git a/frontend/src/views/toolbox/clam/index.vue b/frontend/src/views/toolbox/clam/index.vue
index d7dec111d..2f14f9e2f 100644
--- a/frontend/src/views/toolbox/clam/index.vue
+++ b/frontend/src/views/toolbox/clam/index.vue
@@ -261,7 +261,7 @@ const toDoc = async () => {
};
const onChange = async (row: any) => {
- await await updateClam(row);
+ await updateClam(row);
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
};
diff --git a/frontend/src/views/toolbox/clam/operate/index.vue b/frontend/src/views/toolbox/clam/operate/index.vue
index 973438959..c27e290f3 100644
--- a/frontend/src/views/toolbox/clam/operate/index.vue
+++ b/frontend/src/views/toolbox/clam/operate/index.vue
@@ -121,6 +121,30 @@
+
+
+ {{ $t('alert.clamHelper') }}
+
+
+ {{ $t('toolbox.clam.alertHelper') }}
+
+ {{ $t('license.levelUpPro') }}
+
+
+
+
+ {{ $t('alert.alertCountHelper') }}
+
@@ -184,6 +208,8 @@ const acceptParams = (params: DialogProps): void => {
second: 30,
};
}
+ dialogData.value.rowData.hasAlert = dialogData.value.rowData!.alertCount > 0;
+ dialogData.value.rowData!.alertCount = dialogData.value.rowData!.alertCount || 3;
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisible.value = true;
};
@@ -277,6 +303,19 @@ const verifySpec = (rule: any, value: any, callback: any) => {
}
callback();
};
+
+function checkSendCount(rule: any, value: any, callback: any) {
+ if (value === '') {
+ callback();
+ }
+ const regex = /^(?:[1-9]|[12][0-9]|30)$/;
+ if (!regex.test(value)) {
+ return callback(new Error(i18n.global.t('commons.rule.numberRange', [1, 30])));
+ }
+
+ callback();
+}
+
const rules = reactive({
name: [Rules.simpleName],
path: [Rules.requiredInput, Rules.noSpace],
@@ -284,6 +323,7 @@ const rules = reactive({
{ validator: verifySpec, trigger: 'blur', required: true },
{ validator: verifySpec, trigger: 'change', required: true },
],
+ alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],
});
type FormInstance = InstanceType;
@@ -353,6 +393,16 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
MsgError(i18n.global.t('cronjob.cronSpecHelper'));
return;
}
+ dialogData.value.rowData.alertCount = dialogData.value.rowData!.hasAlert
+ ? dialogData.value.rowData.alertCount
+ : 0;
+ dialogData.value.rowData.alertTitle = i18n.global.t('toolbox.clam.alertTitle', [
+ dialogData.value.rowData.name,
+ ]);
+ } else {
+ dialogData.value.rowData.alertTitle = '';
+ dialogData.value.rowData.alertCount = 0;
+ dialogData.value.rowData.hasAlert = false;
}
dialogData.value.rowData.spec = spec;