From b3a69725bf983013b1fb58045aa26c34158187bd Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 27 Feb 2023 11:46:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=95=9C=E5=83=8F=E4=BB=93=E5=BA=93?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/image_repo.go | 28 ++++- backend/app/service/image_repo.go | 48 +++++-- backend/router/ro_container.go | 1 + cmd/server/docs/docs.go | 117 ++++++++++-------- cmd/server/docs/swagger.json | 117 ++++++++++-------- cmd/server/docs/swagger.yaml | 91 +++++++------- frontend/src/api/modules/container.ts | 3 + frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + frontend/src/views/container/repo/index.vue | 20 ++- .../views/database/mysql/password/index.vue | 14 ++- frontend/src/views/setting/panel/index.vue | 4 +- 12 files changed, 280 insertions(+), 165 deletions(-) diff --git a/backend/app/api/v1/image_repo.go b/backend/app/api/v1/image_repo.go index 35375d7cd..448668b67 100644 --- a/backend/app/api/v1/image_repo.go +++ b/backend/app/api/v1/image_repo.go @@ -57,11 +57,37 @@ func (b *BaseApi) ListRepo(c *gin.Context) { helper.SuccessWithData(c, list) } +// @Tags Container Image-repo +// @Summary Load repo status +// @Description 获取 docker 仓库状态 +// @Accept json +// @Param request body dto.OperateByID true "request" +// @Produce json +// @Success 200 +// @Security ApiKeyAuth +// @Router /containers/repo/status [get] +func (b *BaseApi) CheckRepoStatus(c *gin.Context) { + var req dto.OperateByID + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := imageRepoService.Login(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + // @Tags Container Image-repo // @Summary Create image repo // @Description 创建镜像仓库 // @Accept json -// @Param request body dto.ImageRepoCreate true "request" +// @Param request body dto.ImageRepoDelete true "request" // @Produce json // @Success 200 // @Security ApiKeyAuth diff --git a/backend/app/service/image_repo.go b/backend/app/service/image_repo.go index eb2446637..6f440bdf1 100644 --- a/backend/app/service/image_repo.go +++ b/backend/app/service/image_repo.go @@ -23,6 +23,7 @@ type ImageRepoService struct{} type IImageRepoService interface { Page(search dto.SearchWithPage) (int64, interface{}, error) List() ([]dto.ImageRepoOption, error) + Login(req dto.OperateByID) error Create(req dto.ImageRepoCreate) error Update(req dto.ImageRepoUpdate) error BatchDelete(req dto.ImageRepoDelete) error @@ -45,6 +46,19 @@ func (u *ImageRepoService) Page(req dto.SearchWithPage) (int64, interface{}, err return total, dtoOps, err } +func (u *ImageRepoService) Login(req dto.OperateByID) error { + repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID)) + if err != nil { + return err + } + if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil { + _ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error}) + return err + } + _ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess}) + return nil +} + func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) { ops, err := imageRepoRepo.List(commonRepo.WithOrderBy("created_at desc")) var dtoOps []dto.ImageRepoOption @@ -67,22 +81,30 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { } if req.Protocol == "http" { _ = u.handleRegistries(req.DownloadUrl, "", "create") + stdout, err := cmd.Exec("systemctl restart docker") + if err != nil { + return errors.New(string(stdout)) + } ticker := time.NewTicker(3 * time.Second) ctx, cancle := context.WithTimeout(context.Background(), time.Second*20) - for range ticker.C { - select { - case <-ctx.Done(): - cancle() - return errors.New("the docker service cannot be restarted") - default: - stdout, err := cmd.Exec("systemctl is-active docker") - if string(stdout) == "active\n" && err == nil { - global.LOG.Info("docker restart with new conf successful!") + if err := func() error { + for range ticker.C { + select { + case <-ctx.Done(): cancle() + return errors.New("the docker service cannot be restarted") + default: + stdout, err := cmd.Exec("systemctl is-active docker") + if string(stdout) == "active\n" && err == nil { + global.LOG.Info("docker restart with new conf successful!") + return nil + } } } + return nil + }(); err != nil { + return err } - cancle() } if err := copier.Copy(&imageRepo, &req); err != nil { @@ -90,7 +112,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { } imageRepo.Status = constant.StatusSuccess - if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil { + if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { imageRepo.Status = constant.StatusFailed imageRepo.Message = err.Error() } @@ -141,14 +163,14 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error { upMap["status"] = constant.StatusSuccess upMap["message"] = "" - if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil { + if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { upMap["status"] = constant.StatusFailed upMap["message"] = err.Error() } return imageRepoRepo.Update(req.ID, upMap) } -func (u *ImageRepoService) checkConn(host, user, password string) error { +func (u *ImageRepoService) CheckConn(host, user, password string) error { stdout, err := cmd.Execf("docker login -u %s -p %s %s", user, password, host) if err != nil { return errors.New(string(stdout)) diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 22b936bf8..2e8008bda 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -25,6 +25,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.POST("/operate", baseApi.ContainerOperation) baRouter.GET("/repo", baseApi.ListRepo) + baRouter.POST("/repo/status", baseApi.CheckRepoStatus) baRouter.POST("/repo/search", baseApi.SearchRepo) baRouter.POST("/repo/update", baseApi.UpdateRepo) baRouter.POST("/repo", baseApi.CreateRepo) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index e08b9983a..7a65cd1aa 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1947,7 +1947,7 @@ var doc = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.ImageRepoCreate" + "$ref": "#/definitions/dto.ImageRepoDelete" } } ], @@ -2060,6 +2060,42 @@ var doc = `{ } } }, + "/containers/repo/status": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 docker 仓库状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Container Image-repo" + ], + "summary": "Load repo status", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.OperateByID" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, "/containers/repo/update": { "post": { "security": [ @@ -5475,18 +5511,18 @@ var doc = `{ } } }, - "/nginx": { + "/openResty": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取 nginx 配置信息", + "description": "获取 OpenResty 配置信息", "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load nginx conf", + "summary": "Load OpenResty conf", "responses": { "200": { "description": "OK", @@ -5497,21 +5533,21 @@ var doc = `{ } } }, - "/nginx/file": { + "/openResty/file": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "上传更新 nginx 配置文件", + "description": "上传更新 OpenResty 配置文件", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Update nginx conf by upload file", + "summary": "Update OpenResty conf by upload file", "parameters": [ { "description": "request", @@ -5537,21 +5573,21 @@ var doc = `{ } } }, - "/nginx/scope": { + "/openResty/scope": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取部分 nginx 配置信息", + "description": "获取部分 OpenResty 配置信息", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load partial nginx conf", + "summary": "Load partial OpenResty conf", "parameters": [ { "description": "request", @@ -5573,18 +5609,18 @@ var doc = `{ } } }, - "/nginx/status": { + "/openResty/status": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取 nginx 状态信息", + "description": "获取 OpenResty 状态信息", "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load nginx status info", + "summary": "Load OpenResty status info", "responses": { "200": { "description": "OK", @@ -5595,21 +5631,21 @@ var doc = `{ } } }, - "/nginx/update": { + "/openResty/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "更新 nginx 配置信息", + "description": "更新 OpenResty 配置信息", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Update nginx conf", + "summary": "Update OpenResty conf", "parameters": [ { "description": "request", @@ -9589,41 +9625,12 @@ var doc = `{ } } }, - "dto.ImageRepoCreate": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "auth": { - "type": "boolean" - }, - "downloadUrl": { - "type": "string" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - }, - "protocol": { - "type": "string" - }, - "username": { - "type": "string" - } - } - }, "dto.ImageRepoDelete": { "type": "object", "required": [ "ids" ], "properties": { - "deleteInsecure": { - "type": "boolean" - }, "ids": { "type": "array", "items": { @@ -10785,7 +10792,10 @@ var doc = `{ "required": { "type": "string" }, - "shortDesc": { + "shortDescEn": { + "type": "string" + }, + "shortDescZh": { "type": "string" }, "status": { @@ -12108,7 +12118,10 @@ var doc = `{ "required": { "type": "string" }, - "shortDesc": { + "shortDescEn": { + "type": "string" + }, + "shortDescZh": { "type": "string" }, "status": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 5857a3474..0bdac1a9d 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -1933,7 +1933,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dto.ImageRepoCreate" + "$ref": "#/definitions/dto.ImageRepoDelete" } } ], @@ -2046,6 +2046,42 @@ } } }, + "/containers/repo/status": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 docker 仓库状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Container Image-repo" + ], + "summary": "Load repo status", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.OperateByID" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, "/containers/repo/update": { "post": { "security": [ @@ -5461,18 +5497,18 @@ } } }, - "/nginx": { + "/openResty": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取 nginx 配置信息", + "description": "获取 OpenResty 配置信息", "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load nginx conf", + "summary": "Load OpenResty conf", "responses": { "200": { "description": "OK", @@ -5483,21 +5519,21 @@ } } }, - "/nginx/file": { + "/openResty/file": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "上传更新 nginx 配置文件", + "description": "上传更新 OpenResty 配置文件", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Update nginx conf by upload file", + "summary": "Update OpenResty conf by upload file", "parameters": [ { "description": "request", @@ -5523,21 +5559,21 @@ } } }, - "/nginx/scope": { + "/openResty/scope": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取部分 nginx 配置信息", + "description": "获取部分 OpenResty 配置信息", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load partial nginx conf", + "summary": "Load partial OpenResty conf", "parameters": [ { "description": "request", @@ -5559,18 +5595,18 @@ } } }, - "/nginx/status": { + "/openResty/status": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "获取 nginx 状态信息", + "description": "获取 OpenResty 状态信息", "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Load nginx status info", + "summary": "Load OpenResty status info", "responses": { "200": { "description": "OK", @@ -5581,21 +5617,21 @@ } } }, - "/nginx/update": { + "/openResty/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "更新 nginx 配置信息", + "description": "更新 OpenResty 配置信息", "consumes": [ "application/json" ], "tags": [ - "Nginx" + "OpenResty" ], - "summary": "Update nginx conf", + "summary": "Update OpenResty conf", "parameters": [ { "description": "request", @@ -9575,41 +9611,12 @@ } } }, - "dto.ImageRepoCreate": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "auth": { - "type": "boolean" - }, - "downloadUrl": { - "type": "string" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - }, - "protocol": { - "type": "string" - }, - "username": { - "type": "string" - } - } - }, "dto.ImageRepoDelete": { "type": "object", "required": [ "ids" ], "properties": { - "deleteInsecure": { - "type": "boolean" - }, "ids": { "type": "array", "items": { @@ -10771,7 +10778,10 @@ "required": { "type": "string" }, - "shortDesc": { + "shortDescEn": { + "type": "string" + }, + "shortDescZh": { "type": "string" }, "status": { @@ -12094,7 +12104,10 @@ "required": { "type": "string" }, - "shortDesc": { + "shortDescEn": { + "type": "string" + }, + "shortDescZh": { "type": "string" }, "status": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index f65a5eac9..10dd14d52 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -717,27 +717,8 @@ definitions: - repoID - tagName type: object - dto.ImageRepoCreate: - properties: - auth: - type: boolean - downloadUrl: - type: string - name: - type: string - password: - type: string - protocol: - type: string - username: - type: string - required: - - name - type: object dto.ImageRepoDelete: properties: - deleteInsecure: - type: boolean ids: items: type: integer @@ -1509,7 +1490,9 @@ definitions: type: integer required: type: string - shortDesc: + shortDescEn: + type: string + shortDescZh: type: string status: type: string @@ -2392,7 +2375,9 @@ definitions: type: integer required: type: string - shortDesc: + shortDescEn: + type: string + shortDescZh: type: string status: type: string @@ -3860,7 +3845,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/dto.ImageRepoCreate' + $ref: '#/definitions/dto.ImageRepoDelete' produces: - application/json responses: @@ -3937,6 +3922,28 @@ paths: summary: Page image repos tags: - Container Image-repo + /containers/repo/status: + get: + consumes: + - application/json + description: 获取 docker 仓库状态 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.OperateByID' + produces: + - application/json + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Load repo status + tags: + - Container Image-repo /containers/repo/update: post: consumes: @@ -6111,9 +6118,9 @@ paths: summary: Page operation logs tags: - Logs - /nginx: + /openResty: get: - description: 获取 nginx 配置信息 + description: 获取 OpenResty 配置信息 responses: "200": description: OK @@ -6121,14 +6128,14 @@ paths: $ref: '#/definitions/response.FileInfo' security: - ApiKeyAuth: [] - summary: Load nginx conf + summary: Load OpenResty conf tags: - - Nginx - /nginx/file: + - OpenResty + /openResty/file: post: consumes: - application/json - description: 上传更新 nginx 配置文件 + description: 上传更新 OpenResty 配置文件 parameters: - description: request in: body @@ -6141,20 +6148,20 @@ paths: description: "" security: - ApiKeyAuth: [] - summary: Update nginx conf by upload file + summary: Update OpenResty conf by upload file tags: - - Nginx + - OpenResty x-panel-log: BeforeFuntions: [] bodyKeys: [] formatEN: Update nginx conf formatZH: 更新 nginx 配置 paramKeys: [] - /nginx/scope: + /openResty/scope: post: consumes: - application/json - description: 获取部分 nginx 配置信息 + description: 获取部分 OpenResty 配置信息 parameters: - description: request in: body @@ -6169,12 +6176,12 @@ paths: type: anrry security: - ApiKeyAuth: [] - summary: Load partial nginx conf + summary: Load partial OpenResty conf tags: - - Nginx - /nginx/status: + - OpenResty + /openResty/status: get: - description: 获取 nginx 状态信息 + description: 获取 OpenResty 状态信息 responses: "200": description: OK @@ -6182,14 +6189,14 @@ paths: $ref: '#/definitions/response.NginxStatus' security: - ApiKeyAuth: [] - summary: Load nginx status info + summary: Load OpenResty status info tags: - - Nginx - /nginx/update: + - OpenResty + /openResty/update: post: consumes: - application/json - description: 更新 nginx 配置信息 + description: 更新 OpenResty 配置信息 parameters: - description: request in: body @@ -6202,9 +6209,9 @@ paths: description: "" security: - ApiKeyAuth: [] - summary: Update nginx conf + summary: Update OpenResty conf tags: - - Nginx + - OpenResty x-panel-log: BeforeFuntions: - db: websites diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 474483ca1..c16decc02 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -76,6 +76,9 @@ export const createVolume = (params: Container.VolumeCreate) => { }; // repo +export const checkRepoStatus = (id: number) => { + return http.post(`/containers/repo/status`, { id: id }); +}; export const searchImageRepo = (params: SearchWithPage) => { return http.post>(`/containers/repo/search`, params); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 2432c4961..8446ccd31 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -266,6 +266,7 @@ export default { loadBackup: 'Import', setting: 'Settings', remoteAccess: 'Remote access', + remoteHelper: 'One in a row, for example:\n172.16.10.111\n172.16.10.112', remoteConnHelper: 'Remote connection to mysql as user root may have security risks. Therefore, perform this operation with caution.', changePassword: 'Password', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 7399b0deb..6be3fda32 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -278,6 +278,7 @@ export default { loadBackup: '导入备份', setting: '设置', remoteAccess: '远程访问', + remoteHelper: '一行一个,例:\n172.16.10.111\n172.16.10.112', remoteConnHelper: 'root 帐号远程连接 mysql 有安全风险,开启需谨慎!', changePassword: '改密', changePasswordHelper: '当前数据库已经关联应用,修改密码将同步修改应用中数据库密码,修改后重启生效。', diff --git a/frontend/src/views/container/repo/index.vue b/frontend/src/views/container/repo/index.vue index fe0f45a5f..682224c0f 100644 --- a/frontend/src/views/container/repo/index.vue +++ b/frontend/src/views/container/repo/index.vue @@ -82,7 +82,7 @@ import OperatorDialog from '@/views/container/repo/operator/index.vue'; import { reactive, onMounted, ref } from 'vue'; import { dateFormat } from '@/utils/util'; import { Container } from '@/api/interface/container'; -import { deleteImageRepo, loadDockerStatus, searchImageRepo } from '@/api/modules/container'; +import { checkRepoStatus, deleteImageRepo, loadDockerStatus, searchImageRepo } from '@/api/modules/container'; import i18n from '@/lang'; import router from '@/routers'; import { ElMessageBox } from 'element-plus'; @@ -156,7 +156,25 @@ const onDelete = async (row: Container.RepoInfo) => { }); }; +const onCheckConn = async (row: Container.RepoInfo) => { + loading.value = true; + await checkRepoStatus(row.id) + .then(() => { + loading.value = false; + search(); + }) + .catch(() => { + loading.value = false; + }); +}; + const buttons = [ + { + label: i18n.global.t('commons.button.sync'), + click: (row: Container.RepoInfo) => { + onCheckConn(row); + }, + }, { label: i18n.global.t('commons.button.edit'), disabled: (row: Container.RepoInfo) => { diff --git a/frontend/src/views/database/mysql/password/index.vue b/frontend/src/views/database/mysql/password/index.vue index fce2fd0a6..3581f9545 100644 --- a/frontend/src/views/database/mysql/password/index.vue +++ b/frontend/src/views/database/mysql/password/index.vue @@ -38,7 +38,13 @@ prop="privilegeIPs" :rules="Rules.requiredInput" > - + @@ -145,7 +151,11 @@ const submitChangeInfo = async (formEl: FormInstance | undefined) => { } return; } - param.value = changeForm.privilege; + if (changeForm.privilege !== 'ip') { + param.value = changeForm.privilege; + } else { + param.value = changeForm.privilegeIPs.replaceAll('/n', ','); + } loading.value = true; await updateMysqlAccess(param) .then(() => { diff --git a/frontend/src/views/setting/panel/index.vue b/frontend/src/views/setting/panel/index.vue index 5a7cefc6b..050aaeb5d 100644 --- a/frontend/src/views/setting/panel/index.vue +++ b/frontend/src/views/setting/panel/index.vue @@ -217,10 +217,10 @@ const onSave = async (formEl: FormInstance | undefined, key: string, val: any) = }; await updateSetting(param) .then(async () => { - loading.value = false; - MsgSuccess(i18n.t('commons.msg.operationSuccess')); if (param.key === 'UserName') { await logOutApi(); + loading.value = false; + MsgSuccess(i18n.t('commons.msg.operationSuccess')); router.push({ name: 'login', params: { code: '' } }); globalStore.setLogStatus(false); return;