mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 增加病毒扫描和计划任务告警 (#6709)
This commit is contained in:
parent
556441d811
commit
acf94b052b
20
backend/app/dto/alert.go
Normal file
20
backend/app/dto/alert.go
Normal file
@ -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"`
|
||||||
|
}
|
@ -33,6 +33,7 @@ type ClamInfo struct {
|
|||||||
LastHandleDate string `json:"lastHandleDate"`
|
LastHandleDate string `json:"lastHandleDate"`
|
||||||
Spec string `json:"spec"`
|
Spec string `json:"spec"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClamLogSearch struct {
|
type ClamLogSearch struct {
|
||||||
@ -71,6 +72,8 @@ type ClamCreate struct {
|
|||||||
InfectedDir string `json:"infectedDir"`
|
InfectedDir string `json:"infectedDir"`
|
||||||
Spec string `json:"spec"`
|
Spec string `json:"spec"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
|
AlertTitle string `json:"alertTitle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClamUpdate struct {
|
type ClamUpdate struct {
|
||||||
@ -82,6 +85,8 @@ type ClamUpdate struct {
|
|||||||
InfectedDir string `json:"infectedDir"`
|
InfectedDir string `json:"infectedDir"`
|
||||||
Spec string `json:"spec"`
|
Spec string `json:"spec"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
|
AlertTitle string `json:"alertTitle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClamUpdateStatus struct {
|
type ClamUpdateStatus struct {
|
||||||
|
@ -31,10 +31,13 @@ type CronjobCreate struct {
|
|||||||
DefaultDownload string `json:"defaultDownload"`
|
DefaultDownload string `json:"defaultDownload"`
|
||||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
|
AlertTitle string `json:"alertTitle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronjobUpdate struct {
|
type CronjobUpdate struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Type string `json:"type" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Spec string `json:"spec" validate:"required"`
|
Spec string `json:"spec" validate:"required"`
|
||||||
|
|
||||||
@ -53,6 +56,8 @@ type CronjobUpdate struct {
|
|||||||
DefaultDownload string `json:"defaultDownload"`
|
DefaultDownload string `json:"defaultDownload"`
|
||||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
|
AlertTitle string `json:"alertTitle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronjobUpdateStatus struct {
|
type CronjobUpdateStatus struct {
|
||||||
@ -99,6 +104,7 @@ type CronjobInfo struct {
|
|||||||
LastRecordTime string `json:"lastRecordTime"`
|
LastRecordTime string `json:"lastRecordTime"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
AlertCount uint `json:"alertCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchRecord struct {
|
type SearchRecord struct {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/xpack/utils/alert"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/robfig/cron/v3"
|
"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)
|
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
|
return total, datas, err
|
||||||
}
|
}
|
||||||
@ -180,6 +192,19 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
|||||||
if err := clamRepo.Create(&clam); err != nil {
|
if err := clamRepo.Create(&clam); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +250,16 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
|
|||||||
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,6 +300,14 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
|
|||||||
if err := clamRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
if err := clamRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
alertBase := dto.AlertBase{
|
||||||
|
AlertType: "clams",
|
||||||
|
EntryID: clam.ID,
|
||||||
|
}
|
||||||
|
err := xpack.DeleteAlert(alertBase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
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)
|
stdout, err := cmd.Execf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
|
||||||
|
handleAlert(stdout, clam.Name, clam.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
|
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
|
||||||
}
|
}
|
||||||
@ -585,3 +629,28 @@ func (c *ClamService) loadLogPath(name string) string {
|
|||||||
|
|
||||||
return ""
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
@ -53,6 +54,16 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
|
|||||||
} else {
|
} else {
|
||||||
item.LastRecordTime = "-"
|
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)
|
dtoCronjobs = append(dtoCronjobs, item)
|
||||||
}
|
}
|
||||||
return total, dtoCronjobs, err
|
return total, dtoCronjobs, err
|
||||||
@ -190,6 +201,18 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
|||||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +255,14 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
|||||||
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
alertBase := dto.AlertBase{
|
||||||
|
AlertType: cronjob.Type,
|
||||||
|
EntryID: cronjob.ID,
|
||||||
|
}
|
||||||
|
err := xpack.DeleteAlert(alertBase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -281,7 +312,21 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
|
|||||||
upMap["default_download"] = req.DefaultDownload
|
upMap["default_download"] = req.DefaultDownload
|
||||||
upMap["retain_copies"] = req.RetainCopies
|
upMap["retain_copies"] = req.RetainCopies
|
||||||
upMap["secret"] = req.Secret
|
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 {
|
func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||||
@ -293,6 +338,7 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
|||||||
entryIDs string
|
entryIDs string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if status == constant.StatusEnable {
|
if status == constant.StatusEnable {
|
||||||
entryIDs, err = u.StartJob(&cronjob, false)
|
entryIDs, err = u.StartJob(&cronjob, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
"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/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"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/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
|
||||||
"github.com/pkg/errors"
|
"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) {
|
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||||
var (
|
var (
|
||||||
message []byte
|
message []byte
|
||||||
@ -85,12 +89,12 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
|||||||
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
|
||||||
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
|
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if len(message) != 0 {
|
if len(message) != 0 {
|
||||||
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||||
}
|
}
|
||||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||||
|
handleCronJobAlert(cronjob)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(message) != 0 {
|
if len(message) != 0 {
|
||||||
@ -392,3 +396,19 @@ func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.
|
|||||||
func hasBackup(cronjobType string) bool {
|
func hasBackup(cronjobType string) bool {
|
||||||
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log"
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
@ -39,3 +40,23 @@ func LoadXpuInfo() []interface{} {
|
|||||||
func StartClam(startClam model.Clam, isUpdate bool) (int, error) {
|
func StartClam(startClam model.Clam, isUpdate bool) (int, error) {
|
||||||
return 0, buserr.New(constant.ErrXpackNotFound)
|
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
|
||||||
|
}
|
||||||
|
@ -30,6 +30,9 @@ export namespace Cronjob {
|
|||||||
retainCopies: number;
|
retainCopies: number;
|
||||||
status: string;
|
status: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
|
hasAlert: boolean;
|
||||||
|
alertCount: number;
|
||||||
|
alertTitle: string;
|
||||||
}
|
}
|
||||||
export interface CronjobCreate {
|
export interface CronjobCreate {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -139,6 +139,9 @@ export namespace Toolbox {
|
|||||||
spec: string;
|
spec: string;
|
||||||
specObj: Cronjob.SpecObj;
|
specObj: Cronjob.SpecObj;
|
||||||
description: string;
|
description: string;
|
||||||
|
hasAlert: boolean;
|
||||||
|
alertCount: number;
|
||||||
|
alertTitle: string;
|
||||||
}
|
}
|
||||||
export interface ClamCreate {
|
export interface ClamCreate {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -935,6 +935,7 @@ const message = {
|
|||||||
|
|
||||||
requestExpirationTime: 'Upload Request Expiration Time(Hours)',
|
requestExpirationTime: 'Upload Request Expiration Time(Hours)',
|
||||||
unitHours: 'Unit: Hours',
|
unitHours: 'Unit: Hours',
|
||||||
|
alertTitle: 'Planned Task - {0} 「{1}」 Task Failure Alert',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
monitor: 'Monitor',
|
monitor: 'Monitor',
|
||||||
@ -1132,6 +1133,8 @@ const message = {
|
|||||||
clamLog: 'Scan Logs',
|
clamLog: 'Scan Logs',
|
||||||
freshClam: 'Update Virus Definitions',
|
freshClam: 'Update Virus Definitions',
|
||||||
freshClamLog: 'Update Virus Definitions Logs',
|
freshClamLog: 'Update Virus Definitions Logs',
|
||||||
|
alertHelper: 'Professional version supports SMS alert',
|
||||||
|
alertTitle: 'Virus scanning 「{0}」 task failed alert',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
@ -2461,6 +2464,14 @@ const message = {
|
|||||||
manage: 'Management',
|
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 {
|
export default {
|
||||||
|
@ -889,6 +889,7 @@ const message = {
|
|||||||
|
|
||||||
requestExpirationTime: '上傳請求過期時間(小時)',
|
requestExpirationTime: '上傳請求過期時間(小時)',
|
||||||
unitHours: '單位:小時',
|
unitHours: '單位:小時',
|
||||||
|
alertTitle: '計畫任務-{0}「{1}」任務失敗告警',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
monitor: '監控',
|
monitor: '監控',
|
||||||
@ -1069,6 +1070,8 @@ const message = {
|
|||||||
clamLog: '掃描日誌',
|
clamLog: '掃描日誌',
|
||||||
freshClam: '病毒庫刷新配置',
|
freshClam: '病毒庫刷新配置',
|
||||||
freshClamLog: '病毒庫刷新日誌',
|
freshClamLog: '病毒庫刷新日誌',
|
||||||
|
alertHelper: '專業版支持短信告警功能',
|
||||||
|
alertTitle: '病毒掃描「{0}」任務失敗告警',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
@ -2285,6 +2288,14 @@ const message = {
|
|||||||
manage: '管理',
|
manage: '管理',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
alert: {
|
||||||
|
isAlert: '是否告警',
|
||||||
|
alertCount: '告警次數',
|
||||||
|
clamHelper: '掃描到感染檔案時觸發簡訊告警',
|
||||||
|
cronJobHelper: '定時執行計畫任務失敗時觸發簡訊告警',
|
||||||
|
licenseHelper: '專業版支持簡訊告警功能',
|
||||||
|
alertCountHelper: '每日最大告警次數',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
...fit2cloudTwLocale,
|
...fit2cloudTwLocale,
|
||||||
|
@ -890,6 +890,7 @@ const message = {
|
|||||||
|
|
||||||
requestExpirationTime: '上传请求过期时间(小时)',
|
requestExpirationTime: '上传请求过期时间(小时)',
|
||||||
unitHours: '单位:小时',
|
unitHours: '单位:小时',
|
||||||
|
alertTitle: '计划任务-{0}「 {1} 」任务失败告警',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
monitor: '监控',
|
monitor: '监控',
|
||||||
@ -1071,6 +1072,8 @@ const message = {
|
|||||||
clamLog: '扫描日志',
|
clamLog: '扫描日志',
|
||||||
freshClam: '病毒库刷新配置',
|
freshClam: '病毒库刷新配置',
|
||||||
freshClamLog: '病毒库刷新日志',
|
freshClamLog: '病毒库刷新日志',
|
||||||
|
alertHelper: '专业版支持短信告警功能',
|
||||||
|
alertTitle: '病毒扫描「 {0} 」任务失败告警',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
@ -2288,6 +2291,14 @@ const message = {
|
|||||||
manage: '管理',
|
manage: '管理',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
alert: {
|
||||||
|
isAlert: '是否告警',
|
||||||
|
alertCount: '告警次数',
|
||||||
|
clamHelper: '扫描到感染文件时触发短信告警',
|
||||||
|
cronJobHelper: '定时执行计划任务失败时触发短信告警',
|
||||||
|
licenseHelper: '专业版支持短信告警功能',
|
||||||
|
alertCountHelper: '每日最大告警次数',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
...fit2cloudZhLocale,
|
...fit2cloudZhLocale,
|
||||||
|
@ -332,6 +332,31 @@
|
|||||||
<el-input clearable v-model.trim="dialogData.rowData!.url" />
|
<el-input clearable v-model.trim="dialogData.rowData!.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="hasAlert" v-if="alertTypes.includes(dialogData.rowData!.type)">
|
||||||
|
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" />
|
||||||
|
<span class="input-help">{{ $t('alert.cronJobHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
prop="alertCount"
|
||||||
|
v-if="dialogData.rowData!.hasAlert && isProductPro"
|
||||||
|
:label="$t('alert.alertCount')"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
style="width: 200px"
|
||||||
|
:min="1"
|
||||||
|
step-strictly
|
||||||
|
:step="1"
|
||||||
|
v-model.number="dialogData.rowData!.alertCount"
|
||||||
|
></el-input-number>
|
||||||
|
<span class="input-help">{{ $t('alert.alertCountHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="dialogData.rowData!.hasAlert && !isProductPro">
|
||||||
|
<span>{{ $t('alert.licenseHelper') }}</span>
|
||||||
|
<el-button link type="primary" @click="toUpload">
|
||||||
|
{{ $t('license.levelUpPro') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="hasExclusionRules()"
|
v-if="hasExclusionRules()"
|
||||||
:label="$t('cronjob.exclusionRules')"
|
:label="$t('cronjob.exclusionRules')"
|
||||||
@ -357,6 +382,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<LicenseImport ref="licenseRef" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -378,6 +404,9 @@ import { listContainer } from '@/api/modules/container';
|
|||||||
import { Database } from '@/api/interface/database';
|
import { Database } from '@/api/interface/database';
|
||||||
import { ListAppInstalled } from '@/api/modules/app';
|
import { ListAppInstalled } from '@/api/modules/app';
|
||||||
import { loadDefaultSpec, specOptions, transObjToSpec, transSpecToObj, weekOptions } from './../helper';
|
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();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -393,6 +422,11 @@ const dialogData = ref<DialogProps>({
|
|||||||
title: '',
|
title: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const globalStore = GlobalStore();
|
||||||
|
const licenseRef = ref();
|
||||||
|
const { isProductPro } = storeToRefs(globalStore);
|
||||||
|
const alertTypes = ['app', 'website', 'database', 'directory', 'log', 'snapshot'];
|
||||||
|
|
||||||
const acceptParams = (params: DialogProps): void => {
|
const acceptParams = (params: DialogProps): void => {
|
||||||
dialogData.value = params;
|
dialogData.value = params;
|
||||||
if (dialogData.value.rowData?.spec) {
|
if (dialogData.value.rowData?.spec) {
|
||||||
@ -419,6 +453,8 @@ const acceptParams = (params: DialogProps): void => {
|
|||||||
if (dialogData.value.rowData.dbName) {
|
if (dialogData.value.rowData.dbName) {
|
||||||
dialogData.value.rowData.dbNameList = dialogData.value.rowData.dbName.split(',');
|
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!.command = dialogData.value.rowData!.command || 'sh';
|
||||||
dialogData.value.rowData!.isCustom =
|
dialogData.value.rowData!.isCustom =
|
||||||
dialogData.value.rowData!.command !== 'sh' &&
|
dialogData.value.rowData!.command !== 'sh' &&
|
||||||
@ -556,6 +592,18 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
|||||||
callback();
|
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({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput, Rules.noSpace],
|
name: [Rules.requiredInput, Rules.noSpace],
|
||||||
type: [Rules.requiredSelect],
|
type: [Rules.requiredSelect],
|
||||||
@ -574,6 +622,7 @@ const rules = reactive({
|
|||||||
backupAccounts: [Rules.requiredSelect],
|
backupAccounts: [Rules.requiredSelect],
|
||||||
defaultDownload: [Rules.requiredSelect],
|
defaultDownload: [Rules.requiredSelect],
|
||||||
retainCopies: [Rules.number],
|
retainCopies: [Rules.number],
|
||||||
|
alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
@ -752,6 +801,17 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
if (dialogData.value?.rowData?.exclusionRules) {
|
if (dialogData.value?.rowData?.exclusionRules) {
|
||||||
dialogData.value.rowData.exclusionRules = dialogData.value.rowData.exclusionRules.replaceAll('\n', ',');
|
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.rowData) return;
|
||||||
if (dialogData.value.title === 'create') {
|
if (dialogData.value.title === 'create') {
|
||||||
await addCronjob(dialogData.value.rowData);
|
await addCronjob(dialogData.value.rowData);
|
||||||
@ -766,6 +826,10 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toUpload = () => {
|
||||||
|
licenseRef.value.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
acceptParams,
|
acceptParams,
|
||||||
});
|
});
|
||||||
|
@ -261,7 +261,7 @@ const toDoc = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onChange = async (row: any) => {
|
const onChange = async (row: any) => {
|
||||||
await await updateClam(row);
|
await updateClam(row);
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,6 +121,30 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item prop="hasAlert">
|
||||||
|
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" />
|
||||||
|
<span class="input-help">{{ $t('alert.clamHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="dialogData.rowData!.hasAlert && !isProductPro">
|
||||||
|
<span>{{ $t('toolbox.clam.alertHelper') }}</span>
|
||||||
|
<el-button link type="primary" @click="toUpload">
|
||||||
|
{{ $t('license.levelUpPro') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
prop="alertCount"
|
||||||
|
v-if="dialogData.rowData!.hasAlert && isProductPro"
|
||||||
|
:label="$t('alert.alertCount')"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
style="width: 200px"
|
||||||
|
:min="1"
|
||||||
|
step-strictly
|
||||||
|
:step="1"
|
||||||
|
v-model.number="dialogData.rowData!.alertCount"
|
||||||
|
></el-input-number>
|
||||||
|
<span class="input-help">{{ $t('alert.alertCountHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
|
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -184,6 +208,8 @@ const acceptParams = (params: DialogProps): void => {
|
|||||||
second: 30,
|
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);
|
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
|
||||||
drawerVisible.value = true;
|
drawerVisible.value = true;
|
||||||
};
|
};
|
||||||
@ -277,6 +303,19 @@ const verifySpec = (rule: any, value: any, callback: any) => {
|
|||||||
}
|
}
|
||||||
callback();
|
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({
|
const rules = reactive({
|
||||||
name: [Rules.simpleName],
|
name: [Rules.simpleName],
|
||||||
path: [Rules.requiredInput, Rules.noSpace],
|
path: [Rules.requiredInput, Rules.noSpace],
|
||||||
@ -284,6 +323,7 @@ const rules = reactive({
|
|||||||
{ validator: verifySpec, trigger: 'blur', required: true },
|
{ validator: verifySpec, trigger: 'blur', required: true },
|
||||||
{ validator: verifySpec, trigger: 'change', required: true },
|
{ validator: verifySpec, trigger: 'change', required: true },
|
||||||
],
|
],
|
||||||
|
alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
@ -353,6 +393,16 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
MsgError(i18n.global.t('cronjob.cronSpecHelper'));
|
MsgError(i18n.global.t('cronjob.cronSpecHelper'));
|
||||||
return;
|
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;
|
dialogData.value.rowData.spec = spec;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user