mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
feat: 增加病毒扫描工具 (#5546)
This commit is contained in:
parent
a08ce58563
commit
97b790a092
251
backend/app/api/v1/clam.go
Normal file
251
backend/app/api/v1/clam.go
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Create clam
|
||||||
|
// @Description 创建扫描规则
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ClamCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建扫描规则 [name][path]","formatEN":"create clam [name][path]"}
|
||||||
|
func (b *BaseApi) CreateClam(c *gin.Context) {
|
||||||
|
var req dto.ClamCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := clamService.Create(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Update clam
|
||||||
|
// @Description 修改扫描规则
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ClamUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/update [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改扫描规则 [name][path]","formatEN":"update clam [name][path]"}
|
||||||
|
func (b *BaseApi) UpdateClam(c *gin.Context) {
|
||||||
|
var req dto.ClamUpdate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := clamService.Update(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Page clam
|
||||||
|
// @Description 获取扫描规则列表分页
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.SearchWithPage true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/search [post]
|
||||||
|
func (b *BaseApi) SearchClam(c *gin.Context) {
|
||||||
|
var req dto.SearchWithPage
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := clamService.SearchWithPage(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Load clam base info
|
||||||
|
// @Description 获取 Clam 基础信息
|
||||||
|
// @Accept json
|
||||||
|
// @Success 200 {object} dto.ClamBaseInfo
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/base [get]
|
||||||
|
func (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {
|
||||||
|
info, err := clamService.LoadBaseInfo()
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Operate Clam
|
||||||
|
// @Description 修改 Clam 状态
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.Operate true "request"
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/operate [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] Clam","formatEN":"[operation] FTP"}
|
||||||
|
func (b *BaseApi) OperateClam(c *gin.Context) {
|
||||||
|
var req dto.Operate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := clamService.Operate(req.Operation); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Clean clam record
|
||||||
|
// @Description 清空扫描报告
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperateByID true "request"
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/record/clean [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"清空扫描报告 [name]","formatEN":"clean clam record [name]"}
|
||||||
|
func (b *BaseApi) CleanClamRecord(c *gin.Context) {
|
||||||
|
var req dto.OperateByID
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := clamService.CleanRecord(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Page clam record
|
||||||
|
// @Description 获取扫描结果列表分页
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ClamLogSearch true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/record/search [post]
|
||||||
|
func (b *BaseApi) SearchClamRecord(c *gin.Context) {
|
||||||
|
var req dto.ClamLogSearch
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := clamService.LoadRecords(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Load clam file
|
||||||
|
// @Description 获取扫描文件
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperationWithName true "request"
|
||||||
|
// @Success 200 {object} dto.PageResult
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/file/search [post]
|
||||||
|
func (b *BaseApi) SearchClamFile(c *gin.Context) {
|
||||||
|
var req dto.OperationWithName
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := clamService.LoadFile(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Update clam file
|
||||||
|
// @Description 更新病毒扫描配置文件
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.UpdateByNameAndFile true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/file/update [post]
|
||||||
|
func (b *BaseApi) UpdateFile(c *gin.Context) {
|
||||||
|
var req dto.UpdateByNameAndFile
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := clamService.UpdateFile(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Delete clam
|
||||||
|
// @Description 删除扫描规则
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.BatchDeleteReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @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]"}
|
||||||
|
func (b *BaseApi) DeleteClam(c *gin.Context) {
|
||||||
|
var req dto.BatchDeleteReq
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := clamService.Delete(req.Ids); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Clam
|
||||||
|
// @Summary Handle clam scan
|
||||||
|
// @Description 执行病毒扫描
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.OperateByID true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /toolbox/clam/handle [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"执行病毒扫描 [name]","formatEN":"handle clam scan [name]"}
|
||||||
|
func (b *BaseApi) HandleClamScan(c *gin.Context) {
|
||||||
|
var req dto.OperateByID
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := clamService.HandleOnce(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
@ -38,6 +38,7 @@ var (
|
|||||||
deviceService = service.NewIDeviceService()
|
deviceService = service.NewIDeviceService()
|
||||||
fail2banService = service.NewIFail2BanService()
|
fail2banService = service.NewIFail2BanService()
|
||||||
ftpService = service.NewIFtpService()
|
ftpService = service.NewIFtpService()
|
||||||
|
clamService = service.NewIClamService()
|
||||||
|
|
||||||
settingService = service.NewISettingService()
|
settingService = service.NewISettingService()
|
||||||
backupService = service.NewIBackupService()
|
backupService = service.NewIBackupService()
|
||||||
|
52
backend/app/dto/clam.go
Normal file
52
backend/app/dto/clam.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClamBaseInfo struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
IsActive bool `json:"isActive"`
|
||||||
|
IsExist bool `json:"isExist"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
LastHandleDate string `json:"lastHandleDate"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamLogSearch struct {
|
||||||
|
PageInfo
|
||||||
|
|
||||||
|
ClamID uint `json:"clamID"`
|
||||||
|
StartTime time.Time `json:"startTime"`
|
||||||
|
EndTime time.Time `json:"endTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamLog struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ScanDate string `json:"scanDate"`
|
||||||
|
ScanTime string `json:"scanTime"`
|
||||||
|
InfectedFiles string `json:"infectedFiles"`
|
||||||
|
Log string `json:"log"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamCreate struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClamUpdate struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
9
backend/app/model/clam.go
Normal file
9
backend/app/model/clam.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Clam struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||||
|
Path string `gorm:"type:varchar(64);not null" json:"path"`
|
||||||
|
Description string `gorm:"type:varchar(64);not null" json:"description"`
|
||||||
|
}
|
58
backend/app/repo/clam.go
Normal file
58
backend/app/repo/clam.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClamRepo struct{}
|
||||||
|
|
||||||
|
type IClamRepo interface {
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.Clam, error)
|
||||||
|
Create(clam *model.Clam) error
|
||||||
|
Update(id uint, vars map[string]interface{}) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
Get(opts ...DBOption) (model.Clam, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIClamRepo() IClamRepo {
|
||||||
|
return &ClamRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamRepo) Get(opts ...DBOption) (model.Clam, error) {
|
||||||
|
var clam model.Clam
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.First(&clam).Error
|
||||||
|
return clam, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamRepo) Page(page, size int, opts ...DBOption) (int64, []model.Clam, error) {
|
||||||
|
var users []model.Clam
|
||||||
|
db := global.DB.Model(&model.Clam{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||||
|
return count, users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamRepo) Create(clam *model.Clam) error {
|
||||||
|
return global.DB.Create(clam).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamRepo) Update(id uint, vars map[string]interface{}) error {
|
||||||
|
return global.DB.Model(&model.Clam{}).Where("id = ?", id).Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.Clam{}).Error
|
||||||
|
}
|
366
backend/app/service/clam.go
Normal file
366
backend/app/service/clam.go
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
clamServiceNameCentOs = "clamd@scan.service"
|
||||||
|
clamServiceNameUbuntu = "clamav-daemon.service"
|
||||||
|
scanDir = "scan-result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClamService struct {
|
||||||
|
serviceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IClamService interface {
|
||||||
|
LoadBaseInfo() (dto.ClamBaseInfo, error)
|
||||||
|
Operate(operate string) error
|
||||||
|
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||||
|
Create(req dto.ClamCreate) error
|
||||||
|
Update(req dto.ClamUpdate) error
|
||||||
|
Delete(ids []uint) error
|
||||||
|
HandleOnce(req dto.OperateByID) error
|
||||||
|
LoadFile(req dto.OperationWithName) (string, error)
|
||||||
|
UpdateFile(req dto.UpdateByNameAndFile) error
|
||||||
|
LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error)
|
||||||
|
CleanRecord(req dto.OperateByID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIClamService() IClamService {
|
||||||
|
return &ClamService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
|
||||||
|
var baseInfo dto.ClamBaseInfo
|
||||||
|
baseInfo.Version = "-"
|
||||||
|
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||||
|
if exist1 {
|
||||||
|
f.serviceName = clamServiceNameCentOs
|
||||||
|
baseInfo.IsExist = true
|
||||||
|
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||||
|
}
|
||||||
|
exist2, _ := systemctl.IsExist(clamServiceNameCentOs)
|
||||||
|
if exist2 {
|
||||||
|
f.serviceName = clamServiceNameCentOs
|
||||||
|
baseInfo.IsExist = true
|
||||||
|
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseInfo.IsActive {
|
||||||
|
version, err := cmd.Exec("clamdscan --version")
|
||||||
|
if err != nil {
|
||||||
|
return baseInfo, nil
|
||||||
|
}
|
||||||
|
if strings.Contains(version, "/") {
|
||||||
|
baseInfo.Version = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
|
||||||
|
} else {
|
||||||
|
baseInfo.Version = strings.TrimPrefix(version, "ClamAV ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baseInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ClamService) Operate(operate string) error {
|
||||||
|
switch operate {
|
||||||
|
case "start", "restart", "stop":
|
||||||
|
stdout, err := cmd.Execf("systemctl %s %s", operate, f.serviceName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s the %s failed, err: %s", operate, f.serviceName, stdout)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("not support such operation: %v", operate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ClamService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||||
|
total, commands, err := clamRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info))
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
var datas []dto.ClamInfo
|
||||||
|
for _, command := range commands {
|
||||||
|
var item dto.ClamInfo
|
||||||
|
if err := copier.Copy(&item, &command); err != nil {
|
||||||
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
item.LastHandleDate = "-"
|
||||||
|
datas = append(datas, item)
|
||||||
|
}
|
||||||
|
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||||
|
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("20060102150405", logPaths[0], nyc)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
datas[i].LastHandleDate = t1.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total, datas, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ClamService) Create(req dto.ClamCreate) error {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
|
||||||
|
if clam.ID != 0 {
|
||||||
|
return constant.ErrRecordExist
|
||||||
|
}
|
||||||
|
if err := copier.Copy(&clam, &req); err != nil {
|
||||||
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
if err := clamRepo.Create(&clam); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ClamService) Update(req dto.ClamUpdate) error {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
|
||||||
|
if clam.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
upMap := map[string]interface{}{}
|
||||||
|
upMap["name"] = req.Name
|
||||||
|
upMap["path"] = req.Path
|
||||||
|
upMap["description"] = req.Description
|
||||||
|
if err := clamRepo.Update(req.ID, upMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) Delete(ids []uint) error {
|
||||||
|
if len(ids) == 1 {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByID(ids[0]))
|
||||||
|
if clam.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return clamRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||||
|
}
|
||||||
|
return clamRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) HandleOnce(req dto.OperateByID) error {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
|
||||||
|
if clam.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
if cmd.CheckIllegal(clam.Path) {
|
||||||
|
return buserr.New(constant.ErrCmdIllegal)
|
||||||
|
}
|
||||||
|
logFile := path.Join(global.CONF.System.DataDir, scanDir, clam.Name, time.Now().Format("20060102150405"))
|
||||||
|
if _, err := os.Stat(path.Dir(logFile)); err != nil {
|
||||||
|
_ = os.MkdirAll(path.Dir(logFile), os.ModePerm)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
cmd := exec.Command("clamdscan", "--fdpass", clam.Path, "-l", logFile)
|
||||||
|
_, _ = cmd.CombinedOutput()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ClamID))
|
||||||
|
if clam.ID == 0 {
|
||||||
|
return 0, nil, constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
logPaths := loadFileByName(clam.Name)
|
||||||
|
if len(logPaths) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var filterFiles []string
|
||||||
|
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||||
|
for _, item := range logPaths {
|
||||||
|
t1, err := time.ParseInLocation("20060102150405", item, nyc)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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.CONF.System.DataDir, scanDir, clam.Name, records[i]))
|
||||||
|
datas = append(datas, item)
|
||||||
|
}
|
||||||
|
return int64(total), datas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) CleanRecord(req dto.OperateByID) error {
|
||||||
|
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
|
||||||
|
if clam.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
pathItem := path.Join(global.CONF.System.DataDir, scanDir, clam.Name)
|
||||||
|
_ = os.RemoveAll(pathItem)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) LoadFile(req dto.OperationWithName) (string, error) {
|
||||||
|
filePath := ""
|
||||||
|
switch req.Name {
|
||||||
|
case "clamd":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
filePath = "/etc/clamav/clamd.conf"
|
||||||
|
} else {
|
||||||
|
filePath = "/etc/clamd.d/scan.conf"
|
||||||
|
}
|
||||||
|
case "clamd-log":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
filePath = "/var/log/clamav/clamav.log"
|
||||||
|
} else {
|
||||||
|
filePath = "/var/log/clamd.scan"
|
||||||
|
}
|
||||||
|
case "freshclam":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
filePath = "/etc/clamav/freshclam.conf"
|
||||||
|
} else {
|
||||||
|
filePath = "/etc/freshclam.conf"
|
||||||
|
}
|
||||||
|
case "freshclam-log":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
filePath = "/var/log/clamav/freshclam.log"
|
||||||
|
} else {
|
||||||
|
filePath = "/var/log/clamav/freshclam.log"
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("not support such type")
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filePath); err != nil {
|
||||||
|
return "", buserr.New("ErrHttpReqNotFound")
|
||||||
|
}
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
|
||||||
|
filePath := ""
|
||||||
|
service := ""
|
||||||
|
switch req.Name {
|
||||||
|
case "clamd":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
service = clamServiceNameCentOs
|
||||||
|
filePath = "/etc/clamav/clamd.conf"
|
||||||
|
} else {
|
||||||
|
service = clamServiceNameCentOs
|
||||||
|
filePath = "/etc/clamd.d/scan.conf"
|
||||||
|
}
|
||||||
|
case "freshclam":
|
||||||
|
if u.serviceName == clamServiceNameCentOs {
|
||||||
|
filePath = "/etc/clamav/freshclam.conf"
|
||||||
|
} else {
|
||||||
|
filePath = "/etc/freshclam.conf"
|
||||||
|
}
|
||||||
|
service = "clamav-freshclam.service"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("not support such type")
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
write := bufio.NewWriter(file)
|
||||||
|
_, _ = write.WriteString(req.File)
|
||||||
|
write.Flush()
|
||||||
|
|
||||||
|
_ = systemctl.Restart(service)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFileByName(name string) []string {
|
||||||
|
var logPaths []string
|
||||||
|
pathItem := path.Join(global.CONF.System.DataDir, scanDir, name)
|
||||||
|
_ = filepath.Walk(pathItem, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.IsDir() || info.Name() == name {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logPaths = append(logPaths, info.Name())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
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
|
||||||
|
}
|
||||||
|
data.Log = string(file)
|
||||||
|
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, "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
|
||||||
|
}
|
@ -25,6 +25,7 @@ var (
|
|||||||
groupRepo = repo.NewIGroupRepo()
|
groupRepo = repo.NewIGroupRepo()
|
||||||
commandRepo = repo.NewICommandRepo()
|
commandRepo = repo.NewICommandRepo()
|
||||||
ftpRepo = repo.NewIFtpRepo()
|
ftpRepo = repo.NewIFtpRepo()
|
||||||
|
clamRepo = repo.NewIClamRepo()
|
||||||
|
|
||||||
settingRepo = repo.NewISettingRepo()
|
settingRepo = repo.NewISettingRepo()
|
||||||
backupRepo = repo.NewIBackupRepo()
|
backupRepo = repo.NewIBackupRepo()
|
||||||
|
@ -91,6 +91,7 @@ func Init() {
|
|||||||
migrations.AddCronJobColumn,
|
migrations.AddCronJobColumn,
|
||||||
migrations.AddForward,
|
migrations.AddForward,
|
||||||
migrations.AddShellColumn,
|
migrations.AddShellColumn,
|
||||||
|
migrations.AddClam,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
@ -268,3 +268,13 @@ var AddShellColumn = &gormigrate.Migration{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddClam = &gormigrate.Migration{
|
||||||
|
ID: "20240624-add-clam",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.Clam{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -45,5 +45,17 @@ func (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
toolboxRouter.POST("/ftp/update", baseApi.UpdateFtp)
|
toolboxRouter.POST("/ftp/update", baseApi.UpdateFtp)
|
||||||
toolboxRouter.POST("/ftp/del", baseApi.DeleteFtp)
|
toolboxRouter.POST("/ftp/del", baseApi.DeleteFtp)
|
||||||
toolboxRouter.POST("/ftp/sync", baseApi.SyncFtp)
|
toolboxRouter.POST("/ftp/sync", baseApi.SyncFtp)
|
||||||
|
|
||||||
|
toolboxRouter.POST("/clam/search", baseApi.SearchClam)
|
||||||
|
toolboxRouter.POST("/clam/record/search", baseApi.SearchClamRecord)
|
||||||
|
toolboxRouter.POST("/clam/record/clean", baseApi.CleanClamRecord)
|
||||||
|
toolboxRouter.POST("/clam/file/search", baseApi.SearchClamFile)
|
||||||
|
toolboxRouter.POST("/clam/file/update", baseApi.UpdateFile)
|
||||||
|
toolboxRouter.POST("/clam", baseApi.CreateClam)
|
||||||
|
toolboxRouter.POST("/clam/base", baseApi.LoadClamBaseInfo)
|
||||||
|
toolboxRouter.POST("/clam/operate", baseApi.OperateClam)
|
||||||
|
toolboxRouter.POST("/clam/update", baseApi.UpdateClam)
|
||||||
|
toolboxRouter.POST("/clam/del", baseApi.DeleteClam)
|
||||||
|
toolboxRouter.POST("/clam/handle", baseApi.HandleClamScan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11065,6 +11065,445 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/toolbox/clam": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Create clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "create clam [name][path]",
|
||||||
|
"formatZH": "创建扫描规则 [name][path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/base": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取 Clam 基础信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Load clam base info",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamBaseInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/del": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "删除扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Delete clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.BatchDeleteReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "ids",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "names"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"ids"
|
||||||
|
],
|
||||||
|
"formatEN": "delete clam [names]",
|
||||||
|
"formatZH": "删除扫描规则 [names]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/file/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Load clam file",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperationWithName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/file/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新病毒扫描配置文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Update clam file",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.UpdateByNameAndFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/handle": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "执行病毒扫描",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Handle clam scan",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperateByID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "handle clam scan [name]",
|
||||||
|
"formatZH": "执行病毒扫描 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/operate": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "修改 Clam 状态",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Operate Clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.Operate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"operation"
|
||||||
|
],
|
||||||
|
"formatEN": "[operation] FTP",
|
||||||
|
"formatZH": "[operation] Clam",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/record/clean": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "清空扫描报告",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Clean clam record",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperateByID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "clean clam record [name]",
|
||||||
|
"formatZH": "清空扫描报告 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/record/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描结果列表分页",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Page clam record",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamLogSearch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描规则列表分页",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Page clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.SearchWithPage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "修改扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Update clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "update clam [name][path]",
|
||||||
|
"formatZH": "修改扫描规则 [name][path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/toolbox/clean": {
|
"/toolbox/clean": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12748,6 +13187,73 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/websites/default/html/:type": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取默认 html",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get default html",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.FileInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/websites/default/html/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新默认 html",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Update default html",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.WebsiteHtmlUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"formatEN": "Update default html",
|
||||||
|
"formatZH": "更新默认 html",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/websites/default/server": {
|
"/websites/default/server": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -14934,6 +15440,75 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.ClamBaseInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"isActive": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"isExist": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamCreate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamLogSearch": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"page",
|
||||||
|
"pageSize"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"clamID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"endTime": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pageSize": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"startTime": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.Clean": {
|
"dto.Clean": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -20144,6 +20719,9 @@ const docTemplate = `{
|
|||||||
"ID": {
|
"ID": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"latest": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -21388,6 +21966,21 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.WebsiteHtmlUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"content",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.WebsiteInstallCheckReq": {
|
"request.WebsiteInstallCheckReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -11058,6 +11058,445 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/toolbox/clam": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Create clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "create clam [name][path]",
|
||||||
|
"formatZH": "创建扫描规则 [name][path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/base": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取 Clam 基础信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Load clam base info",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamBaseInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/del": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "删除扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Delete clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.BatchDeleteReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "ids",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "names"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"ids"
|
||||||
|
],
|
||||||
|
"formatEN": "delete clam [names]",
|
||||||
|
"formatZH": "删除扫描规则 [names]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/file/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Load clam file",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperationWithName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/file/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新病毒扫描配置文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Update clam file",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.UpdateByNameAndFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/handle": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "执行病毒扫描",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Handle clam scan",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperateByID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "handle clam scan [name]",
|
||||||
|
"formatZH": "执行病毒扫描 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/operate": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "修改 Clam 状态",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Operate Clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.Operate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"operation"
|
||||||
|
],
|
||||||
|
"formatEN": "[operation] FTP",
|
||||||
|
"formatZH": "[operation] Clam",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/record/clean": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "清空扫描报告",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Clean clam record",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.OperateByID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "clams",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": true,
|
||||||
|
"output_column": "name",
|
||||||
|
"output_value": "name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "clean clam record [name]",
|
||||||
|
"formatZH": "清空扫描报告 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/record/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描结果列表分页",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Page clam record",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamLogSearch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取扫描规则列表分页",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Page clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.SearchWithPage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageResult"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/toolbox/clam/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "修改扫描规则",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Clam"
|
||||||
|
],
|
||||||
|
"summary": "Update clam",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ClamUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "update clam [name][path]",
|
||||||
|
"formatZH": "修改扫描规则 [name][path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/toolbox/clean": {
|
"/toolbox/clean": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12741,6 +13180,73 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/websites/default/html/:type": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取默认 html",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get default html",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.FileInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/websites/default/html/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新默认 html",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Update default html",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.WebsiteHtmlUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"formatEN": "Update default html",
|
||||||
|
"formatZH": "更新默认 html",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/websites/default/server": {
|
"/websites/default/server": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -14927,6 +15433,75 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dto.ClamBaseInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"isActive": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"isExist": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamCreate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamLogSearch": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"page",
|
||||||
|
"pageSize"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"clamID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"endTime": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pageSize": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"startTime": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dto.ClamUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dto.Clean": {
|
"dto.Clean": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -20137,6 +20712,9 @@
|
|||||||
"ID": {
|
"ID": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"latest": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -21381,6 +21959,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.WebsiteHtmlUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"content",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.WebsiteInstallCheckReq": {
|
"request.WebsiteInstallCheckReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -216,6 +216,51 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- database
|
- database
|
||||||
type: object
|
type: object
|
||||||
|
dto.ClamBaseInfo:
|
||||||
|
properties:
|
||||||
|
isActive:
|
||||||
|
type: boolean
|
||||||
|
isExist:
|
||||||
|
type: boolean
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
dto.ClamCreate:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
dto.ClamLogSearch:
|
||||||
|
properties:
|
||||||
|
clamID:
|
||||||
|
type: integer
|
||||||
|
endTime:
|
||||||
|
type: string
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
pageSize:
|
||||||
|
type: integer
|
||||||
|
startTime:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- page
|
||||||
|
- pageSize
|
||||||
|
type: object
|
||||||
|
dto.ClamUpdate:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
dto.Clean:
|
dto.Clean:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
@ -3720,6 +3765,8 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
ID:
|
ID:
|
||||||
type: integer
|
type: integer
|
||||||
|
latest:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
page:
|
page:
|
||||||
@ -4565,6 +4612,16 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- websiteId
|
- websiteId
|
||||||
type: object
|
type: object
|
||||||
|
request.WebsiteHtmlUpdate:
|
||||||
|
properties:
|
||||||
|
content:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- content
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
request.WebsiteInstallCheckReq:
|
request.WebsiteInstallCheckReq:
|
||||||
properties:
|
properties:
|
||||||
InstallIds:
|
InstallIds:
|
||||||
@ -12360,6 +12417,285 @@ paths:
|
|||||||
formatEN: upgrade system => [version]
|
formatEN: upgrade system => [version]
|
||||||
formatZH: 更新系统 => [version]
|
formatZH: 更新系统 => [version]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/toolbox/clam:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建扫描规则
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ClamCreate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Create clam
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- name
|
||||||
|
- path
|
||||||
|
formatEN: create clam [name][path]
|
||||||
|
formatZH: 创建扫描规则 [name][path]
|
||||||
|
paramKeys: []
|
||||||
|
/toolbox/clam/base:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取 Clam 基础信息
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ClamBaseInfo'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Load clam base info
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
/toolbox/clam/del:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 删除扫描规则
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.BatchDeleteReq'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Delete clam
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions:
|
||||||
|
- db: clams
|
||||||
|
input_column: id
|
||||||
|
input_value: ids
|
||||||
|
isList: true
|
||||||
|
output_column: name
|
||||||
|
output_value: names
|
||||||
|
bodyKeys:
|
||||||
|
- ids
|
||||||
|
formatEN: delete clam [names]
|
||||||
|
formatZH: 删除扫描规则 [names]
|
||||||
|
paramKeys: []
|
||||||
|
/toolbox/clam/file/search:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取扫描文件
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.OperationWithName'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.PageResult'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Load clam file
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
/toolbox/clam/file/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新病毒扫描配置文件
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.UpdateByNameAndFile'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Update clam file
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
/toolbox/clam/handle:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 执行病毒扫描
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.OperateByID'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Handle clam scan
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions:
|
||||||
|
- db: clams
|
||||||
|
input_column: id
|
||||||
|
input_value: id
|
||||||
|
isList: true
|
||||||
|
output_column: name
|
||||||
|
output_value: name
|
||||||
|
bodyKeys:
|
||||||
|
- id
|
||||||
|
formatEN: handle clam scan [name]
|
||||||
|
formatZH: 执行病毒扫描 [name]
|
||||||
|
paramKeys: []
|
||||||
|
/toolbox/clam/operate:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 修改 Clam 状态
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.Operate'
|
||||||
|
responses: {}
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Operate Clam
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- operation
|
||||||
|
formatEN: '[operation] FTP'
|
||||||
|
formatZH: '[operation] Clam'
|
||||||
|
paramKeys: []
|
||||||
|
/toolbox/clam/record/clean:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 清空扫描报告
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.OperateByID'
|
||||||
|
responses: {}
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Clean clam record
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions:
|
||||||
|
- db: clams
|
||||||
|
input_column: id
|
||||||
|
input_value: id
|
||||||
|
isList: true
|
||||||
|
output_column: name
|
||||||
|
output_value: name
|
||||||
|
bodyKeys:
|
||||||
|
- id
|
||||||
|
formatEN: clean clam record [name]
|
||||||
|
formatZH: 清空扫描报告 [name]
|
||||||
|
paramKeys: []
|
||||||
|
/toolbox/clam/record/search:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取扫描结果列表分页
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ClamLogSearch'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.PageResult'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Page clam record
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
/toolbox/clam/search:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取扫描规则列表分页
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.SearchWithPage'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.PageResult'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Page clam
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
/toolbox/clam/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 修改扫描规则
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ClamUpdate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Update clam
|
||||||
|
tags:
|
||||||
|
- Clam
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- name
|
||||||
|
- path
|
||||||
|
formatEN: update clam [name][path]
|
||||||
|
formatZH: 修改扫描规则 [name][path]
|
||||||
|
paramKeys: []
|
||||||
/toolbox/clean:
|
/toolbox/clean:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@ -13422,6 +13758,48 @@ paths:
|
|||||||
formatEN: Nginx conf update [domain]
|
formatEN: Nginx conf update [domain]
|
||||||
formatZH: nginx 配置修改 [domain]
|
formatZH: nginx 配置修改 [domain]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/websites/default/html/:type:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取默认 html
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.FileInfo'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Get default html
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
|
/websites/default/html/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新默认 html
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.WebsiteHtmlUpdate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Update default html
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- type
|
||||||
|
formatEN: Update default html
|
||||||
|
formatZH: 更新默认 html
|
||||||
|
paramKeys: []
|
||||||
/websites/default/server:
|
/websites/default/server:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -116,4 +116,42 @@ export namespace Toolbox {
|
|||||||
status: string;
|
status: string;
|
||||||
size: string;
|
size: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClamBaseInfo {
|
||||||
|
version: string;
|
||||||
|
isActive: boolean;
|
||||||
|
isExist: boolean;
|
||||||
|
}
|
||||||
|
export interface ClamInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
lastHandleDate: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export interface ClamCreate {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export interface ClamUpdate {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export interface ClamSearchLog extends ReqPage {
|
||||||
|
clamID: number;
|
||||||
|
startTime: Date;
|
||||||
|
endTime: Date;
|
||||||
|
}
|
||||||
|
export interface ClamLog {
|
||||||
|
name: string;
|
||||||
|
scanDate: string;
|
||||||
|
scanTime: string;
|
||||||
|
scannedFiles: string;
|
||||||
|
infectedFiles: string;
|
||||||
|
log: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,3 +106,38 @@ export const updateFtp = (params: Toolbox.FtpUpdate) => {
|
|||||||
export const deleteFtp = (params: { ids: number[] }) => {
|
export const deleteFtp = (params: { ids: number[] }) => {
|
||||||
return http.post(`/toolbox/ftp/del`, params);
|
return http.post(`/toolbox/ftp/del`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// clam
|
||||||
|
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 searchClamFile = (name: string) => {
|
||||||
|
return http.post<string>(`/toolbox/clam/file/search`, { name: name });
|
||||||
|
};
|
||||||
|
export const updateClamFile = (name: string, file: string) => {
|
||||||
|
return http.post(`/toolbox/clam/file/update`, { name: name, file: file });
|
||||||
|
};
|
||||||
|
export const searchClamBaseInfo = () => {
|
||||||
|
return http.post<Toolbox.ClamBaseInfo>(`/toolbox/clam/base`);
|
||||||
|
};
|
||||||
|
export const updateClamBaseInfo = (operate: string) => {
|
||||||
|
return http.post(`/toolbox/clam/operate`, { Operation: operate });
|
||||||
|
};
|
||||||
|
export const searchClam = (param: ReqPage) => {
|
||||||
|
return http.post<ResPage<Toolbox.ClamInfo>>(`/toolbox/clam/search`, param);
|
||||||
|
};
|
||||||
|
export const createClam = (params: Toolbox.ClamCreate) => {
|
||||||
|
return http.post(`/toolbox/clam`, params);
|
||||||
|
};
|
||||||
|
export const updateClam = (params: Toolbox.ClamUpdate) => {
|
||||||
|
return http.post(`/toolbox/clam/update`, params);
|
||||||
|
};
|
||||||
|
export const deleteClam = (params: { ids: number[] }) => {
|
||||||
|
return http.post(`/toolbox/clam/del`, params);
|
||||||
|
};
|
||||||
|
export const handleClamScan = (id: number) => {
|
||||||
|
return http.post(`/toolbox/clam/handle`, { id: id });
|
||||||
|
};
|
||||||
|
@ -19,7 +19,6 @@ const props = defineProps({
|
|||||||
code: String,
|
code: String,
|
||||||
});
|
});
|
||||||
const loadErrInfo = () => {
|
const loadErrInfo = () => {
|
||||||
console.log(props.code);
|
|
||||||
switch (props.code) {
|
switch (props.code) {
|
||||||
case '400':
|
case '400':
|
||||||
return '400 Bad Request';
|
return '400 Bad Request';
|
||||||
|
@ -229,6 +229,7 @@ const message = {
|
|||||||
status: {
|
status: {
|
||||||
running: 'Running',
|
running: 'Running',
|
||||||
done: 'Done',
|
done: 'Done',
|
||||||
|
scanFailed: 'Incomplete',
|
||||||
success: 'Success',
|
success: 'Success',
|
||||||
waiting: 'Waiting',
|
waiting: 'Waiting',
|
||||||
waiting1: 'Waiting',
|
waiting1: 'Waiting',
|
||||||
@ -1058,6 +1059,19 @@ const message = {
|
|||||||
'Disabling the selected FTP account will revoke its access permissions. Do you want to continue?',
|
'Disabling the selected FTP account will revoke its access permissions. Do you want to continue?',
|
||||||
syncHelper: 'Sync FTP account data between server and database. Do you want to continue?',
|
syncHelper: 'Sync FTP account data between server and database. Do you want to continue?',
|
||||||
},
|
},
|
||||||
|
clam: {
|
||||||
|
clam: 'Virus Scan',
|
||||||
|
clamCreate: 'Create Scan Rule',
|
||||||
|
scanDate: 'Scan Date',
|
||||||
|
scanTime: 'Elapsed Time',
|
||||||
|
scannedFiles: 'Number of Scanned Files',
|
||||||
|
infectedFiles: 'Number of Infected Files',
|
||||||
|
log: 'Details',
|
||||||
|
clamConf: 'Scan Configuration',
|
||||||
|
clamLog: 'Scan Log',
|
||||||
|
freshClam: 'Virus Database Refresh Configuration',
|
||||||
|
freshClamLog: 'Virus Database Refresh Log',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
panelLog: 'Panel logs',
|
panelLog: 'Panel logs',
|
||||||
|
@ -224,6 +224,7 @@ const message = {
|
|||||||
status: {
|
status: {
|
||||||
running: '已啟動',
|
running: '已啟動',
|
||||||
done: '已完成',
|
done: '已完成',
|
||||||
|
scanFailed: '未完成',
|
||||||
success: '成功',
|
success: '成功',
|
||||||
waiting: '執行中',
|
waiting: '執行中',
|
||||||
waiting1: '等待中',
|
waiting1: '等待中',
|
||||||
@ -1000,6 +1001,19 @@ const message = {
|
|||||||
disableHelper: '停用選取的 FTP 帳號後,該 FTP 帳號將失去訪問權限,是否繼續操作?',
|
disableHelper: '停用選取的 FTP 帳號後,該 FTP 帳號將失去訪問權限,是否繼續操作?',
|
||||||
syncHelper: '同步伺服器與資料庫中的 FTP 帳戶資料,是否繼續操作?',
|
syncHelper: '同步伺服器與資料庫中的 FTP 帳戶資料,是否繼續操作?',
|
||||||
},
|
},
|
||||||
|
clam: {
|
||||||
|
clam: '病毒掃描',
|
||||||
|
clamCreate: '創建掃描規則',
|
||||||
|
scanDate: '掃描時間',
|
||||||
|
scanTime: '耗時',
|
||||||
|
scannedFiles: '掃描文件數',
|
||||||
|
infectedFiles: '危險文件數',
|
||||||
|
log: '詳情',
|
||||||
|
clamConf: '掃描配置',
|
||||||
|
clamLog: '掃描日誌',
|
||||||
|
freshClam: '病毒庫刷新配置',
|
||||||
|
freshClamLog: '病毒庫刷新日誌',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
panelLog: '面板日誌',
|
panelLog: '面板日誌',
|
||||||
|
@ -225,6 +225,7 @@ const message = {
|
|||||||
status: {
|
status: {
|
||||||
running: '已启动',
|
running: '已启动',
|
||||||
done: '已完成',
|
done: '已完成',
|
||||||
|
scanFailed: '未完成',
|
||||||
success: '成功',
|
success: '成功',
|
||||||
waiting: '执行中',
|
waiting: '执行中',
|
||||||
waiting1: '等待中',
|
waiting1: '等待中',
|
||||||
@ -1002,6 +1003,19 @@ const message = {
|
|||||||
disableHelper: '停用选中的 FTP 账号后,该 FTP 账号将失去访问权限,是否继续操作?',
|
disableHelper: '停用选中的 FTP 账号后,该 FTP 账号将失去访问权限,是否继续操作?',
|
||||||
syncHelper: '同步服务器与数据库中的 FTP 账户数据,是否继续操作?',
|
syncHelper: '同步服务器与数据库中的 FTP 账户数据,是否继续操作?',
|
||||||
},
|
},
|
||||||
|
clam: {
|
||||||
|
clam: '病毒扫描',
|
||||||
|
clamCreate: '创建扫描规则',
|
||||||
|
scanDate: '扫描时间',
|
||||||
|
scanTime: '耗时',
|
||||||
|
scannedFiles: '扫描文件数',
|
||||||
|
infectedFiles: '危险文件数',
|
||||||
|
log: '详情',
|
||||||
|
clamConf: '扫描配置',
|
||||||
|
clamLog: '扫描日志',
|
||||||
|
freshClam: '病毒库刷新配置',
|
||||||
|
freshClamLog: '病毒库刷新日志',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
logs: {
|
logs: {
|
||||||
panelLog: '面板日志',
|
panelLog: '面板日志',
|
||||||
|
@ -37,6 +37,26 @@ const toolboxRouter = {
|
|||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'clam',
|
||||||
|
name: 'Clam',
|
||||||
|
component: () => import('@/views/toolbox/clam/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/toolbox',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'clam/setting',
|
||||||
|
name: 'Clam-Setting',
|
||||||
|
component: () => import('@/views/toolbox/clam/setting/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/toolbox',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'ftp',
|
path: 'ftp',
|
||||||
name: 'FTP',
|
name: 'FTP',
|
||||||
|
@ -236,7 +236,6 @@ const onSavePort = async (formEl: FormInstance | undefined) => {
|
|||||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||||
};
|
};
|
||||||
confirmPortRef.value!.acceptParams(params);
|
confirmPortRef.value!.acceptParams(params);
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
function callback(error: any) {
|
function callback(error: any) {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
246
frontend/src/views/toolbox/clam/index.vue
Normal file
246
frontend/src/views/toolbox/clam/index.vue
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<LayoutContent v-loading="loading" v-if="!isRecordShow && !isSettingShow" :title="$t('toolbox.clam.clam')">
|
||||||
|
<template #app>
|
||||||
|
<ClamStatus
|
||||||
|
@setting="setting"
|
||||||
|
v-model:loading="loading"
|
||||||
|
@get-status="getStatus"
|
||||||
|
v-model:mask-show="maskShow"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #toolbar>
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
|
||||||
|
<el-button type="primary" :disabled="!form.isActive" @click="onOpenDialog('add')">
|
||||||
|
{{ $t('toolbox.clam.clamCreate') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button plain :disabled="selects.length === 0 || !form.isActive" @click="onDelete(null)">
|
||||||
|
{{ $t('commons.button.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8">
|
||||||
|
<TableSearch @search="search()" v-model:searchName="searchName" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable
|
||||||
|
v-if="!isSettingShow"
|
||||||
|
:pagination-config="paginationConfig"
|
||||||
|
v-model:selects="selects"
|
||||||
|
@sort-change="search"
|
||||||
|
@search="search"
|
||||||
|
:data="data"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" fix />
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('commons.table.name')"
|
||||||
|
:min-width="60"
|
||||||
|
prop="name"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column :label="$t('file.path')" :min-width="120" prop="path" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button text type="primary" @click="toFolder(row.path)">{{ row.path }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('cronjob.lastRecordTime')"
|
||||||
|
:min-width="100"
|
||||||
|
prop="lastHandleDate"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<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)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<fu-table-operations
|
||||||
|
width="200px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:ellipsis="10"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
|
||||||
|
<OpDialog ref="opRef" @search="search" @submit="onSubmitDelete()" />
|
||||||
|
<OperateDialog @search="search" ref="dialogRef" />
|
||||||
|
<LogDialog ref="dialogLogRef" />
|
||||||
|
<SettingDialog v-if="isSettingShow" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { deleteClam, handleClamScan, searchClam, updateClam } from '@/api/modules/toolbox';
|
||||||
|
import OperateDialog from '@/views/toolbox/clam/operate/index.vue';
|
||||||
|
import LogDialog from '@/views/toolbox/clam/record/index.vue';
|
||||||
|
import ClamStatus from '@/views/toolbox/clam/status/index.vue';
|
||||||
|
import SettingDialog from '@/views/toolbox/clam/setting/index.vue';
|
||||||
|
import { Toolbox } from '@/api/interface/toolbox';
|
||||||
|
import router from '@/routers';
|
||||||
|
|
||||||
|
const loading = ref();
|
||||||
|
const selects = ref<any>([]);
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'clam-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: Number(localStorage.getItem('ftp-page-size')) || 10,
|
||||||
|
total: 0,
|
||||||
|
orderBy: 'created_at',
|
||||||
|
order: 'null',
|
||||||
|
});
|
||||||
|
const searchName = ref();
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
isActive: true,
|
||||||
|
isExist: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const opRef = ref();
|
||||||
|
const dialogRef = ref();
|
||||||
|
const operateIDs = ref();
|
||||||
|
const dialogLogRef = ref();
|
||||||
|
const isRecordShow = ref();
|
||||||
|
|
||||||
|
const isSettingShow = ref();
|
||||||
|
const maskShow = ref(true);
|
||||||
|
const clamStatus = ref({
|
||||||
|
isExist: false,
|
||||||
|
version: false,
|
||||||
|
isActive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
let params = {
|
||||||
|
info: searchName.value,
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
await searchClam(params)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setting = () => {
|
||||||
|
router.push({ name: 'Clam-Setting' });
|
||||||
|
};
|
||||||
|
const getStatus = (status: any) => {
|
||||||
|
clamStatus.value = status;
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toFolder = (folder: string) => {
|
||||||
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = async (row: any) => {
|
||||||
|
await await updateClam(row);
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOpenDialog = async (title: string, rowData: Partial<Toolbox.ClamInfo> = {}) => {
|
||||||
|
let params = {
|
||||||
|
title,
|
||||||
|
rowData: { ...rowData },
|
||||||
|
};
|
||||||
|
dialogRef.value!.acceptParams(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = async (row: Toolbox.ClamInfo | null) => {
|
||||||
|
let names = [];
|
||||||
|
let ids = [];
|
||||||
|
if (row) {
|
||||||
|
ids = [row.id];
|
||||||
|
names = [row.name];
|
||||||
|
} else {
|
||||||
|
for (const item of selects.value) {
|
||||||
|
names.push(item.name);
|
||||||
|
ids.push(item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operateIDs.value = ids;
|
||||||
|
opRef.value.acceptParams({
|
||||||
|
title: i18n.global.t('commons.button.delete'),
|
||||||
|
names: names,
|
||||||
|
msg: i18n.global.t('commons.msg.operatorHelper', [
|
||||||
|
i18n.global.t('cronjob.cronTask'),
|
||||||
|
i18n.global.t('commons.button.delete'),
|
||||||
|
]),
|
||||||
|
api: null,
|
||||||
|
params: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmitDelete = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await deleteClam({ ids: operateIDs.value })
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.handle'),
|
||||||
|
click: async (row: Toolbox.ClamInfo) => {
|
||||||
|
loading.value = true;
|
||||||
|
await handleClamScan(row.id)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: (row: Toolbox.ClamInfo) => {
|
||||||
|
onOpenDialog('edit', row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('cronjob.record'),
|
||||||
|
click: (row: Toolbox.ClamInfo) => {
|
||||||
|
isRecordShow.value = true;
|
||||||
|
let params = {
|
||||||
|
rowData: { ...row },
|
||||||
|
};
|
||||||
|
dialogLogRef.value!.acceptParams(params);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: (row: Toolbox.ClamInfo) => {
|
||||||
|
onDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
133
frontend/src/views/toolbox/clam/operate/index.vue
Normal file
133
frontend/src/views/toolbox/clam/operate/index.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
v-model="drawerVisible"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
size="50%"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader
|
||||||
|
:header="title"
|
||||||
|
:hideResource="dialogData.title === 'add'"
|
||||||
|
:resource="dialogData.rowData?.name"
|
||||||
|
:back="handleClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules" v-loading="loading">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input
|
||||||
|
:disabled="dialogData.title === 'edit'"
|
||||||
|
clearable
|
||||||
|
v-model.trim="dialogData.rowData!.name"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('file.root')" prop="path">
|
||||||
|
<el-input v-model="dialogData.rowData!.path">
|
||||||
|
<template #prepend>
|
||||||
|
<FileList @choose="loadDir" :dir="true"></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
|
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import FileList from '@/components/file-list/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { Toolbox } from '@/api/interface/toolbox';
|
||||||
|
import { createClam, updateClam } from '@/api/modules/toolbox';
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string;
|
||||||
|
rowData?: Toolbox.ClamInfo;
|
||||||
|
getTableList?: () => Promise<any>;
|
||||||
|
}
|
||||||
|
const loading = ref();
|
||||||
|
const title = ref<string>('');
|
||||||
|
const drawerVisible = ref(false);
|
||||||
|
const dialogData = ref<DialogProps>({
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
dialogData.value = params;
|
||||||
|
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
|
||||||
|
drawerVisible.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [Rules.simpleName],
|
||||||
|
path: [Rules.requiredInput, Rules.noSpace],
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const loadDir = async (path: string) => {
|
||||||
|
dialogData.value.rowData!.path = path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
loading.value = true;
|
||||||
|
if (dialogData.value.title === 'edit') {
|
||||||
|
await updateClam(dialogData.value.rowData)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
drawerVisible.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createClam(dialogData.value.rowData)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
drawerVisible.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
345
frontend/src/views/toolbox/clam/record/index.vue
Normal file
345
frontend/src/views/toolbox/clam/record/index.vue
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
<template>
|
||||||
|
<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">
|
||||||
|
{{ 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">
|
||||||
|
{{ 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"
|
||||||
|
>
|
||||||
|
{{ 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>
|
||||||
|
</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="$t('commons.search.timeRange')"
|
||||||
|
:start-placeholder="$t('commons.search.timeStart')"
|
||||||
|
:end-placeholder="$t('commons.search.timeEnd')"
|
||||||
|
:shortcuts="shortcuts"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<div class="mainClass">
|
||||||
|
<el-row :gutter="20" v-show="hasRecords" class="mainRowClass">
|
||||||
|
<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>
|
||||||
|
<el-tag v-if="row.status === 'Done'" type="success">
|
||||||
|
{{ $t('commons.status.done') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="row.status === 'Waiting'" type="info">
|
||||||
|
{{ $t('commons.status.scanFailed') }}
|
||||||
|
</el-tag>
|
||||||
|
<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-col>
|
||||||
|
<el-col :span="17">
|
||||||
|
<el-form label-position="top" :v-key="refresh">
|
||||||
|
<el-row v-if="currentRecord?.status === 'Done'">
|
||||||
|
<el-form-item class="descriptionWide">
|
||||||
|
<template #label>
|
||||||
|
<span class="status-label">{{ $t('toolbox.clam.scanTime') }}</span>
|
||||||
|
</template>
|
||||||
|
<span class="status-count">
|
||||||
|
{{ 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">
|
||||||
|
{{ currentRecord?.infectedFiles }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-row>
|
||||||
|
<el-row v-if="currentRecord?.log">
|
||||||
|
<span>{{ $t('commons.table.records') }}</span>
|
||||||
|
<codemirror
|
||||||
|
ref="mymirror"
|
||||||
|
:autofocus="true"
|
||||||
|
:placeholder="$t('cronjob.noLogs')"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="height: calc(100vh - 488px); width: 100%; margin-top: 5px"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
@ready="handleReady"
|
||||||
|
v-model="currentRecord.log"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="app-warn" v-show="!hasRecords">
|
||||||
|
<div>
|
||||||
|
<span>{{ $t('cronjob.noRecord') }}</span>
|
||||||
|
<div>
|
||||||
|
<img src="@/assets/images/no_app.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { shortcuts } from '@/utils/shortcuts';
|
||||||
|
import { Toolbox } from '@/api/interface/toolbox';
|
||||||
|
import { cleanClamRecord, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';
|
||||||
|
|
||||||
|
const loading = ref();
|
||||||
|
const refresh = ref(false);
|
||||||
|
const hasRecords = ref();
|
||||||
|
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
|
const mymirror = ref();
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
const view = shallowRef();
|
||||||
|
const handleReady = (payload) => {
|
||||||
|
view.value = payload.view;
|
||||||
|
};
|
||||||
|
|
||||||
|
const recordShow = ref(false);
|
||||||
|
interface DialogProps {
|
||||||
|
rowData: Toolbox.ClamInfo;
|
||||||
|
}
|
||||||
|
const dialogData = ref();
|
||||||
|
const records = ref<Array<Toolbox.ClamLog>>([]);
|
||||||
|
const currentRecord = ref<Toolbox.ClamLog>();
|
||||||
|
|
||||||
|
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||||
|
let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey));
|
||||||
|
if (itemSize) {
|
||||||
|
searchInfo.pageSize = itemSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordShow.value = true;
|
||||||
|
dialogData.value = params;
|
||||||
|
search();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
search();
|
||||||
|
}, 1000 * 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSizeChange = (val: number) => {
|
||||||
|
searchInfo.pageSize = val;
|
||||||
|
localStorage.setItem(searchInfo.cacheSizeKey, val + '');
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
const handleCurrentChange = (val: number) => {
|
||||||
|
searchInfo.page = val;
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
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().setHours(23, 59, 59, 999)),
|
||||||
|
]);
|
||||||
|
const searchInfo = reactive({
|
||||||
|
cacheSizeKey: 'clam-record-page-size',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 8,
|
||||||
|
recordTotal: 0,
|
||||||
|
cronjobID: 0,
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onHandle = async (row: Toolbox.ClamInfo) => {
|
||||||
|
loading.value = true;
|
||||||
|
await handleClamScan(row.id)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {
|
||||||
|
searchInfo.startTime = timeRangeLoad.value[0];
|
||||||
|
searchInfo.endTime = timeRangeLoad.value[1];
|
||||||
|
} else {
|
||||||
|
searchInfo.startTime = new Date(new Date().setHours(0, 0, 0, 0));
|
||||||
|
searchInfo.endTime = new Date();
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
page: searchInfo.page,
|
||||||
|
pageSize: searchInfo.pageSize,
|
||||||
|
clamID: dialogData.value.rowData!.id,
|
||||||
|
startTime: searchInfo.startTime,
|
||||||
|
endTime: searchInfo.endTime,
|
||||||
|
};
|
||||||
|
const res = await searchClamRecord(params);
|
||||||
|
records.value = res.data.items;
|
||||||
|
searchInfo.recordTotal = res.data.total;
|
||||||
|
hasRecords.value = searchInfo.recordTotal !== 0;
|
||||||
|
if (!hasRecords.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!currentRecord.value) {
|
||||||
|
currentRecord.value = records.value[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickRow = async (row: Toolbox.ClamLog) => {
|
||||||
|
currentRecord.value = row;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClean = async () => {
|
||||||
|
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('commons.msg.deleteTitle'), {
|
||||||
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
type: 'warning',
|
||||||
|
}).then(async () => {
|
||||||
|
loading.value = true;
|
||||||
|
console.log(dialogData.value.id);
|
||||||
|
cleanClamRecord(dialogData.value.rowData.id)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.infinite-list {
|
||||||
|
height: calc(100vh - 420px);
|
||||||
|
.select-sign {
|
||||||
|
&::before {
|
||||||
|
float: left;
|
||||||
|
margin-left: -3px;
|
||||||
|
position: relative;
|
||||||
|
width: 3px;
|
||||||
|
height: 24px;
|
||||||
|
content: '';
|
||||||
|
background: $primary-color;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-tag {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.descriptionWide {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
.page-item {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1400px) {
|
||||||
|
.mainClass {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.mainRowClass {
|
||||||
|
min-width: 1200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
138
frontend/src/views/toolbox/clam/setting/index.vue
Normal file
138
frontend/src/views/toolbox/clam/setting/index.vue
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<LayoutContent>
|
||||||
|
<template #app>
|
||||||
|
<ClamStatus v-model:loading="loading" />
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<back-button name="Clam" header="Clamav">
|
||||||
|
<template #buttons>
|
||||||
|
<el-button type="primary" :plain="activeName !== 'clamd'" @click="search('clamd')">
|
||||||
|
{{ $t('toolbox.clam.clamConf') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :plain="activeName !== 'freshclam'" @click="search('freshclam')">
|
||||||
|
{{ $t('toolbox.clam.freshClam') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :plain="activeName !== 'clamd-log'" @click="search('clamd-log')">
|
||||||
|
{{ $t('toolbox.clam.clamLog') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:plain="activeName !== 'freshclam-log'"
|
||||||
|
@click="search('freshclam-log')"
|
||||||
|
>
|
||||||
|
{{ $t('toolbox.clam.freshClamLog') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</back-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #main>
|
||||||
|
<div>
|
||||||
|
<codemirror
|
||||||
|
:autofocus="true"
|
||||||
|
:placeholder="$t('commons.msg.noneData')"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
:style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px' }"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
@ready="handleReady"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="content"
|
||||||
|
:disabled="canUpdate()"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" style="margin-top: 10px" v-if="!canUpdate()" @click="onSave">
|
||||||
|
{{ $t('commons.button.save') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
|
||||||
|
<ConfirmDialog ref="confirmRef" @confirm="onSubmit"></ConfirmDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import ClamStatus from '@/views/toolbox/clam/status/index.vue';
|
||||||
|
import { searchClamFile, updateClamFile } from '@/api/modules/toolbox';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { GlobalStore } from '@/store';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
const view = shallowRef();
|
||||||
|
const handleReady = (payload) => {
|
||||||
|
view.value = payload.view;
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeName = ref('clamd');
|
||||||
|
const content = ref();
|
||||||
|
const confirmRef = ref();
|
||||||
|
|
||||||
|
const loadHeight = () => {
|
||||||
|
let height = globalStore.openMenuTabs ? '405px' : '375px';
|
||||||
|
if (canUpdate()) {
|
||||||
|
height = globalStore.openMenuTabs ? '363px' : '333px';
|
||||||
|
}
|
||||||
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const canUpdate = () => {
|
||||||
|
return activeName.value.indexOf('-log') !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async (itemName: string) => {
|
||||||
|
loading.value = true;
|
||||||
|
activeName.value = itemName;
|
||||||
|
await searchClamFile(activeName.value)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
content.value = res.data;
|
||||||
|
nextTick(() => {
|
||||||
|
const state = view.value.state;
|
||||||
|
view.value.dispatch({
|
||||||
|
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
let params = {
|
||||||
|
header: i18n.global.t('database.confChange'),
|
||||||
|
operationInfo: i18n.global.t('database.restartNowHelper'),
|
||||||
|
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||||
|
};
|
||||||
|
confirmRef.value!.acceptParams(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await updateClamFile(activeName.value, content.value)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search(activeName.value);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search(activeName.value);
|
||||||
|
});
|
||||||
|
</script>
|
129
frontend/src/views/toolbox/clam/status/index.vue
Normal file
129
frontend/src/views/toolbox/clam/status/index.vue
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="app-status tool-status" v-if="data.isExist">
|
||||||
|
<el-card>
|
||||||
|
<div>
|
||||||
|
<el-tag effect="dark" type="success">Clamav</el-tag>
|
||||||
|
<el-tag round class="status-content" v-if="data.isActive" type="success">
|
||||||
|
{{ $t('commons.status.running') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag round class="status-content" v-if="!data.isActive" type="info">
|
||||||
|
{{ $t('commons.status.stopped') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag class="status-content">{{ $t('app.version') }}:{{ data.version }}</el-tag>
|
||||||
|
<span class="buttons">
|
||||||
|
<el-button type="primary" v-if="!data.isActive" link @click="onOperate('start')">
|
||||||
|
{{ $t('app.start') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" v-if="data.isActive" link @click="onOperate('stop')">
|
||||||
|
{{ $t('app.stop') }}
|
||||||
|
</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button type="primary" link @click="onOperate('restart')">
|
||||||
|
{{ $t('app.restart') }}
|
||||||
|
</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button type="primary" link @click="setting">
|
||||||
|
{{ $t('commons.button.set') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
<LayoutContent :title="$t('tool.supervisor.list')" :divider="true" v-if="!data.isExist" v-loading="loading">
|
||||||
|
<template #main>
|
||||||
|
<div class="app-warn">
|
||||||
|
<div>
|
||||||
|
<span v-if="!data.isExist">{{ $t('tool.supervisor.notSupport') }}</span>
|
||||||
|
<span @click="toDoc()" v-if="!data.isExist">
|
||||||
|
<el-icon class="ml-2"><Position /></el-icon>
|
||||||
|
{{ $t('firewall.quickJump') }}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<img alt="" src="@/assets/images/no_app.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { searchClamBaseInfo, updateClamBaseInfo } from '@/api/modules/toolbox';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
|
const data = ref({
|
||||||
|
isExist: false,
|
||||||
|
isActive: false,
|
||||||
|
version: '',
|
||||||
|
});
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const em = defineEmits(['setting', 'getStatus', 'update:loading', 'update:maskShow']);
|
||||||
|
|
||||||
|
const setting = () => {
|
||||||
|
em('setting', true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toDoc = async () => {
|
||||||
|
window.open('https://1panel.cn/docs/user_manual/toolbox/supervisor/', '_blank', 'noopener,noreferrer');
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOperate = async (operation: string) => {
|
||||||
|
em('update:maskShow', false);
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
i18n.global.t('commons.msg.operatorHelper', [' Clamav ', i18n.global.t('app.' + operation)]),
|
||||||
|
i18n.global.t('app.' + operation),
|
||||||
|
{
|
||||||
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
type: 'info',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
em('update:loading', true);
|
||||||
|
updateClamBaseInfo(operation)
|
||||||
|
.then(() => {
|
||||||
|
em('update:maskShow', true);
|
||||||
|
getStatus();
|
||||||
|
em('update:loading', false);
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
em('update:loading', false);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
em('update:maskShow', true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatus = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
em('update:loading', true);
|
||||||
|
const res = await searchClamBaseInfo();
|
||||||
|
data.value = res.data;
|
||||||
|
const status = {
|
||||||
|
isExist: data.value.isExist,
|
||||||
|
isRunning: data.value.isActive,
|
||||||
|
};
|
||||||
|
em('getStatus', status);
|
||||||
|
} catch (error) {}
|
||||||
|
em('update:loading', false);
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getStatus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tool-status {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -61,6 +61,10 @@ const buttons = [
|
|||||||
label: i18n.global.t('menu.supervisor'),
|
label: i18n.global.t('menu.supervisor'),
|
||||||
path: '/toolbox/supervisor',
|
path: '/toolbox/supervisor',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('toolbox.clam.clam'),
|
||||||
|
path: '/toolbox/clam',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'FTP',
|
label: 'FTP',
|
||||||
path: '/toolbox/ftp',
|
path: '/toolbox/ftp',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user