From 577dfadb9a2a040364f17ad2a2141e7c153cca84 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:47:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=97=85=E6=AF=92=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=97=85=E6=AF=92=E5=BA=93=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E7=8A=B6=E6=80=81=20(#5710)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/clam.go | 4 +- backend/app/dto/clam.go | 9 ++ backend/app/service/clam.go | 90 ++++++++++++++++-- cmd/server/docs/docs.go | 91 ++++++++++++++++++- cmd/server/docs/swagger.json | 91 ++++++++++++++++++- cmd/server/docs/swagger.yaml | 60 +++++++++++- frontend/src/api/interface/toolbox.ts | 4 + frontend/src/api/modules/toolbox.ts | 4 +- frontend/src/lang/modules/en.ts | 2 + frontend/src/lang/modules/tw.ts | 2 + frontend/src/lang/modules/zh.ts | 2 + .../src/views/toolbox/clam/setting/index.vue | 16 +++- .../src/views/toolbox/clam/status/index.vue | 43 ++++++++- 13 files changed, 400 insertions(+), 18 deletions(-) diff --git a/backend/app/api/v1/clam.go b/backend/app/api/v1/clam.go index b044dd1ce..261a6308a 100644 --- a/backend/app/api/v1/clam.go +++ b/backend/app/api/v1/clam.go @@ -190,12 +190,12 @@ func (b *BaseApi) LoadClamRecordLog(c *gin.Context) { // @Summary Load clam file // @Description 获取扫描文件 // @Accept json -// @Param request body dto.OperationWithName true "request" +// @Param request body dto.ClamFileReq 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 + var req dto.ClamFileReq if err := helper.CheckBindAndValidate(&req, c); err != nil { return } diff --git a/backend/app/dto/clam.go b/backend/app/dto/clam.go index 72cce4a97..4c93b9d0c 100644 --- a/backend/app/dto/clam.go +++ b/backend/app/dto/clam.go @@ -8,6 +8,10 @@ type ClamBaseInfo struct { Version string `json:"version"` IsActive bool `json:"isActive"` IsExist bool `json:"isExist"` + + FreshVersion string `json:"freshVersion"` + FreshIsActive bool `json:"freshIsActive"` + FreshIsExist bool `json:"freshIsExist"` } type ClamInfo struct { @@ -36,6 +40,11 @@ type ClamLogReq struct { RecordName string `json:"recordName"` } +type ClamFileReq struct { + Tail string `json:"tail"` + Name string `json:"name" validate:"required"` +} + type ClamLog struct { Name string `json:"name"` ScanDate string `json:"scanDate"` diff --git a/backend/app/service/clam.go b/backend/app/service/clam.go index bb020152c..6f71c9464 100644 --- a/backend/app/service/clam.go +++ b/backend/app/service/clam.go @@ -26,6 +26,7 @@ import ( const ( clamServiceNameCentOs = "clamd@scan.service" clamServiceNameUbuntu = "clamav-daemon.service" + freshClamService = "clamav-freshclam.service" resultDir = "clamav" ) @@ -41,7 +42,7 @@ type IClamService interface { Update(req dto.ClamUpdate) error Delete(req dto.ClamDelete) error HandleOnce(req dto.OperateByID) error - LoadFile(req dto.OperationWithName) (string, error) + LoadFile(req dto.ClamFileReq) (string, error) UpdateFile(req dto.UpdateByNameAndFile) error LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) CleanRecord(req dto.OperateByID) error @@ -56,6 +57,7 @@ func NewIClamService() IClamService { func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { var baseInfo dto.ClamBaseInfo baseInfo.Version = "-" + baseInfo.FreshVersion = "-" exist1, _ := systemctl.IsExist(clamServiceNameCentOs) if exist1 { f.serviceName = clamServiceNameCentOs @@ -68,6 +70,11 @@ func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { baseInfo.IsExist = true baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu) } + freshExist, _ := systemctl.IsExist(freshClamService) + if freshExist { + baseInfo.FreshIsExist = true + baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService) + } if baseInfo.IsActive { version, err := cmd.Exec("clamdscan --version") @@ -80,6 +87,17 @@ func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { baseInfo.Version = strings.TrimPrefix(version, "ClamAV ") } } + if baseInfo.FreshIsActive { + version, err := cmd.Exec("freshclam --version") + if err != nil { + return baseInfo, nil + } + if strings.Contains(version, "/") { + baseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ") + } else { + baseInfo.FreshVersion = strings.TrimPrefix(version, "ClamAV ") + } + } return baseInfo, nil } @@ -91,6 +109,12 @@ func (f *ClamService) Operate(operate string) error { return fmt.Errorf("%s the %s failed, err: %s", operate, f.serviceName, stdout) } return nil + case "fresh-start", "fresh-restart", "fresh-stop": + stdout, err := cmd.Execf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService) + 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) } @@ -296,7 +320,7 @@ func (u *ClamService) CleanRecord(req dto.OperateByID) error { return nil } -func (u *ClamService) LoadFile(req dto.OperationWithName) (string, error) { +func (u *ClamService) LoadFile(req dto.ClamFileReq) (string, error) { filePath := "" switch req.Name { case "clamd": @@ -306,6 +330,10 @@ func (u *ClamService) LoadFile(req dto.OperationWithName) (string, error) { filePath = "/etc/clamd.d/scan.conf" } case "clamd-log": + filePath = u.loadLogPath("clamd-log") + if len(filePath) != 0 { + break + } if u.serviceName == clamServiceNameUbuntu { filePath = "/var/log/clamav/clamav.log" } else { @@ -318,6 +346,10 @@ func (u *ClamService) LoadFile(req dto.OperationWithName) (string, error) { filePath = "/etc/freshclam.conf" } case "freshclam-log": + filePath = u.loadLogPath("freshclam-log") + if len(filePath) != 0 { + break + } if u.serviceName == clamServiceNameUbuntu { filePath = "/var/log/clamav/freshclam.log" } else { @@ -329,11 +361,18 @@ func (u *ClamService) LoadFile(req dto.OperationWithName) (string, error) { if _, err := os.Stat(filePath); err != nil { return "", buserr.New("ErrHttpReqNotFound") } - content, err := os.ReadFile(filePath) - if err != nil { - return "", err + var tail string + if req.Tail != "0" { + tail = req.Tail + } else { + tail = "+1" } - return string(content), nil + cmd := exec.Command("tail", "-n", tail, filePath) + stdout, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("tail -n %v failed, err: %v", req.Tail, err) + } + return string(stdout), nil } func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error { @@ -419,3 +458,42 @@ func loadResultFromLog(pathItem string) dto.ClamLog { } return data } +func (u *ClamService) loadLogPath(name string) string { + confPath := "" + if name == "clamd-log" { + if u.serviceName == clamServiceNameUbuntu { + confPath = "/etc/clamav/clamd.conf" + } else { + confPath = "/etc/clamd.d/scan.conf" + } + } else { + if u.serviceName == clamServiceNameUbuntu { + confPath = "/etc/clamav/freshclam.conf" + } else { + confPath = "/etc/freshclam.conf" + } + } + if _, err := os.Stat(confPath); err != nil { + return "" + } + content, err := os.ReadFile(confPath) + if err != nil { + return "" + } + lines := strings.Split(string(content), "\n") + if name == "clamd-log" { + for _, line := range lines { + if strings.HasPrefix(line, "LogFile ") { + return strings.Trim(strings.ReplaceAll(line, "LogFile ", ""), " ") + } + } + } else { + for _, line := range lines { + if strings.HasPrefix(line, "UpdateLogFile ") { + return strings.Trim(strings.ReplaceAll(line, "UpdateLogFile ", ""), " ") + } + } + } + + return "" +} diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index bbfbb09ee..e423e7260 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -11212,7 +11212,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.OperationWithName" + "$ref": "#/definitions/dto.ClamFileReq" } } ], @@ -12928,6 +12928,57 @@ const docTemplate = `{ } } }, + "/websites/ca/download": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "下载 CA 证书文件", + "consumes": [ + "application/json" + ], + "tags": [ + "Website CA" + ], + "summary": "Download CA file", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WebsiteResourceReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [ + { + "db": "website_cas", + "input_column": "id", + "input_value": "id", + "isList": false, + "output_column": "name", + "output_value": "name" + } + ], + "bodyKeys": [ + "id" + ], + "formatEN": "download ca file [name]", + "formatZH": "下载 CA 证书文件 [name]", + "paramKeys": [] + } + } + }, "/websites/ca/obtain": { "post": { "security": [ @@ -15482,6 +15533,15 @@ const docTemplate = `{ "dto.ClamBaseInfo": { "type": "object", "properties": { + "freshIsActive": { + "type": "boolean" + }, + "freshIsExist": { + "type": "boolean" + }, + "freshVersion": { + "type": "string" + }, "isActive": { "type": "boolean" }, @@ -15533,6 +15593,20 @@ const docTemplate = `{ } } }, + "dto.ClamFileReq": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "tail": { + "type": "string" + } + } + }, "dto.ClamLogReq": { "type": "object", "properties": { @@ -19778,6 +19852,9 @@ const docTemplate = `{ "group": { "type": "string" }, + "isDetail": { + "type": "boolean" + }, "isDir": { "type": "boolean" }, @@ -20610,6 +20687,9 @@ const docTemplate = `{ "path" ], "properties": { + "isDetail": { + "type": "boolean" + }, "path": { "type": "string" } @@ -20765,6 +20845,9 @@ const docTemplate = `{ "expand": { "type": "boolean" }, + "isDetail": { + "type": "boolean" + }, "page": { "type": "integer" }, @@ -22265,6 +22348,9 @@ const docTemplate = `{ "additionalProperties": { "type": "string" } + }, + "sni": { + "type": "boolean" } } }, @@ -22833,6 +22919,9 @@ const docTemplate = `{ "group": { "type": "string" }, + "isDetail": { + "type": "boolean" + }, "isDir": { "type": "boolean" }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 020f746b0..082ca50fe 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -11205,7 +11205,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.OperationWithName" + "$ref": "#/definitions/dto.ClamFileReq" } } ], @@ -12921,6 +12921,57 @@ } } }, + "/websites/ca/download": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "下载 CA 证书文件", + "consumes": [ + "application/json" + ], + "tags": [ + "Website CA" + ], + "summary": "Download CA file", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WebsiteResourceReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [ + { + "db": "website_cas", + "input_column": "id", + "input_value": "id", + "isList": false, + "output_column": "name", + "output_value": "name" + } + ], + "bodyKeys": [ + "id" + ], + "formatEN": "download ca file [name]", + "formatZH": "下载 CA 证书文件 [name]", + "paramKeys": [] + } + } + }, "/websites/ca/obtain": { "post": { "security": [ @@ -15475,6 +15526,15 @@ "dto.ClamBaseInfo": { "type": "object", "properties": { + "freshIsActive": { + "type": "boolean" + }, + "freshIsExist": { + "type": "boolean" + }, + "freshVersion": { + "type": "string" + }, "isActive": { "type": "boolean" }, @@ -15526,6 +15586,20 @@ } } }, + "dto.ClamFileReq": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "tail": { + "type": "string" + } + } + }, "dto.ClamLogReq": { "type": "object", "properties": { @@ -19771,6 +19845,9 @@ "group": { "type": "string" }, + "isDetail": { + "type": "boolean" + }, "isDir": { "type": "boolean" }, @@ -20603,6 +20680,9 @@ "path" ], "properties": { + "isDetail": { + "type": "boolean" + }, "path": { "type": "string" } @@ -20758,6 +20838,9 @@ "expand": { "type": "boolean" }, + "isDetail": { + "type": "boolean" + }, "page": { "type": "integer" }, @@ -22258,6 +22341,9 @@ "additionalProperties": { "type": "string" } + }, + "sni": { + "type": "boolean" } } }, @@ -22826,6 +22912,9 @@ "group": { "type": "string" }, + "isDetail": { + "type": "boolean" + }, "isDir": { "type": "boolean" }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 74b455c0a..7b975a8ac 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -218,6 +218,12 @@ definitions: type: object dto.ClamBaseInfo: properties: + freshIsActive: + type: boolean + freshIsExist: + type: boolean + freshVersion: + type: string isActive: type: boolean isExist: @@ -251,6 +257,15 @@ definitions: required: - ids type: object + dto.ClamFileReq: + properties: + name: + type: string + tail: + type: string + required: + - name + type: object dto.ClamLogReq: properties: clamName: @@ -3119,6 +3134,8 @@ definitions: type: string group: type: string + isDetail: + type: boolean isDir: type: boolean isHidden: @@ -3668,6 +3685,8 @@ definitions: type: object request.FileContentReq: properties: + isDetail: + type: boolean path: type: string required: @@ -3773,6 +3792,8 @@ definitions: type: boolean expand: type: boolean + isDetail: + type: boolean page: type: integer pageSize: @@ -4780,6 +4801,8 @@ definitions: additionalProperties: type: string type: object + sni: + type: boolean required: - id - match @@ -5167,6 +5190,8 @@ definitions: type: string group: type: string + isDetail: + type: boolean isDir: type: boolean isHidden: @@ -12558,7 +12583,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/dto.OperationWithName' + $ref: '#/definitions/dto.ClamFileReq' responses: "200": description: OK @@ -13665,6 +13690,39 @@ paths: formatEN: Delete website ca [name] formatZH: 删除网站 ca [name] paramKeys: [] + /websites/ca/download: + post: + consumes: + - application/json + description: 下载 CA 证书文件 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.WebsiteResourceReq' + responses: + "200": + description: OK + security: + - ApiKeyAuth: [] + summary: Download CA file + tags: + - Website CA + x-panel-log: + BeforeFunctions: + - db: website_cas + input_column: id + input_value: id + isList: false + output_column: name + output_value: name + bodyKeys: + - id + formatEN: download ca file [name] + formatZH: 下载 CA 证书文件 [name] + paramKeys: [] /websites/ca/obtain: post: consumes: diff --git a/frontend/src/api/interface/toolbox.ts b/frontend/src/api/interface/toolbox.ts index 1f290d15a..7930e066d 100644 --- a/frontend/src/api/interface/toolbox.ts +++ b/frontend/src/api/interface/toolbox.ts @@ -121,6 +121,10 @@ export namespace Toolbox { version: string; isActive: boolean; isExist: boolean; + + freshVersion: string; + freshIsExist: boolean; + freshIsActive: boolean; } export interface ClamInfo { id: number; diff --git a/frontend/src/api/modules/toolbox.ts b/frontend/src/api/modules/toolbox.ts index 0fdcafce3..43010e57c 100644 --- a/frontend/src/api/modules/toolbox.ts +++ b/frontend/src/api/modules/toolbox.ts @@ -117,8 +117,8 @@ export const searchClamRecord = (param: Toolbox.ClamSearchLog) => { export const getClamRecordLog = (param: Toolbox.ClamRecordReq) => { return http.post(`/toolbox/clam/record/log`, param); }; -export const searchClamFile = (name: string) => { - return http.post(`/toolbox/clam/file/search`, { name: name }); +export const searchClamFile = (name: string, tail: string) => { + return http.post(`/toolbox/clam/file/search`, { name: name, tail: tail }); }; export const updateClamFile = (name: string, file: string) => { return http.post(`/toolbox/clam/file/update`, { name: name, file: file }); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 8908a2f0f..016b8379a 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1074,6 +1074,8 @@ const message = { }, clam: { clam: 'Virus Scan', + showFresh: 'Show Virus Database Service', + hideFresh: 'Hide Virus Database Service', clamHelper: 'The minimum recommended configuration for ClamAV is: 3 GiB of RAM or more, single-core CPU with 2.0 GHz or higher, and at least 5 GiB of available hard disk space.', noClam: 'ClamAV service not detected, please refer to the official documentation for installation!', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 9336627fa..8db1a142d 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1016,6 +1016,8 @@ const message = { }, clam: { clam: '病毒掃描', + showFresh: '顯示病毒庫服務', + hideFresh: '隱藏病毒庫服務', clamHelper: 'ClamAV 的最低建議配置為:3 GiB 以上的 RAM,2.0 GHz 以上的單核 CPU,以及至少 5 GiB 的可用硬盤空間。', noClam: '未檢測到 ClamAV 服務,請參考官方文檔進行安裝!', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 881dd5e2d..2bb1d07ee 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1017,6 +1017,8 @@ const message = { }, clam: { clam: '病毒扫描', + showFresh: '显示病毒库服务', + hideFresh: '隐藏病毒库服务', clamHelper: 'ClamAV 的最低建议配置为:3 GiB 以上的 RAM,2.0 GHz 以上的单核 CPU,以及至少 5 GiB 的可用硬盘空间', doc: '帮助文档', diff --git a/frontend/src/views/toolbox/clam/setting/index.vue b/frontend/src/views/toolbox/clam/setting/index.vue index 7694d9d55..7a9b067a2 100644 --- a/frontend/src/views/toolbox/clam/setting/index.vue +++ b/frontend/src/views/toolbox/clam/setting/index.vue @@ -29,6 +29,15 @@