mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 增加运行环境编辑功能
This commit is contained in:
parent
22d9bdacf6
commit
d4c1caa26a
5
Makefile
5
Makefile
@ -22,4 +22,9 @@ build_linux_on_mac:
|
|||||||
cd $(SERVER_PATH) \
|
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)
|
&& 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
|
build_all: build_web build_bin
|
@ -77,3 +77,47 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithOutData(c)
|
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)
|
||||||
|
}
|
||||||
|
@ -78,6 +78,7 @@ type AppFormFields struct {
|
|||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
Edit bool `json:"edit"`
|
Edit bool `json:"edit"`
|
||||||
Rule string `json:"rule"`
|
Rule string `json:"rule"`
|
||||||
|
Multiple bool `json:"multiple"`
|
||||||
Values []AppFormValue `json:"values"`
|
Values []AppFormValue `json:"values"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,5 +19,13 @@ type RuntimeCreate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeDelete 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"`
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ type AppDetailDTO struct {
|
|||||||
model.AppDetail
|
model.AppDetail
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
Params interface{} `json:"params"`
|
Params interface{} `json:"params"`
|
||||||
|
Image string `json:"image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppInstalledDTO struct {
|
type AppInstalledDTO struct {
|
||||||
@ -70,4 +71,6 @@ type AppParam struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Values interface{} `json:"values"`
|
Values interface{} `json:"values"`
|
||||||
ShowValue string `json:"showValue"`
|
ShowValue string `json:"showValue"`
|
||||||
|
Required bool `json:"required"`
|
||||||
|
Multiple bool `json:"multiple"`
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/model"
|
|||||||
|
|
||||||
type RuntimeRes struct {
|
type RuntimeRes struct {
|
||||||
model.Runtime
|
model.Runtime
|
||||||
|
AppParams []AppParam `json:"appParams"`
|
||||||
|
AppID uint `json:"appId"`
|
||||||
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,15 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeRepo struct {
|
type RuntimeRepo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type IRuntimeRepo interface {
|
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)
|
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||||
Create(ctx context.Context, runtime *model.Runtime) error
|
Create(ctx context.Context, runtime *model.Runtime) error
|
||||||
Save(runtime *model.Runtime) error
|
Save(runtime *model.Runtime) error
|
||||||
@ -20,6 +23,18 @@ func NewIRunTimeRepo() IRuntimeRepo {
|
|||||||
return &RuntimeRepo{}
|
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) {
|
func (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) {
|
||||||
var runtimes []model.Runtime
|
var runtimes []model.Runtime
|
||||||
db := getDb(opts...).Model(&model.Runtime{})
|
db := getDb(opts...).Model(&model.Runtime{})
|
||||||
|
@ -156,8 +156,9 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return appDetailDTO, err
|
return appDetailDTO, err
|
||||||
}
|
}
|
||||||
paramsPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build", "config.json")
|
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
|
buildPath := path.Join(constant.AppResourceDir, app.Key, "versions", detail.Version, "build")
|
||||||
|
paramsPath := path.Join(buildPath, "config.json")
|
||||||
if !fileOp.Stat(paramsPath) {
|
if !fileOp.Stat(paramsPath) {
|
||||||
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
return appDetailDTO, buserr.New(constant.ErrFileNotExist)
|
||||||
}
|
}
|
||||||
@ -170,6 +171,24 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
|||||||
return appDetailDTO, err
|
return appDetailDTO, err
|
||||||
}
|
}
|
||||||
appDetailDTO.Params = paramMap
|
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 {
|
} else {
|
||||||
paramMap := make(map[string]interface{})
|
paramMap := make(map[string]interface{})
|
||||||
if err := json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil {
|
if err := json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil {
|
||||||
|
@ -2,18 +2,20 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"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/request"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"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/1Panel-dev/1Panel/backend/utils/files"
|
||||||
"github.com/subosito/gotenv"
|
"github.com/subosito/gotenv"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,13 +26,19 @@ type IRuntimeService interface {
|
|||||||
Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error)
|
Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error)
|
||||||
Create(create request.RuntimeCreate) error
|
Create(create request.RuntimeCreate) error
|
||||||
Delete(id uint) error
|
Delete(id uint) error
|
||||||
|
Update(req request.RuntimeUpdate) error
|
||||||
|
Get(id uint) (res *response.RuntimeRes, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuntimeService() IRuntimeService {
|
func NewRuntimeService() IRuntimeService {
|
||||||
return &RuntimeService{}
|
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 {
|
if create.Resource == constant.ResourceLocal {
|
||||||
runtime := &model.Runtime{
|
runtime := &model.Runtime{
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
@ -40,7 +48,6 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error {
|
|||||||
}
|
}
|
||||||
return runtimeRepo.Create(context.Background(), runtime)
|
return runtimeRepo.Create(context.Background(), runtime)
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -56,8 +63,8 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error {
|
|||||||
}
|
}
|
||||||
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||||
tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano()))
|
tempDir := filepath.Join(runtimeDir, fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||||
if err := fileOp.CopyDir(buildDir, tempDir); err != nil {
|
if err = fileOp.CopyDir(buildDir, tempDir); err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
oldDir := path.Join(tempDir, "build")
|
oldDir := path.Join(tempDir, "build")
|
||||||
newNameDir := path.Join(runtimeDir, create.Name)
|
newNameDir := path.Join(runtimeDir, create.Name)
|
||||||
@ -67,58 +74,38 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if oldDir != newNameDir {
|
if oldDir != newNameDir {
|
||||||
if err := fileOp.Rename(oldDir, newNameDir); err != nil {
|
if err = fileOp.Rename(oldDir, newNameDir); err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
if err := fileOp.DeleteDir(tempDir); err != nil {
|
if err = fileOp.DeleteDir(tempDir); err != nil {
|
||||||
return err
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
newMap := make(map[string]string)
|
|
||||||
handleMap(create.Params, newMap)
|
|
||||||
for k, v := range newMap {
|
|
||||||
env[k] = v
|
|
||||||
}
|
|
||||||
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{
|
runtime := &model.Runtime{
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
DockerCompose: string(composeFile),
|
DockerCompose: string(composeContent),
|
||||||
Env: envStr,
|
Env: string(envContent),
|
||||||
AppDetailID: create.AppDetailID,
|
AppDetailID: create.AppDetailID,
|
||||||
Type: create.Type,
|
Type: create.Type,
|
||||||
Image: create.Image,
|
Image: create.Image,
|
||||||
Resource: create.Resource,
|
Resource: create.Resource,
|
||||||
Status: constant.RuntimeBuildIng,
|
Status: constant.RuntimeBuildIng,
|
||||||
Version: create.Version,
|
Version: create.Version,
|
||||||
|
Params: string(forms),
|
||||||
}
|
}
|
||||||
if err := runtimeRepo.Create(context.Background(), runtime); err != nil {
|
if err = runtimeRepo.Create(context.Background(), runtime); err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
go buildRuntime(runtime, composeService)
|
go buildRuntime(runtime, composeService)
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) {
|
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))
|
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
|
||||||
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
"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) {
|
func buildRuntime(runtime *model.Runtime, service *docker.ComposeService) {
|
||||||
@ -17,3 +22,58 @@ func buildRuntime(runtime *model.Runtime, service *docker.ComposeService) {
|
|||||||
}
|
}
|
||||||
_ = runtimeRepo.Save(runtime)
|
_ = 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
|
||||||
|
}
|
||||||
|
@ -109,4 +109,5 @@ var (
|
|||||||
ErrDirNotFound = "ErrDirNotFound"
|
ErrDirNotFound = "ErrDirNotFound"
|
||||||
ErrFileNotExist = "ErrFileNotExist"
|
ErrFileNotExist = "ErrFileNotExist"
|
||||||
ErrImageBuildErr = "ErrImageBuildErr"
|
ErrImageBuildErr = "ErrImageBuildErr"
|
||||||
|
ErrNameOrImageIsExist = "ErrNameOrImageIsExist"
|
||||||
)
|
)
|
||||||
|
@ -7,4 +7,6 @@ const (
|
|||||||
RuntimeNormal = "normal"
|
RuntimeNormal = "normal"
|
||||||
RuntimeError = "error"
|
RuntimeError = "error"
|
||||||
RuntimeBuildIng = "building"
|
RuntimeBuildIng = "building"
|
||||||
|
|
||||||
|
RuntimePHP = "php"
|
||||||
)
|
)
|
||||||
|
@ -64,3 +64,4 @@ ErrObjectInUsed: "This object is in use and cannot be deleted"
|
|||||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||||
ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity!"
|
ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity!"
|
||||||
ErrImageBuildErr: "Image build failed"
|
ErrImageBuildErr: "Image build failed"
|
||||||
|
ErrNameOrImageIsExist: "Duplicate name or image"
|
@ -64,3 +64,4 @@ ErrObjectInUsed: "该对象正被使用,无法删除"
|
|||||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||||
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
||||||
ErrImageBuildErr: "镜像 build 失败"
|
ErrImageBuildErr: "镜像 build 失败"
|
||||||
|
ErrNameOrImageIsExist: "名称或者镜像重复"
|
@ -20,7 +20,7 @@ func Init() {
|
|||||||
baseDir := "/opt"
|
baseDir := "/opt"
|
||||||
port := "9999"
|
port := "9999"
|
||||||
mode := ""
|
mode := ""
|
||||||
version := ""
|
version := "v1.0.0"
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
v := viper.NewWithOptions()
|
v := viper.NewWithOptions()
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
|
@ -18,5 +18,7 @@ func (r *RuntimeRouter) InitRuntimeRouter(Router *gin.RouterGroup) {
|
|||||||
groupRouter.POST("/search", baseApi.SearchRuntimes)
|
groupRouter.POST("/search", baseApi.SearchRuntimes)
|
||||||
groupRouter.POST("", baseApi.CreateRuntime)
|
groupRouter.POST("", baseApi.CreateRuntime)
|
||||||
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
||||||
|
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
||||||
|
groupRouter.GET("/:id", baseApi.GetRuntime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ export namespace App {
|
|||||||
params: AppParams;
|
params: AppParams;
|
||||||
dockerCompose: string;
|
dockerCompose: string;
|
||||||
enbale: boolean;
|
enbale: boolean;
|
||||||
|
image: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppReq extends ReqPage {
|
export interface AppReq extends ReqPage {
|
||||||
@ -166,5 +167,7 @@ export namespace App {
|
|||||||
type: string;
|
type: string;
|
||||||
values?: any;
|
values?: any;
|
||||||
showValue?: string;
|
showValue?: string;
|
||||||
|
required?: boolean;
|
||||||
|
multiple?: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { CommonModel, ReqPage } from '.';
|
import { CommonModel, ReqPage } from '.';
|
||||||
|
import { App } from './app';
|
||||||
export namespace Runtime {
|
export namespace Runtime {
|
||||||
export interface Runtime extends CommonModel {
|
export interface Runtime extends CommonModel {
|
||||||
name: string;
|
name: string;
|
||||||
appDetailId: string;
|
appDetailId: number;
|
||||||
image: string;
|
image: string;
|
||||||
workDir: string;
|
workDir: string;
|
||||||
dockerCompose: string;
|
dockerCompose: string;
|
||||||
env: string;
|
env: string;
|
||||||
params: string;
|
params: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
resource: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeReq extends ReqPage {
|
export interface RuntimeReq extends ReqPage {
|
||||||
@ -16,10 +18,24 @@ export namespace Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeDTO extends Runtime {
|
export interface RuntimeDTO extends Runtime {
|
||||||
websites: string[];
|
appParams: App.InstallParams[];
|
||||||
|
appId: number;
|
||||||
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeCreate {
|
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;
|
name: string;
|
||||||
appDetailId: number;
|
appDetailId: number;
|
||||||
image: string;
|
image: string;
|
||||||
|
@ -13,3 +13,11 @@ export const CreateRuntime = (req: Runtime.RuntimeCreate) => {
|
|||||||
export const DeleteRuntime = (req: Runtime.RuntimeDelete) => {
|
export const DeleteRuntime = (req: Runtime.RuntimeDelete) => {
|
||||||
return http.post<any>(`/runtimes/del`, req);
|
return http.post<any>(`/runtimes/del`, req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GetRuntime = (id: number) => {
|
||||||
|
return http.get<Runtime.RuntimeDTO>(`/runtimes/${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UpdateRuntime = (req: Runtime.RuntimeUpdate) => {
|
||||||
|
return http.post<any>(`/runtimes/update`, req);
|
||||||
|
};
|
||||||
|
@ -8,18 +8,19 @@
|
|||||||
<el-form
|
<el-form
|
||||||
ref="runtimeForm"
|
ref="runtimeForm"
|
||||||
label-position="top"
|
label-position="top"
|
||||||
:model="runtimeCreate"
|
:model="runtime"
|
||||||
label-width="125px"
|
label-width="125px"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
:validate-on-rule-change="false"
|
:validate-on-rule-change="false"
|
||||||
>
|
>
|
||||||
<el-form-item :label="$t('runtime.name')" prop="name">
|
<el-form-item :label="$t('runtime.name')" prop="name">
|
||||||
<el-input v-model="runtimeCreate.name"></el-input>
|
<el-input :disabled="mode === 'edit'" v-model="runtime.name"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('runtime.resource')" prop="resource">
|
<el-form-item :label="$t('runtime.resource')" prop="resource">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="runtimeCreate.resource"
|
:disabled="mode === 'edit'"
|
||||||
@change="changeResource(runtimeCreate.resource)"
|
v-model="runtime.resource"
|
||||||
|
@change="changeResource(runtime.resource)"
|
||||||
>
|
>
|
||||||
<el-radio :label="'AppStore'" :value="'AppStore'">
|
<el-radio :label="'AppStore'" :value="'AppStore'">
|
||||||
{{ $t('runtime.appstore') }}
|
{{ $t('runtime.appstore') }}
|
||||||
@ -29,11 +30,11 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-if="runtimeCreate.resource === 'AppStore'">
|
<div v-if="runtime.resource === 'AppStore'">
|
||||||
<el-form-item :label="$t('runtime.app')" prop="appId">
|
<el-form-item :label="$t('runtime.app')" prop="appId">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-select v-model="runtimeCreate.appId">
|
<el-select v-model="runtime.appId" :disabled="mode === 'edit'">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(app, index) in apps"
|
v-for="(app, index) in apps"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -43,7 +44,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-select v-model="runtimeCreate.version">
|
<el-select v-model="runtime.version" :disabled="mode === 'edit'">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(version, index) in appVersions"
|
v-for="(version, index) in appVersions"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -54,17 +55,30 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.image')" prop="image">
|
||||||
|
<el-input v-model="runtime.image"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="initParam">
|
||||||
<Params
|
<Params
|
||||||
v-if="initParam"
|
v-if="mode === 'create'"
|
||||||
v-model:form="runtimeCreate"
|
v-model:form="runtime.params"
|
||||||
v-model:params="appParams"
|
v-model:params="appParams"
|
||||||
v-model:rules="rules"
|
v-model:rules="rules"
|
||||||
></Params>
|
></Params>
|
||||||
|
<EditParams
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
v-model:form="runtime.params"
|
||||||
|
v-model:params="editParams"
|
||||||
|
v-model:rules="rules"
|
||||||
|
></EditParams>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
<el-form-item>
|
||||||
<el-alert :title="$t('runtime.localHelper')" type="info" :closable="false" />
|
<el-alert :title="$t('runtime.localHelper')" type="info" :closable="false" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$t('runtime.version')" prop="version">
|
<el-form-item :label="$t('runtime.version')" prop="version">
|
||||||
<el-input v-model="runtimeCreate.version"></el-input>
|
<el-input v-model="runtime.version"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -85,32 +99,41 @@
|
|||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { Runtime } from '@/api/interface/runtime';
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app';
|
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 { Rules } from '@/global/form-rules';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
import { FormInstance } from 'element-plus';
|
import { FormInstance } from 'element-plus';
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import Params from '../param/index.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 open = ref(false);
|
||||||
const apps = ref<App.App[]>([]);
|
const apps = ref<App.App[]>([]);
|
||||||
const runtimeForm = ref<FormInstance>();
|
const runtimeForm = ref<FormInstance>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const initParam = ref(false);
|
const initParam = ref(false);
|
||||||
|
const mode = ref('create');
|
||||||
let appParams = ref<App.AppParams>();
|
let appParams = ref<App.AppParams>();
|
||||||
|
let editParams = ref<App.InstallParams[]>();
|
||||||
let appVersions = ref<string[]>([]);
|
let appVersions = ref<string[]>([]);
|
||||||
let appReq = reactive({
|
let appReq = reactive({
|
||||||
type: 'php',
|
type: 'php',
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
const runtimeCreate = ref<Runtime.RuntimeCreate>({
|
const runtime = ref<Runtime.RuntimeCreate>({
|
||||||
name: '',
|
name: '',
|
||||||
appDetailId: undefined,
|
appDetailId: undefined,
|
||||||
image: '',
|
image: '',
|
||||||
params: {},
|
params: {},
|
||||||
type: '',
|
type: 'php',
|
||||||
resource: 'AppStore',
|
resource: 'AppStore',
|
||||||
});
|
});
|
||||||
let rules = ref<any>({
|
let rules = ref<any>({
|
||||||
@ -118,6 +141,7 @@ let rules = ref<any>({
|
|||||||
resource: [Rules.requiredInput],
|
resource: [Rules.requiredInput],
|
||||||
appId: [Rules.requiredSelect],
|
appId: [Rules.requiredSelect],
|
||||||
version: [Rules.requiredInput],
|
version: [Rules.requiredInput],
|
||||||
|
image: [Rules.requiredInput, Rules.imageName],
|
||||||
});
|
});
|
||||||
|
|
||||||
const em = defineEmits(['close']);
|
const em = defineEmits(['close']);
|
||||||
@ -129,35 +153,48 @@ const handleClose = () => {
|
|||||||
|
|
||||||
const changeResource = (resource: string) => {
|
const changeResource = (resource: string) => {
|
||||||
if (resource === 'Local') {
|
if (resource === 'Local') {
|
||||||
runtimeCreate.value.appDetailId = undefined;
|
runtime.value.appDetailId = undefined;
|
||||||
runtimeCreate.value.version = '';
|
runtime.value.version = '';
|
||||||
runtimeCreate.value.params = {};
|
runtime.value.params = {};
|
||||||
runtimeCreate.value.image = '';
|
runtime.value.image = '';
|
||||||
} else {
|
} else {
|
||||||
runtimeCreate.value.version = '';
|
runtime.value.version = '';
|
||||||
searchApp();
|
searchApp(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchApp = () => {
|
const searchApp = (appId: number) => {
|
||||||
SearchApp(appReq).then((res) => {
|
SearchApp(appReq).then((res) => {
|
||||||
apps.value = res.data.items || [];
|
apps.value = res.data.items || [];
|
||||||
if (res.data && res.data.items && res.data.items.length > 0) {
|
if (res.data && res.data.items && res.data.items.length > 0) {
|
||||||
runtimeCreate.value.appId = res.data.items[0].id;
|
if (appId == null) {
|
||||||
getApp(res.data.items[0].key);
|
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) => {
|
GetApp(appkey).then((res) => {
|
||||||
appVersions.value = res.data.versions || [];
|
appVersions.value = res.data.versions || [];
|
||||||
if (res.data.versions.length > 0) {
|
if (res.data.versions.length > 0) {
|
||||||
runtimeCreate.value.version = res.data.versions[0];
|
runtime.value.version = res.data.versions[0];
|
||||||
GetAppDetail(runtimeCreate.value.appId, runtimeCreate.value.version, 'runtime').then((res) => {
|
if (mode === 'create') {
|
||||||
runtimeCreate.value.appDetailId = res.data.id;
|
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;
|
appParams.value = res.data.params;
|
||||||
initParam.value = true;
|
initParam.value = true;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
initParam.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -169,7 +206,8 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
CreateRuntime(runtimeCreate.value)
|
if (mode.value == 'create') {
|
||||||
|
CreateRuntime(runtime.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
handleClose();
|
handleClose();
|
||||||
@ -177,19 +215,54 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
loading.value = false;
|
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) => {
|
const getRuntime = async (id: number) => {
|
||||||
runtimeCreate.value = {
|
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: '',
|
name: '',
|
||||||
appDetailId: undefined,
|
appDetailId: undefined,
|
||||||
image: '',
|
image: '',
|
||||||
params: {},
|
params: {},
|
||||||
type: type,
|
type: props.type,
|
||||||
resource: 'AppStore',
|
resource: 'AppStore',
|
||||||
};
|
};
|
||||||
searchApp();
|
searchApp(null);
|
||||||
|
} else {
|
||||||
|
getRuntime(props.id);
|
||||||
|
}
|
||||||
open.value = true;
|
open.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
109
frontend/src/views/website/runtime/edit/index.vue
Normal file
109
frontend/src/views/website/runtime/edit/index.vue
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div v-for="(p, index) in paramObjs" :key="index">
|
||||||
|
<el-form-item :label="getLabel(p)" :prop="p.prop">
|
||||||
|
<el-select
|
||||||
|
v-model="form[p.key]"
|
||||||
|
v-if="p.type == 'select'"
|
||||||
|
:multiple="p.multiple"
|
||||||
|
filterable
|
||||||
|
@change="updateParam"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="service in p.values"
|
||||||
|
:key="service.label"
|
||||||
|
:value="service.value"
|
||||||
|
:label="service.label"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
interface ParamObj extends App.InstallParams {
|
||||||
|
prop: string;
|
||||||
|
}
|
||||||
|
const props = defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
type: Array<App.InstallParams>,
|
||||||
|
default: function () {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let form = reactive({});
|
||||||
|
let rules = reactive({});
|
||||||
|
const params = computed({
|
||||||
|
get() {
|
||||||
|
return props.params;
|
||||||
|
},
|
||||||
|
set() {},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['update:form', 'update:rules']);
|
||||||
|
const updateParam = () => {
|
||||||
|
emit('update:form', form);
|
||||||
|
};
|
||||||
|
const paramObjs = ref<ParamObj[]>([]);
|
||||||
|
|
||||||
|
const handleParams = () => {
|
||||||
|
rules = props.rules;
|
||||||
|
if (params.value != undefined) {
|
||||||
|
for (const p of params.value) {
|
||||||
|
form[p.key] = p.value;
|
||||||
|
paramObjs.value.push({
|
||||||
|
prop: p.key,
|
||||||
|
labelEn: p.labelEn,
|
||||||
|
labelZh: p.labelZh,
|
||||||
|
values: p.values,
|
||||||
|
value: p.value,
|
||||||
|
required: p.required,
|
||||||
|
edit: p.edit,
|
||||||
|
key: p.key,
|
||||||
|
rule: p.rule,
|
||||||
|
type: p.type,
|
||||||
|
multiple: p.multiple,
|
||||||
|
});
|
||||||
|
if (p.required) {
|
||||||
|
if (p.type === 'select') {
|
||||||
|
rules[p.key] = [Rules.requiredSelect];
|
||||||
|
} else {
|
||||||
|
rules[p.key] = [Rules.requiredInput];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit('update:rules', rules);
|
||||||
|
updateParam();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLabel = (row: ParamObj): string => {
|
||||||
|
const language = useI18n().locale.value;
|
||||||
|
if (language == 'zh') {
|
||||||
|
return row.labelZh;
|
||||||
|
} else {
|
||||||
|
return row.labelEn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleParams();
|
||||||
|
});
|
||||||
|
</script>
|
@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
||||||
<el-table-column :label="$t('runtime.image')" prop="image"></el-table-column>
|
<el-table-column :label="$t('runtime.image')" prop="image" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column :label="$t('runtime.status')" prop="status">
|
<el-table-column :label="$t('runtime.status')" prop="status">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-popover
|
<el-popover
|
||||||
@ -92,6 +92,12 @@ let req = reactive<Runtime.RuntimeReq>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDetail(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
click: function (row: Runtime.Runtime) {
|
click: function (row: Runtime.Runtime) {
|
||||||
@ -118,7 +124,11 @@ const search = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openCreate = () => {
|
const openCreate = () => {
|
||||||
createRef.value.acceptParams('php');
|
createRef.value.acceptParams({ type: 'php', mode: 'create' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDetail = (row: Runtime.Runtime) => {
|
||||||
|
createRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
const openDelete = async (row: Runtime.Runtime) => {
|
const openDelete = async (row: Runtime.Runtime) => {
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-for="(p, index) in paramObjs" :key="index">
|
<div v-for="(p, index) in paramObjs" :key="index">
|
||||||
<el-form-item :label="getLabel(p)" :prop="p.prop">
|
<el-form-item :label="getLabel(p)" :prop="p.prop">
|
||||||
<el-select v-model="form[p.envKey]" v-if="p.type == 'select'" :multiple="p.multiple" filterable>
|
<el-select
|
||||||
|
v-model="form[p.envKey]"
|
||||||
|
v-if="p.type == 'select'"
|
||||||
|
:multiple="p.multiple"
|
||||||
|
filterable
|
||||||
|
@change="updateParam"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="service in p.values"
|
v-for="service in p.values"
|
||||||
:key="service.label"
|
:key="service.label"
|
||||||
@ -54,20 +60,21 @@ const params = computed({
|
|||||||
},
|
},
|
||||||
set() {},
|
set() {},
|
||||||
});
|
});
|
||||||
|
const emit = defineEmits(['update:form', 'update:rules']);
|
||||||
|
const updateParam = () => {
|
||||||
|
emit('update:form', form);
|
||||||
|
};
|
||||||
const paramObjs = ref<ParamObj[]>([]);
|
const paramObjs = ref<ParamObj[]>([]);
|
||||||
|
|
||||||
const handleParams = () => {
|
const handleParams = () => {
|
||||||
rules = props.rules;
|
rules = props.rules;
|
||||||
form = props.form;
|
|
||||||
if (params.value != undefined && params.value.formFields != undefined) {
|
if (params.value != undefined && params.value.formFields != undefined) {
|
||||||
for (const p of params.value.formFields) {
|
for (const p of params.value.formFields) {
|
||||||
const pObj = p;
|
const pObj = p;
|
||||||
pObj.prop = p.envKey;
|
pObj.prop = p.envKey;
|
||||||
pObj.disabled = p.disabled;
|
pObj.disabled = p.disabled;
|
||||||
form[p.envKey] = p.default;
|
form[p.envKey] = '';
|
||||||
paramObjs.value.push(pObj);
|
paramObjs.value.push(pObj);
|
||||||
console.log(p);
|
|
||||||
if (p.required) {
|
if (p.required) {
|
||||||
if (p.type === 'select') {
|
if (p.type === 'select') {
|
||||||
rules[p.envKey] = [Rules.requiredSelect];
|
rules[p.envKey] = [Rules.requiredSelect];
|
||||||
@ -76,6 +83,8 @@ const handleParams = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit('update:rules', rules);
|
||||||
|
updateParam();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user