From c61828fb39a01eec13b30a1bbd27decaccba637e Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:13:32 +0800 Subject: [PATCH] fix: Adjust the display logic of the clam scan report (#10264) Refs #10251 --- agent/app/api/v2/clam.go | 29 +- agent/app/dto/clam.go | 22 +- agent/app/model/clam.go | 19 +- agent/app/repo/clam.go | 79 ++++ agent/app/service/clam.go | 330 +++++------------ agent/app/task/task.go | 2 + agent/constant/host_tool.go | 4 + agent/i18n/lang/en.yaml | 4 + agent/i18n/lang/ja.yaml | 4 + agent/i18n/lang/ko.yaml | 6 +- agent/i18n/lang/ms.yaml | 4 + agent/i18n/lang/pt-BR.yaml | 4 + agent/i18n/lang/ru.yaml | 4 + agent/i18n/lang/tr.yaml | 4 + agent/i18n/lang/zh-Hant.yaml | 4 + agent/i18n/lang/zh.yaml | 4 + agent/init/hook/hook.go | 19 + agent/init/migration/migrations/init.go | 3 +- agent/router/ro_toolbox.go | 1 - agent/utils/clam/clam.go | 88 +++++ agent/utils/xpack/xpack.go | 2 +- frontend/src/api/interface/toolbox.ts | 9 +- frontend/src/api/modules/toolbox.ts | 7 +- frontend/src/lang/modules/en.ts | 1 - frontend/src/lang/modules/ja.ts | 1 - frontend/src/lang/modules/ko.ts | 1 - frontend/src/lang/modules/ms.ts | 2 - frontend/src/lang/modules/pt-br.ts | 2 - frontend/src/lang/modules/ru.ts | 1 - frontend/src/lang/modules/tr.ts | 2 - frontend/src/lang/modules/zh-Hant.ts | 1 - frontend/src/lang/modules/zh.ts | 1 - .../views/cronjob/cronjob/record/index.vue | 2 +- frontend/src/views/toolbox/clam/index.vue | 20 +- .../src/views/toolbox/clam/record/index.vue | 345 +++++++++--------- 35 files changed, 561 insertions(+), 470 deletions(-) create mode 100644 agent/utils/clam/clam.go diff --git a/agent/app/api/v2/clam.go b/agent/app/api/v2/clam.go index 4d478e3bf..bf0f40ff7 100644 --- a/agent/app/api/v2/clam.go +++ b/agent/app/api/v2/clam.go @@ -152,7 +152,7 @@ func (b *BaseApi) CleanClamRecord(c *gin.Context) { if err := helper.CheckBindAndValidate(&req, c); err != nil { return } - if err := clamService.CleanRecord(req); err != nil { + if err := clamService.CleanRecord(req.ID); err != nil { helper.InternalServer(c, err) return } @@ -174,7 +174,7 @@ func (b *BaseApi) SearchClamRecord(c *gin.Context) { return } - total, list, err := clamService.LoadRecords(req) + total, list, err := clamService.SearchRecords(req) if err != nil { helper.InternalServer(c, err) return @@ -186,29 +186,6 @@ func (b *BaseApi) SearchClamRecord(c *gin.Context) { }) } -// @Tags Clam -// @Summary Load clam record detail -// @Accept json -// @Param request body dto.ClamLogReq true "request" -// @Success 200 {string} content -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /toolbox/clam/record/log [post] -func (b *BaseApi) LoadClamRecordLog(c *gin.Context) { - var req dto.ClamLogReq - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - - content, err := clamService.LoadRecordLog(req) - if err != nil { - helper.InternalServer(c, err) - return - } - - helper.SuccessWithData(c, content) -} - // @Tags Clam // @Summary Load clam file // @Accept json @@ -289,7 +266,7 @@ func (b *BaseApi) HandleClamScan(c *gin.Context) { return } - if err := clamService.HandleOnce(req); err != nil { + if err := clamService.HandleOnce(req.ID); err != nil { helper.InternalServer(c, err) return } diff --git a/agent/app/dto/clam.go b/agent/app/dto/clam.go index 7b8bb644d..60a24da1a 100644 --- a/agent/app/dto/clam.go +++ b/agent/app/dto/clam.go @@ -30,7 +30,8 @@ type ClamInfo struct { Path string `json:"path"` InfectedStrategy string `json:"infectedStrategy"` InfectedDir string `json:"infectedDir"` - LastHandleDate string `json:"lastHandleDate"` + LastRecordStatus string `json:"lastRecordStatus"` + LastRecordTime string `json:"lastRecordTime"` Spec string `json:"spec"` Description string `json:"description"` AlertCount uint `json:"alertCount"` @@ -41,6 +42,7 @@ type ClamLogSearch struct { PageInfo ClamID uint `json:"clamID"` + Status string `json:"status"` StartTime time.Time `json:"startTime"` EndTime time.Time `json:"endTime"` } @@ -56,13 +58,16 @@ type ClamFileReq struct { Name string `json:"name" validate:"required"` } -type ClamLog struct { - Name string `json:"name"` - ScanDate string `json:"scanDate"` - ScanTime string `json:"scanTime"` - InfectedFiles string `json:"infectedFiles"` - TotalError string `json:"totalError"` - Status string `json:"status"` +type ClamRecord struct { + ID uint `json:"id"` + TaskID string `json:"taskID"` + StartTime time.Time `json:"startTime"` + ScanTime string `json:"scanTime"` + InfectedFiles string `json:"infectedFiles"` + TotalError string `json:"totalError"` + + Status string `json:"status"` + Message string `json:"message"` } type ClamCreate struct { @@ -98,7 +103,6 @@ type ClamUpdateStatus struct { } type ClamDelete struct { - RemoveRecord bool `json:"removeRecord"` RemoveInfected bool `json:"removeInfected"` Ids []uint `json:"ids" validate:"required"` } diff --git a/agent/app/model/clam.go b/agent/app/model/clam.go index a71fc3ed5..f4bf173ec 100644 --- a/agent/app/model/clam.go +++ b/agent/app/model/clam.go @@ -1,14 +1,31 @@ package model +import "time" + type Clam struct { BaseModel Name string `gorm:"not null" json:"name"` - Status string `json:"status"` Path string `gorm:"not null" json:"path"` InfectedStrategy string `json:"infectedStrategy"` InfectedDir string `json:"infectedDir"` Spec string `json:"spec"` EntryID int `json:"entryID"` Description string `json:"description"` + + Status string `json:"status"` + IsExecuting bool `json:"isExecuting"` +} + +type ClamRecord struct { + BaseModel + + ClamID uint `json:"clamID"` + TaskID string `json:"taskID"` + StartTime time.Time `json:"startTime"` + ScanTime string `json:"scanTime"` + InfectedFiles string `json:"infectedFiles"` + TotalError string `json:"totalError"` + Status string `json:"status"` + Message string `json:"message"` } diff --git a/agent/app/repo/clam.go b/agent/app/repo/clam.go index d1fdcecda..9322993a7 100644 --- a/agent/app/repo/clam.go +++ b/agent/app/repo/clam.go @@ -1,8 +1,13 @@ package repo import ( + "time" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/google/uuid" + "gorm.io/gorm" ) type ClamRepo struct{} @@ -14,6 +19,14 @@ type IClamRepo interface { Delete(opts ...DBOption) error Get(opts ...DBOption) (model.Clam, error) List(opts ...DBOption) ([]model.Clam, error) + + WithByClamID(id uint) DBOption + ListRecord(opts ...DBOption) ([]model.ClamRecord, error) + RecordFirst(id uint) (model.ClamRecord, error) + DeleteRecord(opts ...DBOption) error + StartRecords(clamID uint) model.ClamRecord + EndRecords(record model.ClamRecord, status, message string) + PageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error) } func NewIClamRepo() IClamRepo { @@ -67,3 +80,69 @@ func (u *ClamRepo) Delete(opts ...DBOption) error { } return db.Delete(&model.Clam{}).Error } + +func (c *ClamRepo) WithByClamID(id uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("clam_id = ?", id) + } +} + +func (u *ClamRepo) ListRecord(opts ...DBOption) ([]model.ClamRecord, error) { + var record []model.ClamRecord + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.Find(&record).Error + return record, err +} + +func (u *ClamRepo) RecordFirst(id uint) (model.ClamRecord, error) { + var record model.ClamRecord + err := global.DB.Where("clam_id = ?", id).Order("created_at desc").First(&record).Error + return record, err +} + +func (u *ClamRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error) { + var records []model.ClamRecord + db := global.DB.Model(&model.ClamRecord{}) + for _, opt := range opts { + db = opt(db) + } + count := int64(0) + db = db.Count(&count) + err := db.Order("created_at desc").Limit(size).Offset(size * (page - 1)).Find(&records).Error + return count, records, err +} +func (u *ClamRepo) StartRecords(clamID uint) model.ClamRecord { + var record model.ClamRecord + record.StartTime = time.Now() + record.ClamID = clamID + record.TaskID = uuid.New().String() + record.Status = constant.StatusWaiting + if err := global.DB.Create(&record).Error; err != nil { + global.LOG.Errorf("create record status failed, err: %v", err) + } + _ = u.Update(clamID, map[string]interface{}{"is_executing": true}) + return record +} +func (u *ClamRepo) EndRecords(record model.ClamRecord, status, message string) { + upMap := make(map[string]interface{}) + upMap["status"] = status + upMap["message"] = message + upMap["task_id"] = record.TaskID + upMap["scan_time"] = record.ScanTime + upMap["infected_files"] = record.InfectedFiles + upMap["total_error"] = record.TotalError + if err := global.DB.Model(&model.ClamRecord{}).Where("id = ?", record.ID).Updates(upMap).Error; err != nil { + global.LOG.Errorf("update record status failed, err: %v", err) + } + _ = u.Update(record.ClamID, map[string]interface{}{"is_executing": false}) +} +func (u *ClamRepo) DeleteRecord(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.ClamRecord{}).Error +} diff --git a/agent/app/service/clam.go b/agent/app/service/clam.go index ee1597527..0e064937d 100644 --- a/agent/app/service/clam.go +++ b/agent/app/service/clam.go @@ -6,33 +6,25 @@ import ( "os" "os/exec" "path" - "sort" "strconv" "strings" - "time" "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/app/task" "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/alert_push" + "github.com/1Panel-dev/1Panel/agent/utils/clam" "github.com/1Panel-dev/1Panel/agent/utils/cmd" - "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/systemctl" "github.com/1Panel-dev/1Panel/agent/utils/xpack" "github.com/jinzhu/copier" "github.com/robfig/cron/v3" ) -const ( - clamServiceNameCentOs = "clamd@scan.service" - clamServiceNameUbuntu = "clamav-daemon.service" - freshClamService = "clamav-freshclam.service" - resultDir = "clamav" -) - type ClamService struct { serviceName string } @@ -45,13 +37,13 @@ type IClamService interface { Update(req dto.ClamUpdate) error UpdateStatus(id uint, status string) error Delete(req dto.ClamDelete) error - HandleOnce(req dto.OperateByID) error + HandleOnce(id uint) error + LoadFile(req dto.ClamFileReq) (string, error) UpdateFile(req dto.UpdateByNameAndFile) error - LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) - CleanRecord(req dto.OperateByID) error - LoadRecordLog(req dto.ClamLogReq) (string, error) + SearchRecords(req dto.ClamLogSearch) (int64, interface{}, error) + CleanRecord(id uint) error } func NewIClamService() IClamService { @@ -62,22 +54,22 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { var baseInfo dto.ClamBaseInfo baseInfo.Version = "-" baseInfo.FreshVersion = "-" - exist1, _ := systemctl.IsExist(clamServiceNameCentOs) + exist1, _ := systemctl.IsExist(constant.ClamServiceNameCentOs) if exist1 { - c.serviceName = clamServiceNameCentOs + c.serviceName = constant.ClamServiceNameCentOs baseInfo.IsExist = true - baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs) + baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameCentOs) } - exist2, _ := systemctl.IsExist(clamServiceNameUbuntu) + exist2, _ := systemctl.IsExist(constant.ClamServiceNameUbuntu) if exist2 { - c.serviceName = clamServiceNameUbuntu + c.serviceName = constant.ClamServiceNameUbuntu baseInfo.IsExist = true - baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu) + baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameUbuntu) } - freshExist, _ := systemctl.IsExist(freshClamService) + freshExist, _ := systemctl.IsExist(constant.FreshClamService) if freshExist { baseInfo.FreshIsExist = true - baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService) + baseInfo.FreshIsActive, _ = systemctl.IsActive(constant.FreshClamService) } if !cmd.Which("clamdscan") { baseInfo.IsActive = false @@ -93,7 +85,7 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { } } } else { - _ = StopAllCronJob(false) + _ = clam.CheckClamIsActive(false, clamRepo) } if baseInfo.FreshIsActive { version, err := cmd.RunDefaultWithStdoutBashC("freshclam --version") @@ -117,7 +109,7 @@ func (c *ClamService) Operate(operate string) error { } return nil case "fresh-start", "fresh-restart", "fresh-stop": - stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService) + stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), constant.FreshClamService) if err != nil { return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout) } @@ -128,31 +120,25 @@ func (c *ClamService) Operate(operate string) error { } func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) { - total, commands, err := clamRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order)) + total, clams, err := clamRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order)) if err != nil { return 0, nil, err } var datas []dto.ClamInfo - for _, command := range commands { + for _, clam := range clams { var item dto.ClamInfo - if err := copier.Copy(&item, &command); err != nil { + if err := copier.Copy(&item, &clam); err != nil { return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil) } - item.LastHandleDate = "-" datas = append(datas, item) } - nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) for i := 0; i < len(datas); i++ { - logPaths := loadFileByName(datas[i].Name) - sort.Slice(logPaths, func(i, j int) bool { - return logPaths[i] > logPaths[j] - }) - if len(logPaths) != 0 { - t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, logPaths[0], nyc) - if err != nil { - continue - } - datas[i].LastHandleDate = t1.Format(constant.DateTimeLayout) + record, _ := clamRepo.RecordFirst(datas[i].ID) + if record.ID != 0 { + datas[i].LastRecordStatus = record.Status + datas[i].LastRecordTime = record.StartTime.Format(constant.DateTimeLayout) + } else { + datas[i].LastRecordTime = "-" } alertBase := dto.AlertBase{ AlertType: "clams", @@ -174,6 +160,9 @@ func (c *ClamService) Create(req dto.ClamCreate) error { if clam.ID != 0 { return buserr.New("ErrRecordExist") } + if cmd.CheckIllegal(req.Path) { + return buserr.New("ErrCmdIllegal") + } if err := copier.Copy(&clam, &req); err != nil { return buserr.WithDetail("ErrStructTransform", err.Error(), nil) } @@ -181,7 +170,7 @@ func (c *ClamService) Create(req dto.ClamCreate) error { clam.InfectedDir = "" } if len(req.Spec) != 0 { - entryID, err := xpack.StartClam(clam, false) + entryID, err := xpack.StartClam(&clam, false) if err != nil { return err } @@ -209,6 +198,9 @@ func (c *ClamService) Create(req dto.ClamCreate) error { } func (c *ClamService) Update(req dto.ClamUpdate) error { + if cmd.CheckIllegal(req.Path) { + return buserr.New("ErrCmdIllegal") + } clam, _ := clamRepo.Get(repo.WithByName(req.Name)) if clam.ID == 0 { return buserr.New("ErrRecordNotFound") @@ -231,7 +223,7 @@ func (c *ClamService) Update(req dto.ClamUpdate) error { upMap["entry_id"] = 0 } if len(req.Spec) != 0 && clam.Status != constant.StatusDisable { - newEntryID, err := xpack.StartClam(clamItem, true) + newEntryID, err := xpack.StartClam(&clamItem, true) if err != nil { return err } @@ -274,7 +266,7 @@ func (c *ClamService) UpdateStatus(id uint, status string) error { err error ) if status == constant.StatusEnable { - entryID, err = xpack.StartClam(clam, true) + entryID, err = xpack.StartClam(&clam, true) if err != nil { return err } @@ -292,9 +284,10 @@ func (c *ClamService) Delete(req dto.ClamDelete) error { if clam.ID == 0 { continue } - if req.RemoveRecord { - _ = os.RemoveAll(path.Join(global.Dir.DataDir, resultDir, clam.Name)) + if len(clam.Spec) != 0 { + global.Cron.Remove(cron.EntryID(clam.EntryID)) } + _ = c.CleanRecord(clam.ID) if req.RemoveInfected { _ = os.RemoveAll(path.Join(clam.InfectedDir, "1panel-infected", clam.Name)) } @@ -309,128 +302,78 @@ func (c *ClamService) Delete(req dto.ClamDelete) error { return nil } -func (c *ClamService) HandleOnce(req dto.OperateByID) error { - if cleaned := StopAllCronJob(true); cleaned { +func (c *ClamService) HandleOnce(id uint) error { + if active := clam.CheckClamIsActive(true, clamRepo); !active { return buserr.New("ErrClamdscanNotFound") } - clam, _ := clamRepo.Get(repo.WithByID(req.ID)) - if clam.ID == 0 { + clamItem, _ := clamRepo.Get(repo.WithByID(id)) + if clamItem.ID == 0 { return buserr.New("ErrRecordNotFound") } - if cmd.CheckIllegal(clam.Path) { - return buserr.New("ErrCmdIllegal") - } - timeNow := time.Now().Format(constant.DateTimeSlimLayout) - logFile := path.Join(global.Dir.DataDir, resultDir, clam.Name, timeNow) - if _, err := os.Stat(path.Dir(logFile)); err != nil { - _ = os.MkdirAll(path.Dir(logFile), os.ModePerm) + record := clamRepo.StartRecords(clamItem.ID) + taskItem, err := task.NewTaskWithOps("clam-"+clamItem.Name, task.TaskScan, task.TaskScopeClam, record.TaskID, clamItem.ID) + if err != nil { + return fmt.Errorf("new task for exec shell failed, err: %v", err) } + clam.AddScanTask(taskItem, clamItem, record.StartTime.Format(constant.DateTimeSlimLayout)) go func() { - strategy := "" - switch clam.InfectedStrategy { - case "remove": - strategy = "--remove" - case "move": - dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow) - strategy = "--move=" + dir - if _, err := os.Stat(dir); err != nil { - _ = os.MkdirAll(dir, os.ModePerm) - } - case "copy": - dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow) - strategy = "--copy=" + dir - if _, err := os.Stat(dir); err != nil { - _ = os.MkdirAll(dir, os.ModePerm) - } + err := taskItem.Execute() + taskRepo := repo.NewITaskRepo() + taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID)) + if len(taskItem.ID) == 0 { + record.TaskID = "" } - global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile) - stdout, err := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(30*time.Minute)).RunWithStdoutBashCf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile) if err != nil { - global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err) + clamRepo.EndRecords(record, constant.StatusFailed, err.Error()) + return } - handleAlert(stdout, clam.Name, clam.ID) + handleAlert(record.InfectedFiles, clamItem.Name, clamItem.ID) + clam.AnalysisFromLog(taskItem.LogFile, &record) + clamRepo.EndRecords(record, constant.StatusDone, "") }() return nil } -func (c *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) { +func (c *ClamService) SearchRecords(req dto.ClamLogSearch) (int64, interface{}, error) { clam, _ := clamRepo.Get(repo.WithByID(req.ClamID)) if clam.ID == 0 { return 0, nil, buserr.New("ErrRecordNotFound") } - logPaths := loadFileByName(clam.Name) - if len(logPaths) == 0 { - return 0, nil, nil - } - var filterFiles []string - nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) - for _, item := range logPaths { - t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, item, nyc) - if err != nil { - continue + total, records, err := clamRepo.PageRecords(req.Page, req.PageSize, clamRepo.WithByClamID(req.ClamID), repo.WithByStatus(req.Status), repo.WithByCreatedAt(req.StartTime, req.EndTime)) + if err != nil { + return 0, nil, err + } + var datas []dto.ClamRecord + for _, record := range records { + var item dto.ClamRecord + if err := copier.Copy(&item, &record); err != nil { + return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil) } - if t1.After(req.StartTime) && t1.Before(req.EndTime) { - filterFiles = append(filterFiles, item) - } - } - if len(filterFiles) == 0 { - return 0, nil, nil - } - - sort.Slice(filterFiles, func(i, j int) bool { - return filterFiles[i] > filterFiles[j] - }) - - var records []string - total, start, end := len(filterFiles), (req.Page-1)*req.PageSize, req.Page*req.PageSize - if start > total { - records = make([]string, 0) - } else { - if end >= total { - end = total - } - records = filterFiles[start:end] - } - - var datas []dto.ClamLog - for i := 0; i < len(records); i++ { - item := loadResultFromLog(path.Join(global.Dir.DataDir, resultDir, clam.Name, records[i])) datas = append(datas, item) } return int64(total), datas, nil } -func (c *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) { - logPath := path.Join(global.Dir.DataDir, resultDir, req.ClamName, req.RecordName) - var tail string - if req.Tail != "0" { - tail = req.Tail - } else { - tail = "+1" - } - cmd := exec.Command("tail", "-n", tail, logPath) - stdout, err := cmd.CombinedOutput() - if err != nil { - return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err) - } - return string(stdout), nil -} -func (c *ClamService) CleanRecord(req dto.OperateByID) error { - clam, _ := clamRepo.Get(repo.WithByID(req.ID)) - if clam.ID == 0 { - return buserr.New("ErrRecordNotFound") +func (c *ClamService) CleanRecord(id uint) error { + record, _ := clamRepo.ListRecord() + for _, item := range record { + if len(item.TaskID) != 0 { + continue + } + taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(item.TaskID)) + if len(taskItem.LogFile) != 0 { + _ = os.Remove(taskItem.LogFile) + } } - pathItem := path.Join(global.Dir.DataDir, resultDir, clam.Name) - _ = os.RemoveAll(pathItem) - return nil + return clamRepo.DeleteRecord(clamRepo.WithByClamID(id)) } func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) { filePath := "" switch req.Name { case "clamd": - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { filePath = "/etc/clamav/clamd.conf" } else { filePath = "/etc/clamd.d/scan.conf" @@ -440,13 +383,13 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) { if len(filePath) != 0 { break } - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { filePath = "/var/log/clamav/clamav.log" } else { filePath = "/var/log/clamd.scan" } case "freshclam": - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { filePath = "/etc/clamav/freshclam.conf" } else { filePath = "/etc/freshclam.conf" @@ -456,7 +399,7 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) { if len(filePath) != 0 { break } - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { filePath = "/var/log/clamav/freshclam.log" } else { filePath = "/var/log/freshclam.log" @@ -486,15 +429,15 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error { service := "" switch req.Name { case "clamd": - if c.serviceName == clamServiceNameUbuntu { - service = clamServiceNameUbuntu + if c.serviceName == constant.ClamServiceNameUbuntu { + service = constant.ClamServiceNameUbuntu filePath = "/etc/clamav/clamd.conf" } else { - service = clamServiceNameCentOs + service = constant.ClamServiceNameCentOs filePath = "/etc/clamd.d/scan.conf" } case "freshclam": - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { filePath = "/etc/clamav/freshclam.conf" } else { filePath = "/etc/freshclam.conf" @@ -516,86 +459,16 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error { return nil } -func StopAllCronJob(withCheck bool) bool { - if withCheck { - isActive := false - exist1, _ := systemctl.IsExist(clamServiceNameCentOs) - if exist1 { - isActive, _ = systemctl.IsActive(clamServiceNameCentOs) - } - exist2, _ := systemctl.IsExist(clamServiceNameUbuntu) - if exist2 { - isActive, _ = systemctl.IsActive(clamServiceNameUbuntu) - } - if isActive { - return false - } - } - clams, _ := clamRepo.List(repo.WithByStatus(constant.StatusEnable)) - for i := 0; i < len(clams); i++ { - global.Cron.Remove(cron.EntryID(clams[i].EntryID)) - _ = clamRepo.Update(clams[i].ID, map[string]interface{}{"status": constant.StatusDisable, "entry_id": 0}) - } - return true -} - -func loadFileByName(name string) []string { - var logPaths []string - pathItem := path.Join(global.Dir.DataDir, resultDir, name) - fileItems, err := os.ReadDir(pathItem) - if err != nil { - return logPaths - } - for _, item := range fileItems { - if !item.IsDir() { - logPaths = append(logPaths, item.Name()) - } - } - return logPaths -} -func loadResultFromLog(pathItem string) dto.ClamLog { - var data dto.ClamLog - data.Name = path.Base(pathItem) - data.Status = constant.StatusWaiting - file, err := os.ReadFile(pathItem) - if err != nil { - return data - } - lines := strings.Split(string(file), "\n") - for _, line := range lines { - if strings.Contains(line, "- SCAN SUMMARY -") { - data.Status = constant.StatusDone - } - if data.Status != constant.StatusDone { - continue - } - switch { - case strings.HasPrefix(line, "Infected files:"): - data.InfectedFiles = strings.TrimPrefix(line, "Infected files:") - case strings.HasPrefix(line, "Total errors:"): - data.TotalError = strings.TrimPrefix(line, "Total errors:") - case strings.HasPrefix(line, "Time:"): - if strings.Contains(line, "(") { - data.ScanTime = strings.ReplaceAll(strings.Split(line, "(")[1], ")", "") - continue - } - data.ScanTime = strings.TrimPrefix(line, "Time:") - case strings.HasPrefix(line, "Start Date:"): - data.ScanDate = strings.TrimPrefix(line, "Start Date:") - } - } - return data -} func (c *ClamService) loadLogPath(name string) string { confPath := "" if name == "clamd-log" { - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { confPath = "/etc/clamav/clamd.conf" } else { confPath = "/etc/clamd.d/scan.conf" } } else { - if c.serviceName == clamServiceNameUbuntu { + if c.serviceName == constant.ClamServiceNameUbuntu { confPath = "/etc/clamav/freshclam.conf" } else { confPath = "/etc/freshclam.conf" @@ -626,23 +499,16 @@ 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: ") { - infectedFiles, _ := strconv.Atoi(strings.TrimPrefix(line, "Infected files: ")) - if infectedFiles > 0 { - pushAlert := dto.PushAlert{ - TaskName: clamName, - AlertType: "clams", - EntryID: clamId, - Param: strconv.Itoa(infectedFiles), - } - _ = alert_push.PushAlert(pushAlert) - break - } - } - } +func handleAlert(infectedFiles, clamName string, clamId uint) { + itemInfected, _ := strconv.Atoi(strings.TrimSpace(infectedFiles)) + if itemInfected < 0 { + return } + pushAlert := dto.PushAlert{ + TaskName: clamName, + AlertType: "clams", + EntryID: clamId, + Param: strconv.Itoa(itemInfected), + } + _ = alert_push.PushAlert(pushAlert) } diff --git a/agent/app/task/task.go b/agent/app/task/task.go index c02d88a8a..1fbf90ffd 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -67,6 +67,7 @@ const ( TaskPush = "TaskPush" TaskClean = "TaskClean" TaskHandle = "TaskHandle" + TaskScan = "TaskScan" TaskExec = "TaskExec" ) @@ -77,6 +78,7 @@ const ( TaskScopeRuntime = "Runtime" TaskScopeDatabase = "Database" TaskScopeCronjob = "Cronjob" + TaskScopeClam = "Clam" TaskScopeSystem = "System" TaskScopeAppStore = "AppStore" TaskScopeSnapshot = "Snapshot" diff --git a/agent/constant/host_tool.go b/agent/constant/host_tool.go index 60f483b02..603117e9c 100644 --- a/agent/constant/host_tool.go +++ b/agent/constant/host_tool.go @@ -5,4 +5,8 @@ const ( Supervisor = "supervisor" SupervisorConfigPath = "SupervisorConfigPath" SupervisorServiceName = "SupervisorServiceName" + + ClamServiceNameCentOs = "clamd@scan.service" + ClamServiceNameUbuntu = "clamav-daemon.service" + FreshClamService = "clamav-freshclam.service" ) diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 2eef03d6a..e4265f3e4 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: 'Task is running' CustomAppstore: 'Custom application warehouse' TaskExec: 'Execute' +# task - clam +Clamscan: "Scan {{ .name }}" +TaskScan: "Scan" + # task - ai OllamaModelPull: 'Pull Ollama model {{ .name }}' OllamaModelSize: 'Get the size of Ollama model {{ .name }}' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index c646dbb87..e382b660a 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: 'タスクは実行中です' CustomAppstore: 'カスタム アプリケーション ウェアハウス' TaskExec: '実行' +# task - clam +Clamscan: "{{ .name }} をスキャン" +TaskScan: "スキャン" + #task - ai OllamaModelPull: 'Ollama モデル {{ .name }} をプルします' OllamaModelSize: "Ollama モデル {{ .name }} のサイズを取得します" diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index 4f316713c..645b97566 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -349,7 +349,11 @@ SubTask: '하위 작업' RuntimeExtension: '런타임 환경 확장' TaskIsExecuting: '작업이 실행 중입니다' CustomAppstore: '사용자 정의 애플리케이션 웨어하우스' -TaskExec": '실행' +TaskExec: '실행' + +# task - clam +Clamscan: "{{ .name }} 스캔" +TaskScan: "스캔" # 작업 - ai OllamaModelPull: 'Ollama 모델 {{ .name }}을(를) 끌어오세요' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index 0afb80eaf..2c3c89747 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: 'Tugas sedang berjalan' CustomAppstore: 'Gudang aplikasi tersuai' TaskExec: 'Laksanakan' +# task - clam +Clamscan: "Imbas {{ .name }}" +TaskScan: "Imbas" + # tugasan - ai OllamaModelPull: 'Tarik model Ollama {{ .name }}' OllamaModelSize: 'Dapatkan saiz model Ollama {{ .name }}' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 0c7a538de..3e17107fd 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: 'A tarefa está em execução' CustomAppstore: 'Armazém de aplicativos personalizados' TaskExec: 'Executar' +# task - clam +Clamscan: "Escanear {{ .name }}" +TaskScan: "Escanear" + # tarefa - ai OllamaModelPull: 'Puxar modelo Ollama {{ .name }}' OllamaModelSize: 'Obtenha o tamanho do modelo Ollama {{ .name }}' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index ee4361c4b..fe35d0974 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: 'Задача выполняется' CustomAppstore: 'Хранилище пользовательских приложений' TaskExec: 'Выполнить' +# task - clam +Clamscan: "Сканировать {{ .name }}" +TaskScan: "Сканировать" + # задача - ай OllamaModelPull: 'Вытянуть модель Ollama {{ .name }}' OllamaModelSize: 'Получить размер модели Ollama {{ .name }}' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index c5831213c..4a684dc45 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -352,6 +352,10 @@ TaskIsExecuting: 'Görev çalışıyor' CustomAppstore: 'Özel uygulama deposu' TaskExec: 'Çalıştır' +# task - clam +Clamscan: "{{ .name }} Tara" +TaskScan: "Tara" + # task - ai OllamaModelPull: 'Ollama modeli {{ .name }} çek' OllamaModelSize: 'Ollama modeli {{ .name }} boyutunu al' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 9eccdf39b..c8c98ac9e 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -350,6 +350,10 @@ TaskIsExecuting: '任務正在運作' CustomAppstore: '自訂應用程式倉庫' TaskExec: '執行' +# task - clam +Clamscan: "掃描 {{ .name }}" +TaskScan: "掃描" + # task - ai OllamaModelPull: '拉取 Ollama 模型{{ .name }} ' OllamaModelSize: '取得 Ollama 模型{{ .name }} 大小' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index e9da43e9b..46a9ded50 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -351,6 +351,10 @@ TaskIsExecuting: "任务正在运行" CustomAppstore: "自定义应用仓库" TaskExec: "执行" +# task - clam +Clamscan: "扫描 {{ .name }}" +TaskScan: "扫描" + # task - ai OllamaModelPull: "拉取 Ollama 模型 {{ .name }} " OllamaModelSize: "获取 Ollama 模型 {{ .name }} 大小 " diff --git a/agent/init/hook/hook.go b/agent/init/hook/hook.go index 7bdc2675b..e0a094f73 100644 --- a/agent/init/hook/hook.go +++ b/agent/init/hook/hook.go @@ -18,6 +18,7 @@ import ( func Init() { initGlobalData() handleCronjobStatus() + handleClamStatus() handleSnapStatus() handleOllamaModelStatus() @@ -88,6 +89,24 @@ func handleCronjobStatus() { } } +func handleClamStatus() { + var jobRecords []model.ClamRecord + _ = global.DB.Model(&model.Clam{}).Where("is_executing = ?", true).Updates(map[string]interface{}{"is_executing": false}).Error + _ = global.DB.Where("status = ?", constant.StatusWaiting).Find(&jobRecords).Error + for _, record := range jobRecords { + err := global.DB.Model(&model.ClamRecord{}).Where("status = ?", constant.StatusWaiting). + Updates(map[string]interface{}{ + "status": constant.StatusFailed, + "message": "the task was interrupted due to the restart of the 1panel service", + }).Error + + if err != nil { + global.LOG.Errorf("Failed to update job ID: %v, Error:%v", record.ID, err) + continue + } + } +} + func handleOllamaModelStatus() { message := "the task was interrupted due to the restart of the 1panel service" _ = global.DB.Model(&model.OllamaModel{}).Where("status = ?", constant.StatusWaiting).Updates(map[string]interface{}{"status": constant.StatusCanceled, "message": message}).Error diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 4efade8ca..4da705642 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -19,7 +19,7 @@ import ( ) var AddTable = &gormigrate.Migration{ - ID: "20250828-add-table", + ID: "20250902-add-table", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate( &model.AppDetail{}, @@ -64,6 +64,7 @@ var AddTable = &gormigrate.Migration{ &model.AppIgnoreUpgrade{}, &model.McpServer{}, &model.RootCert{}, + &model.ClamRecord{}, ) }, } diff --git a/agent/router/ro_toolbox.go b/agent/router/ro_toolbox.go index a65f02293..06693e8e7 100644 --- a/agent/router/ro_toolbox.go +++ b/agent/router/ro_toolbox.go @@ -45,7 +45,6 @@ func (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) { toolboxRouter.POST("/clam/search", baseApi.SearchClam) toolboxRouter.POST("/clam/record/search", baseApi.SearchClamRecord) toolboxRouter.POST("/clam/record/clean", baseApi.CleanClamRecord) - toolboxRouter.POST("/clam/record/log", baseApi.LoadClamRecordLog) toolboxRouter.POST("/clam/file/search", baseApi.SearchClamFile) toolboxRouter.POST("/clam/file/update", baseApi.UpdateFile) toolboxRouter.POST("/clam", baseApi.CreateClam) diff --git a/agent/utils/clam/clam.go b/agent/utils/clam/clam.go new file mode 100644 index 000000000..a48e99bc8 --- /dev/null +++ b/agent/utils/clam/clam.go @@ -0,0 +1,88 @@ +package clam + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/app/task" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/i18n" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/systemctl" + "github.com/robfig/cron/v3" +) + +func AddScanTask(taskItem *task.Task, clam model.Clam, timeNow string) { + taskItem.AddSubTask(i18n.GetWithName("Clamscan", clam.Path), func(t *task.Task) error { + strategy := "" + switch clam.InfectedStrategy { + case "remove": + strategy = "--remove" + case "move", "copy": + dir := path.Join(clam.InfectedDir, "1panel-infected", clam.Name, timeNow) + taskItem.Log("infected dir: " + dir) + if _, err := os.Stat(dir); err != nil { + _ = os.MkdirAll(dir, os.ModePerm) + } + strategy = fmt.Sprintf("--%s=%s", clam.InfectedStrategy, dir) + } + taskItem.Logf("clamdscan --fdpass %s %s", strategy, clam.Path) + mgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(10*time.Hour), cmd.WithTask(*taskItem)) + stdout, err := mgr.RunWithStdoutBashCf("clamdscan --fdpass %s %s", strategy, clam.Path) + if err != nil { + return fmt.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err) + } + return nil + }, nil) +} + +func CheckClamIsActive(withCheck bool, clamRepo repo.IClamRepo) bool { + if withCheck { + isActive := false + exist1, _ := systemctl.IsExist(constant.ClamServiceNameCentOs) + if exist1 { + isActive, _ = systemctl.IsActive(constant.ClamServiceNameCentOs) + } + exist2, _ := systemctl.IsExist(constant.ClamServiceNameUbuntu) + if exist2 { + isActive, _ = systemctl.IsActive(constant.ClamServiceNameUbuntu) + } + if isActive { + return true + } + } + clams, _ := clamRepo.List(repo.WithByStatus(constant.StatusEnable)) + for i := 0; i < len(clams); i++ { + global.Cron.Remove(cron.EntryID(clams[i].EntryID)) + _ = clamRepo.Update(clams[i].ID, map[string]interface{}{"status": constant.StatusDisable, "entry_id": 0}) + } + return false +} + +func AnalysisFromLog(pathItem string, record *model.ClamRecord) { + file, err := os.ReadFile(pathItem) + if err != nil { + return + } + lines := strings.Split(string(file), "\n") + for _, line := range lines { + if len(line) < 20 { + continue + } + line = line[20:] + switch { + case strings.HasPrefix(line, "Infected files: "): + record.InfectedFiles = strings.TrimPrefix(line, "Infected files: ") + case strings.HasPrefix(line, "Total errors: "): + record.TotalError = strings.TrimPrefix(line, "Total errors: ") + case strings.HasPrefix(line, "Time: "): + record.ScanTime = strings.TrimPrefix(line, "Time: ") + } + } +} diff --git a/agent/utils/xpack/xpack.go b/agent/utils/xpack/xpack.go index 6ba1c2520..75a6f3c42 100644 --- a/agent/utils/xpack/xpack.go +++ b/agent/utils/xpack/xpack.go @@ -20,7 +20,7 @@ import ( func RemoveTamper(website string) {} -func StartClam(startClam model.Clam, isUpdate bool) (int, error) { +func StartClam(startClam *model.Clam, isUpdate bool) (int, error) { return 0, buserr.New("ErrXpackNotFound") } diff --git a/frontend/src/api/interface/toolbox.ts b/frontend/src/api/interface/toolbox.ts index 2bfbfebc4..9038b888e 100644 --- a/frontend/src/api/interface/toolbox.ts +++ b/frontend/src/api/interface/toolbox.ts @@ -174,12 +174,15 @@ export namespace Toolbox { clamName: string; recordName: string; } - export interface ClamLog { - name: string; + export interface ClamRecord { + id: number; + taskID: string; scanDate: string; scanTime: string; - totalError: string; infectedFiles: string; + totalError: string; + startTime: Date; status: string; + message: string; } } diff --git a/frontend/src/api/modules/toolbox.ts b/frontend/src/api/modules/toolbox.ts index 99489f35f..3eb174427 100644 --- a/frontend/src/api/modules/toolbox.ts +++ b/frontend/src/api/modules/toolbox.ts @@ -115,10 +115,7 @@ export const cleanClamRecord = (id: number) => { return http.post(`/toolbox/clam/record/clean`, { id: id }); }; export const searchClamRecord = (param: Toolbox.ClamSearchLog) => { - return http.post>(`/toolbox/clam/record/search`, param); -}; -export const getClamRecordLog = (param: Toolbox.ClamRecordReq) => { - return http.post(`/toolbox/clam/record/log`, param); + return http.post>(`/toolbox/clam/record/search`, param); }; export const searchClamFile = (name: string, tail: string) => { return http.post(`/toolbox/clam/file/search`, { name: name, tail: tail }); @@ -144,7 +141,7 @@ export const updateClam = (params: Toolbox.ClamUpdate) => { export const updateClamStatus = (id: number, status: string) => { return http.post(`/toolbox/clam/status/update`, { id: id, status: status }); }; -export const deleteClam = (params: { ids: number[]; removeRecord: boolean; removeInfected: boolean }) => { +export const deleteClam = (params: { ids: number[]; removeInfected: boolean }) => { return http.post(`/toolbox/clam/del`, params); }; export const handleClamScan = (id: number) => { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6a2196f41..e5336ddb3 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1313,7 +1313,6 @@ const message = { notStart: 'ClamAV service is currently not running, please start it first!', removeRecord: 'Delete peport files', noRecords: 'Click the "Trigger" button to start the scan and you will see records here.', - removeResultHelper: 'Delete report files generated during task execution to free up storage space.', removeInfected: 'Delete virus files', removeInfectedHelper: 'Delete virus files detected during the task to ensure server security and normal operation.', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 1865510f1..6748b526d 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -1259,7 +1259,6 @@ const message = { notStart: 'Clamav Serviceは現在実行されていません。最初に開始してください!', removeRecord: 'ペポートファイルを削除します', noRecords: '[トリガー]ボタンをクリックしてスキャンを開始すると、ここにレコードが表示されます。', - removeResultHelper: 'タスク実行中に生成されたレポートファイルを削除して、ストレージスペースを解放します。', removeInfected: 'ウイルスファイルを削除します', removeInfectedHelper: 'サーバーのセキュリティと通常の操作を確保するために、タスク中に検出されたウイルスファイルを削除します。', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 82e623601..a7b82a64f 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -1246,7 +1246,6 @@ const message = { notStart: 'ClamAV 서비스가 현재 실행 중이 아닙니다. 먼저 시작하세요!', removeRecord: '보고서 파일 삭제', noRecords: '"Trigger" 버튼을 클릭하여 스캔을 시작하면 이곳에서 기록을 확인할 수 있습니다.', - removeResultHelper: '작업 실행 중 생성된 보고서 파일을 삭제하여 저장 공간을 확보합니다.', removeInfected: '바이러스 파일 삭제', removeInfectedHelper: '작업 중 감지된 바이러스 파일을 삭제하여 서버 보안 및 정상 작동을 보장합니다.', clamCreate: '스캔 규칙 생성', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index f8b97ad0f..2c13aa827 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -1299,8 +1299,6 @@ const message = { notStart: 'Perkhidmatan ClamAV tidak berjalan pada masa ini, sila mulakan dahulu!', removeRecord: 'Padam fail laporan', noRecords: 'Klik butang "Picu" untuk memulakan imbasan dan anda akan melihat rekod di sini.', - removeResultHelper: - 'Padam fail laporan yang dijana semasa pelaksanaan tugas untuk membebaskan ruang storan.', removeInfected: 'Padam fail virus', removeInfectedHelper: 'Padam fail virus yang dikesan semasa tugas untuk memastikan keselamatan pelayan dan operasi normal.', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index b2555831c..cf3dfcc4f 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -1286,8 +1286,6 @@ const message = { notStart: 'O serviço ClamAV não está em execução, por favor, inicie-o primeiro!', removeRecord: 'Excluir arquivos de relatório', noRecords: 'Clique no botão "Acionar" para iniciar o scan e você verá registros aqui.', - removeResultHelper: - 'Excluir arquivos de relatório gerados durante a execução da tarefa para liberar espaço de armazenamento.', removeInfected: 'Excluir arquivos infectados', removeInfectedHelper: 'Excluir arquivos de vírus detectados durante a tarefa para garantir a segurança e o funcionamento normal do servidor.', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 32f3832c0..f39e32eb8 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -1290,7 +1290,6 @@ const message = { notStart: 'Служба ClamAV в настоящее время не запущена, пожалуйста, сначала запустите её!', removeRecord: 'Удалить файлы отчетов', noRecords: 'Нажмите кнопку "Запустить" для начала сканирования, и вы увидите записи здесь.', - removeResultHelper: 'Удалить файлы отчетов, созданные во время выполнения задачи, чтобы освободить место.', removeInfected: 'Удалить зараженные файлы', removeInfectedHelper: 'Удалить зараженные файлы, обнаруженные во время задачи, для обеспечения безопасности и нормальной работы сервера.', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 8492f3b2f..3aaed55a4 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -1329,8 +1329,6 @@ const message = { notStart: 'ClamAV servisi şu anda çalışmıyor, lütfen önce başlatın!', removeRecord: 'Rapor dosyalarını sil', noRecords: 'Taramayı başlatmak için "Tetikle" düğmesine tıklayın ve kayıtları burada göreceksiniz.', - removeResultHelper: - 'Depolama alanını boşaltmak için görev yürütme sırasında oluşturulan rapor dosyalarını silin.', removeInfected: 'Virüs dosyalarını sil', removeInfectedHelper: 'Sunucu güvenliğini ve normal çalışmasını sağlamak için görev sırasında tespit edilen virüs dosyalarını silin.', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 168b0d1b7..35b837b35 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -1244,7 +1244,6 @@ const message = { notStart: '當前未 ClamAV 服務,請先開啟!', removeRecord: '刪除報告文件', noRecords: '點擊“執行”按鈕開始掃描,掃描結果將會記錄在這裏。', - removeResultHelper: '刪除任務執行過程中生成的報告文件,以清理存儲空間。', removeInfected: '刪除病毒文件', removeInfectedHelper: '刪除任務檢測到的病毒文件,以確保伺服器的安全和正常運行。', clamCreate: '創建掃描規則', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 3e74f48b1..a5d926617 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1243,7 +1243,6 @@ const message = { notStart: '当前未开启 ClamAV 服务,请先开启!', removeRecord: '删除报告文件', noRecords: '点击“执行”按钮开始扫描,扫描结果将会记录在这里。', - removeResultHelper: '删除任务执行过程中生成的报告文件,以清理存储空间。', removeInfected: '删除病毒文件', removeInfectedHelper: '删除任务检测到的病毒文件,以确保服务器的安全和正常运行。', clamCreate: '创建扫描规则', diff --git a/frontend/src/views/cronjob/cronjob/record/index.vue b/frontend/src/views/cronjob/cronjob/record/index.vue index 11f1a4a07..62b972606 100644 --- a/frontend/src/views/cronjob/cronjob/record/index.vue +++ b/frontend/src/views/cronjob/cronjob/record/index.vue @@ -306,7 +306,7 @@ const timeRangeLoad = ref<[Date, Date]>([ const searchInfo = reactive({ cacheSizeKey: 'cronjob-record-page-size', page: 1, - pageSize: 8, + pageSize: 10, recordTotal: 0, cronjobID: 0, startTime: new Date(), diff --git a/frontend/src/views/toolbox/clam/index.vue b/frontend/src/views/toolbox/clam/index.vue index 2fe117575..f2448884a 100644 --- a/frontend/src/views/toolbox/clam/index.vue +++ b/frontend/src/views/toolbox/clam/index.vue @@ -124,10 +124,17 @@ + prop="lastRecordTime" + > + +