mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 07:00:48 +08:00
fix: Adjust the display logic of the clam scan report (#10264)
Refs #10251
This commit is contained in:
parent
23876ed186
commit
c61828fb39
35 changed files with 561 additions and 470 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -5,4 +5,8 @@ const (
|
|||
Supervisor = "supervisor"
|
||||
SupervisorConfigPath = "SupervisorConfigPath"
|
||||
SupervisorServiceName = "SupervisorServiceName"
|
||||
|
||||
ClamServiceNameCentOs = "clamd@scan.service"
|
||||
ClamServiceNameUbuntu = "clamav-daemon.service"
|
||||
FreshClamService = "clamav-freshclam.service"
|
||||
)
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -351,6 +351,10 @@ TaskIsExecuting: 'タスクは実行中です'
|
|||
CustomAppstore: 'カスタム アプリケーション ウェアハウス'
|
||||
TaskExec: '実行'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "{{ .name }} をスキャン"
|
||||
TaskScan: "スキャン"
|
||||
|
||||
#task - ai
|
||||
OllamaModelPull: 'Ollama モデル {{ .name }} をプルします'
|
||||
OllamaModelSize: "Ollama モデル {{ .name }} のサイズを取得します"
|
||||
|
|
|
@ -349,7 +349,11 @@ SubTask: '하위 작업'
|
|||
RuntimeExtension: '런타임 환경 확장'
|
||||
TaskIsExecuting: '작업이 실행 중입니다'
|
||||
CustomAppstore: '사용자 정의 애플리케이션 웨어하우스'
|
||||
TaskExec": '실행'
|
||||
TaskExec: '실행'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "{{ .name }} 스캔"
|
||||
TaskScan: "스캔"
|
||||
|
||||
# 작업 - ai
|
||||
OllamaModelPull: 'Ollama 모델 {{ .name }}을(를) 끌어오세요'
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -351,6 +351,10 @@ TaskIsExecuting: 'Задача выполняется'
|
|||
CustomAppstore: 'Хранилище пользовательских приложений'
|
||||
TaskExec: 'Выполнить'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "Сканировать {{ .name }}"
|
||||
TaskScan: "Сканировать"
|
||||
|
||||
# задача - ай
|
||||
OllamaModelPull: 'Вытянуть модель Ollama {{ .name }}'
|
||||
OllamaModelSize: 'Получить размер модели Ollama {{ .name }}'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -350,6 +350,10 @@ TaskIsExecuting: '任務正在運作'
|
|||
CustomAppstore: '自訂應用程式倉庫'
|
||||
TaskExec: '執行'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "掃描 {{ .name }}"
|
||||
TaskScan: "掃描"
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: '拉取 Ollama 模型{{ .name }} '
|
||||
OllamaModelSize: '取得 Ollama 模型{{ .name }} 大小'
|
||||
|
|
|
@ -351,6 +351,10 @@ TaskIsExecuting: "任务正在运行"
|
|||
CustomAppstore: "自定义应用仓库"
|
||||
TaskExec: "执行"
|
||||
|
||||
# task - clam
|
||||
Clamscan: "扫描 {{ .name }}"
|
||||
TaskScan: "扫描"
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: "拉取 Ollama 模型 {{ .name }} "
|
||||
OllamaModelSize: "获取 Ollama 模型 {{ .name }} 大小 "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
88
agent/utils/clam/clam.go
Normal file
88
agent/utils/clam/clam.go
Normal file
|
@ -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: ")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ResPage<Toolbox.ClamLog>>(`/toolbox/clam/record/search`, param);
|
||||
};
|
||||
export const getClamRecordLog = (param: Toolbox.ClamRecordReq) => {
|
||||
return http.post<string>(`/toolbox/clam/record/log`, param);
|
||||
return http.post<ResPage<Toolbox.ClamRecord>>(`/toolbox/clam/record/search`, param);
|
||||
};
|
||||
export const searchClamFile = (name: string, tail: string) => {
|
||||
return http.post<string>(`/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) => {
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -1259,7 +1259,6 @@ const message = {
|
|||
notStart: 'Clamav Serviceは現在実行されていません。最初に開始してください!',
|
||||
removeRecord: 'ペポートファイルを削除します',
|
||||
noRecords: '[トリガー]ボタンをクリックしてスキャンを開始すると、ここにレコードが表示されます。',
|
||||
removeResultHelper: 'タスク実行中に生成されたレポートファイルを削除して、ストレージスペースを解放します。',
|
||||
removeInfected: 'ウイルスファイルを削除します',
|
||||
removeInfectedHelper:
|
||||
'サーバーのセキュリティと通常の操作を確保するために、タスク中に検出されたウイルスファイルを削除します。',
|
||||
|
|
|
@ -1246,7 +1246,6 @@ const message = {
|
|||
notStart: 'ClamAV 서비스가 현재 실행 중이 아닙니다. 먼저 시작하세요!',
|
||||
removeRecord: '보고서 파일 삭제',
|
||||
noRecords: '"Trigger" 버튼을 클릭하여 스캔을 시작하면 이곳에서 기록을 확인할 수 있습니다.',
|
||||
removeResultHelper: '작업 실행 중 생성된 보고서 파일을 삭제하여 저장 공간을 확보합니다.',
|
||||
removeInfected: '바이러스 파일 삭제',
|
||||
removeInfectedHelper: '작업 중 감지된 바이러스 파일을 삭제하여 서버 보안 및 정상 작동을 보장합니다.',
|
||||
clamCreate: '스캔 규칙 생성',
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -1290,7 +1290,6 @@ const message = {
|
|||
notStart: 'Служба ClamAV в настоящее время не запущена, пожалуйста, сначала запустите её!',
|
||||
removeRecord: 'Удалить файлы отчетов',
|
||||
noRecords: 'Нажмите кнопку "Запустить" для начала сканирования, и вы увидите записи здесь.',
|
||||
removeResultHelper: 'Удалить файлы отчетов, созданные во время выполнения задачи, чтобы освободить место.',
|
||||
removeInfected: 'Удалить зараженные файлы',
|
||||
removeInfectedHelper:
|
||||
'Удалить зараженные файлы, обнаруженные во время задачи, для обеспечения безопасности и нормальной работы сервера.',
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -1244,7 +1244,6 @@ const message = {
|
|||
notStart: '當前未 ClamAV 服務,請先開啟!',
|
||||
removeRecord: '刪除報告文件',
|
||||
noRecords: '點擊“執行”按鈕開始掃描,掃描結果將會記錄在這裏。',
|
||||
removeResultHelper: '刪除任務執行過程中生成的報告文件,以清理存儲空間。',
|
||||
removeInfected: '刪除病毒文件',
|
||||
removeInfectedHelper: '刪除任務檢測到的病毒文件,以確保伺服器的安全和正常運行。',
|
||||
clamCreate: '創建掃描規則',
|
||||
|
|
|
@ -1243,7 +1243,6 @@ const message = {
|
|||
notStart: '当前未开启 ClamAV 服务,请先开启!',
|
||||
removeRecord: '删除报告文件',
|
||||
noRecords: '点击“执行”按钮开始扫描,扫描结果将会记录在这里。',
|
||||
removeResultHelper: '删除任务执行过程中生成的报告文件,以清理存储空间。',
|
||||
removeInfected: '删除病毒文件',
|
||||
removeInfectedHelper: '删除任务检测到的病毒文件,以确保服务器的安全和正常运行。',
|
||||
clamCreate: '创建扫描规则',
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -124,10 +124,17 @@
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('cronjob.lastRecordTime')"
|
||||
:min-width="100"
|
||||
prop="lastHandleDate"
|
||||
:min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
prop="lastRecordTime"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.lastRecordStatus === 'Done'" icon="Select" link type="success" />
|
||||
<el-button v-if="row.lastRecordStatus === 'Failed'" icon="CloseBold" link type="danger" />
|
||||
<el-button v-if="row.lastRecordStatus === 'Waiting'" :loading="true" link type="info" />
|
||||
{{ row.lastRecordTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.description')" prop="description" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<fu-input-rw-switch v-model="row.description" @blur="onChange(row)" />
|
||||
|
@ -147,10 +154,6 @@
|
|||
<OpDialog ref="opRef" @search="search" @submit="onSubmitDelete()">
|
||||
<template #content>
|
||||
<el-form class="mt-4 mb-1" ref="deleteForm" label-position="left">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="removeRecord" :label="$t('toolbox.clam.removeRecord')" />
|
||||
<span class="input-help">{{ $t('toolbox.clam.removeResultHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="removeInfected" :label="$t('toolbox.clam.removeInfected')" />
|
||||
<span class="input-help">{{ $t('toolbox.clam.removeInfectedHelper') }}</span>
|
||||
|
@ -201,7 +204,6 @@ const operateIDs = ref();
|
|||
const dialogLogRef = ref();
|
||||
const isRecordShow = ref();
|
||||
|
||||
const removeRecord = ref();
|
||||
const removeInfected = ref();
|
||||
|
||||
const isSettingShow = ref();
|
||||
|
@ -305,7 +307,7 @@ const onDelete = async (row: Toolbox.ClamInfo | null) => {
|
|||
|
||||
const onSubmitDelete = async () => {
|
||||
loading.value = true;
|
||||
await deleteClam({ ids: operateIDs.value, removeRecord: removeRecord.value, removeInfected: removeInfected.value })
|
||||
await deleteClam({ ids: operateIDs.value, removeInfected: removeInfected.value })
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||
|
|
|
@ -2,145 +2,167 @@
|
|||
<div v-if="recordShow" v-loading="loading">
|
||||
<div class="app-status p-mt-20">
|
||||
<el-card>
|
||||
<div>
|
||||
<el-tag class="float-left" effect="dark" type="success">
|
||||
{{ $t('commons.table.name') }}: {{ dialogData.rowData.name }}
|
||||
</el-tag>
|
||||
<el-popover
|
||||
v-if="dialogData.rowData.path.length >= 35"
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="250"
|
||||
:content="dialogData.rowData.path"
|
||||
>
|
||||
<template #reference>
|
||||
<el-tag style="float: left" effect="dark" type="success">
|
||||
{{ $t('file.path') }}: {{ dialogData.rowData.path.substring(0, 20) }}...
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-tag
|
||||
v-if="dialogData.rowData.path.length < 35"
|
||||
class="float-left ml-5"
|
||||
effect="dark"
|
||||
type="success"
|
||||
>
|
||||
{{ $t('toolbox.clam.scanDir') }}: {{ dialogData.rowData.path }}
|
||||
</el-tag>
|
||||
<div class="flex w-full flex-col gap-4 md:flex-row">
|
||||
<div class="flex flex-wrap gap-4 ml-3">
|
||||
<el-tag class="float-left" effect="dark" type="success">
|
||||
{{ $t('commons.table.name') }}: {{ dialogData.rowData.name }}
|
||||
</el-tag>
|
||||
<el-popover
|
||||
v-if="dialogData.rowData.path.length >= 35"
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="250"
|
||||
:content="dialogData.rowData.path"
|
||||
>
|
||||
<template #reference>
|
||||
<el-tag style="float: left" effect="dark" type="success">
|
||||
{{ $t('file.path') }}: {{ dialogData.rowData.path.substring(0, 20) }}...
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-tag
|
||||
v-if="dialogData.rowData.path.length < 35"
|
||||
class="float-left"
|
||||
effect="dark"
|
||||
type="success"
|
||||
>
|
||||
{{ $t('toolbox.clam.scanDir') }}: {{ dialogData.rowData.path }}
|
||||
</el-tag>
|
||||
|
||||
<span class="buttons">
|
||||
<el-button type="primary" @click="onHandle(dialogData.rowData)" link>
|
||||
{{ $t('commons.button.handle') }}
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button :disabled="!hasRecords" type="primary" @click="onClean" link>
|
||||
{{ $t('commons.button.clean') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span class="mt-0.5">
|
||||
<el-button type="primary" @click="onHandle(dialogData.rowData)" link>
|
||||
{{ $t('commons.button.handle') }}
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button :disabled="!hasRecords" type="primary" @click="onClean" link>
|
||||
{{ $t('commons.button.clean') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<LayoutContent :title="$t('cronjob.record')" :reload="true">
|
||||
<template #search>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-date-picker
|
||||
style="width: calc(100% - 20px)"
|
||||
@change="search()"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
></el-date-picker>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #rightToolBar>
|
||||
<el-date-picker
|
||||
class="mr-2.5"
|
||||
@change="search(true)"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
></el-date-picker>
|
||||
<el-select @change="search(true)" v-model="searchInfo.status" class="p-w-200">
|
||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value="" />
|
||||
<el-option :label="$t('commons.status.done')" value="Done" />
|
||||
<el-option :label="$t('commons.status.waiting')" value="Waiting" />
|
||||
<el-option :label="$t('commons.status.failed')" value="Failed" />
|
||||
</el-select>
|
||||
<TableRefresh @search="search(false)" />
|
||||
</template>
|
||||
<template #main>
|
||||
<div class="mainClass">
|
||||
<el-row :gutter="20" v-show="hasRecords" class="mainRowClass">
|
||||
<el-row :gutter="20" v-show="hasRecords" class="mainRowClass row-box">
|
||||
<el-col :span="7">
|
||||
<div class="infinite-list" style="overflow: auto">
|
||||
<el-table
|
||||
style="cursor: pointer"
|
||||
:data="records"
|
||||
border
|
||||
:show-header="false"
|
||||
@row-click="clickRow"
|
||||
>
|
||||
<el-table-column>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.name === currentRecord.name" class="select-sign"></span>
|
||||
<Status :status="row.status" />
|
||||
<span>
|
||||
{{ row.name }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="page-item">
|
||||
<el-pagination
|
||||
:page-size="searchInfo.pageSize"
|
||||
:current-page="searchInfo.page"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
:pager-count="3"
|
||||
:page-sizes="[6, 8, 10, 12, 14]"
|
||||
small
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="searchInfo.recordTotal"
|
||||
/>
|
||||
</div>
|
||||
<el-card class="el-card">
|
||||
<div class="infinite-list" style="overflow: auto">
|
||||
<el-table
|
||||
style="cursor: pointer"
|
||||
:data="records"
|
||||
border
|
||||
:show-header="false"
|
||||
@row-click="forDetail"
|
||||
>
|
||||
<el-table-column>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.id === currentRecord.id" class="select-sign"></span>
|
||||
<Status class="mr-2 ml-1 float-left" :status="row.status" />
|
||||
<div class="mt-0.5 float-left">
|
||||
<span>
|
||||
{{ dateFormat(0, 0, row.startTime) }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button
|
||||
class="mt-0.5 float-right"
|
||||
type="danger"
|
||||
icon="Warning"
|
||||
link
|
||||
v-if="row.infectedFiles && row.infectedFiles !== '0'"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="page-item">
|
||||
<el-pagination
|
||||
:page-size="searchInfo.pageSize"
|
||||
:current-page="searchInfo.page"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
:pager-count="5"
|
||||
:page-sizes="[5, 10, 20, 50, 100, 200, 500, 1000]"
|
||||
small
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="searchInfo.recordTotal"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="17">
|
||||
<el-form label-position="top" :v-key="refresh">
|
||||
<el-row>
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.interval') }}</span>
|
||||
</template>
|
||||
<span class="status-count">
|
||||
{{ currentRecord?.status === 'Done' ? currentRecord?.scanTime : '-' }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('toolbox.clam.infectedFiles') }}</span>
|
||||
</template>
|
||||
<span class="status-count" v-if="!hasInfectedDir()">
|
||||
{{ currentRecord?.status === 'Done' ? currentRecord?.infectedFiles : '-' }}
|
||||
</span>
|
||||
<div class="count" v-else>
|
||||
<span @click="toFolder(currentRecord?.name)">
|
||||
<el-card class="el-card">
|
||||
<el-form label-position="top" :v-key="refresh">
|
||||
<el-row>
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.interval') }}</span>
|
||||
</template>
|
||||
<span class="status-count">
|
||||
{{ currentRecord?.status === 'Done' ? currentRecord?.scanTime : '-' }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('toolbox.clam.infectedFiles') }}</span>
|
||||
</template>
|
||||
<span class="status-count" v-if="!hasInfectedDir()">
|
||||
{{
|
||||
currentRecord?.status === 'Done'
|
||||
? currentRecord?.infectedFiles
|
||||
: '-'
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-select
|
||||
class="descriptionWide"
|
||||
@change="search"
|
||||
v-model.number="searchInfo.tail"
|
||||
>
|
||||
<template #prefix>{{ $t('toolbox.clam.scanResult') }}</template>
|
||||
<el-option :value="0" :label="$t('commons.table.all')" />
|
||||
<el-option :value="10" :label="10" />
|
||||
<el-option :value="100" :label="100" />
|
||||
<el-option :value="200" :label="200" />
|
||||
<el-option :value="500" :label="500" />
|
||||
<el-option :value="1000" :label="1000" />
|
||||
</el-select>
|
||||
<HighlightLog :modelValue="logContent" :heightDiff="533" />
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="count" v-else>
|
||||
<span @click="toFolder(currentRecord)">
|
||||
{{
|
||||
currentRecord?.status === 'Done'
|
||||
? currentRecord?.infectedFiles
|
||||
: '-'
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row v-if="currentRecord?.taskID && currentRecord?.taskID != ''">
|
||||
<LogFile
|
||||
:defaultButton="true"
|
||||
class="w-full"
|
||||
:key="currentRecord?.taskID"
|
||||
@stop-reading="search(false)"
|
||||
:heightDiff="430"
|
||||
:config="{
|
||||
type: 'task',
|
||||
colorMode: 'task',
|
||||
taskID: currentRecord?.taskID,
|
||||
tail: true,
|
||||
}"
|
||||
/>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
@ -158,30 +180,28 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, reactive, ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { shortcuts } from '@/utils/shortcuts';
|
||||
import { dateFormat, dateFormatForName } from '@/utils/util';
|
||||
import { Toolbox } from '@/api/interface/toolbox';
|
||||
import HighlightLog from '@/components/log/hightlight-log/index.vue';
|
||||
import { cleanClamRecord, getClamRecordLog, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';
|
||||
import LogFile from '@/components/log/file/index.vue';
|
||||
import { cleanClamRecord, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';
|
||||
import { routerToFileWithPath } from '@/utils/router';
|
||||
|
||||
const loading = ref();
|
||||
const refresh = ref(false);
|
||||
const hasRecords = ref();
|
||||
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const recordShow = ref(false);
|
||||
interface DialogProps {
|
||||
rowData: Toolbox.ClamInfo;
|
||||
}
|
||||
const dialogData = ref();
|
||||
const records = ref<Array<Toolbox.ClamLog>>([]);
|
||||
const currentRecord = ref<Toolbox.ClamLog>();
|
||||
const logContent = ref();
|
||||
const records = ref<Array<Toolbox.ClamRecord>>([]);
|
||||
const currentRecord = ref<Toolbox.ClamRecord>();
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey));
|
||||
|
@ -191,20 +211,17 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
|
|||
|
||||
recordShow.value = true;
|
||||
dialogData.value = params;
|
||||
search();
|
||||
timer = setInterval(() => {
|
||||
search();
|
||||
}, 1000 * 5);
|
||||
search(true);
|
||||
};
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
searchInfo.pageSize = val;
|
||||
localStorage.setItem(searchInfo.cacheSizeKey, val + '');
|
||||
search();
|
||||
search(true);
|
||||
};
|
||||
const handleCurrentChange = (val: number) => {
|
||||
searchInfo.page = val;
|
||||
search();
|
||||
search(false);
|
||||
};
|
||||
const hasInfectedDir = () => {
|
||||
return (
|
||||
|
@ -219,8 +236,8 @@ const timeRangeLoad = ref<[Date, Date]>([
|
|||
const searchInfo = reactive({
|
||||
cacheSizeKey: 'clam-record-page-size',
|
||||
page: 1,
|
||||
pageSize: 8,
|
||||
tail: '100',
|
||||
pageSize: 10,
|
||||
status: '',
|
||||
recordTotal: 0,
|
||||
startTime: new Date(),
|
||||
endTime: new Date(),
|
||||
|
@ -232,18 +249,23 @@ const onHandle = async (row: Toolbox.ClamInfo) => {
|
|||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
search(true);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const toFolder = async (path: string) => {
|
||||
let folder = dialogData.value.rowData!.infectedDir + '/1panel-infected/' + path;
|
||||
const toFolder = async (row: any) => {
|
||||
let folder =
|
||||
dialogData.value.rowData!.infectedDir +
|
||||
'/1panel-infected/' +
|
||||
dialogData.value.rowData!.name +
|
||||
'/' +
|
||||
dateFormatForName(row.startTime);
|
||||
routerToFileWithPath(folder);
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
const search = async (changeToLatest: boolean) => {
|
||||
if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {
|
||||
searchInfo.startTime = timeRangeLoad.value[0];
|
||||
searchInfo.endTime = timeRangeLoad.value[1];
|
||||
|
@ -255,7 +277,7 @@ const search = async () => {
|
|||
page: searchInfo.page,
|
||||
pageSize: searchInfo.pageSize,
|
||||
clamID: dialogData.value.rowData!.id,
|
||||
tail: searchInfo.tail,
|
||||
status: searchInfo.status,
|
||||
startTime: searchInfo.startTime,
|
||||
endTime: searchInfo.endTime,
|
||||
};
|
||||
|
@ -266,28 +288,20 @@ const search = async () => {
|
|||
if (!hasRecords.value) {
|
||||
return;
|
||||
}
|
||||
if (!currentRecord.value) {
|
||||
if (changeToLatest) {
|
||||
currentRecord.value = records.value[0];
|
||||
}
|
||||
loadRecordLog();
|
||||
};
|
||||
|
||||
const clickRow = async (row: Toolbox.ClamLog) => {
|
||||
currentRecord.value = row;
|
||||
loadRecordLog();
|
||||
};
|
||||
|
||||
const loadRecordLog = async () => {
|
||||
let param = {
|
||||
tail: searchInfo.tail + '',
|
||||
clamName: dialogData.value.rowData?.name,
|
||||
recordName: currentRecord.value.name,
|
||||
};
|
||||
const res = await getClamRecordLog(param);
|
||||
if (logContent.value === res.data) {
|
||||
return;
|
||||
}
|
||||
logContent.value = res.data;
|
||||
for (const item of records.value) {
|
||||
if (item.id === currentRecord.value.id) {
|
||||
currentRecord.value = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const forDetail = async (row: Toolbox.ClamRecord) => {
|
||||
currentRecord.value = row;
|
||||
};
|
||||
|
||||
const onClean = async () => {
|
||||
|
@ -301,7 +315,7 @@ const onClean = async () => {
|
|||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
search(false);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
|
@ -309,11 +323,6 @@ const onClean = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
@ -321,7 +330,7 @@ defineExpose({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.infinite-list {
|
||||
height: calc(100vh - 420px);
|
||||
height: calc(100vh - 320px);
|
||||
.select-sign {
|
||||
&::before {
|
||||
float: left;
|
||||
|
|
Loading…
Add table
Reference in a new issue