From ec843f2396bae0370fa632de94d6bcd6bdf8ef1c Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Mon, 29 May 2023 11:24:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20docker=20=E9=85=8D=E7=BD=AE=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E6=A0=B7=E5=BC=8F=E7=BB=9F=E4=B8=80=20(#1173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/docker.go | 30 +- backend/app/dto/docker.go | 5 + backend/app/service/docker.go | 116 ++++-- backend/app/service/firewall.go | 6 +- backend/router/ro_container.go | 1 + cmd/server/docs/docs.go | 177 ++++++++- cmd/server/docs/swagger.json | 177 ++++++++- cmd/server/docs/swagger.yaml | 118 +++++- frontend/src/api/modules/container.ts | 7 +- frontend/src/lang/modules/en.ts | 4 +- frontend/src/lang/modules/zh.ts | 4 +- .../container/container/create/index.vue | 26 +- .../src/views/container/setting/index.vue | 348 ++++++++++-------- .../src/views/container/setting/log/index.vue | 147 ++++++++ .../views/container/setting/mirror/index.vue | 86 +++++ .../container/setting/registry/index.vue | 86 +++++ 16 files changed, 1113 insertions(+), 225 deletions(-) create mode 100644 frontend/src/views/container/setting/log/index.vue create mode 100644 frontend/src/views/container/setting/mirror/index.vue create mode 100644 frontend/src/views/container/setting/registry/index.vue diff --git a/backend/app/api/v1/docker.go b/backend/app/api/v1/docker.go index 87a575ae9..bfe75389d 100644 --- a/backend/app/api/v1/docker.go +++ b/backend/app/api/v1/docker.go @@ -58,13 +58,13 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) { // @Summary Update docker daemon.json // @Description 修改 docker 配置信息 // @Accept json -// @Param request body dto.DaemonJsonConf true "request" +// @Param request body dto.SettingUpdate true "request" // @Success 200 // @Security ApiKeyAuth // @Router /containers/daemonjson/update [post] -// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"} +// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"} func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { - var req dto.DaemonJsonConf + var req dto.SettingUpdate if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return @@ -78,6 +78,30 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { helper.SuccessWithData(c, nil) } +// @Tags Container Docker +// @Summary Update docker daemon.json log option +// @Description 修改 docker 日志配置 +// @Accept json +// @Param request body dto.LogOption true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /containers/daemonjson/update [post] +// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"} +func (b *BaseApi) UpdateLogOption(c *gin.Context) { + var req dto.LogOption + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := dockerService.UpdateLogOption(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, nil) +} + // @Tags Container Docker // @Summary Update docker daemon.json by upload file // @Description 上传替换 docker 配置文件 diff --git a/backend/app/dto/docker.go b/backend/app/dto/docker.go index 240d502be..2f4299e74 100644 --- a/backend/app/dto/docker.go +++ b/backend/app/dto/docker.go @@ -18,6 +18,11 @@ type DaemonJsonConf struct { LogMaxFile string `json:"logMaxFile"` } +type LogOption struct { + LogMaxSize string `json:"logMaxSize"` + LogMaxFile string `json:"logMaxFile"` +} + type DockerOperation struct { Operation string `json:"operation" validate:"required,oneof=start restart stop"` } diff --git a/backend/app/service/docker.go b/backend/app/service/docker.go index 7916c3216..1953f4ead 100644 --- a/backend/app/service/docker.go +++ b/backend/app/service/docker.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "encoding/json" + "fmt" "os" "path" "strings" @@ -18,7 +19,8 @@ import ( type DockerService struct{} type IDockerService interface { - UpdateConf(req dto.DaemonJsonConf) error + UpdateConf(req dto.SettingUpdate) error + UpdateLogOption(req dto.LogOption) error UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error LoadDockerStatus() string LoadDockerConf() *dto.DaemonJsonConf @@ -95,6 +97,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf { return &data } if err := json.Unmarshal(arr, &conf); err != nil { + fmt.Println(err) return &data } if _, ok := deamonMap["iptables"]; !ok { @@ -116,7 +119,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf { return &data } -func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error { +func (u *DockerService) UpdateConf(req dto.SettingUpdate) error { if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) { if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil { return err @@ -131,45 +134,88 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error { deamonMap := make(map[string]interface{}) _ = json.Unmarshal(file, &deamonMap) - if len(req.Registries) == 0 { - delete(deamonMap, "insecure-registries") - } else { - deamonMap["insecure-registries"] = req.Registries - } - if len(req.Mirrors) == 0 { - delete(deamonMap, "registry-mirrors") - } else { - deamonMap["registry-mirrors"] = req.Mirrors - } - - changeLogOption(deamonMap, req.LogMaxFile, req.LogMaxSize) - - if !req.LiveRestore { - delete(deamonMap, "live-restore") - } else { - deamonMap["live-restore"] = req.LiveRestore - } - if req.IPTables { - delete(deamonMap, "iptables") - } else { - deamonMap["iptables"] = false - } - if opts, ok := deamonMap["exec-opts"]; ok { - if optsValue, isArray := opts.([]interface{}); isArray { - for i := 0; i < len(optsValue); i++ { - if opt, isStr := optsValue[i].(string); isStr { - if strings.HasPrefix(opt, "native.cgroupdriver=") { - optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver - break + switch req.Key { + case "Registries": + if len(req.Value) == 0 { + delete(deamonMap, "insecure-registries") + } else { + deamonMap["insecure-registries"] = strings.Split(req.Value, ",") + } + case "Mirrors": + if len(req.Value) == 0 { + delete(deamonMap, "registry-mirrors") + } else { + deamonMap["registry-mirrors"] = strings.Split(req.Value, ",") + } + case "LogOption": + if req.Value == "disable" { + delete(deamonMap, "log-opts") + } + case "LiveRestore": + if req.Value == "disable" { + delete(deamonMap, "live-restore") + } else { + deamonMap["live-restore"] = true + } + case "IPtables": + if req.Value == "enable" { + delete(deamonMap, "iptables") + } else { + deamonMap["iptables"] = false + } + case "Dirver": + if opts, ok := deamonMap["exec-opts"]; ok { + if optsValue, isArray := opts.([]interface{}); isArray { + for i := 0; i < len(optsValue); i++ { + if opt, isStr := optsValue[i].(string); isStr { + if strings.HasPrefix(opt, "native.cgroupdriver=") { + optsValue[i] = "native.cgroupdriver=" + req.Value + break + } } } } + } else { + if req.Value == "systemd" { + deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"} + } } - } else { - if req.CgroupDriver == "systemd" { - deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"} + } + if len(deamonMap) == 0 { + _ = os.Remove(constant.DaemonJsonPath) + return nil + } + newJson, err := json.MarshalIndent(deamonMap, "", "\t") + if err != nil { + return err + } + if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil { + return err + } + + stdout, err := cmd.Exec("systemctl restart docker") + if err != nil { + return errors.New(string(stdout)) + } + return nil +} + +func (u *DockerService) UpdateLogOption(req dto.LogOption) error { + if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) { + if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil { + return err } + _, _ = os.Create(constant.DaemonJsonPath) + } + + file, err := os.ReadFile(constant.DaemonJsonPath) + if err != nil { + return err } + deamonMap := make(map[string]interface{}) + _ = json.Unmarshal(file, &deamonMap) + + changeLogOption(deamonMap, req.LogMaxFile, req.LogMaxSize) if len(deamonMap) == 0 { _ = os.Remove(constant.DaemonJsonPath) return nil diff --git a/backend/app/service/firewall.go b/backend/app/service/firewall.go index d5cb6be12..e9d6a0c18 100644 --- a/backend/app/service/firewall.go +++ b/backend/app/service/firewall.go @@ -379,7 +379,7 @@ func (u *FirewallService) pingStatus() string { return constant.StatusDisable } -func (u *FirewallService) updatePingStatus(enabel string) error { +func (u *FirewallService) updatePingStatus(enable string) error { lineBytes, err := os.ReadFile(confPath) if err != nil { return err @@ -389,14 +389,14 @@ func (u *FirewallService) updatePingStatus(enabel string) error { hasLine := false for _, line := range files { if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") { - newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel) + newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable) hasLine = true } else { newFiles = append(newFiles, line) } } if !hasLine { - newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel) + newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable) } file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 7fc95f635..bf3ae9c5d 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -68,6 +68,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.GET("/docker/status", baseApi.LoadDockerStatus) baRouter.POST("/docker/operate", baseApi.OperateDocker) baRouter.POST("/daemonjson/update", baseApi.UpdateDaemonJson) + baRouter.POST("/logoption/update", baseApi.UpdateLogOption) baRouter.POST("/daemonjson/update/byfile", baseApi.UpdateDaemonJsonByFile) } } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 84f05a2a0..6f2aa9557 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1241,14 +1241,14 @@ var doc = `{ "ApiKeyAuth": [] } ], - "description": "修改 docker 配置信息", + "description": "修改 docker 日志配置", "consumes": [ "application/json" ], "tags": [ "Container Docker" ], - "summary": "Update docker daemon.json", + "summary": "Update docker daemon.json log option", "parameters": [ { "description": "request", @@ -1256,7 +1256,7 @@ var doc = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.DaemonJsonConf" + "$ref": "#/definitions/dto.LogOption" } } ], @@ -1268,8 +1268,8 @@ var doc = `{ "x-panel-log": { "BeforeFuntions": [], "bodyKeys": [], - "formatEN": "Updated the docker daemon.json configuration", - "formatZH": "更新 docker daemon.json 配置", + "formatEN": "Updated the docker daemon.json log option", + "formatZH": "更新 docker daemon.json 日志配置", "paramKeys": [] } } @@ -9040,6 +9040,72 @@ var doc = `{ } } }, + "/websites/leech": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取防盗链配置", + "consumes": [ + "application/json" + ], + "tags": [ + "Website" + ], + "summary": "Get AntiLeech conf", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NginxCommonReq" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/websites/leech/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新防盗链配置", + "consumes": [ + "application/json" + ], + "tags": [ + "Website" + ], + "summary": "Update AntiLeech", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NginxAntiLeechUpdate" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, "/websites/list": { "get": { "security": [ @@ -10691,6 +10757,9 @@ var doc = `{ "script": { "type": "string" }, + "second": { + "type": "integer" + }, "sourceDir": { "type": "string" }, @@ -10770,6 +10839,9 @@ var doc = `{ "script": { "type": "string" }, + "second": { + "type": "integer" + }, "sourceDir": { "type": "string" }, @@ -11531,6 +11603,17 @@ var doc = `{ } } }, + "dto.LogOption": { + "type": "object", + "properties": { + "logMaxFile": { + "type": "string" + }, + "logMaxSize": { + "type": "string" + } + } + }, "dto.Login": { "type": "object", "properties": { @@ -12501,6 +12584,9 @@ var doc = `{ "mfaStatus": { "type": "string" }, + "monitorInterval": { + "type": "string" + }, "monitorStatus": { "type": "string" }, @@ -13634,6 +13720,53 @@ var doc = `{ } } }, + "request.NginxAntiLeechUpdate": { + "type": "object", + "required": [ + "enable", + "extends", + "return", + "websiteID" + ], + "properties": { + "blocked": { + "type": "boolean" + }, + "cache": { + "type": "boolean" + }, + "cacheTime": { + "type": "integer" + }, + "cacheUint": { + "type": "string" + }, + "enable": { + "type": "boolean" + }, + "extends": { + "type": "string" + }, + "logEnable": { + "type": "boolean" + }, + "noneRef": { + "type": "boolean" + }, + "return": { + "type": "string" + }, + "serverNames": { + "type": "array", + "items": { + "type": "string" + } + }, + "websiteID": { + "type": "integer" + } + } + }, "request.NginxAuthReq": { "type": "object", "required": [ @@ -13671,6 +13804,17 @@ var doc = `{ } } }, + "request.NginxCommonReq": { + "type": "object", + "required": [ + "websiteID" + ], + "properties": { + "websiteID": { + "type": "integer" + } + } + }, "request.NginxConfigFileUpdate": { "type": "object", "required": [ @@ -14198,9 +14342,15 @@ var doc = `{ "type": "object", "required": [ "id", - "params" + "scope" ], "properties": { + "disableFunctions": { + "type": "array", + "items": { + "type": "string" + } + }, "id": { "type": "integer" }, @@ -14209,6 +14359,12 @@ var doc = `{ "additionalProperties": { "type": "string" } + }, + "scope": { + "type": "string" + }, + "uploadMaxSize": { + "type": "string" } } }, @@ -14806,11 +14962,20 @@ var doc = `{ "response.PHPConfig": { "type": "object", "properties": { + "disableFunctions": { + "type": "array", + "items": { + "type": "string" + } + }, "params": { "type": "object", "additionalProperties": { "type": "string" } + }, + "uploadMaxSize": { + "type": "string" } } }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index c60b0832e..42442843b 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -1227,14 +1227,14 @@ "ApiKeyAuth": [] } ], - "description": "修改 docker 配置信息", + "description": "修改 docker 日志配置", "consumes": [ "application/json" ], "tags": [ "Container Docker" ], - "summary": "Update docker daemon.json", + "summary": "Update docker daemon.json log option", "parameters": [ { "description": "request", @@ -1242,7 +1242,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.DaemonJsonConf" + "$ref": "#/definitions/dto.LogOption" } } ], @@ -1254,8 +1254,8 @@ "x-panel-log": { "BeforeFuntions": [], "bodyKeys": [], - "formatEN": "Updated the docker daemon.json configuration", - "formatZH": "更新 docker daemon.json 配置", + "formatEN": "Updated the docker daemon.json log option", + "formatZH": "更新 docker daemon.json 日志配置", "paramKeys": [] } } @@ -9026,6 +9026,72 @@ } } }, + "/websites/leech": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取防盗链配置", + "consumes": [ + "application/json" + ], + "tags": [ + "Website" + ], + "summary": "Get AntiLeech conf", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NginxCommonReq" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/websites/leech/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "更新防盗链配置", + "consumes": [ + "application/json" + ], + "tags": [ + "Website" + ], + "summary": "Update AntiLeech", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NginxAntiLeechUpdate" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, "/websites/list": { "get": { "security": [ @@ -10677,6 +10743,9 @@ "script": { "type": "string" }, + "second": { + "type": "integer" + }, "sourceDir": { "type": "string" }, @@ -10756,6 +10825,9 @@ "script": { "type": "string" }, + "second": { + "type": "integer" + }, "sourceDir": { "type": "string" }, @@ -11517,6 +11589,17 @@ } } }, + "dto.LogOption": { + "type": "object", + "properties": { + "logMaxFile": { + "type": "string" + }, + "logMaxSize": { + "type": "string" + } + } + }, "dto.Login": { "type": "object", "properties": { @@ -12487,6 +12570,9 @@ "mfaStatus": { "type": "string" }, + "monitorInterval": { + "type": "string" + }, "monitorStatus": { "type": "string" }, @@ -13620,6 +13706,53 @@ } } }, + "request.NginxAntiLeechUpdate": { + "type": "object", + "required": [ + "enable", + "extends", + "return", + "websiteID" + ], + "properties": { + "blocked": { + "type": "boolean" + }, + "cache": { + "type": "boolean" + }, + "cacheTime": { + "type": "integer" + }, + "cacheUint": { + "type": "string" + }, + "enable": { + "type": "boolean" + }, + "extends": { + "type": "string" + }, + "logEnable": { + "type": "boolean" + }, + "noneRef": { + "type": "boolean" + }, + "return": { + "type": "string" + }, + "serverNames": { + "type": "array", + "items": { + "type": "string" + } + }, + "websiteID": { + "type": "integer" + } + } + }, "request.NginxAuthReq": { "type": "object", "required": [ @@ -13657,6 +13790,17 @@ } } }, + "request.NginxCommonReq": { + "type": "object", + "required": [ + "websiteID" + ], + "properties": { + "websiteID": { + "type": "integer" + } + } + }, "request.NginxConfigFileUpdate": { "type": "object", "required": [ @@ -14184,9 +14328,15 @@ "type": "object", "required": [ "id", - "params" + "scope" ], "properties": { + "disableFunctions": { + "type": "array", + "items": { + "type": "string" + } + }, "id": { "type": "integer" }, @@ -14195,6 +14345,12 @@ "additionalProperties": { "type": "string" } + }, + "scope": { + "type": "string" + }, + "uploadMaxSize": { + "type": "string" } } }, @@ -14792,11 +14948,20 @@ "response.PHPConfig": { "type": "object", "properties": { + "disableFunctions": { + "type": "array", + "items": { + "type": "string" + } + }, "params": { "type": "object", "additionalProperties": { "type": "string" } + }, + "uploadMaxSize": { + "type": "string" } } }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 2a407f02f..aefffd2f2 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -400,6 +400,8 @@ definitions: type: integer script: type: string + second: + type: integer sourceDir: type: string specType: @@ -454,6 +456,8 @@ definitions: type: integer script: type: string + second: + type: integer sourceDir: type: string specType: @@ -969,6 +973,13 @@ definitions: type: type: string type: object + dto.LogOption: + properties: + logMaxFile: + type: string + logMaxSize: + type: string + type: object dto.Login: properties: authMethod: @@ -1614,6 +1625,8 @@ definitions: type: string mfaStatus: type: string + monitorInterval: + type: string monitorStatus: type: string monitorStoreDays: @@ -2367,6 +2380,38 @@ definitions: additionalProperties: true type: object type: object + request.NginxAntiLeechUpdate: + properties: + blocked: + type: boolean + cache: + type: boolean + cacheTime: + type: integer + cacheUint: + type: string + enable: + type: boolean + extends: + type: string + logEnable: + type: boolean + noneRef: + type: boolean + return: + type: string + serverNames: + items: + type: string + type: array + websiteID: + type: integer + required: + - enable + - extends + - return + - websiteID + type: object request.NginxAuthReq: properties: websiteID: @@ -2392,6 +2437,13 @@ definitions: - username - websiteID type: object + request.NginxCommonReq: + properties: + websiteID: + type: integer + required: + - websiteID + type: object request.NginxConfigFileUpdate: properties: backup: @@ -2745,15 +2797,23 @@ definitions: type: object request.WebsitePHPConfigUpdate: properties: + disableFunctions: + items: + type: string + type: array id: type: integer params: additionalProperties: type: string type: object + scope: + type: string + uploadMaxSize: + type: string required: - id - - params + - scope type: object request.WebsitePHPFileUpdate: properties: @@ -3154,10 +3214,16 @@ definitions: type: object response.PHPConfig: properties: + disableFunctions: + items: + type: string + type: array params: additionalProperties: type: string type: object + uploadMaxSize: + type: string type: object response.WebsiteAcmeAccountDTO: properties: @@ -4058,27 +4124,27 @@ paths: post: consumes: - application/json - description: 修改 docker 配置信息 + description: 修改 docker 日志配置 parameters: - description: request in: body name: request required: true schema: - $ref: '#/definitions/dto.DaemonJsonConf' + $ref: '#/definitions/dto.LogOption' responses: "200": description: "" security: - ApiKeyAuth: [] - summary: Update docker daemon.json + summary: Update docker daemon.json log option tags: - Container Docker x-panel-log: BeforeFuntions: [] bodyKeys: [] - formatEN: Updated the docker daemon.json configuration - formatZH: 更新 docker daemon.json 配置 + formatEN: Updated the docker daemon.json log option + formatZH: 更新 docker daemon.json 日志配置 paramKeys: [] /containers/daemonjson/update/byfile: post: @@ -9016,6 +9082,46 @@ paths: formatEN: Delete domain [domain] formatZH: 删除域名 [domain] paramKeys: [] + /websites/leech: + post: + consumes: + - application/json + description: 获取防盗链配置 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.NginxCommonReq' + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Get AntiLeech conf + tags: + - Website + /websites/leech/update: + post: + consumes: + - application/json + description: 更新防盗链配置 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.NginxAntiLeechUpdate' + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Update AntiLeech + tags: + - Website /websites/list: get: description: 获取网站列表 diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 330387d0a..f7c1c8746 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -148,8 +148,11 @@ export const loadDaemonJsonFile = () => { export const loadDockerStatus = () => { return http.get(`/containers/docker/status`); }; -export const updateDaemonJson = (params: Container.DaemonJsonConf) => { - return http.post(`/containers/daemonjson/update`, params); +export const updateDaemonJson = (key: string, value: string) => { + return http.post(`/containers/daemonjson/update`, { key: key, value: value }, 60000); +}; +export const updateLogOption = (maxSize: string, maxFile: string) => { + return http.post(`/containers/logoption/update`, { maxSize: maxSize, maxFile: maxFile }, 60000); }; export const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile) => { return http.post(`/containers/daemonjson/update/byfile`, params); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 0cb32393f..7eadf155f 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -605,9 +605,9 @@ const message = { 'Allows the running container state to be preserved in case of unexpected shutdown or crash of the Docker daemon', liveWithSwarmHelper: 'live-restore daemon configuration is incompatible with swarm mode.', iptablesDisable: 'Close iptables', - iptablesHelper1: 'This setting will disable Docker automatic configuration of iptables rules.', + iptablesHelper1: 'Automatic configuration of iptables rules for Docker.', iptablesHelper2: - 'This may cause the container to be unable to communicate with external networks. Do you want to continue?', + 'Disabling iptables will result in the containers being unable to communicate with external networks.', daemonJsonPath: 'Conf Path', serviceUnavailable: 'Docker service is not started at present, please click', startIn: ' to start', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 4e31ff930..6ea5f307a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -616,8 +616,8 @@ const message = { liveHelper: '允许在 Docker 守护进程发生意外停机或崩溃时保留正在运行的容器状态', liveWithSwarmHelper: 'live-restore 守护进程配置与 Swarm 模式不兼容', iptablesDisable: '关闭 iptables', - iptablesHelper1: '该设置将关闭 Docker 对 iptables 规则的自动配置', - iptablesHelper2: '这可能会导致容器无法与外部网络通信,是否继续?', + iptablesHelper1: 'Docker 对 iptables 规则的自动配置', + iptablesHelper2: '关闭 iptables 会导致容器无法与外部网络通信。', daemonJsonPath: '配置路径', serviceUnavailable: '当前未启动 Docker 服务,请在', startIn: '中开启', diff --git a/frontend/src/views/container/container/create/index.vue b/frontend/src/views/container/container/create/index.vue index 2ef9daf66..3a18c7e4c 100644 --- a/frontend/src/views/container/container/create/index.vue +++ b/frontend/src/views/container/container/create/index.vue @@ -10,7 +10,7 @@ - + - +
@@ -107,21 +107,21 @@ - - - - - -
+ + +
+ - + + + @@ -416,3 +416,9 @@ defineExpose({ acceptParams, }); + + diff --git a/frontend/src/views/container/setting/index.vue b/frontend/src/views/container/setting/index.vue index 31d9edf3b..cd4d36ac6 100644 --- a/frontend/src/views/container/setting/index.vue +++ b/frontend/src/views/container/setting/index.vue @@ -46,12 +46,31 @@ - +
+ + + {{ $t('commons.button.set') }} + +
+ + + {{ $t('container.mirrorsHelper') }} {{ $t('container.mirrorsHelper2') }} @@ -66,60 +85,58 @@
- +
+ + + {{ $t('commons.button.set') }} + +
+ + +
- + -
- - - - - - - - - - - -
- + + {{ $t('container.iptablesHelper1') }} - + {{ $t('container.liveHelper') }} {{ $t('container.liveWithSwarmHelper') }} - + cgroupfs systemd - - - {{ $t('commons.button.save') }} - -
@@ -154,21 +171,47 @@ :close-on-press-escape="false" :show-close="false" > - {{ $t('container.iptablesHelper1') }}
- {{ $t('container.iptablesHelper2') }} + {{ $t('container.iptablesHelper2') }} +
+ {{ $t('database.restartNowHelper') }} +
+
+ {{ $t('commons.msg.operateConfirm') }} + '{{ $t('database.restartNow') }}' +
+
- + + + + + + + + + @@ -178,6 +221,9 @@ import { onMounted, reactive, ref } from 'vue'; import { Codemirror } from 'vue-codemirror'; import { javascript } from '@codemirror/lang-javascript'; import { oneDark } from '@codemirror/theme-one-dark'; +import Mirror from '@/views/container/setting/mirror/index.vue'; +import Registry from '@/views/container/setting/registry/index.vue'; +import LogOption from '@/views/container/setting/log/index.vue'; import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import i18n from '@/lang'; import { @@ -190,11 +236,22 @@ import { import { MsgSuccess } from '@/utils/message'; import { checkNumberRange } from '@/global/form-rules'; +const unset = ref(i18n.global.t('setting.unSetting')); +const submitInput = ref(); + const loading = ref(false); const showDaemonJsonAlert = ref(false); const extensions = [javascript(), oneDark]; const confShowType = ref('base'); +const logOptionRef = ref(); +const confirmDialogRefLog = ref(); +const mirrorRef = ref(); +const registriesRef = ref(); +const confirmDialogRefLive = ref(); +const confirmDialogRefCgroup = ref(); +const confirmDialogRefIptable = ref(); + const form = reactive({ isSwarm: false, status: '', @@ -205,8 +262,7 @@ const form = reactive({ iptables: true, cgroupDriver: '', logOptionShow: false, - logMaxSize: 10, - sizeUnit: 'm', + logMaxSize: '', logMaxFile: 3, }); const rules = reactive({ @@ -220,41 +276,96 @@ const confirmDialogRef = ref(); const iptablesVisiable = ref(); -const onSave = async (formEl: FormInstance | undefined) => { - if (!formEl) return; - formEl.validate(async (valid) => { - if (!valid) return; - if (!valid) return; +const onSaveFile = async () => { + let params = { + header: i18n.global.t('database.confChange'), + operationInfo: i18n.global.t('database.restartNowHelper'), + submitInputInfo: i18n.global.t('database.restartNow'), + }; + confirmDialogRef.value!.acceptParams(params); +}; + +const onChangeMirrors = () => { + mirrorRef.value.acceptParams({ mirrors: form.mirrors }); +}; +const onChangeRegistries = () => { + registriesRef.value.acceptParams({ registries: form.registries }); +}; +const handleLogOption = async () => { + if (form.logOptionShow) { + logOptionRef.value.acceptParams({ logMaxSize: form.logMaxSize, logMaxFile: form.logMaxFile }); + return; + } + let params = { + header: i18n.global.t('database.confChange'), + operationInfo: i18n.global.t('database.restartNowHelper'), + submitInputInfo: i18n.global.t('database.restartNow'), + }; + confirmDialogRefLog.value!.acceptParams(params); +}; +const onSubmitSaveLog = async () => { + save('LogOption', 'disable'); +}; + +const handleIptables = () => { + if (form.iptables) { let params = { header: i18n.global.t('database.confChange'), operationInfo: i18n.global.t('database.restartNowHelper'), submitInputInfo: i18n.global.t('database.restartNow'), }; - confirmDialogRef.value!.acceptParams(params); - }); + confirmDialogRefIptable.value!.acceptParams(params); + return; + } else { + iptablesVisiable.value = true; + } }; -const onSaveFile = async () => { +const onSubmitCloseIPtable = () => { + save('IPtables', 'disable'); +}; +const onSubmitOpenIPtable = () => { + save('IPtables', 'enable'); +}; + +const handleLive = async () => { let params = { header: i18n.global.t('database.confChange'), operationInfo: i18n.global.t('database.restartNowHelper'), submitInputInfo: i18n.global.t('database.restartNow'), }; - confirmDialogRef.value!.acceptParams(params); + confirmDialogRefLive.value!.acceptParams(params); }; - -const toDoc = () => { - window.open('https://1panel.cn/docs/user_manual/containers/setting/', '_blank'); +const onSubmitSaveLive = () => { + save('LiveRestore', form.liveRestore ? 'enable' : 'disable'); +}; +const handleCgroup = async () => { + let params = { + header: i18n.global.t('database.confChange'), + operationInfo: i18n.global.t('database.restartNowHelper'), + submitInputInfo: i18n.global.t('database.restartNow'), + }; + confirmDialogRefCgroup.value!.acceptParams(params); +}; +const onSubmitSaveCgroup = () => { + save('Dirver', form.cgroupDriver); }; -const onChangeIptables = () => { - if (!form.iptables) { - iptablesVisiable.value = true; - } +const save = async (key: string, value: string) => { + loading.value = true; + await updateDaemonJson(key, value) + .then(() => { + loading.value = false; + search(); + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + }) + .catch(() => { + search(); + loading.value = false; + }); }; -const onSaveIptables = (status: boolean) => { - form.iptables = status; - iptablesVisiable.value = false; +const toDoc = () => { + window.open('https://1panel.cn/docs/user_manual/containers/setting/', '_blank'); }; const onOperator = async (operation: string) => { @@ -281,53 +392,18 @@ const onOperator = async (operation: string) => { }); }; -const onSubmitSave = async () => { - if (confShowType.value === 'all') { - let param = { file: dockerConf.value }; - loading.value = true; - await updateDaemonJsonByfile(param) - .then(() => { - loading.value = false; - MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); - }) - .catch(() => { - loading.value = false; - }); - return; - } - let itemMirrors = form.mirrors.split('\n'); - let itemRegistries = form.registries.split('\n'); - let param = { - isSwarm: form.isSwarm, - status: form.status, - version: '', - registryMirrors: itemMirrors.filter(function (el) { - return el !== null && el !== '' && el !== undefined; - }), - insecureRegistries: itemRegistries.filter(function (el) { - return el !== null && el !== '' && el !== undefined; - }), - liveRestore: form.liveRestore, - iptables: form.iptables, - cgroupDriver: form.cgroupDriver, - logMaxSize: form.logMaxSize + form.sizeUnit, - logMaxFile: form.logMaxFile + '', - }; - if (!form.logOptionShow) { - param.logMaxFile = ''; - param.logMaxSize = ''; - } - +const onSubmitSaveFile = async () => { + let param = { file: dockerConf.value }; loading.value = true; - await updateDaemonJson(param) + await updateDaemonJsonByfile(param) .then(() => { loading.value = false; - search(); MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); }) .catch(() => { loading.value = false; }); + return; }; const loadDockerConf = async () => { @@ -349,49 +425,21 @@ const changeMode = async () => { }; const search = async () => { - loading.value = true; - await loadDaemonJson() - .then((res) => { - loading.value = false; - form.isSwarm = res.data.isSwarm; - form.status = res.data.status; - form.version = res.data.version; - form.cgroupDriver = res.data.cgroupDriver; - form.liveRestore = res.data.liveRestore; - form.iptables = res.data.iptables; - form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\n') : ''; - form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : ''; - if (res.data.logMaxFile || res.data.logMaxSize) { - form.logOptionShow = true; - } - form.logMaxFile = Number(res.data.logMaxFile); - form.logMaxSize = loadSize(res.data.logMaxSize); - }) - .catch(() => { - loading.value = false; - }); -}; - -const loadSize = (value: string) => { - if (value.indexOf('b') !== -1 || value.indexOf('B') !== -1) { - form.sizeUnit = 'b'; - return Number(value.replaceAll('b', '').replaceAll('B', '')); - } - if (value.indexOf('k') !== -1 || value.indexOf('K') !== -1) { - form.sizeUnit = 'k'; - return Number(value.replaceAll('k', '').replaceAll('K', '')); - } - if (value.indexOf('m') !== -1 || value.indexOf('M') !== -1) { - form.sizeUnit = 'm'; - return Number(value.replaceAll('m', '').replaceAll('M', '')); - } - if (value.indexOf('g') !== -1 || value.indexOf('G') !== -1) { - form.sizeUnit = 'g'; - return Number(value.replaceAll('g', '').replaceAll('G', '')); - } - if (value.indexOf('t') !== -1 || value.indexOf('T') !== -1) { - form.sizeUnit = 't'; - return Number(value.replaceAll('t', '').replaceAll('T', '')); + const res = await loadDaemonJson(); + form.isSwarm = res.data.isSwarm; + form.status = res.data.status; + form.version = res.data.version; + form.cgroupDriver = res.data.cgroupDriver || 'cgroupfs'; + form.liveRestore = res.data.liveRestore; + form.iptables = res.data.iptables; + form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\n') : ''; + form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : ''; + if (res.data.logMaxFile || res.data.logMaxSize) { + form.logOptionShow = true; + form.logMaxFile = Number(res.data.logMaxFile); + form.logMaxSize = res.data.logMaxSize; + } else { + form.logOptionShow = false; } }; diff --git a/frontend/src/views/container/setting/log/index.vue b/frontend/src/views/container/setting/log/index.vue new file mode 100644 index 000000000..e5420aaa5 --- /dev/null +++ b/frontend/src/views/container/setting/log/index.vue @@ -0,0 +1,147 @@ + + + diff --git a/frontend/src/views/container/setting/mirror/index.vue b/frontend/src/views/container/setting/mirror/index.vue new file mode 100644 index 000000000..8767b42d0 --- /dev/null +++ b/frontend/src/views/container/setting/mirror/index.vue @@ -0,0 +1,86 @@ + + diff --git a/frontend/src/views/container/setting/registry/index.vue b/frontend/src/views/container/setting/registry/index.vue new file mode 100644 index 000000000..839e223ad --- /dev/null +++ b/frontend/src/views/container/setting/registry/index.vue @@ -0,0 +1,86 @@ + +