From d4c1caa26a9f15a955dbae9eb561f36bafd0abe8 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 Date: Sun, 2 Apr 2023 16:54:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 5 + backend/app/api/v1/runtime.go | 44 +++++ backend/app/dto/app.go | 1 + backend/app/dto/request/runtime.go | 10 +- backend/app/dto/response/app.go | 3 + backend/app/dto/response/runtime.go | 3 + backend/app/repo/runtime.go | 15 ++ backend/app/service/app.go | 21 ++- backend/app/service/runtime.go | 159 ++++++++++++---- backend/app/service/runtime_utils.go | 60 ++++++ backend/constant/errs.go | 7 +- backend/constant/runtime.go | 2 + backend/i18n/lang/en.yaml | 3 +- backend/i18n/lang/zh.yaml | 3 +- backend/init/viper/viper.go | 2 +- backend/router/ro_runtime.go | 2 + frontend/src/api/interface/app.ts | 3 + frontend/src/api/interface/runtime.ts | 20 +- frontend/src/api/modules/runtime.ts | 8 + .../views/website/runtime/create/index.vue | 175 +++++++++++++----- .../src/views/website/runtime/edit/index.vue | 109 +++++++++++ frontend/src/views/website/runtime/index.vue | 14 +- .../src/views/website/runtime/param/index.vue | 19 +- 23 files changed, 581 insertions(+), 107 deletions(-) create mode 100644 frontend/src/views/website/runtime/edit/index.vue diff --git a/Makefile b/Makefile index 4a64788c9..849d86190 100644 --- a/Makefile +++ b/Makefile @@ -22,4 +22,9 @@ build_linux_on_mac: cd $(SERVER_PATH) \ && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) +build_on_archlinux: + cd $(SERVER_PATH) \ + && CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) + + build_all: build_web build_bin \ No newline at end of file diff --git a/backend/app/api/v1/runtime.go b/backend/app/api/v1/runtime.go index 19c285205..0a5a73256 100644 --- a/backend/app/api/v1/runtime.go +++ b/backend/app/api/v1/runtime.go @@ -77,3 +77,47 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) { } helper.SuccessWithOutData(c) } + +// @Tags Runtime +// @Summary Update runtime +// @Description 更新运行环境 +// @Accept json +// @Param request body request.RuntimeUpdate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /runtimes/update [post] +// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新运行环境 [name]","formatEN":"Update runtime [name]"} +func (b *BaseApi) UpdateRuntime(c *gin.Context) { + var req request.RuntimeUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := runtimeService.Update(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithOutData(c) +} + +// @Tags Runtime +// @Summary Get runtime +// @Description 获取运行环境 +// @Accept json +// @Param id path string true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /runtimes/:id [get] +func (b *BaseApi) GetRuntime(c *gin.Context) { + id, err := helper.GetIntParamByKey(c, "id") + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil) + return + } + res, err := runtimeService.Get(id) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, res) +} diff --git a/backend/app/dto/app.go b/backend/app/dto/app.go index 112db01f2..0f77a03f3 100644 --- a/backend/app/dto/app.go +++ b/backend/app/dto/app.go @@ -78,6 +78,7 @@ type AppFormFields struct { Disabled bool `json:"disabled"` Edit bool `json:"edit"` Rule string `json:"rule"` + Multiple bool `json:"multiple"` Values []AppFormValue `json:"values"` } diff --git a/backend/app/dto/request/runtime.go b/backend/app/dto/request/runtime.go index fa9d45227..0ccb90bac 100644 --- a/backend/app/dto/request/runtime.go +++ b/backend/app/dto/request/runtime.go @@ -19,5 +19,13 @@ type RuntimeCreate struct { } type RuntimeDelete struct { - ID uint `json:"Id"` + ID uint `json:"id"` +} + +type RuntimeUpdate struct { + Name string `json:"name"` + ID uint `json:"id"` + Params map[string]interface{} `json:"params"` + Image string `json:"image"` + Version string `json:"version"` } diff --git a/backend/app/dto/response/app.go b/backend/app/dto/response/app.go index 60acbb500..df991376f 100644 --- a/backend/app/dto/response/app.go +++ b/backend/app/dto/response/app.go @@ -43,6 +43,7 @@ type AppDetailDTO struct { model.AppDetail Enable bool `json:"enable"` Params interface{} `json:"params"` + Image string `json:"image"` } type AppInstalledDTO struct { @@ -70,4 +71,6 @@ type AppParam struct { Type string `json:"type"` Values interface{} `json:"values"` ShowValue string `json:"showValue"` + Required bool `json:"required"` + Multiple bool `json:"multiple"` } diff --git a/backend/app/dto/response/runtime.go b/backend/app/dto/response/runtime.go index 90b9d65bb..df85cc3c8 100644 --- a/backend/app/dto/response/runtime.go +++ b/backend/app/dto/response/runtime.go @@ -4,4 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/model" type RuntimeRes struct { model.Runtime + AppParams []AppParam `json:"appParams"` + AppID uint `json:"appId"` + Version string `json:"version"` } diff --git a/backend/app/repo/runtime.go b/backend/app/repo/runtime.go index eadf2f535..3e4fe2abb 100644 --- a/backend/app/repo/runtime.go +++ b/backend/app/repo/runtime.go @@ -3,12 +3,15 @@ package repo import ( "context" "github.com/1Panel-dev/1Panel/backend/app/model" + "gorm.io/gorm" ) type RuntimeRepo struct { } type IRuntimeRepo interface { + WithNameOrImage(name string, image string) DBOption + WithOtherNameOrImage(name string, image string, id uint) DBOption Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) Create(ctx context.Context, runtime *model.Runtime) error Save(runtime *model.Runtime) error @@ -20,6 +23,18 @@ func NewIRunTimeRepo() IRuntimeRepo { return &RuntimeRepo{} } +func (r *RuntimeRepo) WithNameOrImage(name string, image string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("name = ? or image = ?", name, image) + } +} + +func (r *RuntimeRepo) WithOtherNameOrImage(name string, image string, id uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("name = ? or image = ? and id != ?", name, image, id) + } +} + func (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) { var runtimes []model.Runtime db := getDb(opts...).Model(&model.Runtime{}) diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 2a77fa9e8..014b0142d 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -156,8 +156,9 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response. if err != nil { return appDetailDTO, err } - paramsPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build", "config.json") fileOp := files.NewFileOp() + buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build") + paramsPath := path.Join(buildPath, "config.json") if !fileOp.Stat(paramsPath) { return appDetailDTO, buserr.New(constant.ErrFileNotExist) } @@ -170,6 +171,24 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response. return appDetailDTO, err } appDetailDTO.Params = paramMap + composePath := path.Join(buildPath, "docker-compose.yml") + if !fileOp.Stat(composePath) { + return appDetailDTO, buserr.New(constant.ErrFileNotExist) + } + compose, err := fileOp.GetContent(composePath) + if err != nil { + return appDetailDTO, err + } + composeMap := make(map[string]interface{}) + if err := yaml.Unmarshal(compose, &composeMap); err != nil { + return appDetailDTO, err + } + if service, ok := composeMap["services"]; ok { + servicesMap := service.(map[string]interface{}) + for k := range servicesMap { + appDetailDTO.Image = k + } + } } else { paramMap := make(map[string]interface{}) if err := json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil { diff --git a/backend/app/service/runtime.go b/backend/app/service/runtime.go index 6d28f09a9..2f51b5ccc 100644 --- a/backend/app/service/runtime.go +++ b/backend/app/service/runtime.go @@ -2,18 +2,20 @@ package service import ( "context" + "encoding/json" "fmt" + "github.com/1Panel-dev/1Panel/backend/app/dto" "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" "github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" - "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/subosito/gotenv" "path" "path/filepath" + "strings" "time" ) @@ -24,13 +26,19 @@ type IRuntimeService interface { Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) Create(create request.RuntimeCreate) error Delete(id uint) error + Update(req request.RuntimeUpdate) error + Get(id uint) (res *response.RuntimeRes, err error) } func NewRuntimeService() IRuntimeService { return &RuntimeService{} } -func (r *RuntimeService) Create(create request.RuntimeCreate) error { +func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) { + exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithNameOrImage(create.Name, create.Image)) + if exist != nil { + return buserr.New(constant.ErrNameOrImageIsExist) + } if create.Resource == constant.ResourceLocal { runtime := &model.Runtime{ Name: create.Name, @@ -40,7 +48,6 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { } return runtimeRepo.Create(context.Background(), runtime) } - var err error appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID)) if err != nil { return err @@ -56,8 +63,8 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { } 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 + if err = fileOp.CopyDir(buildDir, tempDir); err != nil { + return } oldDir := path.Join(tempDir, "build") newNameDir := path.Join(runtimeDir, create.Name) @@ -67,58 +74,38 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error { } }() if oldDir != newNameDir { - if err := fileOp.Rename(oldDir, newNameDir); err != nil { - return err + if err = fileOp.Rename(oldDir, newNameDir); err != nil { + return } - if err := fileOp.DeleteDir(tempDir); err != nil { - return err + if err = fileOp.DeleteDir(tempDir); err != nil { + return } } - composeFile, err := fileOp.GetContent(path.Join(newNameDir, "docker-compose.yml")) + composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Params) if err != nil { - return err + return } - env, err := gotenv.Read(path.Join(newNameDir, ".env")) + composeService, err := getComposeService(create.Name, newNameDir, composeContent, envContent) if err != nil { - return err - } - newMap := make(map[string]string) - handleMap(create.Params, newMap) - for k, v := range newMap { - env[k] = v + return } - envStr, err := gotenv.Marshal(env) - if err != nil { - return err - } - if err := gotenv.Write(env, path.Join(newNameDir, ".env")); err != nil { - return err - } - project, err := docker.GetComposeProject(create.Name, newNameDir, composeFile, []byte(envStr)) - if err != nil { - return err - } - composeService, err := docker.NewComposeService() - if err != nil { - return err - } - composeService.SetProject(project) runtime := &model.Runtime{ Name: create.Name, - DockerCompose: string(composeFile), - Env: envStr, + DockerCompose: string(composeContent), + Env: string(envContent), AppDetailID: create.AppDetailID, Type: create.Type, Image: create.Image, Resource: create.Resource, Status: constant.RuntimeBuildIng, Version: create.Version, + Params: string(forms), } - if err := runtimeRepo.Create(context.Background(), runtime); err != nil { - return err + if err = runtimeRepo.Create(context.Background(), runtime); err != nil { + return } go buildRuntime(runtime, composeService) - return nil + return } func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) { @@ -155,3 +142,97 @@ func (r *RuntimeService) Delete(id uint) error { } return runtimeRepo.DeleteBy(commonRepo.WithByID(id)) } + +func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) { + runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id)) + if err != nil { + return nil, err + } + res := &response.RuntimeRes{} + res.Runtime = *runtime + if runtime.Resource == constant.ResourceLocal { + return res, nil + } + appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID)) + if err != nil { + return nil, err + } + res.AppID = appDetail.AppId + res.Version = appDetail.Version + var ( + appForm dto.AppForm + appParams []response.AppParam + ) + if err := json.Unmarshal([]byte(runtime.Params), &appForm); err != nil { + return nil, err + } + envs, err := gotenv.Unmarshal(runtime.Env) + if err != nil { + return nil, err + } + for _, form := range appForm.FormFields { + if v, ok := envs[form.EnvKey]; ok { + appParam := response.AppParam{ + Edit: false, + Key: form.EnvKey, + Rule: form.Rule, + Type: form.Type, + Required: form.Required, + } + if form.Edit { + appParam.Edit = true + } + appParam.LabelZh = form.LabelZh + appParam.LabelEn = form.LabelEn + appParam.Multiple = form.Multiple + appParam.Value = v + if form.Type == "select" { + if form.Multiple { + appParam.Value = strings.Split(v, ",") + } else { + for _, fv := range form.Values { + if fv.Value == v { + appParam.ShowValue = fv.Label + break + } + } + } + appParam.Values = form.Values + } + appParams = append(appParams, appParam) + } + } + res.AppParams = appParams + return res, nil +} + +func (r *RuntimeService) Update(req request.RuntimeUpdate) error { + exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithOtherNameOrImage(req.Name, req.Image, req.ID)) + if exist != nil { + return buserr.New(constant.ErrNameOrImageIsExist) + } + runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID)) + if err != nil { + return err + } + if runtime.Resource == constant.ResourceLocal { + runtime.Version = req.Version + return runtimeRepo.Save(runtime) + } + runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name) + composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Params) + if err != nil { + return err + } + composeService, err := getComposeService(runtime.Name, runtimeDir, composeContent, envContent) + if err != nil { + return err + } + runtime.Image = req.Image + runtime.Env = string(envContent) + runtime.DockerCompose = string(composeContent) + runtime.Status = constant.RuntimeBuildIng + _ = runtimeRepo.Save(runtime) + go buildRuntime(runtime, composeService) + return nil +} diff --git a/backend/app/service/runtime_utils.go b/backend/app/service/runtime_utils.go index e96619b84..b3ca62403 100644 --- a/backend/app/service/runtime_utils.go +++ b/backend/app/service/runtime_utils.go @@ -1,10 +1,15 @@ package service import ( + "fmt" "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" + "github.com/1Panel-dev/1Panel/backend/utils/files" + "github.com/subosito/gotenv" + "path" + "strings" ) func buildRuntime(runtime *model.Runtime, service *docker.ComposeService) { @@ -17,3 +22,58 @@ func buildRuntime(runtime *model.Runtime, service *docker.ComposeService) { } _ = runtimeRepo.Save(runtime) } + +func handleParams(image, runtimeType, runtimeDir string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) { + fileOp := files.NewFileOp() + composeContent, err = fileOp.GetContent(path.Join(runtimeDir, "docker-compose.yml")) + if err != nil { + return + } + env, err := gotenv.Read(path.Join(runtimeDir, ".env")) + if err != nil { + return + } + forms, err = fileOp.GetContent(path.Join(runtimeDir, "config.json")) + if err != nil { + return + } + params["IMAGE_NAME"] = image + if runtimeType == constant.RuntimePHP { + if extends, ok := params["PHP_EXTENSIONS"]; ok { + if extendsArray, ok := extends.([]interface{}); ok { + strArray := make([]string, len(extendsArray)) + for i, v := range extendsArray { + strArray[i] = fmt.Sprintf("%v", v) + } + params["PHP_EXTENSIONS"] = strings.Join(strArray, ",") + } + } + } + newMap := make(map[string]string) + handleMap(params, newMap) + for k, v := range newMap { + env[k] = v + } + envStr, err := gotenv.Marshal(env) + if err != nil { + return + } + if err = gotenv.Write(env, path.Join(runtimeDir, ".env")); err != nil { + return + } + envContent = []byte(envStr) + return +} + +func getComposeService(name, runtimeDir string, composeFile, env []byte) (*docker.ComposeService, error) { + project, err := docker.GetComposeProject(name, runtimeDir, composeFile, env) + if err != nil { + return nil, err + } + composeService, err := docker.NewComposeService() + if err != nil { + return nil, err + } + composeService.SetProject(project) + return composeService, nil +} diff --git a/backend/constant/errs.go b/backend/constant/errs.go index 00ee9acac..80b57b4c5 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -106,7 +106,8 @@ var ( // runtime var ( - ErrDirNotFound = "ErrDirNotFound" - ErrFileNotExist = "ErrFileNotExist" - ErrImageBuildErr = "ErrImageBuildErr" + ErrDirNotFound = "ErrDirNotFound" + ErrFileNotExist = "ErrFileNotExist" + ErrImageBuildErr = "ErrImageBuildErr" + ErrNameOrImageIsExist = "ErrNameOrImageIsExist" ) diff --git a/backend/constant/runtime.go b/backend/constant/runtime.go index 0f1fec67a..6af7a1fb3 100644 --- a/backend/constant/runtime.go +++ b/backend/constant/runtime.go @@ -7,4 +7,6 @@ const ( RuntimeNormal = "normal" RuntimeError = "error" RuntimeBuildIng = "building" + + RuntimePHP = "php" ) diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index e6ffe7266..841706a2a 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -63,4 +63,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!" -ErrImageBuildErr: "Image build failed" \ No newline at end of file +ErrImageBuildErr: "Image build failed" +ErrNameOrImageIsExist: "Duplicate name or image" \ No newline at end of file diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 82de943ad..b002a7ecd 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -63,4 +63,5 @@ ErrObjectInUsed: "该对象正被使用,无法删除" #runtime ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!" -ErrImageBuildErr: "镜像 build 失败" \ No newline at end of file +ErrImageBuildErr: "镜像 build 失败" +ErrNameOrImageIsExist: "名称或者镜像重复" \ No newline at end of file diff --git a/backend/init/viper/viper.go b/backend/init/viper/viper.go index 3172e0764..bc6b90300 100644 --- a/backend/init/viper/viper.go +++ b/backend/init/viper/viper.go @@ -20,7 +20,7 @@ func Init() { baseDir := "/opt" port := "9999" mode := "" - version := "" + version := "v1.0.0" fileOp := files.NewFileOp() v := viper.NewWithOptions() v.SetConfigType("yaml") diff --git a/backend/router/ro_runtime.go b/backend/router/ro_runtime.go index 969b290f0..1c01298e3 100644 --- a/backend/router/ro_runtime.go +++ b/backend/router/ro_runtime.go @@ -18,5 +18,7 @@ func (r *RuntimeRouter) InitRuntimeRouter(Router *gin.RouterGroup) { groupRouter.POST("/search", baseApi.SearchRuntimes) groupRouter.POST("", baseApi.CreateRuntime) groupRouter.POST("/del", baseApi.DeleteRuntime) + groupRouter.POST("/update", baseApi.UpdateRuntime) + groupRouter.GET("/:id", baseApi.GetRuntime) } } diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 67a0c32ee..42bc0b44f 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -40,6 +40,7 @@ export namespace App { params: AppParams; dockerCompose: string; enbale: boolean; + image: string; } export interface AppReq extends ReqPage { @@ -166,5 +167,7 @@ export namespace App { type: string; values?: any; showValue?: string; + required?: boolean; + multiple?: boolean; } } diff --git a/frontend/src/api/interface/runtime.ts b/frontend/src/api/interface/runtime.ts index ada47e7b8..c544ad901 100644 --- a/frontend/src/api/interface/runtime.ts +++ b/frontend/src/api/interface/runtime.ts @@ -1,14 +1,16 @@ import { CommonModel, ReqPage } from '.'; +import { App } from './app'; export namespace Runtime { export interface Runtime extends CommonModel { name: string; - appDetailId: string; + appDetailId: number; image: string; workDir: string; dockerCompose: string; env: string; params: string; type: string; + resource: string; } export interface RuntimeReq extends ReqPage { @@ -16,10 +18,24 @@ export namespace Runtime { } export interface RuntimeDTO extends Runtime { - websites: string[]; + appParams: App.InstallParams[]; + appId: number; + version: string; } export interface RuntimeCreate { + id?: number; + name: string; + appDetailId: number; + image: string; + params: object; + type: string; + resource: string; + appId?: number; + version?: string; + } + + export interface RuntimeUpdate { name: string; appDetailId: number; image: string; diff --git a/frontend/src/api/modules/runtime.ts b/frontend/src/api/modules/runtime.ts index 813bb8955..8bd7be961 100644 --- a/frontend/src/api/modules/runtime.ts +++ b/frontend/src/api/modules/runtime.ts @@ -13,3 +13,11 @@ export const CreateRuntime = (req: Runtime.RuntimeCreate) => { export const DeleteRuntime = (req: Runtime.RuntimeDelete) => { return http.post(`/runtimes/del`, req); }; + +export const GetRuntime = (id: number) => { + return http.get(`/runtimes/${id}`); +}; + +export const UpdateRuntime = (req: Runtime.RuntimeUpdate) => { + return http.post(`/runtimes/update`, req); +}; diff --git a/frontend/src/views/website/runtime/create/index.vue b/frontend/src/views/website/runtime/create/index.vue index d0a009645..e84cde6c9 100644 --- a/frontend/src/views/website/runtime/create/index.vue +++ b/frontend/src/views/website/runtime/create/index.vue @@ -8,18 +8,19 @@ - + {{ $t('runtime.appstore') }} @@ -29,11 +30,11 @@ -
+
- + - + - + + + +
+ + +
- + + + - +
@@ -85,32 +99,41 @@ import { App } from '@/api/interface/app'; import { Runtime } from '@/api/interface/runtime'; import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app'; -import { CreateRuntime } from '@/api/modules/runtime'; +import { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime'; import { Rules } from '@/global/form-rules'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; import { FormInstance } from 'element-plus'; import { reactive, ref } from 'vue'; import Params from '../param/index.vue'; +import EditParams from '../edit/index.vue'; + +interface OperateRrops { + id?: number; + mode: string; + type: string; +} const open = ref(false); const apps = ref([]); const runtimeForm = ref(); const loading = ref(false); const initParam = ref(false); +const mode = ref('create'); let appParams = ref(); +let editParams = ref(); let appVersions = ref([]); let appReq = reactive({ type: 'php', page: 1, pageSize: 20, }); -const runtimeCreate = ref({ +const runtime = ref({ name: '', appDetailId: undefined, image: '', params: {}, - type: '', + type: 'php', resource: 'AppStore', }); let rules = ref({ @@ -118,6 +141,7 @@ let rules = ref({ resource: [Rules.requiredInput], appId: [Rules.requiredSelect], version: [Rules.requiredInput], + image: [Rules.requiredInput, Rules.imageName], }); const em = defineEmits(['close']); @@ -129,35 +153,48 @@ const handleClose = () => { const changeResource = (resource: string) => { if (resource === 'Local') { - runtimeCreate.value.appDetailId = undefined; - runtimeCreate.value.version = ''; - runtimeCreate.value.params = {}; - runtimeCreate.value.image = ''; + runtime.value.appDetailId = undefined; + runtime.value.version = ''; + runtime.value.params = {}; + runtime.value.image = ''; } else { - runtimeCreate.value.version = ''; - searchApp(); + runtime.value.version = ''; + searchApp(null); } }; -const searchApp = () => { +const searchApp = (appId: number) => { SearchApp(appReq).then((res) => { apps.value = res.data.items || []; if (res.data && res.data.items && res.data.items.length > 0) { - runtimeCreate.value.appId = res.data.items[0].id; - getApp(res.data.items[0].key); + if (appId == null) { + runtime.value.appId = res.data.items[0].id; + getApp(res.data.items[0].key, mode.value); + } else { + res.data.items.forEach((item) => { + if (item.id === appId) { + getApp(item.key, mode.value); + } + }); + } } }); }; -const getApp = (appkey: string) => { +const getApp = (appkey: string, mode: string) => { GetApp(appkey).then((res) => { appVersions.value = res.data.versions || []; if (res.data.versions.length > 0) { - runtimeCreate.value.version = res.data.versions[0]; - GetAppDetail(runtimeCreate.value.appId, runtimeCreate.value.version, 'runtime').then((res) => { - runtimeCreate.value.appDetailId = res.data.id; - appParams.value = res.data.params; + runtime.value.version = res.data.versions[0]; + if (mode === 'create') { + GetAppDetail(runtime.value.appId, runtime.value.version, 'runtime').then((res) => { + runtime.value.appDetailId = res.data.id; + runtime.value.image = res.data.image + ':' + runtime.value.version; + appParams.value = res.data.params; + initParam.value = true; + }); + } else { initParam.value = true; - }); + } } }); }; @@ -169,27 +206,63 @@ const submit = async (formEl: FormInstance | undefined) => { return; } loading.value = true; - CreateRuntime(runtimeCreate.value) - .then(() => { - MsgSuccess(i18n.global.t('commons.msg.createSuccess')); - handleClose(); - }) - .finally(() => { - loading.value = false; - }); + if (mode.value == 'create') { + CreateRuntime(runtime.value) + .then(() => { + MsgSuccess(i18n.global.t('commons.msg.createSuccess')); + handleClose(); + }) + .finally(() => { + loading.value = false; + }); + } else { + UpdateRuntime(runtime.value) + .then(() => { + MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); + handleClose(); + }) + .finally(() => { + loading.value = false; + }); + } }); }; -const acceptParams = async (type: string) => { - runtimeCreate.value = { - name: '', - appDetailId: undefined, - image: '', - params: {}, - type: type, - resource: 'AppStore', - }; - searchApp(); +const getRuntime = async (id: number) => { + try { + const res = await GetRuntime(id); + const data = res.data; + runtime.value = { + id: data.id, + name: data.name, + appDetailId: data.appDetailId, + image: data.image, + params: {}, + type: data.type, + resource: data.resource, + appId: data.appId, + version: data.version, + }; + editParams.value = data.appParams; + searchApp(data.appId); + } catch (error) {} +}; + +const acceptParams = async (props: OperateRrops) => { + mode.value = props.mode; + if (props.mode === 'create') { + runtime.value = { + name: '', + appDetailId: undefined, + image: '', + params: {}, + type: props.type, + resource: 'AppStore', + }; + searchApp(null); + } else { + getRuntime(props.id); + } open.value = true; }; diff --git a/frontend/src/views/website/runtime/edit/index.vue b/frontend/src/views/website/runtime/edit/index.vue new file mode 100644 index 000000000..1daf4b420 --- /dev/null +++ b/frontend/src/views/website/runtime/edit/index.vue @@ -0,0 +1,109 @@ + + + diff --git a/frontend/src/views/website/runtime/index.vue b/frontend/src/views/website/runtime/index.vue index e369ef68a..fa55efef0 100644 --- a/frontend/src/views/website/runtime/index.vue +++ b/frontend/src/views/website/runtime/index.vue @@ -27,7 +27,7 @@ - +