diff --git a/backend/app/api/v1/image_repo.go b/backend/app/api/v1/image_repo.go index 409800b9e..1c3306321 100644 --- a/backend/app/api/v1/image_repo.go +++ b/backend/app/api/v1/image_repo.go @@ -76,7 +76,7 @@ func (b *BaseApi) CheckRepoStatus(c *gin.Context) { // @Tags Container Image-repo // @Summary Create image repo // @Accept json -// @Param request body dto.ImageRepoDelete true "request" +// @Param request body dto.ImageRepoCreate true "request" // @Produce json // @Success 200 // @Security ApiKeyAuth @@ -99,20 +99,20 @@ func (b *BaseApi) CreateRepo(c *gin.Context) { // @Tags Container Image-repo // @Summary Delete image repo // @Accept json -// @Param request body dto.ImageRepoDelete true "request" +// @Param request body dto.OperateByID true "request" // @Produce json // @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /containers/repo/del [post] -// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"} +// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"删除镜像仓库 [name]","formatEN":"delete image repo [name]"} func (b *BaseApi) DeleteRepo(c *gin.Context) { - var req dto.ImageRepoDelete + var req dto.OperateByID if err := helper.CheckBindAndValidate(&req, c); err != nil { return } - if err := imageRepoService.BatchDelete(req); err != nil { + if err := imageRepoService.Delete(req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } diff --git a/backend/app/service/image_repo.go b/backend/app/service/image_repo.go index c8d5a83d6..371f74208 100644 --- a/backend/app/service/image_repo.go +++ b/backend/app/service/image_repo.go @@ -14,7 +14,6 @@ import ( "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" ) @@ -27,7 +26,7 @@ type IImageRepoService interface { Login(req dto.OperateByID) error Create(req dto.ImageRepoCreate) error Update(req dto.ImageRepoUpdate) error - BatchDelete(req dto.ImageRepoDelete) error + Delete(req dto.OperateByID) error } func NewIImageRepoService() IImageRepoService { @@ -90,30 +89,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil { return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err) } - if err := validateDockerConfig(); err != nil { - return err - } - if err := restartDocker(); err != nil { - return err - } - ticker := time.NewTicker(3 * time.Second) - defer ticker.Stop() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - if err := func() error { - for range ticker.C { - select { - case <-ctx.Done(): - cancel() - return errors.New("the docker service cannot be restarted") - default: - if ok, _ := systemctl.IsActive("docker"); ok { - global.LOG.Info("docker restart with new conf successful!") - return nil - } - } - } - return nil - }(); err != nil { + if err := stopBeforeUpdateRepo(); err != nil { return err } } @@ -131,15 +107,32 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { return imageRepoRepo.Create(&imageRepo) } -func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error { - for _, id := range req.Ids { - if id == 1 { - return errors.New("The default value cannot be edit !") - } +func (u *ImageRepoService) Delete(req dto.OperateByID) error { + if req.ID == 1 { + return errors.New("The default value cannot be edit !") } - if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil { + itemRepo, _ := imageRepoRepo.Get(commonRepo.WithByID(req.ID)) + if itemRepo.ID == 0 { + return buserr.New("ErrRecordNotFound") + } + if itemRepo.Auth { + _, _ = cmd.Execf("docker logout -i %s", itemRepo.DownloadUrl) + } + if itemRepo.Protocol == "https" { + return imageRepoRepo.Delete(commonRepo.WithByID(req.ID)) + } + if err := u.handleRegistries("", itemRepo.DownloadUrl, "delete"); err != nil { + return fmt.Errorf("delete registry %s failed, err: %v", itemRepo.DownloadUrl, err) + } + if err := validateDockerConfig(); err != nil { return err } + if err := imageRepoRepo.Delete(commonRepo.WithByID(req.ID)); err != nil { + return err + } + go func() { + _ = restartDocker() + }() return nil } @@ -154,37 +147,37 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error { if err != nil { return err } + needRestart := false if repo.Protocol == "http" && req.Protocol == "https" { if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil { return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err) } + needRestart = true } if repo.Protocol == "http" && req.Protocol == "http" { if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil { return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err) } + needRestart = repo.DownloadUrl == req.DownloadUrl } if repo.Protocol == "https" && req.Protocol == "http" { if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil { return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err) } + needRestart = true } - if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl { - if repo.Auth { - _, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl) - } - if req.Auth { - if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { - return err - } + if needRestart { + if err := stopBeforeUpdateRepo(); err != nil { + return err } } - - if err := validateDockerConfig(); err != nil { - return err + if repo.Auth { + _, _ = cmd.Execf("docker logout -i %s", repo.DownloadUrl) } - if err := restartDocker(); err != nil { - return err + if req.Auth { + if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { + return err + } } upMap := make(map[string]interface{}) @@ -256,3 +249,34 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err } return nil } + +func stopBeforeUpdateRepo() error { + if err := validateDockerConfig(); err != nil { + return err + } + if err := restartDocker(); err != nil { + return err + } + ticker := time.NewTicker(3 * time.Second) + defer ticker.Stop() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + if err := func() error { + for range ticker.C { + select { + case <-ctx.Done(): + cancel() + 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 + } + return nil +} diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index bad9293cd..92d36b613 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -212,9 +212,6 @@ export namespace Container { password: string; auth: boolean; } - export interface RepoDelete { - ids: Array; - } export interface RepoInfo { id: number; createdAt: Date; diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 9048b5abe..e4264c004 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -135,8 +135,8 @@ export const createImageRepo = (params: Container.RepoCreate) => { export const updateImageRepo = (params: Container.RepoUpdate) => { return http.post(`/containers/repo/update`, params, TimeoutEnum.T_40S); }; -export const deleteImageRepo = (params: Container.RepoDelete) => { - return http.post(`/containers/repo/del`, params, TimeoutEnum.T_40S); +export const deleteImageRepo = (id: Number) => { + return http.post(`/containers/repo/del`, { id: id }, TimeoutEnum.T_40S); }; // composeTemplate diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index f9e8cfa2b..64847895a 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -833,7 +833,7 @@ const message = { repo: 'Registries', createRepo: 'Add', - createRepoHelper: 'Adding an HTTP-type repository requires restarting the Docker service.', + httpRepoHelper: 'Operating an HTTP-type repository requires restarting the Docker service.', httpRepo: 'Choosing HTTP protocol requires restarting the Docker service to add it into insecure registries.', delInsecure: 'Deletion of credit', delInsecureHelper: diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 24375f8a3..154c6a53e 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -829,7 +829,7 @@ const message = { repo: 'レジストリ', createRepo: '追加', - createRepoHelper: 'HTTPタイプのリポジトリを追加するにはDockerサービスの再起動が必要です。', + httpRepoHelper: 'HTTPタイプのリポジトリを操作するにはDockerサービスの再起動が必要です。', httpRepo: 'HTTPプロトコルを選択するには、Dockerサービスを再起動して不安定なレジストリに追加する必要があります。', delInsecure: 'クレジットの削除', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index e86294fac..3e7ec6d4b 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -819,7 +819,7 @@ const message = { repo: '레지스트리', createRepo: '추가', - createRepoHelper: 'HTTP 타입 저장소 추가 시 Docker 서비스 재시작이 필요합니다.', + httpRepoHelper: 'HTTP 타입 저장소 작업 시 Docker 서비스 재시작이 필요합니다.', httpRepo: 'HTTP 프로토콜을 선택하면 Docker 서비스를 재시작하여 불안정한 레지스트리에 추가해야 합니다.', delInsecure: '신뢰할 수 없는 항목 삭제', delInsecureHelper: diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index e8f94b5bb..e477305f6 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -845,7 +845,7 @@ const message = { repo: 'Pendaftaran', createRepo: 'Tambah', - createRepoHelper: 'Menambah repositori jenis HTTP memerlukan mulakan semula perkhidmatan Docker.', + httpRepoHelper: 'Mengoperasikan repositori jenis HTTP memerlukan mulakan semula perkhidmatan Docker.', httpRepo: 'Memilih protokol HTTP memerlukan memulakan semula perkhidmatan Docker untuk menambahkannya ke pendaftaran tidak selamat.', delInsecure: 'Padamkan pendaftaran tidak selamat', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 4e764de4c..04b49e237 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -841,7 +841,7 @@ const message = { repo: 'Registries', createRepo: 'Adicionar', - createRepoHelper: 'Adicionar um repositório do tipo HTTP requer reinicialização do serviço Docker.', + httpRepoHelper: 'Operar um repositório do tipo HTTP requer reinicialização do serviço Docker.', httpRepo: 'Escolher o protocolo HTTP requer reiniciar o serviço Docker para adicioná-lo a registries inseguros.', delInsecure: 'Remover da lista de segurança', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index ff4c5d520..937c2da42 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -843,7 +843,7 @@ const message = { repo: 'Реестры', createRepo: 'Добавить', - createRepoHelper: 'Добавление репозитория HTTP-типа требует перезапуска службы Docker.', + httpRepoHelper: 'Работа с репозиторием HTTP-типа требует перезапуска службы Docker.', httpRepo: 'Выбор HTTP протокола требует перезапуска службы Docker для добавления в небезопасные реестры.', delInsecure: 'Удаление учетных данных', delInsecureHelper: 'Это перезапустит службу Docker для удаления из небезопасных реестров. Хотите продолжить?', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 46ce55a5d..3263f6d9b 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -804,7 +804,7 @@ const message = { repo: '倉庫', createRepo: '新增倉庫', - createRepoHelper: '添加 HTTP 類型倉庫需要重啟 Docker 服務。', + httpRepoHelper: '操作 HTTP 類型倉庫需要重啟 Docker 服務。', downloadUrl: '下載網址', imageRepo: '鏡像倉庫', repoHelper: '是否包含鏡像倉庫/組織/項目?', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index b4c3dc36a..4ac8475dc 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -806,7 +806,7 @@ const message = { repo: '仓库', createRepo: '添加仓库', - createRepoHelper: '添加 http 类型仓库需要重启 Docker 服务。', + httpRepoHelper: '操作 http 类型仓库需要重启 Docker 服务。', downloadUrl: '下载地址', imageRepo: '镜像仓库', repoHelper: '是否包含镜像仓库/组织/项目?', diff --git a/frontend/src/views/container/repo/index.vue b/frontend/src/views/container/repo/index.vue index c844729bd..03e1be27f 100644 --- a/frontend/src/views/container/repo/index.vue +++ b/frontend/src/views/container/repo/index.vue @@ -61,8 +61,9 @@ - + + @@ -87,6 +88,8 @@ const paginationConfig = reactive({ const searchName = ref(); const opRef = ref(); +const confirmDialog = ref(); +const currentRepo = ref(); const dockerStatus = ref('Running'); const loadStatus = async () => { @@ -141,7 +144,16 @@ const onOpenDialog = async ( }; const onDelete = async (row: Container.RepoInfo) => { - let ids = [row.id]; + currentRepo.value = row.id; + if (row.protocol === 'http') { + let params = { + header: i18n.global.t('container.repo'), + operationInfo: i18n.global.t('container.httpRepoHelper'), + submitInputInfo: i18n.global.t('database.restartNow'), + }; + confirmDialog.value!.acceptParams(params); + return; + } opRef.value.acceptParams({ title: i18n.global.t('commons.button.delete'), names: [row.name], @@ -149,11 +161,23 @@ const onDelete = async (row: Container.RepoInfo) => { i18n.global.t('container.repo'), i18n.global.t('commons.button.delete'), ]), - api: deleteImageRepo, - params: { ids: ids }, + api: null, + params: null, }); }; +const submitDelete = async () => { + loading.value = true; + await deleteImageRepo(currentRepo.value) + .then(() => { + loading.value = false; + search(); + }) + .catch(() => { + loading.value = false; + }); +}; + const onCheckConn = async (row: Container.RepoInfo) => { loading.value = true; await checkRepoStatus(row.id) diff --git a/frontend/src/views/container/repo/operator/index.vue b/frontend/src/views/container/repo/operator/index.vue index 3fa195e04..51c3c10d4 100644 --- a/frontend/src/views/container/repo/operator/index.vue +++ b/frontend/src/views/container/repo/operator/index.vue @@ -108,6 +108,8 @@ const dialogData = ref({ title: '', }); const confirmDialog = ref(); +const oldUrl = ref(); +const oldProto = ref(); const acceptParams = (params: DialogProps): void => { dialogData.value = params; @@ -147,13 +149,23 @@ const onSubmit = async (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.validate(async (valid) => { if (!valid) return; - if (dialogData.value.rowData.protocol === 'https') { + let newProto = dialogData.value.rowData.protocol; + if (newProto === 'https' && dialogData.value.title === 'add') { submit(); return; } + if (newProto === oldProto.value) { + if ( + (oldProto.value === 'http' && dialogData.value.rowData.downloadUrl === oldUrl.value) || + oldProto.value === 'https' + ) { + submit(); + return; + } + } let params = { - header: i18n.global.t('container.imageRepo'), - operationInfo: i18n.global.t('container.createRepoHelper'), + header: i18n.global.t('container.repo'), + operationInfo: i18n.global.t('container.httpRepoHelper'), submitInputInfo: i18n.global.t('database.restartNow'), }; confirmDialog.value!.acceptParams(params);