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" "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" "time" ) 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 { return &RuntimeService{} } func (r *RuntimeService) Create(create request.RuntimeCreate) error { if create.Resource == constant.ResourceLocal { runtime := &model.Runtime{ Name: create.Name, Resource: create.Resource, Type: create.Type, Status: constant.RuntimeNormal, } return runtimeRepo.Create(context.Background(), runtime) } var err error 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() buildDir := path.Join(constant.AppResourceDir, app.Key, "versions", appDetail.Version, "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 err } 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 err } if err := fileOp.DeleteDir(tempDir); err != nil { return err } } composeFile, err := fileOp.GetContent(path.Join(newNameDir, "docker-compose.yml")) if err != nil { return err } env, err := gotenv.Read(path.Join(newNameDir, ".env")) if err != nil { return err } 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{ Name: create.Name, DockerCompose: string(composeFile), Env: envStr, AppDetailID: create.AppDetailID, Type: create.Type, Image: create.Image, Resource: create.Resource, Status: constant.RuntimeBuildIng, Version: create.Version, } 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) { var ( opts []repo.DBOption res []response.RuntimeRes ) if req.Name != "" { opts = append(opts, commonRepo.WithLikeName(req.Name)) } 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 } //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)) }