diff --git a/agent/app/dto/app.go b/agent/app/dto/app.go index f648ddc3b..6161bee50 100644 --- a/agent/app/dto/app.go +++ b/agent/app/dto/app.go @@ -160,3 +160,9 @@ type DelAppLink struct { Install *model.AppInstall ForceDelete bool } + +type PHPForm struct { + AdditionalProperties struct { + FormFields []interface{} `yaml:"formFields"` + } `yaml:"additionalProperties"` +} diff --git a/agent/app/model/app.go b/agent/app/model/app.go index 071bb5b9d..db670c649 100644 --- a/agent/app/model/app.go +++ b/agent/app/model/app.go @@ -9,26 +9,27 @@ import ( type App struct { BaseModel - Name string `json:"name" gorm:"not null"` - Key string `json:"key" gorm:"not null;"` - ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"` - ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"` - Icon string `json:"icon"` - Type string `json:"type" gorm:"not null"` - Status string `json:"status" gorm:"not null"` - Required string `json:"required"` - CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"` - Limit int `json:"limit" gorm:"not null"` - Website string `json:"website" gorm:"not null"` - Github string `json:"github" gorm:"not null"` - Document string `json:"document" gorm:"not null"` - Recommend int `json:"recommend" gorm:"not null"` - Resource string `json:"resource" gorm:"not null;default:remote"` - ReadMe string `json:"readMe"` - LastModified int `json:"lastModified"` - Architectures string `json:"architectures"` - MemoryRequired int `json:"memoryRequired"` - GpuSupport bool `json:"gpuSupport"` + Name string `json:"name" gorm:"not null"` + Key string `json:"key" gorm:"not null;"` + ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"` + ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"` + Icon string `json:"icon"` + Type string `json:"type" gorm:"not null"` + Status string `json:"status" gorm:"not null"` + Required string `json:"required"` + CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"` + Limit int `json:"limit" gorm:"not null"` + Website string `json:"website" gorm:"not null"` + Github string `json:"github" gorm:"not null"` + Document string `json:"document" gorm:"not null"` + Recommend int `json:"recommend" gorm:"not null"` + Resource string `json:"resource" gorm:"not null;default:remote"` + ReadMe string `json:"readMe"` + LastModified int `json:"lastModified"` + Architectures string `json:"architectures"` + MemoryRequired int `json:"memoryRequired"` + GpuSupport bool `json:"gpuSupport"` + RequiredPanelVersion float64 `json:"requiredPanelVersion"` Details []AppDetail `json:"-" gorm:"-:migration"` TagsKey []string `json:"tags" yaml:"tags" gorm:"-"` diff --git a/agent/app/repo/app.go b/agent/app/repo/app.go index 001a06e34..88946e29f 100644 --- a/agent/app/repo/app.go +++ b/agent/app/repo/app.go @@ -19,6 +19,9 @@ type IAppRepo interface { GetRecommend() DBOption WithResource(resource string) DBOption WithByLikeName(name string) DBOption + WithArch(arch string) DBOption + WithPanelVersion(panelVersion string) DBOption + Page(page, size int, opts ...DBOption) (int64, []model.App, error) GetFirst(opts ...DBOption) (model.App, error) GetBy(opts ...DBOption) ([]model.App, error) @@ -27,7 +30,6 @@ type IAppRepo interface { Create(ctx context.Context, app *model.App) error Save(ctx context.Context, app *model.App) error BatchDelete(ctx context.Context, apps []model.App) error - WithArch(arch string) DBOption } func NewIAppRepo() IAppRepo { @@ -79,6 +81,12 @@ func (a AppRepo) WithArch(arch string) DBOption { } } +func (a AppRepo) WithPanelVersion(panelVersion string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("required_panel_version >= ?", panelVersion) + } +} + func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) { var apps []model.App db := getDb(opts...).Model(&model.App{}) diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 57d217aef..5eb7c1b8c 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -70,6 +70,10 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) { if req.Resource != "" && req.Resource != "all" { opts = append(opts, appRepo.WithResource(req.Resource)) } + if req.Type == "php" { + info, _ := NewISettingService().GetSettingInfo() + opts = append(opts, appRepo.WithPanelVersion(info.SystemVersion)) + } if req.ShowCurrentArch { info, err := NewIDashboardService().LoadOsInfo() if err != nil { @@ -202,23 +206,22 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response. } switch app.Type { case constant.RuntimePHP: - buildPath := filepath.Join(versionPath, "build") - paramsPath := filepath.Join(buildPath, "config.json") + paramsPath := filepath.Join(versionPath, "data.yml") if !fileOp.Stat(paramsPath) { - return appDetailDTO, buserr.New(constant.ErrFileNotExist) + return appDetailDTO, buserr.WithDetail(constant.ErrFileNotExist, paramsPath, nil) } param, err := fileOp.GetContent(paramsPath) if err != nil { return appDetailDTO, err } paramMap := make(map[string]interface{}) - if err := json.Unmarshal(param, ¶mMap); err != nil { + if err = yaml.Unmarshal(param, ¶mMap); err != nil { return appDetailDTO, err } - appDetailDTO.Params = paramMap - composePath := filepath.Join(buildPath, "docker-compose.yml") + appDetailDTO.Params = paramMap["additionalProperties"] + composePath := filepath.Join(versionPath, "docker-compose.yml") if !fileOp.Stat(composePath) { - return appDetailDTO, buserr.New(constant.ErrFileNotExist) + return appDetailDTO, buserr.WithDetail(constant.ErrFileNotExist, composePath, nil) } compose, err := fileOp.GetContent(composePath) if err != nil { diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 0141506d9..13c221581 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -716,6 +716,18 @@ func upgradeInstall(req request.AppInstallUpgrade) error { if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { return buserr.WithErr(constant.ErrUpdateBuWebsite, err) } + t.Log(i18n.GetMsgByKey("DeleteRuntimePHP")) + _ = fileOp.DeleteDir(path.Join(constant.RuntimeDir, "php")) + websites, _ := websiteRepo.List(commonRepo.WithByType("runtime")) + for _, website := range websites { + runtime, _ := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID)) + if runtime != nil && runtime.Type == "php" { + website.Type = constant.Static + website.RuntimeID = 0 + _ = websiteRepo.SaveWithoutCtx(&website) + } + } + _ = runtimeRepo.DeleteBy(commonRepo.WithByType("php")) t.Log(i18n.GetMsgByKey("MoveSiteDirSuccess")) } @@ -1153,6 +1165,7 @@ func getApps(oldApps []model.App, items []dto.AppDefine, systemVersion string, t if !ok { app = model.App{} } + app.RequiredPanelVersion = config.Version app.Resource = constant.AppResourceRemote app.Name = item.Name app.Limit = config.Limit diff --git a/agent/app/service/container.go b/agent/app/service/container.go index 9d7fefc9e..aac779099 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -699,7 +699,7 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType, commandName := "docker" commandArg := []string{"logs", container} if containerType == "compose" { - commandName = "docker-compose" + commandName = "docker compose" commandArg = []string{"-f", container, "logs"} } if tail != "0" { @@ -782,7 +782,7 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since commandName := "docker" commandArg := []string{"logs", container} if containerType == "compose" { - commandName = "docker-compose" + commandName = "docker compose" commandArg = []string{"-f", container, "logs"} } if tail != "0" { diff --git a/agent/app/service/container_compose.go b/agent/app/service/container_compose.go index ca48ff6f3..d76dc14f3 100644 --- a/agent/app/service/container_compose.go +++ b/agent/app/service/container_compose.go @@ -141,7 +141,7 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { if err := u.loadPath(&req); err != nil { return false, err } - cmd := exec.Command("docker-compose", "-f", req.Path, "config") + cmd := exec.Command("docker compose", "-f", req.Path, "config") stdout, err := cmd.CombinedOutput() if err != nil { return false, errors.New(string(stdout)) diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go index ae4730f11..a6cfaa0f6 100644 --- a/agent/app/service/runtime.go +++ b/agent/app/service/runtime.go @@ -83,6 +83,10 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e if exist != nil { return nil, buserr.New(constant.ErrImageExist) } + portValue, _ := create.Params["PANEL_APP_PORT_HTTP"] + if err := checkPortExist(int(portValue.(float64))); err != nil { + return nil, err + } case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: if !fileOp.Stat(create.CodeDir) { return nil, buserr.New(constant.ErrPathNotFound) @@ -96,10 +100,10 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e return nil, err } } - if containerName, ok := create.Params["CONTAINER_NAME"]; ok { - if err := checkContainerName(containerName.(string)); err != nil { - return nil, err - } + } + if containerName, ok := create.Params["CONTAINER_NAME"]; ok { + if err := checkContainerName(containerName.(string)); err != nil { + return nil, err } } @@ -130,6 +134,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e switch create.Type { case constant.RuntimePHP: + runtime.Port = int(create.Params["PANEL_APP_PORT_HTTP"].(float64)) if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil { return nil, err } @@ -263,6 +268,19 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) { if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok { res.Source = v } + res.Params = make(map[string]interface{}) + for k, v := range envs { + if k == "PANEL_APP_PORT_HTTP" { + port, err := strconv.Atoi(v) + if err != nil { + return nil, err + } + res.Params[k] = port + continue + } + res.Params[k] = v + } + for _, form := range appForm.FormFields { if v, ok := envs[form.EnvKey]; ok { appParam := response.AppParam{ @@ -373,18 +391,6 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { return err } } - if containerName, ok := req.Params["CONTAINER_NAME"]; ok { - envs, err := gotenv.Unmarshal(runtime.Env) - if err != nil { - return err - } - oldContainerName := envs["CONTAINER_NAME"] - if containerName != oldContainerName { - if err := checkContainerName(containerName.(string)); err != nil { - return err - } - } - } appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID)) if err != nil { @@ -405,6 +411,19 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { } } + if containerName, ok := req.Params["CONTAINER_NAME"]; ok { + envs, err := gotenv.Unmarshal(runtime.Env) + if err != nil { + return err + } + oldContainerName := envs["CONTAINER_NAME"] + if containerName != oldContainerName { + if err := checkContainerName(containerName.(string)); err != nil { + return err + } + } + } + projectDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name) create := request.RuntimeCreate{ Image: req.Image, diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index 710bdbd47..854dd8f45 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -2,7 +2,21 @@ package service import ( "bytes" + "encoding/json" "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/utils/compose" + "github.com/1Panel-dev/1Panel/agent/utils/docker" + "github.com/1Panel-dev/1Panel/agent/utils/files" + httpUtil "github.com/1Panel-dev/1Panel/agent/utils/http" + "github.com/pkg/errors" + "github.com/subosito/gotenv" + "gopkg.in/yaml.v3" "io" "net/http" "os" @@ -10,19 +24,6 @@ import ( "path" "path/filepath" "strings" - "time" - - "github.com/1Panel-dev/1Panel/agent/app/dto/request" - "github.com/1Panel-dev/1Panel/agent/app/model" - "github.com/1Panel-dev/1Panel/agent/buserr" - "github.com/1Panel-dev/1Panel/agent/constant" - "github.com/1Panel-dev/1Panel/agent/global" - "github.com/1Panel-dev/1Panel/agent/utils/docker" - "github.com/1Panel-dev/1Panel/agent/utils/files" - httpUtil "github.com/1Panel-dev/1Panel/agent/utils/http" - "github.com/pkg/errors" - "github.com/subosito/gotenv" - "gopkg.in/yaml.v3" ) func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) { @@ -66,30 +67,16 @@ func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fil } func handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) { - 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 { + if err = fileOp.CopyDirWithNewName(appVersionDir, runtimeDir, create.Name); err != nil { return } - oldDir := path.Join(tempDir, "build") projectDir := path.Join(runtimeDir, create.Name) defer func() { if err != nil { _ = fileOp.DeleteDir(projectDir) } }() - if oldDir != projectDir { - if err = fileOp.Rename(oldDir, projectDir); err != nil { - return - } - if err = fileOp.DeleteDir(tempDir); err != nil { - return - } - } composeContent, envContent, forms, err := handleParams(create, projectDir) if err != nil { return @@ -140,9 +127,9 @@ func reCreateRuntime(runtime *model.Runtime) { } func runComposeCmdWithLog(operate string, composePath string, logPath string) error { - cmd := exec.Command("docker-compose", "-f", composePath, operate) + cmd := exec.Command("docker compose", "-f", composePath, operate) if operate == "up" { - cmd = exec.Command("docker-compose", "-f", composePath, operate, "-d") + cmd = exec.Command("docker compose", "-f", composePath, operate, "-d") } logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { @@ -218,7 +205,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) { _ = logFile.Close() }() - cmd := exec.Command("docker-compose", "-f", composePath, "build") + cmd := exec.Command("docker", "compose", "-f", composePath, "build") multiWriterStdout := io.MultiWriter(os.Stdout, logFile) cmd.Stdout = multiWriterStdout var stderrBuf bytes.Buffer @@ -229,8 +216,10 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) { if err != nil { runtime.Status = constant.RuntimeError runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + stderrBuf.String() + if stderrBuf.String() == "" { + runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error() + } } else { - runtime.Status = constant.RuntimeNormal runtime.Message = "" if oldImageID != "" { client, err := docker.NewClient() @@ -272,6 +261,14 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) { } } } + runtime.Status = constant.RuntimeStarting + _ = runtimeRepo.Save(runtime) + if out, err := compose.Up(composePath); err != nil { + runtime.Status = constant.RuntimeStartErr + runtime.Message = out + } else { + runtime.Status = constant.RuntimeRunning + } } _ = runtimeRepo.Save(runtime) } @@ -293,7 +290,20 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte switch create.Type { case constant.RuntimePHP: create.Params["IMAGE_NAME"] = create.Image - forms, err = fileOp.GetContent(path.Join(projectDir, "config.json")) + var fromYml []byte + fromYml, err = fileOp.GetContent(path.Join(projectDir, "data.yml")) + if err != nil { + return + } + var data dto.PHPForm + err = yaml.Unmarshal(fromYml, &data) + if err != nil { + return + } + formFields := data.AdditionalProperties.FormFields + forms, err = json.MarshalIndent(map[string]interface{}{ + "formFields": formFields, + }, "", " ") if err != nil { return } @@ -307,6 +317,8 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte } } create.Params["CONTAINER_PACKAGE_URL"] = create.Source + siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR")) + create.Params["PANEL_WEBSITE_DIR"] = siteDir.Value case constant.RuntimeNode: create.Params["CODE_DIR"] = create.CodeDir create.Params["NODE_VERSION"] = create.Version diff --git a/agent/app/service/snapshot_recover.go b/agent/app/service/snapshot_recover.go index f125c6e75..7397a8a3a 100644 --- a/agent/app/service/snapshot_recover.go +++ b/agent/app/service/snapshot_recover.go @@ -251,7 +251,7 @@ func restartCompose(composePath string) { if _, err := os.Stat(pathItem); err != nil { continue } - upCmd := fmt.Sprintf("docker-compose -f %s up -d", pathItem) + upCmd := fmt.Sprintf("docker compose -f %s up -d", pathItem) stdout, err := cmd.Exec(upCmd) if err != nil { global.LOG.Debugf("%s failed, err: %v", upCmd, stdout) diff --git a/agent/app/service/website.go b/agent/app/service/website.go index 4b277249e..c24a2da9d 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -162,13 +162,11 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons appName = appInstall.Name appInstallID = appInstall.ID case constant.Runtime: - runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(web.RuntimeID)) - if err != nil { - return 0, nil, err + runtime, _ := runtimeRepo.GetFirst(commonRepo.WithByID(web.RuntimeID)) + if runtime != nil { + runtimeName = runtime.Name + runtimeType = runtime.Type } - runtimeName = runtime.Name - runtimeType = runtime.Type - appInstallID = runtime.ID } sitePath := GetSitePath(web, SiteDir) @@ -375,24 +373,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) switch runtime.Type { case constant.RuntimePHP: if runtime.Resource == constant.ResourceAppstore { - var ( - req request.AppInstallCreate - install *model.AppInstall - ) - reg, _ := regexp.Compile(`[^a-z0-9_-]+`) - req.Name = reg.ReplaceAllString(strings.ToLower(alias), "") - req.AppDetailId = create.AppInstall.AppDetailId - req.Params = create.AppInstall.Params - req.Params["IMAGE_NAME"] = runtime.Image - req.AppContainerConfig = create.AppInstall.AppContainerConfig - req.Params["PANEL_WEBSITE_DIR"] = path.Join(nginxInstall.GetPath(), "/www") - install, err = NewIAppService().Install(req) - if err != nil { - return err - } - website.AppInstallID = install.ID - appInstall = install - website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort) + website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port) } else { website.ProxyType = create.ProxyType if website.ProxyType == constant.RuntimeProxyUnix { diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go index 7bd1adf14..3ef5c209b 100644 --- a/agent/app/service/website_utils.go +++ b/agent/app/service/website_utils.go @@ -42,15 +42,11 @@ func handleChineseDomain(domain string) (string, error) { } func createIndexFile(website *model.Website, runtime *model.Runtime) error { - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } var ( indexPath string indexContent string websiteService = NewIWebsiteService() - indexFolder = path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index") + indexFolder = GetSitePath(*website, SiteIndexDir) ) switch website.Type { @@ -132,9 +128,8 @@ func createProxyFile(website *model.Website) error { return nil } -func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website, runtime *model.Runtime) error { - nginxFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name) - siteFolder := path.Join(nginxFolder, "www", "sites", website.Alias) +func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error { + siteFolder := GteSiteDir(website.Alias) fileOp := files.NewFileOp() if !fileOp.Stat(siteFolder) { if err := fileOp.CreateDir(siteFolder, 0755); err != nil { @@ -185,11 +180,10 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a if err != nil { return err } - if err = createWebsiteFolder(nginxInstall, website, runtime); err != nil { + if err = createWebsiteFolder(website, runtime); err != nil { return err } - nginxFileName := website.Alias + ".conf" - configPath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "conf", "conf.d", nginxFileName) + configPath := GetSitePath(*website, SiteConf) nginxContent := string(nginx_conf.WebsiteDefault) config, err := parser.NewStringParser(nginxContent).Parse() if err != nil { @@ -232,7 +226,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a server.UpdateDirective("error_page", []string{"404", "/404.html"}) if runtime.Resource == constant.ResourceLocal { server.UpdateRoot(rootIndex) - localPath := path.Join(nginxInstall.GetPath(), rootIndex, "index.php") + localPath := path.Join(GetSitePath(*website, SiteIndexDir), "index.php") server.UpdatePHPProxy([]string{website.Proxy}, localPath) } else { server.UpdateRoot(rootIndex) @@ -381,6 +375,21 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain) erro } func delNginxConfig(website model.Website, force bool) error { + configPath := GetSitePath(website, SiteConf) + fileOp := files.NewFileOp() + + if !fileOp.Stat(configPath) { + return nil + } + if err := fileOp.DeleteFile(configPath); err != nil { + return err + } + sitePath := GteSiteDir(website.Alias) + if fileOp.Stat(sitePath) { + xpack.RemoveTamper(website.Alias) + _ = fileOp.DeleteDir(sitePath) + } + nginxApp, err := appRepo.GetFirst(appRepo.WithKey(constant.AppOpenresty)) if err != nil { return err @@ -392,23 +401,6 @@ func delNginxConfig(website model.Website, force bool) error { } return err } - - nginxFileName := website.Alias + ".conf" - configPath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "conf", "conf.d", nginxFileName) - fileOp := files.NewFileOp() - - if !fileOp.Stat(configPath) { - return nil - } - if err := fileOp.DeleteFile(configPath); err != nil { - return err - } - sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias) - if fileOp.Stat(sitePath) { - xpack.RemoveTamper(website.Alias) - _ = fileOp.DeleteDir(sitePath) - } - if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { if force { return nil diff --git a/agent/constant/runtime.go b/agent/constant/runtime.go index 55a524983..f2aa70e8a 100644 --- a/agent/constant/runtime.go +++ b/agent/constant/runtime.go @@ -13,6 +13,7 @@ const ( RuntimeStopped = "stopped" RuntimeUnhealthy = "unhealthy" RuntimeCreating = "creating" + RuntimeStartErr = "startErr" RuntimePHP = "php" RuntimeNode = "node" diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index ce4b2098b..fa702a9af 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -70,7 +70,7 @@ MoveSiteDir: "The current upgrade requires migrating the OpenResty website direc MoveSiteToDir: "Migrate the website directory to {{ .name }}" ErrMoveSiteDir: "Failed to migrate the website directory" MoveSiteDirSuccess: "Successfully migrated the website directory" - +DeleteRuntimePHP: "Delete PHP runtime environment" #file ErrFileCanNotRead: "File can not read" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 165694457..e0e437c6f 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -71,7 +71,7 @@ MoveSiteDir: "當前升級需要遷移 OpenResty 網站目錄" MoveSiteToDir: "遷移網站目錄到 {{ .name }}" ErrMoveSiteDir: "遷移網站目錄失敗" MoveSiteDirSuccess: "遷移網站目錄成功" - +DeleteRuntimePHP: "刪除運行環境 PHP 版本" #file ErrFileCanNotRead: "此文件不支持預覽" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 6ed9adaf1..8a2dd7ddc 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -70,6 +70,7 @@ MoveSiteDir: "当前升级需要迁移 OpenResty 网站目录" MoveSiteToDir: "迁移网站目录到 {{ .name }}" ErrMoveSiteDir: "迁移网站目录失败" MoveSiteDirSuccess: "迁移网站目录成功" +DeleteRuntimePHP: "删除 PHP 运行环境" #file ErrFileCanNotRead: "此文件不支持预览" diff --git a/agent/init/business/business.go b/agent/init/business/business.go index 663036fc4..fb39efbff 100644 --- a/agent/init/business/business.go +++ b/agent/init/business/business.go @@ -7,7 +7,8 @@ import ( ) func Init() { - go syncApp() + //TODO 国际化处理 + //go syncApp() go syncInstalledApp() go syncRuntime() go syncSSL() diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index 54f693f19..36d24b8e5 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -15,12 +15,6 @@ func Init() { migrations.InitImageRepo, migrations.InitDefaultCA, migrations.InitPHPExtensions, - migrations.AddTask, - migrations.UpdateWebsite, - migrations.UpdateWebsiteDomain, - migrations.UpdateApp, - migrations.AddTaskDB, - migrations.UpdateAppInstall, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 2d7d27dd7..3537f183f 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -16,7 +16,7 @@ import ( ) var AddTable = &gormigrate.Migration{ - ID: "20240826-add-table", + ID: "20240902-add-table", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate( &model.AppDetail{}, @@ -54,6 +54,7 @@ var AddTable = &gormigrate.Migration{ &model.WebsiteDnsAccount{}, &model.WebsiteDomain{}, &model.WebsiteSSL{}, + &model.Task{}, ) }, } @@ -210,52 +211,3 @@ var InitPHPExtensions = &gormigrate.Migration{ return nil }, } - -var AddTask = &gormigrate.Migration{ - ID: "20240802-add-task", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.Task{}) - }, -} - -var UpdateWebsite = &gormigrate.Migration{ - ID: "20240812-update-website", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.Website{}) - }, -} - -var UpdateWebsiteDomain = &gormigrate.Migration{ - ID: "20240808-update-website-domain", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.WebsiteDomain{}) - }, -} - -var AddTaskDB = &gormigrate.Migration{ - ID: "20240822-add-task-table", - Migrate: func(tx *gorm.DB) error { - return global.TaskDB.AutoMigrate( - &model.Task{}, - ) - }, -} - -var UpdateApp = &gormigrate.Migration{ - ID: "20240826-update-app", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.App{}) - }, -} - -var UpdateAppInstall = &gormigrate.Migration{ - ID: "20240828-update-app-install", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.AppInstall{}) - }, -} diff --git a/agent/server/server.go b/agent/server/server.go index 4139a89c6..b44535306 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -43,12 +43,14 @@ func Start() { server := &http.Server{ Handler: rootRouter, } + if global.IsMaster { _ = os.Remove("/tmp/agent.sock") listener, err := net.Listen("unix", "/tmp/agent.sock") if err != nil { panic(err) } + business.Init() _ = server.Serve(listener) return } else { @@ -73,8 +75,8 @@ func Start() { Certificates: []tls.Certificate{tlsCert}, ClientAuth: tls.RequireAnyClientCert, } - global.LOG.Info("listen at https://0.0.0.0:9999") business.Init() + global.LOG.Info("listen at https://0.0.0.0:9999") if err := server.ListenAndServeTLS("", ""); err != nil { panic(err) } diff --git a/agent/utils/compose/compose.go b/agent/utils/compose/compose.go index 3480a0d97..7d7a53546 100644 --- a/agent/utils/compose/compose.go +++ b/agent/utils/compose/compose.go @@ -5,36 +5,36 @@ import ( ) func Pull(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s pull", filePath) + stdout, err := cmd.Execf("docker compose -f %s pull", filePath) return stdout, err } func Up(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath) + stdout, err := cmd.Execf("docker compose -f %s up -d", filePath) return stdout, err } func Down(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s down --remove-orphans", filePath) + stdout, err := cmd.Execf("docker compose -f %s down --remove-orphans", filePath) return stdout, err } func Start(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s start", filePath) + stdout, err := cmd.Execf("docker compose -f %s start", filePath) return stdout, err } func Stop(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s stop", filePath) + stdout, err := cmd.Execf("docker compose -f %s stop", filePath) return stdout, err } func Restart(filePath string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s restart", filePath) + stdout, err := cmd.Execf("docker compose -f %s restart", filePath) return stdout, err } func Operate(filePath, operation string) (string, error) { - stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation) + stdout, err := cmd.Execf("docker compose -f %s %s", filePath, operation) return stdout, err } diff --git a/agent/utils/files/file_op.go b/agent/utils/files/file_op.go index 8fe90bc6e..4248f72ce 100644 --- a/agent/utils/files/file_op.go +++ b/agent/utils/files/file_op.go @@ -438,6 +438,12 @@ func (f FileOp) CopyAndReName(src, dst, name string, cover bool) error { } } +func (f FileOp) CopyDirWithNewName(src, dst, newName string) error { + dstDir := filepath.Join(dst, newName) + str := fmt.Sprintf(`cp -rf '%s' '%s'`, src, dstDir) + return cmd.ExecCmd(str) +} + func (f FileOp) CopyDir(src, dst string) error { srcInfo, err := f.Fs.Stat(src) if err != nil { diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 1328ff818..81ff75521 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -77,6 +77,7 @@ export namespace App { child?: FromFieldChild; params?: FromParam[]; multiple?: boolean; + allowCreate?: boolean; } export interface FromFieldChild extends FromField { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 0e7f070b2..efb810ba1 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2388,7 +2388,7 @@ const message = { version: 'Version', versionHelper: 'PHP version, e.g. v8.0', buildHelper: - 'The more extensions you select, the more CPU will be occupied during the image making process, so avoid selecting all extensions,If there is no extension you want, you can manually enter it and select it', + 'The more extensions you select, the more CPU will be used during the image creation process. You can install extensions after the environment is created.', openrestyWarn: 'PHP needs to be upgraded to OpenResty to version 1.21.4.1 or later to use', toupgrade: 'To Upgrade', edit: 'Edit runtime', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index abd598290..511dfab1d 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -2217,7 +2217,7 @@ const message = { localHelper: '本地運行環境需要自行安裝', version: '版本', versionHelper: 'PHP的版本,例 v8.0', - buildHelper: '選擇的擴展越多,製作鏡像過程中占用 CPU 越多,請盡量避免選擇全部擴展', + buildHelper: '選擇的擴展越多,製作鏡像過程中占用 CPU 越多,可以在創建完環境之後再安裝擴展', openrestyWarn: 'PHP 需要升級 OpenResty 至 1.21.4.1 版本以上才能使用', toupgrade: '去升級', edit: '編輯運行環境', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index af9184d47..03a1c0289 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2219,7 +2219,7 @@ const message = { localHelper: '本地运行环境需要自行安装', version: '版本', versionHelper: 'PHP的版本,例 v8.0', - buildHelper: '选择的扩展越多,制作镜像过程中占用 CPU 越多,请尽量避免选择全部扩展', + buildHelper: '选择的扩展越多,制作镜像过程中占用 CPU 越多,可以在创建完环境之后再安装扩展', openrestyWarn: 'PHP 需要升级 OpenResty 至 1.21.4.1 版本以上才能使用', toupgrade: '去升级', edit: '编辑运行环境', diff --git a/frontend/src/views/app-store/detail/params/index.vue b/frontend/src/views/app-store/detail/params/index.vue index 1b796a854..609daae6c 100644 --- a/frontend/src/views/app-store/detail/params/index.vue +++ b/frontend/src/views/app-store/detail/params/index.vue @@ -42,7 +42,13 @@ {{ $t('app.toInstall') }} - +
-
- - - - - - - - - {{ $t('runtime.phpsourceHelper') }} - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- + + {{ $t('runtime.rebuild') }} + - {{ $t('runtime.extendHelper') }} - - {{ $t('php.toExtensionsList') }} - + {{ $t('runtime.rebuildHelper') }}
-
- - - {{ $t('runtime.rebuild') }} - - - - - {{ $t('runtime.rebuildHelper') }} -
-
-
-
@@ -169,8 +202,6 @@ 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; @@ -186,7 +217,6 @@ const loading = ref(false); const initParam = ref(false); const mode = ref('create'); const appParams = ref(); -const editParams = ref(); const appVersions = ref([]); const phpExtensions = ref([]); const appReq = reactive({ @@ -205,6 +235,7 @@ const initData = (type: string) => ({ source: 'mirrors.ustc.edu.cn', }); const extensions = ref(); +const formFields = ref(); let runtime = reactive(initData('php')); @@ -215,38 +246,22 @@ const rules = ref({ version: [Rules.requiredInput, Rules.paramCommon], image: [Rules.requiredInput, Rules.imageName], source: [Rules.requiredSelect], + params: { + PANEL_APP_PORT_HTTP: [Rules.requiredInput, Rules.port], + PHP_VERSION: [Rules.requiredSelect], + CONTAINER_PACKAGE_URL: [Rules.requiredSelect], + CONTAINER_NAME: [Rules.containerName, Rules.requiredInput], + }, }); -const phpSources = [ - { - label: i18n.global.t('runtime.ustc'), - value: 'mirrors.ustc.edu.cn', - }, - { - label: i18n.global.t('runtime.netease'), - value: 'mirrors.163.com', - }, - { - label: i18n.global.t('runtime.aliyun'), - value: 'mirrors.aliyun.com', - }, - { - label: i18n.global.t('runtime.tsinghua'), - value: 'mirrors.tuna.tsinghua.edu.cn', - }, - { - label: i18n.global.t('runtime.xtomhk'), - value: 'mirrors.xtom.com.hk', - }, - { - label: i18n.global.t('runtime.xtom'), - value: 'mirrors.xtom.com', - }, - { - label: i18n.global.t('commons.table.default'), - value: 'dl-cdn.alpinelinux.org', - }, -]; +const getLabel = (row: App.FromField): string => { + const language = localStorage.getItem('lang') || 'zh'; + if (language == 'zh' || language == 'tw') { + return row.labelZh; + } else { + return row.labelEn; + } +}; const em = defineEmits(['close', 'submit']); @@ -296,6 +311,10 @@ const changeApp = (appId: number) => { } }; +const changePHPVersion = (version: string) => { + runtime.image = 'php:' + version; +}; + const changeVersion = () => { loading.value = true; initParam.value = false; @@ -305,6 +324,15 @@ const changeVersion = () => { runtime.appDetailID = res.data.id; runtime.image = res.data.image + ':' + runtime.version; appParams.value = res.data.params; + const fileds = res.data.params.formFields; + formFields.value = {}; + for (const index in fileds) { + formFields.value[fileds[index]['envKey']] = fileds[index]; + runtime.params[fileds[index]['envKey']] = fileds[index]['default']; + if (fileds[index]['envKey'] == 'PHP_VERSION') { + runtime.image = 'php:' + fileds[index]['default']; + } + } initParam.value = true; }) .finally(() => { @@ -367,7 +395,7 @@ const getRuntime = async (id: number) => { name: data.name, appDetailID: data.appDetailID, image: data.image, - params: {}, + params: data.params, type: data.type, resource: data.resource, appID: data.appID, @@ -375,12 +403,15 @@ const getRuntime = async (id: number) => { rebuild: true, source: data.source, }); - editParams.value = data.appParams; - if (mode.value == 'create') { - searchApp(data.appID); - } else { - initParam.value = true; + + const fileds = data.appParams; + const forms = {}; + for (const index in fileds) { + forms[fileds[index].key] = fileds[index]; } + formFields.value = forms; + runtime.params['PHP_EXTENSIONS'] = runtime.params['PHP_EXTENSIONS'].split(','); + initParam.value = true; } catch (error) {} }; diff --git a/frontend/src/views/website/runtime/php/index.vue b/frontend/src/views/website/runtime/php/index.vue index 0359c64c9..4416ed133 100644 --- a/frontend/src/views/website/runtime/php/index.vue +++ b/frontend/src/views/website/runtime/php/index.vue @@ -50,8 +50,15 @@ {{ $t('runtime.' + toLowerCase(row.resource)) }} - + + + + + + - -