1Panel/backend/app/service/runtime.go

278 lines
7.4 KiB
Go

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/global"
"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"
)
type RuntimeService struct {
}
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) (err error) {
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name))
if exist != nil {
return buserr.New(constant.ErrNameIsExist)
}
if create.Resource == constant.ResourceLocal {
runtime := &model.Runtime{
Name: create.Name,
Resource: create.Resource,
Type: create.Type,
Version: create.Version,
Status: constant.RuntimeNormal,
}
return runtimeRepo.Create(context.Background(), runtime)
}
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
if exist != nil {
return buserr.New(constant.ErrImageExist)
}
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
if err != nil {
return err
}
app, err := appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil {
return err
}
fileOp := files.NewFileOp()
appVersionDir := path.Join(constant.AppResourceDir, app.Resource, app.Key, appDetail.Version)
if !fileOp.Stat(appVersionDir) || appDetail.Update {
if err := downloadApp(app, appDetail, nil); err != nil {
return err
}
}
buildDir := path.Join(appVersionDir, "build")
if !fileOp.Stat(buildDir) {
return buserr.New(constant.ErrDirNotFound)
}
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
}
oldDir := path.Join(tempDir, "build")
newNameDir := path.Join(runtimeDir, create.Name)
defer func() {
if err != nil {
_ = fileOp.DeleteDir(newNameDir)
}
}()
if oldDir != newNameDir {
if err = fileOp.Rename(oldDir, newNameDir); err != nil {
return
}
if err = fileOp.DeleteDir(tempDir); err != nil {
return
}
}
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Params)
if err != nil {
return
}
runtime := &model.Runtime{
Name: create.Name,
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
}
go buildRuntime(runtime, "")
return
}
func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeRes, error) {
var (
opts []repo.DBOption
res []response.RuntimeRes
)
if req.Name != "" {
opts = append(opts, commonRepo.WithLikeName(req.Name))
}
if req.Status != "" {
opts = append(opts, runtimeRepo.WithStatus(req.Status))
}
total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)
if err != nil {
return 0, nil, err
}
for _, runtime := range runtimes {
res = append(res, response.RuntimeRes{
Runtime: runtime,
})
}
return total, res, nil
}
func (r *RuntimeService) Delete(id uint) error {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return err
}
website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(id))
if website.ID > 0 {
return buserr.New(constant.ErrDelWithWebsite)
}
if runtime.Resource == constant.ResourceAppstore {
client, err := docker.NewClient()
if err != nil {
return err
}
imageID, err := client.GetImageIDByName(runtime.Image)
if err != nil {
return err
}
if imageID != "" {
if err := client.DeleteImage(imageID); err != nil {
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
}
}
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))
}
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
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 {
if v == "" {
appParam.Value = []string{}
} else {
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 {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
oldImage := runtime.Image
if runtime.Resource == constant.ResourceLocal {
runtime.Version = req.Version
return runtimeRepo.Save(runtime)
}
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
if exist != nil {
return buserr.New(constant.ErrImageExist)
}
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
}
if err != nil {
return err
}
runtime.Image = req.Image
runtime.Env = string(envContent)
runtime.DockerCompose = string(composeContent)
runtime.Status = constant.RuntimeBuildIng
_ = runtimeRepo.Save(runtime)
client, err := docker.NewClient()
if err != nil {
return err
}
imageID, err := client.GetImageIDByName(oldImage)
if err != nil {
return err
}
go buildRuntime(runtime, imageID)
return nil
}