mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 00:09:16 +08:00
feat: ClamAV 增加病毒文件处理策略 (#5635)
This commit is contained in:
parent
d71514885d
commit
f131aae344
@ -210,18 +210,18 @@ func (b *BaseApi) UpdateFile(c *gin.Context) {
|
|||||||
// @Summary Delete clam
|
// @Summary Delete clam
|
||||||
// @Description 删除扫描规则
|
// @Description 删除扫描规则
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.BatchDeleteReq true "request"
|
// @Param request body dto.ClamDelete true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /toolbox/clam/del [post]
|
// @Router /toolbox/clam/del [post]
|
||||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"clams","output_column":"name","output_value":"names"}],"formatZH":"删除扫描规则 [names]","formatEN":"delete clam [names]"}
|
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"clams","output_column":"name","output_value":"names"}],"formatZH":"删除扫描规则 [names]","formatEN":"delete clam [names]"}
|
||||||
func (b *BaseApi) DeleteClam(c *gin.Context) {
|
func (b *BaseApi) DeleteClam(c *gin.Context) {
|
||||||
var req dto.BatchDeleteReq
|
var req dto.ClamDelete
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := clamService.Delete(req.Ids); err != nil {
|
if err := clamService.Delete(req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,12 @@ type ClamInfo struct {
|
|||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
LastHandleDate string `json:"lastHandleDate"`
|
InfectedStrategy string `json:"infectedStrategy"`
|
||||||
Description string `json:"description"`
|
InfectedDir string `json:"infectedDir"`
|
||||||
|
LastHandleDate string `json:"lastHandleDate"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClamLogSearch struct {
|
type ClamLogSearch struct {
|
||||||
@ -38,15 +40,25 @@ type ClamLog struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ClamCreate struct {
|
type ClamCreate struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Description string `json:"description"`
|
InfectedStrategy string `json:"infectedStrategy"`
|
||||||
|
InfectedDir string `json:"infectedDir"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClamUpdate struct {
|
type ClamUpdate struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Description string `json:"description"`
|
InfectedStrategy string `json:"infectedStrategy"`
|
||||||
|
InfectedDir string `json:"infectedDir"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamDelete struct {
|
||||||
|
RemoveResult bool `json:"removeResult"`
|
||||||
|
RemoveInfected bool `json:"removeInfected"`
|
||||||
|
Ids []uint `json:"ids" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ package model
|
|||||||
type Clam struct {
|
type Clam struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||||
Path string `gorm:"type:varchar(64);not null" json:"path"`
|
Path string `gorm:"type:varchar(64);not null" json:"path"`
|
||||||
Description string `gorm:"type:varchar(64);not null" json:"description"`
|
InfectedStrategy string `gorm:"type:varchar(64)" json:"infectedStrategy"`
|
||||||
|
InfectedDir string `gorm:"type:varchar(64)" json:"infectedDir"`
|
||||||
|
Description string `gorm:"type:varchar(64)" json:"description"`
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
clamServiceNameCentOs = "clamd@scan.service"
|
clamServiceNameCentOs = "clamd@scan.service"
|
||||||
clamServiceNameUbuntu = "clamav-daemon.service"
|
clamServiceNameUbuntu = "clamav-daemon.service"
|
||||||
scanDir = "scan-result"
|
resultDir = "clamav"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClamService struct {
|
type ClamService struct {
|
||||||
@ -39,7 +39,7 @@ type IClamService interface {
|
|||||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||||
Create(req dto.ClamCreate) error
|
Create(req dto.ClamCreate) error
|
||||||
Update(req dto.ClamUpdate) error
|
Update(req dto.ClamUpdate) error
|
||||||
Delete(ids []uint) error
|
Delete(req dto.ClamDelete) error
|
||||||
HandleOnce(req dto.OperateByID) error
|
HandleOnce(req dto.OperateByID) error
|
||||||
LoadFile(req dto.OperationWithName) (string, error)
|
LoadFile(req dto.OperationWithName) (string, error)
|
||||||
UpdateFile(req dto.UpdateByNameAndFile) error
|
UpdateFile(req dto.UpdateByNameAndFile) error
|
||||||
@ -154,15 +154,21 @@ func (f *ClamService) Update(req dto.ClamUpdate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ClamService) Delete(ids []uint) error {
|
func (u *ClamService) Delete(req dto.ClamDelete) error {
|
||||||
if len(ids) == 1 {
|
for _, id := range req.Ids {
|
||||||
clam, _ := clamRepo.Get(commonRepo.WithByID(ids[0]))
|
clam, _ := clamRepo.Get(commonRepo.WithByID(id))
|
||||||
if clam.ID == 0 {
|
if clam.ID == 0 {
|
||||||
return constant.ErrRecordNotFound
|
continue
|
||||||
}
|
}
|
||||||
return clamRepo.Delete(commonRepo.WithByID(ids[0]))
|
if req.RemoveResult {
|
||||||
|
_ = os.RemoveAll(path.Join(global.CONF.System.DataDir, resultDir, clam.Name))
|
||||||
|
}
|
||||||
|
if req.RemoveInfected {
|
||||||
|
_ = os.RemoveAll(path.Join(clam.InfectedDir, "1panel-infected", clam.Name))
|
||||||
|
}
|
||||||
|
return clamRepo.Delete(commonRepo.WithByID(id))
|
||||||
}
|
}
|
||||||
return clamRepo.Delete(commonRepo.WithIdsIn(ids))
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ClamService) HandleOnce(req dto.OperateByID) error {
|
func (u *ClamService) HandleOnce(req dto.OperateByID) error {
|
||||||
@ -173,12 +179,30 @@ func (u *ClamService) HandleOnce(req dto.OperateByID) error {
|
|||||||
if cmd.CheckIllegal(clam.Path) {
|
if cmd.CheckIllegal(clam.Path) {
|
||||||
return buserr.New(constant.ErrCmdIllegal)
|
return buserr.New(constant.ErrCmdIllegal)
|
||||||
}
|
}
|
||||||
logFile := path.Join(global.CONF.System.DataDir, scanDir, clam.Name, time.Now().Format(constant.DateTimeSlimLayout))
|
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
|
||||||
|
logFile := path.Join(global.CONF.System.DataDir, resultDir, clam.Name, timeNow)
|
||||||
if _, err := os.Stat(path.Dir(logFile)); err != nil {
|
if _, err := os.Stat(path.Dir(logFile)); err != nil {
|
||||||
_ = os.MkdirAll(path.Dir(logFile), os.ModePerm)
|
_ = os.MkdirAll(path.Dir(logFile), os.ModePerm)
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
cmd := exec.Command("clamdscan", "--fdpass", clam.Path, "-l", logFile)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd := exec.Command("clamdscan", "--fdpass", strategy, clam.Path, "-l", logFile)
|
||||||
_, _ = cmd.CombinedOutput()
|
_, _ = cmd.CombinedOutput()
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
@ -226,7 +250,7 @@ func (u *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, er
|
|||||||
|
|
||||||
var datas []dto.ClamLog
|
var datas []dto.ClamLog
|
||||||
for i := 0; i < len(records); i++ {
|
for i := 0; i < len(records); i++ {
|
||||||
item := loadResultFromLog(path.Join(global.CONF.System.DataDir, scanDir, clam.Name, records[i]))
|
item := loadResultFromLog(path.Join(global.CONF.System.DataDir, resultDir, clam.Name, records[i]))
|
||||||
datas = append(datas, item)
|
datas = append(datas, item)
|
||||||
}
|
}
|
||||||
return int64(total), datas, nil
|
return int64(total), datas, nil
|
||||||
@ -237,7 +261,7 @@ func (u *ClamService) CleanRecord(req dto.OperateByID) error {
|
|||||||
if clam.ID == 0 {
|
if clam.ID == 0 {
|
||||||
return constant.ErrRecordNotFound
|
return constant.ErrRecordNotFound
|
||||||
}
|
}
|
||||||
pathItem := path.Join(global.CONF.System.DataDir, scanDir, clam.Name)
|
pathItem := path.Join(global.CONF.System.DataDir, resultDir, clam.Name)
|
||||||
_ = os.RemoveAll(pathItem)
|
_ = os.RemoveAll(pathItem)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -319,7 +343,7 @@ func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
|||||||
|
|
||||||
func loadFileByName(name string) []string {
|
func loadFileByName(name string) []string {
|
||||||
var logPaths []string
|
var logPaths []string
|
||||||
pathItem := path.Join(global.CONF.System.DataDir, scanDir, name)
|
pathItem := path.Join(global.CONF.System.DataDir, resultDir, name)
|
||||||
_ = filepath.Walk(pathItem, func(path string, info os.FileInfo, err error) error {
|
_ = filepath.Walk(pathItem, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -270,7 +270,7 @@ var AddShellColumn = &gormigrate.Migration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var AddClam = &gormigrate.Migration{
|
var AddClam = &gormigrate.Migration{
|
||||||
ID: "20240624-add-clam",
|
ID: "20240701-add-clam",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
if err := tx.AutoMigrate(&model.Clam{}); err != nil {
|
if err := tx.AutoMigrate(&model.Clam{}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -72,7 +72,7 @@ func (sws *LocalWsSession) masterWrite(data []byte) error {
|
|||||||
func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
|
func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
global.LOG.Errorf("[xpack] A panic occurred during receive ws message, error message: %v", r)
|
global.LOG.Errorf("A panic occurred during receive ws message, error message: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
wsConn := sws.wsConn
|
wsConn := sws.wsConn
|
||||||
|
@ -118,7 +118,7 @@ func (sws *LogicSshWsSession) Start(quitChan chan bool) {
|
|||||||
func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
|
func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
global.LOG.Errorf("[xpack] A panic occurred during receive ws message, error message: %v", r)
|
global.LOG.Errorf("[A panic occurred during receive ws message, error message: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
wsConn := sws.wsConn
|
wsConn := sws.wsConn
|
||||||
|
@ -1585,6 +1585,12 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/containers/download/log": {
|
||||||
|
"post": {
|
||||||
|
"description": "下载容器日志",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/containers/image": {
|
"/containers/image": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -11155,7 +11161,7 @@ const docTemplate = `{
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/dto.BatchDeleteReq"
|
"$ref": "#/definitions/dto.ClamDelete"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -15460,6 +15466,12 @@ const docTemplate = `{
|
|||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"infectedDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"infectedStrategy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -15468,6 +15480,26 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.ClamDelete": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"ids"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"removeInfected": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"removeResult": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.ClamLogSearch": {
|
"dto.ClamLogSearch": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -15501,6 +15533,12 @@ const docTemplate = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"infectedDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"infectedStrategy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -20116,6 +20154,9 @@ const docTemplate = `{
|
|||||||
"domains": {
|
"domains": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"expireDate": {
|
"expireDate": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -20152,6 +20193,9 @@ const docTemplate = `{
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@ -21663,6 +21707,9 @@ const docTemplate = `{
|
|||||||
"domains": {
|
"domains": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -21683,6 +21730,9 @@ const docTemplate = `{
|
|||||||
"renew": {
|
"renew": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"sslID": {
|
"sslID": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22242,6 +22292,9 @@ const docTemplate = `{
|
|||||||
"dnsAccountId": {
|
"dnsAccountId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22266,6 +22319,9 @@ const docTemplate = `{
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -22318,6 +22374,9 @@ const docTemplate = `{
|
|||||||
"dnsAccountId": {
|
"dnsAccountId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22342,6 +22401,9 @@ const docTemplate = `{
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -22786,9 +22848,15 @@ const docTemplate = `{
|
|||||||
"$ref": "#/definitions/response.FileTree"
|
"$ref": "#/definitions/response.FileTree"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"extension": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isDir": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -1578,6 +1578,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/containers/download/log": {
|
||||||
|
"post": {
|
||||||
|
"description": "下载容器日志",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/containers/image": {
|
"/containers/image": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -11148,7 +11154,7 @@
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/dto.BatchDeleteReq"
|
"$ref": "#/definitions/dto.ClamDelete"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -15453,6 +15459,12 @@
|
|||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"infectedDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"infectedStrategy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -15461,6 +15473,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.ClamDelete": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"ids"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"removeInfected": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"removeResult": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.ClamLogSearch": {
|
"dto.ClamLogSearch": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -15494,6 +15526,12 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"infectedDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"infectedStrategy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -20109,6 +20147,9 @@
|
|||||||
"domains": {
|
"domains": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"expireDate": {
|
"expireDate": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -20145,6 +20186,9 @@
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@ -21656,6 +21700,9 @@
|
|||||||
"domains": {
|
"domains": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -21676,6 +21723,9 @@
|
|||||||
"renew": {
|
"renew": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"sslID": {
|
"sslID": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22235,6 +22285,9 @@
|
|||||||
"dnsAccountId": {
|
"dnsAccountId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22259,6 +22312,9 @@
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -22311,6 +22367,9 @@
|
|||||||
"dnsAccountId": {
|
"dnsAccountId": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"execShell": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -22335,6 +22394,9 @@
|
|||||||
"pushDir": {
|
"pushDir": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"shell": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"skipDNS": {
|
"skipDNS": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -22779,9 +22841,15 @@
|
|||||||
"$ref": "#/definitions/response.FileTree"
|
"$ref": "#/definitions/response.FileTree"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"extension": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isDir": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -229,11 +229,28 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
infectedDir:
|
||||||
|
type: string
|
||||||
|
infectedStrategy:
|
||||||
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
dto.ClamDelete:
|
||||||
|
properties:
|
||||||
|
ids:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
removeInfected:
|
||||||
|
type: boolean
|
||||||
|
removeResult:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- ids
|
||||||
|
type: object
|
||||||
dto.ClamLogSearch:
|
dto.ClamLogSearch:
|
||||||
properties:
|
properties:
|
||||||
clamID:
|
clamID:
|
||||||
@ -256,6 +273,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
infectedDir:
|
||||||
|
type: string
|
||||||
|
infectedStrategy:
|
||||||
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
path:
|
path:
|
||||||
@ -3368,6 +3389,8 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
domains:
|
domains:
|
||||||
type: string
|
type: string
|
||||||
|
execShell:
|
||||||
|
type: boolean
|
||||||
expireDate:
|
expireDate:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
@ -3392,6 +3415,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
pushDir:
|
pushDir:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
shell:
|
||||||
|
type: string
|
||||||
skipDNS:
|
skipDNS:
|
||||||
type: boolean
|
type: boolean
|
||||||
startDate:
|
startDate:
|
||||||
@ -4401,6 +4426,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
domains:
|
domains:
|
||||||
type: string
|
type: string
|
||||||
|
execShell:
|
||||||
|
type: boolean
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
keyType:
|
keyType:
|
||||||
@ -4416,6 +4443,8 @@ definitions:
|
|||||||
type: boolean
|
type: boolean
|
||||||
renew:
|
renew:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
shell:
|
||||||
|
type: string
|
||||||
sslID:
|
sslID:
|
||||||
type: integer
|
type: integer
|
||||||
time:
|
time:
|
||||||
@ -4793,6 +4822,8 @@ definitions:
|
|||||||
type: boolean
|
type: boolean
|
||||||
dnsAccountId:
|
dnsAccountId:
|
||||||
type: integer
|
type: integer
|
||||||
|
execShell:
|
||||||
|
type: boolean
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
keyType:
|
keyType:
|
||||||
@ -4809,6 +4840,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
pushDir:
|
pushDir:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
shell:
|
||||||
|
type: string
|
||||||
skipDNS:
|
skipDNS:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
@ -4844,6 +4877,8 @@ definitions:
|
|||||||
type: boolean
|
type: boolean
|
||||||
dnsAccountId:
|
dnsAccountId:
|
||||||
type: integer
|
type: integer
|
||||||
|
execShell:
|
||||||
|
type: boolean
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
keyType:
|
keyType:
|
||||||
@ -4860,6 +4895,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
pushDir:
|
pushDir:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
shell:
|
||||||
|
type: string
|
||||||
skipDNS:
|
skipDNS:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
@ -5162,8 +5199,12 @@ definitions:
|
|||||||
items:
|
items:
|
||||||
$ref: '#/definitions/response.FileTree'
|
$ref: '#/definitions/response.FileTree'
|
||||||
type: array
|
type: array
|
||||||
|
extension:
|
||||||
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
isDir:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
path:
|
path:
|
||||||
@ -6414,6 +6455,10 @@ paths:
|
|||||||
summary: Load docker status
|
summary: Load docker status
|
||||||
tags:
|
tags:
|
||||||
- Container Docker
|
- Container Docker
|
||||||
|
/containers/download/log:
|
||||||
|
post:
|
||||||
|
description: 下载容器日志
|
||||||
|
responses: {}
|
||||||
/containers/image:
|
/containers/image:
|
||||||
get:
|
get:
|
||||||
description: 获取镜像名称列表
|
description: 获取镜像名称列表
|
||||||
@ -12471,7 +12516,7 @@ paths:
|
|||||||
name: request
|
name: request
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/dto.BatchDeleteReq'
|
$ref: '#/definitions/dto.ClamDelete'
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -118,7 +118,7 @@ export namespace Toolbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ClamBaseInfo {
|
export interface ClamBaseInfo {
|
||||||
version: string;
|
version: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isExist: boolean;
|
isExist: boolean;
|
||||||
}
|
}
|
||||||
@ -126,18 +126,24 @@ export namespace Toolbox {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
infectedStrategy: string;
|
||||||
|
infectedDir: string;
|
||||||
lastHandleDate: string;
|
lastHandleDate: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface ClamCreate {
|
export interface ClamCreate {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
infectedStrategy: string;
|
||||||
|
infectedDir: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface ClamUpdate {
|
export interface ClamUpdate {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
infectedStrategy: string;
|
||||||
|
infectedDir: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
export interface ClamSearchLog extends ReqPage {
|
export interface ClamSearchLog extends ReqPage {
|
||||||
|
@ -135,7 +135,7 @@ export const createClam = (params: Toolbox.ClamCreate) => {
|
|||||||
export const updateClam = (params: Toolbox.ClamUpdate) => {
|
export const updateClam = (params: Toolbox.ClamUpdate) => {
|
||||||
return http.post(`/toolbox/clam/update`, params);
|
return http.post(`/toolbox/clam/update`, params);
|
||||||
};
|
};
|
||||||
export const deleteClam = (params: { ids: number[] }) => {
|
export const deleteClam = (params: { ids: number[]; removeResult: boolean; removeInfected: boolean }) => {
|
||||||
return http.post(`/toolbox/clam/del`, params);
|
return http.post(`/toolbox/clam/del`, params);
|
||||||
};
|
};
|
||||||
export const handleClamScan = (id: number) => {
|
export const handleClamScan = (id: number) => {
|
||||||
|
@ -1066,18 +1066,36 @@ const message = {
|
|||||||
},
|
},
|
||||||
clam: {
|
clam: {
|
||||||
clam: 'Virus Scan',
|
clam: 'Virus Scan',
|
||||||
|
clamHelper:
|
||||||
|
'The minimum recommended configuration for ClamAV is: 3 GiB of RAM or more, single-core CPU with 2.0 GHz or higher, and at least 5 GiB of available hard disk space.',
|
||||||
noClam: 'ClamAV service not detected, please refer to the official documentation for installation!',
|
noClam: 'ClamAV service not detected, please refer to the official documentation for installation!',
|
||||||
notStart: 'ClamAV service is currently not running, please start it first!',
|
notStart: 'ClamAV service is currently not running, please start it first!',
|
||||||
clamCreate: 'Create Scan Rule',
|
removeResult: 'Delete Report Files',
|
||||||
|
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.',
|
||||||
|
clamCreate: 'Create Scan Rules',
|
||||||
|
infectedStrategy: 'Virus Strategy',
|
||||||
|
remove: 'Delete',
|
||||||
|
removeHelper: 'Delete virus files, choose carefully!',
|
||||||
|
move: 'Move',
|
||||||
|
moveHelper: 'Move virus files to specified directory',
|
||||||
|
copy: 'Copy',
|
||||||
|
copyHelper: 'Copy virus files to specified directory',
|
||||||
|
none: 'Do Nothing',
|
||||||
|
noneHelper: 'Take no action on virus files',
|
||||||
|
scanDir: 'Scan Directory',
|
||||||
|
infectedDir: 'Infected Directory',
|
||||||
scanDate: 'Scan Date',
|
scanDate: 'Scan Date',
|
||||||
scanTime: 'Elapsed Time',
|
scanTime: 'Time Taken',
|
||||||
scannedFiles: 'Number of Scanned Files',
|
scannedFiles: 'Scanned Files',
|
||||||
infectedFiles: 'Number of Infected Files',
|
infectedFiles: 'Infected Files',
|
||||||
log: 'Details',
|
log: 'Details',
|
||||||
clamConf: 'Scan Configuration',
|
clamConf: 'Scan Configuration',
|
||||||
clamLog: 'Scan Log',
|
clamLog: 'Scan Logs',
|
||||||
freshClam: 'Virus Database Refresh Configuration',
|
freshClam: 'Update Virus Definitions',
|
||||||
freshClamLog: 'Virus Database Refresh Log',
|
freshClamLog: 'Update Virus Definitions Logs',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
|
@ -1008,9 +1008,26 @@ const message = {
|
|||||||
},
|
},
|
||||||
clam: {
|
clam: {
|
||||||
clam: '病毒掃描',
|
clam: '病毒掃描',
|
||||||
|
clamHelper:
|
||||||
|
'ClamAV 的最低建議配置為:3 GiB 以上的 RAM,2.0 GHz 以上的單核 CPU,以及至少 5 GiB 的可用硬盤空間。',
|
||||||
noClam: '未檢測到 ClamAV 服務,請參考官方文檔進行安裝!',
|
noClam: '未檢測到 ClamAV 服務,請參考官方文檔進行安裝!',
|
||||||
notStart: '目前沒有運行 ClamAV 服務,請先啟動!',
|
notStart: '當前未 ClamAV 服務,請先開啟!',
|
||||||
|
removeResult: '刪除報告文件',
|
||||||
|
removeResultHelper: '刪除任務執行過程中生成的報告文件,以清理存儲空間。',
|
||||||
|
removeInfected: '刪除病毒文件',
|
||||||
|
removeInfectedHelper: '刪除任務檢測到的病毒文件,以確保伺服器的安全和正常運行。',
|
||||||
clamCreate: '創建掃描規則',
|
clamCreate: '創建掃描規則',
|
||||||
|
infectedStrategy: '病毒策略',
|
||||||
|
remove: '刪除',
|
||||||
|
removeHelper: '刪除病毒文件,請謹慎選擇!',
|
||||||
|
move: '移動',
|
||||||
|
moveHelper: '將病毒文件移動到指定目錄下',
|
||||||
|
copy: '複製',
|
||||||
|
copyHelper: '將病毒文件複製到指定目錄下',
|
||||||
|
none: '不操作',
|
||||||
|
noneHelper: '不對病毒文件採取任何操作',
|
||||||
|
scanDir: '掃描目錄',
|
||||||
|
infectedDir: '病毒目錄',
|
||||||
scanDate: '掃描時間',
|
scanDate: '掃描時間',
|
||||||
scanTime: '耗時',
|
scanTime: '耗時',
|
||||||
scannedFiles: '掃描文件數',
|
scannedFiles: '掃描文件數',
|
||||||
|
@ -1009,9 +1009,26 @@ const message = {
|
|||||||
},
|
},
|
||||||
clam: {
|
clam: {
|
||||||
clam: '病毒扫描',
|
clam: '病毒扫描',
|
||||||
|
clamHelper:
|
||||||
|
'ClamAV 的最低建议配置为:3 GiB 以上的 RAM,2.0 GHz 以上的单核 CPU,以及至少 5 GiB 的可用硬盘空间。',
|
||||||
noClam: '未检测到 ClamAV 服务,请参考官方文档进行安装!',
|
noClam: '未检测到 ClamAV 服务,请参考官方文档进行安装!',
|
||||||
notStart: '当前未 ClamAV 服务,请先开启!',
|
notStart: '当前未 ClamAV 服务,请先开启!',
|
||||||
|
removeResult: '删除报告文件',
|
||||||
|
removeResultHelper: '删除任务执行过程中生成的报告文件,以清理存储空间。',
|
||||||
|
removeInfected: '删除病毒文件',
|
||||||
|
removeInfectedHelper: '删除任务检测到的病毒文件,以确保服务器的安全和正常运行。',
|
||||||
clamCreate: '创建扫描规则',
|
clamCreate: '创建扫描规则',
|
||||||
|
infectedStrategy: '病毒策略',
|
||||||
|
remove: '删除',
|
||||||
|
removeHelper: '删除病毒文件,请谨慎选择!',
|
||||||
|
move: '移动',
|
||||||
|
moveHelper: '将病毒文件移动到指定目录下',
|
||||||
|
copy: '复制',
|
||||||
|
copyHelper: '将病毒文件复制到指定目录下',
|
||||||
|
none: '不操作',
|
||||||
|
noneHelper: '不对病毒文件采取任何操作',
|
||||||
|
scanDir: '扫描目录',
|
||||||
|
infectedDir: '病毒目录',
|
||||||
scanDate: '扫描时间',
|
scanDate: '扫描时间',
|
||||||
scanTime: '耗时',
|
scanTime: '耗时',
|
||||||
scannedFiles: '扫描文件数',
|
scannedFiles: '扫描文件数',
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<LayoutContent v-loading="loading" v-if="!isRecordShow && !isSettingShow" :title="$t('toolbox.clam.clam')">
|
<LayoutContent v-loading="loading" v-if="!isRecordShow && !isSettingShow" :title="$t('toolbox.clam.clam')">
|
||||||
|
<template #prompt>
|
||||||
|
<el-alert type="info" :closable="false">
|
||||||
|
<template #title>
|
||||||
|
{{ $t('toolbox.clam.clamHelper') }}
|
||||||
|
<el-link
|
||||||
|
style="font-size: 12px; margin-left: 5px"
|
||||||
|
icon="Position"
|
||||||
|
@click="toDoc()"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{{ $t('firewall.quickJump') }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
<template #app>
|
<template #app>
|
||||||
<ClamStatus
|
<ClamStatus
|
||||||
@setting="setting"
|
@setting="setting"
|
||||||
@ -28,7 +43,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
<el-card v-if="!clamStatus.isRunning && maskShow" class="mask-prompt">
|
<el-card v-if="clamStatus.isExist && !clamStatus.isRunning && maskShow" class="mask-prompt">
|
||||||
<span>{{ $t('toolbox.clam.notStart') }}</span>
|
<span>{{ $t('toolbox.clam.notStart') }}</span>
|
||||||
</el-card>
|
</el-card>
|
||||||
<template #main v-if="clamStatus.isExist">
|
<template #main v-if="clamStatus.isExist">
|
||||||
@ -48,9 +63,30 @@
|
|||||||
prop="name"
|
prop="name"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
<el-table-column :label="$t('file.path')" :min-width="120" prop="path" show-overflow-tooltip>
|
<el-table-column
|
||||||
|
:label="$t('toolbox.clam.scanDir')"
|
||||||
|
:min-width="120"
|
||||||
|
prop="path"
|
||||||
|
show-overflow-tooltip
|
||||||
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button text type="primary" @click="toFolder(row.path)">{{ row.path }}</el-button>
|
<el-button link type="primary" @click="toFolder(row.path)">{{ row.path }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('toolbox.clam.infectedDir')"
|
||||||
|
:min-width="120"
|
||||||
|
prop="path"
|
||||||
|
show-overflow-tooltip
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="toFolder(row.infectedDir + '/1panel-infected/' + row.name)"
|
||||||
|
>
|
||||||
|
{{ row.infectedDir + '/1panel-infected/' + row.name }}
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@ -75,7 +111,20 @@
|
|||||||
</template>
|
</template>
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
|
|
||||||
<OpDialog ref="opRef" @search="search" @submit="onSubmitDelete()" />
|
<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="removeResult" :label="$t('toolbox.clam.removeResult')" />
|
||||||
|
<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>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</OpDialog>
|
||||||
<OperateDialog @search="search" ref="dialogRef" />
|
<OperateDialog @search="search" ref="dialogRef" />
|
||||||
<LogDialog ref="dialogLogRef" />
|
<LogDialog ref="dialogLogRef" />
|
||||||
<SettingDialog v-if="isSettingShow" />
|
<SettingDialog v-if="isSettingShow" />
|
||||||
@ -114,6 +163,9 @@ const operateIDs = ref();
|
|||||||
const dialogLogRef = ref();
|
const dialogLogRef = ref();
|
||||||
const isRecordShow = ref();
|
const isRecordShow = ref();
|
||||||
|
|
||||||
|
const removeResult = ref();
|
||||||
|
const removeInfected = ref();
|
||||||
|
|
||||||
const isSettingShow = ref();
|
const isSettingShow = ref();
|
||||||
const maskShow = ref(true);
|
const maskShow = ref(true);
|
||||||
const clamStatus = ref({
|
const clamStatus = ref({
|
||||||
@ -144,20 +196,27 @@ const setting = () => {
|
|||||||
};
|
};
|
||||||
const getStatus = (status: any) => {
|
const getStatus = (status: any) => {
|
||||||
clamStatus.value = status;
|
clamStatus.value = status;
|
||||||
console.log(clamStatus.value);
|
|
||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
const toFolder = (folder: string) => {
|
const toFolder = (folder: string) => {
|
||||||
router.push({ path: '/hosts/files', query: { path: folder } });
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
};
|
};
|
||||||
|
const toDoc = async () => {
|
||||||
|
window.open('https://1panel.cn/docs/user_manual/toolbox/clam/', '_blank', 'noopener,noreferrer');
|
||||||
|
};
|
||||||
|
|
||||||
const onChange = async (row: any) => {
|
const onChange = async (row: any) => {
|
||||||
await await updateClam(row);
|
await await updateClam(row);
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenDialog = async (title: string, rowData: Partial<Toolbox.ClamInfo> = {}) => {
|
const onOpenDialog = async (
|
||||||
|
title: string,
|
||||||
|
rowData: Partial<Toolbox.ClamInfo> = {
|
||||||
|
infectedStrategy: 'none',
|
||||||
|
},
|
||||||
|
) => {
|
||||||
let params = {
|
let params = {
|
||||||
title,
|
title,
|
||||||
rowData: { ...rowData },
|
rowData: { ...rowData },
|
||||||
@ -182,7 +241,7 @@ const onDelete = async (row: Toolbox.ClamInfo | null) => {
|
|||||||
title: i18n.global.t('commons.button.delete'),
|
title: i18n.global.t('commons.button.delete'),
|
||||||
names: names,
|
names: names,
|
||||||
msg: i18n.global.t('commons.msg.operatorHelper', [
|
msg: i18n.global.t('commons.msg.operatorHelper', [
|
||||||
i18n.global.t('cronjob.cronTask'),
|
i18n.global.t('toolbox.clam.clam'),
|
||||||
i18n.global.t('commons.button.delete'),
|
i18n.global.t('commons.button.delete'),
|
||||||
]),
|
]),
|
||||||
api: null,
|
api: null,
|
||||||
@ -192,7 +251,7 @@ const onDelete = async (row: Toolbox.ClamInfo | null) => {
|
|||||||
|
|
||||||
const onSubmitDelete = async () => {
|
const onSubmitDelete = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await deleteClam({ ids: operateIDs.value })
|
await deleteClam({ ids: operateIDs.value, removeResult: removeResult.value, removeInfected: removeInfected.value })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
@ -24,13 +24,32 @@
|
|||||||
v-model.trim="dialogData.rowData!.name"
|
v-model.trim="dialogData.rowData!.name"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('file.root')" prop="path">
|
<el-form-item :label="$t('toolbox.clam.scanDir')" prop="path">
|
||||||
<el-input v-model="dialogData.rowData!.path">
|
<el-input v-model="dialogData.rowData!.path">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<FileList @choose="loadDir" :dir="true"></FileList>
|
<FileList @choose="loadDir" :dir="true"></FileList>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('toolbox.clam.infectedStrategy')" prop="infectedStrategy">
|
||||||
|
<el-radio-group v-model="dialogData.rowData!.infectedStrategy">
|
||||||
|
<el-radio value="none">{{ $t('toolbox.clam.none') }}</el-radio>
|
||||||
|
<el-radio value="remove">{{ $t('toolbox.clam.remove') }}</el-radio>
|
||||||
|
<el-radio value="move">{{ $t('toolbox.clam.move') }}</el-radio>
|
||||||
|
<el-radio value="copy">{{ $t('toolbox.clam.copy') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('toolbox.clam.' + dialogData.rowData!.infectedStrategy + 'Helper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="hasInfectedDir()" :label="$t('toolbox.clam.infectedDir')" prop="infectedDir">
|
||||||
|
<el-input v-model="dialogData.rowData!.infectedDir">
|
||||||
|
<template #prepend>
|
||||||
|
<FileList @choose="loadInfectedDir" :dir="true"></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
|
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -90,9 +109,17 @@ const rules = reactive({
|
|||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const hasInfectedDir = () => {
|
||||||
|
return (
|
||||||
|
dialogData.value.rowData!.infectedStrategy === 'move' || dialogData.value.rowData!.infectedStrategy === 'copy'
|
||||||
|
);
|
||||||
|
};
|
||||||
const loadDir = async (path: string) => {
|
const loadDir = async (path: string) => {
|
||||||
dialogData.value.rowData!.path = path;
|
dialogData.value.rowData!.path = path;
|
||||||
};
|
};
|
||||||
|
const loadInfectedDir = async (path: string) => {
|
||||||
|
dialogData.value.rowData!.infectedDir = path;
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<el-card>
|
<el-card>
|
||||||
<div>
|
<div>
|
||||||
<el-tag class="float-left" effect="dark" type="success">
|
<el-tag class="float-left" effect="dark" type="success">
|
||||||
{{ dialogData.rowData.name }}
|
{{ $t('commons.table.name') }}: {{ dialogData.rowData.name }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-popover
|
<el-popover
|
||||||
v-if="dialogData.rowData.path.length >= 35"
|
v-if="dialogData.rowData.path.length >= 35"
|
||||||
@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-tag style="float: left" effect="dark" type="success">
|
<el-tag style="float: left" effect="dark" type="success">
|
||||||
{{ dialogData.rowData.path.substring(0, 20) }}...
|
{{ $t('file.path') }}: {{ dialogData.rowData.path.substring(0, 20) }}...
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
effect="dark"
|
effect="dark"
|
||||||
type="success"
|
type="success"
|
||||||
>
|
>
|
||||||
{{ dialogData.rowData.path }}
|
{{ $t('file.path') }}: {{ dialogData.rowData.path }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
@ -115,9 +115,14 @@
|
|||||||
<template #label>
|
<template #label>
|
||||||
<span class="status-label">{{ $t('toolbox.clam.infectedFiles') }}</span>
|
<span class="status-label">{{ $t('toolbox.clam.infectedFiles') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<span class="status-count">
|
<span class="status-count" v-if="!hasInfectedDir()">
|
||||||
{{ currentRecord?.infectedFiles }}
|
{{ currentRecord?.infectedFiles }}
|
||||||
</span>
|
</span>
|
||||||
|
<div class="count" v-else>
|
||||||
|
<span @click="toFolder(currentRecord?.name)">
|
||||||
|
{{ currentRecord?.infectedFiles }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row v-if="currentRecord?.log">
|
<el-row v-if="currentRecord?.log">
|
||||||
@ -167,6 +172,8 @@ import { MsgSuccess } from '@/utils/message';
|
|||||||
import { shortcuts } from '@/utils/shortcuts';
|
import { shortcuts } from '@/utils/shortcuts';
|
||||||
import { Toolbox } from '@/api/interface/toolbox';
|
import { Toolbox } from '@/api/interface/toolbox';
|
||||||
import { cleanClamRecord, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';
|
import { cleanClamRecord, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
const refresh = ref(false);
|
const refresh = ref(false);
|
||||||
@ -212,6 +219,11 @@ const handleCurrentChange = (val: number) => {
|
|||||||
searchInfo.page = val;
|
searchInfo.page = val;
|
||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
const hasInfectedDir = () => {
|
||||||
|
return (
|
||||||
|
dialogData.value.rowData!.infectedStrategy === 'move' || dialogData.value.rowData!.infectedStrategy === 'copy'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const timeRangeLoad = ref<[Date, Date]>([
|
const timeRangeLoad = ref<[Date, Date]>([
|
||||||
new Date(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7).setHours(0, 0, 0, 0)),
|
new Date(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7).setHours(0, 0, 0, 0)),
|
||||||
@ -239,6 +251,10 @@ const onHandle = async (row: Toolbox.ClamInfo) => {
|
|||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const toFolder = async (path: string) => {
|
||||||
|
let folder = dialogData.value.rowData!.infectedDir + '/1panel-infected/' + path;
|
||||||
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
|
};
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {
|
if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {
|
||||||
@ -278,7 +294,6 @@ const onClean = async () => {
|
|||||||
type: 'warning',
|
type: 'warning',
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
console.log(dialogData.value.id);
|
|
||||||
cleanClamRecord(dialogData.value.rowData.id)
|
cleanClamRecord(dialogData.value.rowData.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@ -334,6 +349,16 @@ defineExpose({
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
span {
|
||||||
|
font-size: 25px;
|
||||||
|
color: $primary-color;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1400px) {
|
@media only screen and (max-width: 1400px) {
|
||||||
.mainClass {
|
.mainClass {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<ClamStatus v-model:loading="loading" />
|
<ClamStatus v-model:loading="loading" />
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<template #title>
|
||||||
<back-button name="Clam" header="Clamav">
|
<back-button name="Clam" header="ClamAV">
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<el-button type="primary" :plain="activeName !== 'clamd'" @click="search('clamd')">
|
<el-button type="primary" :plain="activeName !== 'clamd'" @click="search('clamd')">
|
||||||
{{ $t('toolbox.clam.clamConf') }}
|
{{ $t('toolbox.clam.clamConf') }}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="app-status tool-status" v-if="data.isExist">
|
<div class="app-status tool-status" v-if="data.isExist">
|
||||||
<el-card>
|
<el-card>
|
||||||
<div>
|
<div>
|
||||||
<el-tag effect="dark" type="success">Clamav</el-tag>
|
<el-tag effect="dark" type="success">ClamAV</el-tag>
|
||||||
<el-tag round class="status-content" v-if="data.isActive" type="success">
|
<el-tag round class="status-content" v-if="data.isActive" type="success">
|
||||||
{{ $t('commons.status.running') }}
|
{{ $t('commons.status.running') }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
@ -75,7 +75,7 @@ const toDoc = async () => {
|
|||||||
const onOperate = async (operation: string) => {
|
const onOperate = async (operation: string) => {
|
||||||
em('update:maskShow', false);
|
em('update:maskShow', false);
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
i18n.global.t('commons.msg.operatorHelper', [' Clamav ', i18n.global.t('app.' + operation)]),
|
i18n.global.t('commons.msg.operatorHelper', [' ClamAV ', i18n.global.t('app.' + operation)]),
|
||||||
i18n.global.t('app.' + operation),
|
i18n.global.t('app.' + operation),
|
||||||
{
|
{
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user