diff --git a/backend/app/api/v1/helper/helper.go b/backend/app/api/v1/helper/helper.go index 1afe5961..831d516e 100644 --- a/backend/app/api/v1/helper/helper.go +++ b/backend/app/api/v1/helper/helper.go @@ -83,6 +83,15 @@ func SuccessWithData(ctx *gin.Context, data interface{}) { ctx.Abort() } +func SuccessWithOutData(ctx *gin.Context) { + res := dto.Response{ + Code: constant.CodeSuccess, + Message: "success", + } + ctx.JSON(http.StatusOK, res) + ctx.Abort() +} + func SuccessWithMsg(ctx *gin.Context, msg string) { res := dto.Response{ Code: constant.CodeSuccess, diff --git a/backend/app/api/v1/runtime.go b/backend/app/api/v1/runtime.go index 863582e7..19c28520 100644 --- a/backend/app/api/v1/runtime.go +++ b/backend/app/api/v1/runtime.go @@ -52,5 +52,28 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - helper.SuccessWithData(c, nil) + helper.SuccessWithOutData(c) +} + +// @Tags Website +// @Summary Delete runtime +// @Description 删除运行环境 +// @Accept json +// @Param request body request.RuntimeDelete true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /runtimes/del [post] +// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"删除网站 [name]","formatEN":"Delete website [name]"} +func (b *BaseApi) DeleteRuntime(c *gin.Context) { + var req request.RuntimeDelete + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + err := runtimeService.Delete(req.ID) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithOutData(c) } diff --git a/backend/app/dto/request/runtime.go b/backend/app/dto/request/runtime.go index 6ed5f6df..fa9d4522 100644 --- a/backend/app/dto/request/runtime.go +++ b/backend/app/dto/request/runtime.go @@ -17,3 +17,7 @@ type RuntimeCreate struct { Type string `json:"type"` Version string `json:"version"` } + +type RuntimeDelete struct { + ID uint `json:"Id"` +} diff --git a/backend/app/repo/runtime.go b/backend/app/repo/runtime.go index cda8f6b5..eadf2f53 100644 --- a/backend/app/repo/runtime.go +++ b/backend/app/repo/runtime.go @@ -13,6 +13,7 @@ type IRuntimeRepo interface { Create(ctx context.Context, runtime *model.Runtime) error Save(runtime *model.Runtime) error DeleteBy(opts ...DBOption) error + GetFirst(opts ...DBOption) (*model.Runtime, error) } func NewIRunTimeRepo() IRuntimeRepo { @@ -40,3 +41,11 @@ func (r *RuntimeRepo) Save(runtime *model.Runtime) error { func (r *RuntimeRepo) DeleteBy(opts ...DBOption) error { return getDb(opts...).Delete(&model.Runtime{}).Error } + +func (r *RuntimeRepo) GetFirst(opts ...DBOption) (*model.Runtime, error) { + var runtime model.Runtime + if err := getDb(opts...).First(&runtime).Error; err != nil { + return nil, err + } + return &runtime, nil +} diff --git a/backend/app/service/runtime.go b/backend/app/service/runtime.go index e79f797f..6d28f09a 100644 --- a/backend/app/service/runtime.go +++ b/backend/app/service/runtime.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "github.com/1Panel-dev/1Panel/backend/app/dto/request" "github.com/1Panel-dev/1Panel/backend/app/dto/response" "github.com/1Panel-dev/1Panel/backend/app/model" @@ -12,6 +13,8 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/subosito/gotenv" "path" + "path/filepath" + "time" ) type RuntimeService struct { @@ -20,6 +23,7 @@ type RuntimeService struct { type IRuntimeService interface { Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) Create(create request.RuntimeCreate) error + Delete(id uint) error } func NewRuntimeService() IRuntimeService { @@ -50,21 +54,25 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { if !fileOp.Stat(buildDir) { return buserr.New(constant.ErrDirNotFound) } - tempDir := path.Join(constant.RuntimeDir, app.Key) + runtimeDir := path.Join(constant.RuntimeDir, create.Type) + tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano())) if err := fileOp.CopyDir(buildDir, tempDir); err != nil { return err } oldDir := path.Join(tempDir, "build") - newNameDir := path.Join(tempDir, create.Name) - defer func(defErr *error) { - if defErr != nil { + newNameDir := path.Join(runtimeDir, create.Name) + defer func() { + if err != nil { _ = fileOp.DeleteDir(newNameDir) } - }(&err) + }() if oldDir != newNameDir { if err := fileOp.Rename(oldDir, newNameDir); err != nil { return err } + if err := fileOp.DeleteDir(tempDir); err != nil { + return err + } } composeFile, err := fileOp.GetContent(path.Join(newNameDir, "docker-compose.yml")) if err != nil { @@ -95,9 +103,6 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { return err } composeService.SetProject(project) - if err := composeService.ComposeBuild(); err != nil { - return err - } runtime := &model.Runtime{ Name: create.Name, DockerCompose: string(composeFile), @@ -106,9 +111,14 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { Type: create.Type, Image: create.Image, Resource: create.Resource, - Status: constant.RuntimeNormal, + Status: constant.RuntimeBuildIng, + Version: create.Version, } - return runtimeRepo.Create(context.Background(), runtime) + if err := runtimeRepo.Create(context.Background(), runtime); err != nil { + return err + } + go buildRuntime(runtime, composeService) + return nil } func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) { @@ -130,3 +140,18 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt } return total, res, nil } + +func (r *RuntimeService) Delete(id uint) error { + runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id)) + if err != nil { + return err + } + //TODO 校验网站关联 + if runtime.Resource == constant.ResourceAppstore { + runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name) + if err := files.NewFileOp().DeleteDir(runtimeDir); err != nil { + return err + } + } + return runtimeRepo.DeleteBy(commonRepo.WithByID(id)) +} diff --git a/backend/app/service/runtime_utils.go b/backend/app/service/runtime_utils.go index 6d43c336..e96619b8 100644 --- a/backend/app/service/runtime_utils.go +++ b/backend/app/service/runtime_utils.go @@ -1 +1,19 @@ package service + +import ( + "github.com/1Panel-dev/1Panel/backend/app/model" + "github.com/1Panel-dev/1Panel/backend/buserr" + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/1Panel-dev/1Panel/backend/utils/docker" +) + +func buildRuntime(runtime *model.Runtime, service *docker.ComposeService) { + err := service.ComposeBuild() + if err != nil { + runtime.Status = constant.RuntimeError + runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error() + } else { + runtime.Status = constant.RuntimeNormal + } + _ = runtimeRepo.Save(runtime) +} diff --git a/backend/constant/errs.go b/backend/constant/errs.go index 4734c6b6..00ee9aca 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -104,9 +104,9 @@ var ( ErrObjectInUsed = "ErrObjectInUsed" ) -//runtime - +// runtime var ( - ErrDirNotFound = "ErrDirNotFound" - ErrFileNotExist = "ErrFileNotExist" + ErrDirNotFound = "ErrDirNotFound" + ErrFileNotExist = "ErrFileNotExist" + ErrImageBuildErr = "ErrImageBuildErr" ) diff --git a/backend/constant/runtime.go b/backend/constant/runtime.go index 1363a0db..0f1fec67 100644 --- a/backend/constant/runtime.go +++ b/backend/constant/runtime.go @@ -1,10 +1,10 @@ package constant const ( - ResourceLocal = "Local" - ResourceAppstore = "Appstore" + ResourceLocal = "local" + ResourceAppstore = "appstore" - RuntimeNormal = "Normal" - RuntimeBuildSuccess = "BuildSuccess" - RuntimeBuildFailed = "BuildFailed" + RuntimeNormal = "normal" + RuntimeError = "error" + RuntimeBuildIng = "building" ) diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 68d098ec..e6ffe726 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -62,4 +62,5 @@ ErrObjectInUsed: "This object is in use and cannot be deleted" #runtime ErrDirNotFound: "The build folder does not exist! Please check file integrity!" -ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity!" \ No newline at end of file +ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity!" +ErrImageBuildErr: "Image build failed" \ No newline at end of file diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index de2f02f1..82de943a 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -62,4 +62,5 @@ ErrObjectInUsed: "该对象正被使用,无法删除" #runtime ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" -ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!" \ No newline at end of file +ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!" +ErrImageBuildErr: "镜像 build 失败" \ No newline at end of file diff --git a/backend/router/ro_runtime.go b/backend/router/ro_runtime.go index d8755c36..969b290f 100644 --- a/backend/router/ro_runtime.go +++ b/backend/router/ro_runtime.go @@ -17,5 +17,6 @@ func (r *RuntimeRouter) InitRuntimeRouter(Router *gin.RouterGroup) { { groupRouter.POST("/search", baseApi.SearchRuntimes) groupRouter.POST("", baseApi.CreateRuntime) + groupRouter.POST("/del", baseApi.DeleteRuntime) } } diff --git a/frontend/src/api/interface/runtime.ts b/frontend/src/api/interface/runtime.ts index bdaa5200..ada47e7b 100644 --- a/frontend/src/api/interface/runtime.ts +++ b/frontend/src/api/interface/runtime.ts @@ -29,4 +29,8 @@ export namespace Runtime { appId?: number; version?: string; } + + export interface RuntimeDelete { + id: number; + } } diff --git a/frontend/src/api/modules/runtime.ts b/frontend/src/api/modules/runtime.ts index 076cbadb..813bb895 100644 --- a/frontend/src/api/modules/runtime.ts +++ b/frontend/src/api/modules/runtime.ts @@ -9,3 +9,7 @@ export const SearchRuntimes = (req: Runtime.RuntimeReq) => { export const CreateRuntime = (req: Runtime.RuntimeCreate) => { return http.post(`/runtimes`, req); }; + +export const DeleteRuntime = (req: Runtime.RuntimeDelete) => { + return http.post(`/runtimes/del`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index ff87b981..c38c10c5 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -199,6 +199,8 @@ const message = { exited: 'Exited', enabled: 'Enabled', disabled: 'Disabled', + normal: 'Normal', + building: 'Building', }, }, menu: { @@ -1237,7 +1239,16 @@ const message = { runtime: { runtime: 'Runtime', image: 'Image', - workDir: 'WorkDir', + workDir: 'working directory', + create: 'Create runtime', + name: 'Name', + resource: 'Source', + appstore: 'App Store', + local: 'Local', + app: 'Application', + localHelper: 'The local operating environment needs to be installed by itself', + version: 'Version', + status: 'Status', }, }; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 0c1192b9..95260cf7 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -203,6 +203,8 @@ const message = { installing: '安装中', enabled: '已启用', disabled: '已停止', + normal: '正常', + building: '制作镜像中', }, }, menu: { @@ -1231,11 +1233,12 @@ const message = { create: '创建运行环境', name: '名称', resource: '来源', - appStore: '应用商店', + appstore: '应用商店', local: '本地', app: '应用', localHelper: '本地运行环境需要自行安装', version: '版本', + status: '状态', }, }; export default { diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index d66935d0..7f084482 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -226,3 +226,7 @@ export function isJson(str: string) { return false; } } + +export function toLowerCase(str: string) { + return str.toLowerCase(); +} diff --git a/frontend/src/views/website/runtime/create/index.vue b/frontend/src/views/website/runtime/create/index.vue index 635f3c6f..d0a00964 100644 --- a/frontend/src/views/website/runtime/create/index.vue +++ b/frontend/src/views/website/runtime/create/index.vue @@ -22,7 +22,7 @@ @change="changeResource(runtimeCreate.resource)" > - {{ $t('runtime.appStore') }} + {{ $t('runtime.appstore') }} {{ $t('runtime.local') }} @@ -135,6 +135,7 @@ const changeResource = (resource: string) => { runtimeCreate.value.image = ''; } else { runtimeCreate.value.version = ''; + searchApp(); } }; @@ -179,7 +180,15 @@ const submit = async (formEl: FormInstance | undefined) => { }); }; -const acceptParams = async () => { +const acceptParams = async (type: string) => { + runtimeCreate.value = { + name: '', + appDetailId: undefined, + image: '', + params: {}, + type: type, + resource: 'AppStore', + }; searchApp(); open.value = true; }; diff --git a/frontend/src/views/website/runtime/index.vue b/frontend/src/views/website/runtime/index.vue index 2bb80a56..e369ef68 100644 --- a/frontend/src/views/website/runtime/index.vue +++ b/frontend/src/views/website/runtime/index.vue @@ -21,30 +21,64 @@ + + + + - + + + + - +