diff --git a/backend/app/dto/container.go b/backend/app/dto/container.go index 082739d59..3f8d4bfe8 100644 --- a/backend/app/dto/container.go +++ b/backend/app/dto/container.go @@ -22,6 +22,7 @@ type ContainerInfo struct { State string `json:"state"` RunTime string `json:"runTime"` + IsFromApp bool `json:"isFromApp"` IsFromCompose bool `json:"isFromCompose"` } diff --git a/backend/app/service/container.go b/backend/app/service/container.go index 0a0b44738..9f54045a1 100644 --- a/backend/app/service/container.go +++ b/backend/app/service/container.go @@ -98,6 +98,10 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro if _, ok := container.Labels[composeProjectLabel]; ok { IsFromCompose = true } + IsFromApp := false + if created, ok := container.Labels[composeCreatedBy]; ok && created == "Apps" { + IsFromApp = true + } backDatas = append(backDatas, dto.ContainerInfo{ ContainerID: container.ID, CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"), @@ -106,6 +110,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro ImageName: container.Image, State: container.State, RunTime: container.Status, + IsFromApp: IsFromApp, IsFromCompose: IsFromCompose, }) } diff --git a/backend/app/service/container_volume.go b/backend/app/service/container_volume.go index 492a85e09..9069f243f 100644 --- a/backend/app/service/container_volume.go +++ b/backend/app/service/container_volume.go @@ -7,6 +7,7 @@ import ( "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/utils/docker" "github.com/docker/docker/api/types" @@ -95,6 +96,9 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error { } for _, id := range req.Names { if err := client.VolumeRemove(context.TODO(), id, true); err != nil { + if strings.Contains(err.Error(), "volume is in use") { + return buserr.New(constant.ErrInUsed) + } return err } } diff --git a/backend/constant/errs.go b/backend/constant/errs.go index 2a9ce983b..28f10a2f6 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -95,3 +95,8 @@ var ( var ( ErrTypeOfRedis = "ErrTypeOfRedis" ) + +//container +var ( + ErrInUsed = "ErrInUsed" +) diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index f11ee56cb..8860463b8 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -52,4 +52,7 @@ ErrUserIsExist: "The current user already exists. Please enter a new user" ErrDatabaseIsExist: "The current database already exists. Please enter a new database" #redis -ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again" \ No newline at end of file +ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again" + +#container +ErrInUsed: "The deleted object is in use and cannot be deleted" \ No newline at end of file diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 169b5421f..32d462e42 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -52,4 +52,7 @@ ErrUserIsExist: "当前用户已存在,请重新输入" ErrDatabaseIsExist: "当前数据库已存在,请重新输入" #redis -ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试" \ No newline at end of file +ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试" + +#container +ErrInUsed: "该删除对象正被使用,无法删除" \ No newline at end of file diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index ac3170e38..c30107d7e 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -43,7 +43,8 @@ export namespace Container { state: string; runTime: string; - isFromCompose: string; + isFromApp: boolean; + isFromCompose: boolean; } export interface ContainerStats { cpuPercent: number; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 211738a2b..00f921475 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -407,6 +407,8 @@ export default { createContainer: 'Create container', containerList: 'Container list', operatorHelper: '{0} will be performed on the selected container. Do you want to continue?', + operatorAppHelper: + 'There is a container from the App store. The {0} operation may affect the normal use of this service. Are you sure?', start: 'Start', stop: 'Stop', restart: 'Restart', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index b7cdd4d19..55cafbfd6 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -419,6 +419,7 @@ export default { createContainer: '创建容器', containerList: '容器列表', operatorHelper: '将对选中容器进行 {0} 操作,是否继续?', + operatorAppHelper: '存在来源于应用商店的容器,{0} 操作可能会影响到该服务的正常使用,是否确认?', start: '启动', stop: '停止', restart: '重启', diff --git a/frontend/src/views/container/compose/detail/index.vue b/frontend/src/views/container/compose/detail/index.vue index dcef1d5a7..b57e1f11b 100644 --- a/frontend/src/views/container/compose/detail/index.vue +++ b/frontend/src/views/container/compose/detail/index.vue @@ -228,15 +228,18 @@ const checkStatus = (operation: string) => { }; const onOperate = async (operation: string) => { - ElMessageBox.confirm( - i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + operation)]), - i18n.global.t('container.' + operation), - { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - type: 'info', - }, - ).then(() => { + let msg = i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + operation)]); + for (const item of selects.value) { + if (item.isFromApp) { + msg = i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + operation)]); + break; + } + } + ElMessageBox.confirm(msg, i18n.global.t('container.' + operation), { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'info', + }).then(() => { let ps = []; for (const item of selects.value) { const param = { diff --git a/frontend/src/views/container/container/index.vue b/frontend/src/views/container/container/index.vue index 5980ca20c..036e07816 100644 --- a/frontend/src/views/container/container/index.vue +++ b/frontend/src/views/container/container/index.vue @@ -252,15 +252,18 @@ const checkStatus = (operation: string) => { } }; const onOperate = async (operation: string) => { - ElMessageBox.confirm( - i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + operation)]), - i18n.global.t('container.' + operation), - { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - type: 'info', - }, - ).then(() => { + let msg = i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + operation)]); + for (const item of selects.value) { + if (item.isFromApp) { + msg = i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + operation)]); + break; + } + } + ElMessageBox.confirm(msg, i18n.global.t('container.' + operation), { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'info', + }).then(() => { let ps = []; for (const item of selects.value) { const param = { diff --git a/frontend/src/views/container/image/index.vue b/frontend/src/views/container/image/index.vue index 2bcf4cf3e..adb665836 100644 --- a/frontend/src/views/container/image/index.vue +++ b/frontend/src/views/container/image/index.vue @@ -193,6 +193,7 @@ const buttons = [ click: async (row: Container.ImageInfo) => { if (row.tags.length <= 1) { await useDeleteData(imageRemove, { names: [row.id] }, 'commons.msg.delete'); + search(); return; } let params = {