From d20d5946f258dd435b1e3e41f20b7308594ba458 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 18 May 2023 16:48:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E9=80=BB=E8=BE=91=20(#1075)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/dto/request/app.go | 11 +- backend/app/dto/response/app.go | 9 +- backend/app/service/app.go | 66 +--- backend/app/service/app_install.go | 104 +++--- backend/app/service/app_utils.go | 297 +++++++++++++++--- backend/constant/app.go | 2 + backend/utils/compose/compose.go | 12 +- backend/utils/files/file_op.go | 6 +- frontend/src/components/status/index.vue | 7 +- frontend/src/lang/modules/en.ts | 3 + frontend/src/lang/modules/zh.ts | 3 + frontend/src/views/app-store/index.scss | 3 + .../app-store/installed/detail/index.vue | 2 +- .../src/views/app-store/installed/index.vue | 44 ++- .../app-store/installed/upgrade/index.vue | 2 +- 15 files changed, 370 insertions(+), 201 deletions(-) diff --git a/backend/app/dto/request/app.go b/backend/app/dto/request/app.go index cf6eb951b..ee3cabd30 100644 --- a/backend/app/dto/request/app.go +++ b/backend/app/dto/request/app.go @@ -59,14 +59,9 @@ type AppInstalledOperate struct { } type AppInstalledUpdate struct { - InstallId uint `json:"installId" validate:"required"` - Params map[string]interface{} `json:"params" validate:"required"` - Advanced bool `json:"advanced"` - CpuQuota float64 `json:"cpuQuota"` - MemoryLimit float64 `json:"memoryLimit"` - MemoryUnit string `json:"memoryUnit"` - ContainerName string `json:"containerName"` - AllowPort bool `json:"allowPort"` + InstallId uint `json:"installId" validate:"required"` + Params map[string]interface{} `json:"params" validate:"required"` + AppContainerConfig } type PortUpdate struct { diff --git a/backend/app/dto/response/app.go b/backend/app/dto/response/app.go index ed0f2e053..a848cb6a1 100644 --- a/backend/app/dto/response/app.go +++ b/backend/app/dto/response/app.go @@ -2,6 +2,7 @@ package response import ( "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/app/dto/request" "time" "github.com/1Panel-dev/1Panel/backend/app/model" @@ -84,10 +85,6 @@ type AppParam struct { } type AppConfig struct { - Params []AppParam `json:"params"` - CpuQuota float64 `json:"cpuQuota"` - MemoryLimit float64 `json:"memoryLimit"` - MemoryUnit string `json:"memoryUnit"` - ContainerName string `json:"containerName"` - AllowPort bool `json:"allowPort"` + Params []AppParam `json:"params"` + request.AppContainerConfig } diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 2fac73955..8c3ee7915 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -5,25 +5,23 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/1Panel-dev/1Panel/backend/buserr" - "github.com/1Panel-dev/1Panel/backend/utils/docker" - "io" - "net/http" - "os" - "path" - "strconv" - "strings" - "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/common" + "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/files" "gopkg.in/yaml.v3" + "io" + "net/http" + "os" + "path" + "strconv" ) type AppService struct { @@ -301,50 +299,11 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) ( servicesMap[v] = servicesMap[k] delete(servicesMap, k) } - serviceValue := servicesMap[appInstall.ServiceName].(map[string]interface{}) - if req.Advanced && (req.CpuQuota > 0 || req.MemoryLimit > 0) { - deploy := map[string]interface{}{ - "resources": map[string]interface{}{ - "limits": map[string]interface{}{ - "cpus": "${CPUS}", - "memory": "${MEMORY_LIMIT}", - }, - }, - } - req.Params[constant.CPUS] = "0" - if req.CpuQuota > 0 { - req.Params[constant.CPUS] = req.CpuQuota - } - req.Params[constant.MemoryLimit] = "0" - if req.MemoryLimit > 0 { - req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit - } - serviceValue["deploy"] = deploy - } - ports, ok := serviceValue["ports"].([]interface{}) - if ok { - allowHost := "127.0.0.1" - if req.AllowPort { - allowHost = "0.0.0.0" - } - req.Params[constant.HostIP] = allowHost - for i, port := range ports { - portStr, portOK := port.(string) - if !portOK { - continue - } - portArray := strings.Split(portStr, ":") - if len(portArray) == 2 { - portArray = append([]string{"${HOST_IP}"}, portArray...) - } - ports[i] = strings.Join(portArray, ":") - } - serviceValue["ports"] = ports + if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil { + return } - servicesMap[appInstall.ServiceName] = serviceValue - var ( composeByte []byte paramByte []byte @@ -379,14 +338,17 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) ( return } go func() { - if err = downloadApp(app, appDetail, appInstall, req); err != nil { + if err = copyData(app, appDetail, appInstall, req); err != nil { if appInstall.Status == constant.Installing { appInstall.Status = constant.Error appInstall.Message = err.Error() } - _ = appInstallRepo.Save(ctx, appInstall) + _ = appInstallRepo.Save(context.Background(), appInstall) return } + go func() { + _, _ = http.Get(appDetail.DownloadCallBackUrl) + }() upApp(appInstall) }() go updateToolApp(appInstall) diff --git a/backend/app/service/app_install.go b/backend/app/service/app_install.go index f59636829..262ad4679 100644 --- a/backend/app/service/app_install.go +++ b/backend/app/service/app_install.go @@ -5,11 +5,11 @@ import ( "encoding/json" "fmt" "github.com/1Panel-dev/1Panel/backend/utils/files" + "gopkg.in/yaml.v3" "math" "os" "path" "reflect" - "regexp" "strconv" "strings" @@ -262,23 +262,17 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { } } + backupDockerCompose := installed.DockerCompose if req.Advanced { - if req.ContainerName != "" { - req.Params[constant.ContainerName] = req.ContainerName + composeMap := make(map[string]interface{}) + if err := addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil { + return err } - req.Params[constant.CPUS] = "0" - if req.CpuQuota > 0 { - req.Params[constant.CPUS] = req.CpuQuota + composeByte, err := yaml.Marshal(composeMap) + if err != nil { + return err } - req.Params[constant.MemoryLimit] = "0" - if req.MemoryLimit > 0 { - req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit - } - allowHost := "127.0.0.1" - if req.AllowPort { - allowHost = "0.0.0.0" - } - req.Params[constant.HostIP] = allowHost + installed.DockerCompose = string(composeByte) } envPath := path.Join(installed.GetPath(), ".env") @@ -286,6 +280,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { if err != nil { return err } + backupEnvMaps := oldEnvMaps handleMap(req.Params, oldEnvMaps) paramByte, err := json.Marshal(oldEnvMaps) if err != nil { @@ -295,36 +290,42 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error { if err := env.Write(oldEnvMaps, envPath); err != nil { return err } - _ = appInstallRepo.Save(context.Background(), &installed) - + fileOp := files.NewFileOp() + _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), 0755) if err := rebuildApp(installed); err != nil { + _ = env.Write(backupEnvMaps, envPath) + _ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755) return err } + installed.Status = constant.Running + _ = appInstallRepo.Save(context.Background(), &installed) + website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID)) if changePort && website.ID != 0 && website.Status == constant.Running { - nginxInstall, err := getNginxFull(&website) - if err != nil { - return buserr.WithErr(constant.ErrUpdateBuWebsite, err) - } - config := nginxInstall.SiteConfig.Config - servers := config.FindServers() - if len(servers) == 0 { - return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")) - } - server := servers[0] - proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort) - server.UpdateRootProxy([]string{proxy}) - - if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { - return buserr.WithErr(constant.ErrUpdateBuWebsite, err) - } - if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil { - return buserr.WithErr(constant.ErrUpdateBuWebsite, err) - } - } - if changePort { go func() { - _ = OperateFirewallPort(oldPorts, newPorts) + nginxInstall, err := getNginxFull(&website) + if err != nil { + global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error()) + return + } + config := nginxInstall.SiteConfig.Config + servers := config.FindServers() + if len(servers) == 0 { + global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error()) + return + } + server := servers[0] + proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort) + server.UpdateRootProxy([]string{proxy}) + + if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error()) + return + } + if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil { + global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error()) + return + } }() } return nil @@ -543,31 +544,10 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) { params = append(params, appParam) } } - res.ContainerName = envs[constant.ContainerName].(string) - res.AllowPort = envs[constant.HostIP].(string) == "0.0.0.0" - numStr, ok := envs[constant.CPUS].(string) - if ok { - num, err := strconv.ParseFloat(numStr, 64) - if err == nil { - res.CpuQuota = num - } - } - num64, ok := envs[constant.CPUS].(float64) - if ok { - res.CpuQuota = num64 - } - re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*([KMGT]?B)`) - matches := re.FindStringSubmatch(envs[constant.MemoryLimit].(string)) - if len(matches) == 3 { - num, err := strconv.ParseFloat(matches[1], 64) - if err == nil { - unit := matches[2] - res.MemoryLimit = num - res.MemoryUnit = unit - } - } + config := getAppCommonConfig(envs) res.Params = params + res.AppContainerConfig = config return &res, nil } diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go index e9d424b0a..6232f324c 100644 --- a/backend/app/service/app_utils.go +++ b/backend/app/service/app_utils.go @@ -8,11 +8,14 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/dto/request" "github.com/compose-spec/compose-go/types" "github.com/subosito/gotenv" + "gopkg.in/yaml.v3" "math" + "net/http" "os" "os/exec" "path" "reflect" + "regexp" "strconv" "strings" @@ -226,40 +229,110 @@ func upgradeInstall(installId uint, detailId uint) error { return err } - detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version) - if install.App.Resource == constant.AppResourceLocal { - detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version) - } + install.Status = constant.Upgrading - cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath())) - stdout, err := cmd.CombinedOutput() - if err != nil { - if stdout != nil { - return errors.New(string(stdout)) - } - return err - } + go func() { + var upErr error + defer func() { + if upErr != nil { + install.Status = constant.UpgradeErr + install.Message = err.Error() + _ = appInstallRepo.Save(context.Background(), &install) + } + }() - if out, err := compose.Down(install.GetComposePath()); err != nil { - if out != "" { - return errors.New(out) + if upErr = downloadApp(install.App, detail, &install); upErr != nil { + return } - return err - } - install.DockerCompose = detail.DockerCompose - install.Version = detail.Version - install.AppDetailId = detailId - fileOp := files.NewFileOp() - if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil { - return err - } - if out, err := compose.Up(install.GetComposePath()); err != nil { - if out != "" { - return errors.New(out) + detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version) + if install.App.Resource == constant.AppResourceLocal { + detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version) } - return err - } + + cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath())) + stdout, err := cmd.CombinedOutput() + if err != nil { + if stdout != nil { + upErr = errors.New(string(stdout)) + return + } + upErr = err + return + } + + composeMap := make(map[string]interface{}) + if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil { + return + } + value, ok := composeMap["services"] + if !ok { + upErr = buserr.New(constant.ErrFileParse) + return + } + servicesMap := value.(map[string]interface{}) + index := 0 + oldServiceName := "" + for k := range servicesMap { + oldServiceName = k + index++ + if index > 0 { + break + } + } + servicesMap[install.ServiceName] = servicesMap[oldServiceName] + delete(servicesMap, oldServiceName) + + envs := make(map[string]interface{}) + if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil { + return + } + config := getAppCommonConfig(envs) + config.Advanced = true + if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil { + return + } + paramByte, upErr := json.Marshal(envs) + if upErr != nil { + return + } + install.Env = string(paramByte) + composeByte, upErr := yaml.Marshal(composeMap) + if upErr != nil { + return + } + + install.DockerCompose = string(composeByte) + install.Version = detail.Version + install.AppDetailId = detailId + + go func() { + _, _ = http.Get(detail.DownloadCallBackUrl) + }() + + if out, err := compose.Down(install.GetComposePath()); err != nil { + if out != "" { + upErr = errors.New(out) + return + } + return + } + fileOp := files.NewFileOp() + if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil { + return + } + if out, err := compose.Up(install.GetComposePath()); err != nil { + if out != "" { + upErr = errors.New(out) + return + } + upErr = err + return + } + install.Status = constant.Running + _ = appInstallRepo.Save(context.Background(), &install) + }() + return appInstallRepo.Save(context.Background(), &install) } @@ -332,32 +405,51 @@ func handleMap(params map[string]interface{}, envParams map[string]string) { } } -func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) { +func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) { + appResourceDir := path.Join(constant.AppResourceDir, app.Resource) + if !appDetail.Update { + return + } + fileOp := files.NewFileOp() + appDownloadDir := path.Join(appResourceDir, app.Key) + if !fileOp.Stat(appDownloadDir) { + _ = fileOp.CreateDir(appDownloadDir, 0755) + } + appVersionDir := path.Join(appDownloadDir, appDetail.Version) + if !fileOp.Stat(appVersionDir) { + _ = fileOp.CreateDir(appVersionDir, 0755) + } + global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl) + filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz") + + defer func() { + if err != nil { + appInstall.Status = constant.DownloadErr + appInstall.Message = err.Error() + } + }() + + if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil { + global.LOG.Errorf("download app[%s] error %v", app.Name, err) + return + } + if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil { + global.LOG.Errorf("decompress app[%s] error %v", app.Name, err) + return + } + _ = fileOp.DeleteFile(filePath) + return +} + +func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) { fileOp := files.NewFileOp() appResourceDir := path.Join(constant.AppResourceDir, app.Resource) - if app.Resource == constant.AppResourceRemote && appDetail.Update { - appDownloadDir := path.Join(appResourceDir, app.Key) - if !fileOp.Stat(appDownloadDir) { - _ = fileOp.CreateDir(appDownloadDir, 0755) - } - appVersionDir := path.Join(appDownloadDir, appDetail.Version) - if !fileOp.Stat(appVersionDir) { - _ = fileOp.CreateDir(appVersionDir, 0755) - } - global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl) - filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz") - if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil { - appInstall.Status = constant.DownloadErr - global.LOG.Errorf("download app[%s] error %v", app.Name, err) + if app.Resource == constant.AppResourceRemote { + err = downloadApp(app, appDetail, appInstall) + if err != nil { return } - if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil { - global.LOG.Errorf("decompress app[%s] error %v", app.Name, err) - appInstall.Status = constant.DownloadErr - return - } - _ = fileOp.DeleteFile(filePath) } appKey := app.Key installAppDir := path.Join(constant.AppInstallDir, app.Key) @@ -639,3 +731,108 @@ func updateToolApp(installed *model.AppInstall) { return } } + +func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error { + services, serviceValid := composeMap["services"].(map[string]interface{}) + if !serviceValid { + return buserr.New(constant.ErrFileParse) + } + service, serviceExist := services[serviceName] + if !serviceExist { + return buserr.New(constant.ErrFileParse) + } + serviceValue := service.(map[string]interface{}) + deploy := map[string]interface{}{ + "resources": map[string]interface{}{ + "limits": map[string]interface{}{ + "cpus": "${CPUS}", + "memory": "${MEMORY_LIMIT}", + }, + }, + } + serviceValue["deploy"] = deploy + + ports, ok := serviceValue["ports"].([]interface{}) + if ok { + for i, port := range ports { + portStr, portOK := port.(string) + if !portOK { + continue + } + portArray := strings.Split(portStr, ":") + if len(portArray) == 2 { + portArray = append([]string{"${HOST_IP}"}, portArray...) + } + ports[i] = strings.Join(portArray, ":") + } + serviceValue["ports"] = ports + } + + params[constant.CPUS] = "0" + params[constant.MemoryLimit] = "0" + if req.Advanced { + if req.CpuQuota > 0 { + params[constant.CPUS] = req.CpuQuota + } + if req.MemoryLimit > 0 { + params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit + } + } + _, portExist := serviceValue["ports"].([]interface{}) + if portExist { + allowHost := "127.0.0.1" + if req.Advanced && req.AllowPort { + allowHost = "0.0.0.0" + } + params[constant.HostIP] = allowHost + } + services[serviceName] = serviceValue + return nil +} + +func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig { + config := request.AppContainerConfig{} + + if hostIp, ok := envs[constant.HostIP]; ok { + config.AllowPort = hostIp.(string) == "0.0.0.0" + } else { + config.AllowPort = true + } + if cpuCore, ok := envs[constant.CPUS]; ok { + numStr, ok := cpuCore.(string) + if ok { + num, err := strconv.ParseFloat(numStr, 64) + if err == nil { + config.CpuQuota = num + } + } else { + num64, flOk := cpuCore.(float64) + if flOk { + config.CpuQuota = num64 + } + } + } else { + config.CpuQuota = 0 + } + if memLimit, ok := envs[constant.MemoryLimit]; ok { + re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*([KMGT]?B)`) + matches := re.FindStringSubmatch(memLimit.(string)) + if len(matches) == 3 { + num, err := strconv.ParseFloat(matches[1], 64) + if err == nil { + unit := matches[2] + config.MemoryLimit = num + config.MemoryUnit = unit + } + } + } else { + config.MemoryLimit = 0 + config.MemoryUnit = "M" + } + + if containerName, ok := envs[constant.ContainerName]; ok { + config.ContainerName = containerName.(string) + } + + return config +} diff --git a/backend/constant/app.go b/backend/constant/app.go index 37cbb2e05..93be575a2 100644 --- a/backend/constant/app.go +++ b/backend/constant/app.go @@ -9,6 +9,8 @@ const ( Syncing = "Syncing" DownloadErr = "DownloadErr" DirNotFound = "DirNotFound" + Upgrading = "Upgrading" + UpgradeErr = "UpgradeErr" ContainerPrefix = "1Panel-" diff --git a/backend/utils/compose/compose.go b/backend/utils/compose/compose.go index 022c68525..9262fe776 100644 --- a/backend/utils/compose/compose.go +++ b/backend/utils/compose/compose.go @@ -5,31 +5,31 @@ import ( ) 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/backend/utils/files/file_op.go b/backend/utils/files/file_op.go index 81d0400ea..c8c920f88 100644 --- a/backend/utils/files/file_op.go +++ b/backend/utils/files/file_op.go @@ -477,12 +477,16 @@ func (f FileOp) Compress(srcRiles []string, dst string, name string, cType Compr return nil } +func isIgnoreFile(name string) bool { + return strings.HasPrefix(name, "__MACOSX") || strings.HasSuffix(name, ".DS_Store") || strings.HasPrefix(name, "._") +} + func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error { format := getFormat(cType) handler := func(ctx context.Context, archFile archiver.File) error { info := archFile.FileInfo - if strings.HasPrefix(archFile.NameInArchive, "__MACOSX") { + if isIgnoreFile(archFile.Name()) { return nil } filePath := filepath.Join(dst, archFile.NameInArchive) diff --git a/frontend/src/components/status/index.vue b/frontend/src/components/status/index.vue index 86801c253..6788024fa 100644 --- a/frontend/src/components/status/index.vue +++ b/frontend/src/components/status/index.vue @@ -19,11 +19,12 @@ const props = defineProps({ let status = ref('running'); const getType = (status: string) => { + if (status.includes('error') || status.includes('err')) { + return 'danger'; + } switch (status) { case 'running': return 'success'; - case 'error': - return 'danger'; case 'stopped': return 'danger'; default: @@ -31,7 +32,7 @@ const getType = (status: string) => { } }; -const loadingStatus = ['installing', 'building', 'restarting']; +const loadingStatus = ['installing', 'building', 'restarting', 'upgrading']; const loadingIcon = (status: string): boolean => { return loadingStatus.indexOf(status) > -1; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 9349f4d4a..8a89ac911 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -196,6 +196,8 @@ const message = { disabled: 'Disabled', normal: 'Normal', building: 'Building', + downloaderr: 'Download Error', + upgrading: 'Upgrading', }, }, menu: { @@ -1091,6 +1093,7 @@ const message = { 'Allowing external port access will release the firewall port, please do not release the php operating environment', appInstallWarn: 'The application does not release the external access port by default, you can choose to release it in the advanced settings', + upgradeStart: 'Start upgrading! Please refresh the page later', }, website: { website: 'Website', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 2ec21df2d..1c8267597 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -201,6 +201,8 @@ const message = { disabled: '已停止', normal: '正常', building: '制作镜像中', + downloaderr: '下载失败', + upgrading: '升级中', }, units: { second: '秒', @@ -1089,6 +1091,7 @@ const message = { allowPort: '端口外部访问', allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开', appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开', + upgradeStart: '开始升级!请稍后刷新页面', }, website: { website: '网站', diff --git a/frontend/src/views/app-store/index.scss b/frontend/src/views/app-store/index.scss index de4568971..ac766654e 100644 --- a/frontend/src/views/app-store/index.scss +++ b/frontend/src/views/app-store/index.scss @@ -24,6 +24,9 @@ float: right; margin-right: 10px; } + .msg { + margin-left: 10px; + } } .d-description { diff --git a/frontend/src/views/app-store/installed/detail/index.vue b/frontend/src/views/app-store/installed/detail/index.vue index 1833a47b1..444438a6c 100644 --- a/frontend/src/views/app-store/installed/detail/index.vue +++ b/frontend/src/views/app-store/installed/detail/index.vue @@ -179,7 +179,7 @@ const get = async () => { } paramModel.value.memoryLimit = res.data.memoryLimit; paramModel.value.cpuQuota = res.data.cpuQuota; - paramModel.value.memoryUnit = res.data.memoryUnit; + paramModel.value.memoryUnit = res.data.memoryUnit !== '' ? res.data.memoryUnit : 'MB'; paramModel.value.allowPort = res.data.allowPort; paramModel.value.containerName = res.data.containerName; paramModel.value.advanced = false; diff --git a/frontend/src/views/app-store/installed/index.vue b/frontend/src/views/app-store/installed/index.vue index 99a9fec3a..17a7a7af4 100644 --- a/frontend/src/views/app-store/installed/index.vue +++ b/frontend/src/views/app-store/installed/index.vue @@ -81,25 +81,25 @@
- {{ installed.name }} + + {{ installed.name }} + + + + + - - - @@ -337,18 +338,27 @@ const buttons = [ click: (row: any) => { openOperate(row, 'sync'); }, + disabled: (row: any) => { + return row.status === 'DownloadErr' || row.status === 'Upgrading'; + }, }, { label: i18n.global.t('app.rebuild'), click: (row: any) => { openOperate(row, 'rebuild'); }, + disabled: (row: any) => { + return row.status === 'DownloadErr' || row.status === 'Upgrading'; + }, }, { label: i18n.global.t('app.restart'), click: (row: any) => { openOperate(row, 'restart'); }, + disabled: (row: any) => { + return row.status === 'DownloadErr' || row.status === 'Upgrading'; + }, }, { label: i18n.global.t('app.start'), @@ -356,7 +366,12 @@ const buttons = [ openOperate(row, 'start'); }, disabled: (row: any) => { - return row.status === 'Running' || row.status === 'Error'; + return ( + row.status === 'Running' || + row.status === 'Error' || + row.status === 'DownloadErr' || + row.status === 'Upgrading' + ); }, }, { @@ -365,7 +380,7 @@ const buttons = [ openOperate(row, 'stop'); }, disabled: (row: any) => { - return row.status !== 'Running'; + return row.status !== 'Running' || row.status === 'DownloadErr' || row.status === 'Upgrading'; }, }, { @@ -379,6 +394,9 @@ const buttons = [ click: (row: any) => { openParam(row.id); }, + disabled: (row: any) => { + return row.status === 'DownloadErr' || row.status === 'Upgrading'; + }, }, ]; @@ -404,6 +422,10 @@ const openParam = (installId: number) => { appParamRef.value.acceptParams({ id: installId }); }; +const isAppErr = (row: any) => { + return row.status.includes('Err') || row.status.includes('Error'); +}; + onMounted(() => { const path = router.currentRoute.value.path; if (path == '/apps/upgrade') { diff --git a/frontend/src/views/app-store/installed/upgrade/index.vue b/frontend/src/views/app-store/installed/upgrade/index.vue index d624602df..3db1fd61d 100644 --- a/frontend/src/views/app-store/installed/upgrade/index.vue +++ b/frontend/src/views/app-store/installed/upgrade/index.vue @@ -83,7 +83,7 @@ const operate = async () => { loading.value = true; await InstalledOp(operateReq) .then(() => { - MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + MsgSuccess(i18n.global.t('app.upgradeStart')); bus.emit('upgrade', true); handleClose(); })