mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-01 03:24:14 +08:00
feat: 优化应用升级逻辑 (#1075)
This commit is contained in:
parent
72bc99bddc
commit
d20d5946f2
@ -61,12 +61,7 @@ type AppInstalledOperate struct {
|
|||||||
type AppInstalledUpdate struct {
|
type AppInstalledUpdate struct {
|
||||||
InstallId uint `json:"installId" validate:"required"`
|
InstallId uint `json:"installId" validate:"required"`
|
||||||
Params map[string]interface{} `json:"params" validate:"required"`
|
Params map[string]interface{} `json:"params" validate:"required"`
|
||||||
Advanced bool `json:"advanced"`
|
AppContainerConfig
|
||||||
CpuQuota float64 `json:"cpuQuota"`
|
|
||||||
MemoryLimit float64 `json:"memoryLimit"`
|
|
||||||
MemoryUnit string `json:"memoryUnit"`
|
|
||||||
ContainerName string `json:"containerName"`
|
|
||||||
AllowPort bool `json:"allowPort"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortUpdate struct {
|
type PortUpdate struct {
|
||||||
|
@ -2,6 +2,7 @@ package response
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
@ -85,9 +86,5 @@ type AppParam struct {
|
|||||||
|
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Params []AppParam `json:"params"`
|
Params []AppParam `json:"params"`
|
||||||
CpuQuota float64 `json:"cpuQuota"`
|
request.AppContainerConfig
|
||||||
MemoryLimit float64 `json:"memoryLimit"`
|
|
||||||
MemoryUnit string `json:"memoryUnit"`
|
|
||||||
ContainerName string `json:"containerName"`
|
|
||||||
AllowPort bool `json:"allowPort"`
|
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,23 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"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"
|
||||||
"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/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppService struct {
|
type AppService struct {
|
||||||
@ -301,49 +299,10 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
|||||||
servicesMap[v] = servicesMap[k]
|
servicesMap[v] = servicesMap[k]
|
||||||
delete(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 err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||||
if ok {
|
return
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
servicesMap[appInstall.ServiceName] = serviceValue
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
composeByte []byte
|
composeByte []byte
|
||||||
@ -379,14 +338,17 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
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 {
|
if appInstall.Status == constant.Installing {
|
||||||
appInstall.Status = constant.Error
|
appInstall.Status = constant.Error
|
||||||
appInstall.Message = err.Error()
|
appInstall.Message = err.Error()
|
||||||
}
|
}
|
||||||
_ = appInstallRepo.Save(ctx, appInstall)
|
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
go func() {
|
||||||
|
_, _ = http.Get(appDetail.DownloadCallBackUrl)
|
||||||
|
}()
|
||||||
upApp(appInstall)
|
upApp(appInstall)
|
||||||
}()
|
}()
|
||||||
go updateToolApp(appInstall)
|
go updateToolApp(appInstall)
|
||||||
|
@ -5,11 +5,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -262,23 +262,17 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backupDockerCompose := installed.DockerCompose
|
||||||
if req.Advanced {
|
if req.Advanced {
|
||||||
if req.ContainerName != "" {
|
composeMap := make(map[string]interface{})
|
||||||
req.Params[constant.ContainerName] = req.ContainerName
|
if err := addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
req.Params[constant.CPUS] = "0"
|
composeByte, err := yaml.Marshal(composeMap)
|
||||||
if req.CpuQuota > 0 {
|
if err != nil {
|
||||||
req.Params[constant.CPUS] = req.CpuQuota
|
return err
|
||||||
}
|
}
|
||||||
req.Params[constant.MemoryLimit] = "0"
|
installed.DockerCompose = string(composeByte)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
envPath := path.Join(installed.GetPath(), ".env")
|
envPath := path.Join(installed.GetPath(), ".env")
|
||||||
@ -286,6 +280,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
backupEnvMaps := oldEnvMaps
|
||||||
handleMap(req.Params, oldEnvMaps)
|
handleMap(req.Params, oldEnvMaps)
|
||||||
paramByte, err := json.Marshal(oldEnvMaps)
|
paramByte, err := json.Marshal(oldEnvMaps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -295,36 +290,42 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
|||||||
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
||||||
return err
|
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 {
|
if err := rebuildApp(installed); err != nil {
|
||||||
|
_ = env.Write(backupEnvMaps, envPath)
|
||||||
|
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
installed.Status = constant.Running
|
||||||
|
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||||
|
|
||||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
||||||
if changePort && website.ID != 0 && website.Status == constant.Running {
|
if changePort && website.ID != 0 && website.Status == constant.Running {
|
||||||
|
go func() {
|
||||||
nginxInstall, err := getNginxFull(&website)
|
nginxInstall, err := getNginxFull(&website)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
config := nginxInstall.SiteConfig.Config
|
config := nginxInstall.SiteConfig.Config
|
||||||
servers := config.FindServers()
|
servers := config.FindServers()
|
||||||
if len(servers) == 0 {
|
if len(servers) == 0 {
|
||||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid"))
|
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
server := servers[0]
|
server := servers[0]
|
||||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||||
server.UpdateRootProxy([]string{proxy})
|
server.UpdateRootProxy([]string{proxy})
|
||||||
|
|
||||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if changePort {
|
|
||||||
go func() {
|
|
||||||
_ = OperateFirewallPort(oldPorts, newPorts)
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -543,31 +544,10 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
|||||||
params = append(params, appParam)
|
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)`)
|
config := getAppCommonConfig(envs)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.Params = params
|
res.Params = params
|
||||||
|
res.AppContainerConfig = config
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,11 +8,14 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/subosito/gotenv"
|
"github.com/subosito/gotenv"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -226,40 +229,110 @@ func upgradeInstall(installId uint, detailId uint) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install.Status = constant.Upgrading
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var upErr error
|
||||||
|
defer func() {
|
||||||
|
if upErr != nil {
|
||||||
|
install.Status = constant.UpgradeErr
|
||||||
|
install.Message = err.Error()
|
||||||
|
_ = appInstallRepo.Save(context.Background(), &install)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||||
if install.App.Resource == constant.AppResourceLocal {
|
if install.App.Resource == constant.AppResourceLocal {
|
||||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||||
stdout, err := cmd.CombinedOutput()
|
stdout, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if stdout != nil {
|
if stdout != nil {
|
||||||
return errors.New(string(stdout))
|
upErr = errors.New(string(stdout))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return err
|
upErr = err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
composeMap := make(map[string]interface{})
|
||||||
if out != "" {
|
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||||
return errors.New(out)
|
return
|
||||||
}
|
}
|
||||||
return err
|
value, ok := composeMap["services"]
|
||||||
|
if !ok {
|
||||||
|
upErr = buserr.New(constant.ErrFileParse)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
install.DockerCompose = detail.DockerCompose
|
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.Version = detail.Version
|
||||||
install.AppDetailId = detailId
|
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()
|
fileOp := files.NewFileOp()
|
||||||
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
|
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
return errors.New(out)
|
upErr = errors.New(out)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return err
|
upErr = err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
install.Status = constant.Running
|
||||||
|
_ = appInstallRepo.Save(context.Background(), &install)
|
||||||
|
}()
|
||||||
|
|
||||||
return appInstallRepo.Save(context.Background(), &install)
|
return appInstallRepo.Save(context.Background(), &install)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,11 +405,12 @@ 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) {
|
||||||
fileOp := files.NewFileOp()
|
|
||||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||||
|
if !appDetail.Update {
|
||||||
if app.Resource == constant.AppResourceRemote && appDetail.Update {
|
return
|
||||||
|
}
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
appDownloadDir := path.Join(appResourceDir, app.Key)
|
appDownloadDir := path.Join(appResourceDir, app.Key)
|
||||||
if !fileOp.Stat(appDownloadDir) {
|
if !fileOp.Stat(appDownloadDir) {
|
||||||
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
||||||
@ -347,17 +421,35 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
|||||||
}
|
}
|
||||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||||
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
||||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
appInstall.Status = constant.DownloadErr
|
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)
|
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
||||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||||
appInstall.Status = constant.DownloadErr
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = fileOp.DeleteFile(filePath)
|
_ = 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 {
|
||||||
|
err = downloadApp(app, appDetail, appInstall)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appKey := app.Key
|
appKey := app.Key
|
||||||
installAppDir := path.Join(constant.AppInstallDir, app.Key)
|
installAppDir := path.Join(constant.AppInstallDir, app.Key)
|
||||||
@ -639,3 +731,108 @@ func updateToolApp(installed *model.AppInstall) {
|
|||||||
return
|
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
|
||||||
|
}
|
||||||
|
@ -9,6 +9,8 @@ const (
|
|||||||
Syncing = "Syncing"
|
Syncing = "Syncing"
|
||||||
DownloadErr = "DownloadErr"
|
DownloadErr = "DownloadErr"
|
||||||
DirNotFound = "DirNotFound"
|
DirNotFound = "DirNotFound"
|
||||||
|
Upgrading = "Upgrading"
|
||||||
|
UpgradeErr = "UpgradeErr"
|
||||||
|
|
||||||
ContainerPrefix = "1Panel-"
|
ContainerPrefix = "1Panel-"
|
||||||
|
|
||||||
|
@ -5,31 +5,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Up(filePath string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Down(filePath string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(filePath string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Stop(filePath string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Restart(filePath string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Operate(filePath, operation string) (string, error) {
|
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
|
return stdout, err
|
||||||
}
|
}
|
||||||
|
@ -477,12 +477,16 @@ func (f FileOp) Compress(srcRiles []string, dst string, name string, cType Compr
|
|||||||
return nil
|
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 {
|
func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error {
|
||||||
format := getFormat(cType)
|
format := getFormat(cType)
|
||||||
|
|
||||||
handler := func(ctx context.Context, archFile archiver.File) error {
|
handler := func(ctx context.Context, archFile archiver.File) error {
|
||||||
info := archFile.FileInfo
|
info := archFile.FileInfo
|
||||||
if strings.HasPrefix(archFile.NameInArchive, "__MACOSX") {
|
if isIgnoreFile(archFile.Name()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
filePath := filepath.Join(dst, archFile.NameInArchive)
|
filePath := filepath.Join(dst, archFile.NameInArchive)
|
||||||
|
@ -19,11 +19,12 @@ const props = defineProps({
|
|||||||
let status = ref('running');
|
let status = ref('running');
|
||||||
|
|
||||||
const getType = (status: string) => {
|
const getType = (status: string) => {
|
||||||
|
if (status.includes('error') || status.includes('err')) {
|
||||||
|
return 'danger';
|
||||||
|
}
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'running':
|
case 'running':
|
||||||
return 'success';
|
return 'success';
|
||||||
case 'error':
|
|
||||||
return 'danger';
|
|
||||||
case 'stopped':
|
case 'stopped':
|
||||||
return 'danger';
|
return 'danger';
|
||||||
default:
|
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 => {
|
const loadingIcon = (status: string): boolean => {
|
||||||
return loadingStatus.indexOf(status) > -1;
|
return loadingStatus.indexOf(status) > -1;
|
||||||
|
@ -196,6 +196,8 @@ const message = {
|
|||||||
disabled: 'Disabled',
|
disabled: 'Disabled',
|
||||||
normal: 'Normal',
|
normal: 'Normal',
|
||||||
building: 'Building',
|
building: 'Building',
|
||||||
|
downloaderr: 'Download Error',
|
||||||
|
upgrading: 'Upgrading',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
@ -1091,6 +1093,7 @@ const message = {
|
|||||||
'Allowing external port access will release the firewall port, please do not release the php operating environment',
|
'Allowing external port access will release the firewall port, please do not release the php operating environment',
|
||||||
appInstallWarn:
|
appInstallWarn:
|
||||||
'The application does not release the external access port by default, you can choose to release it in the advanced settings',
|
'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: 'Website',
|
website: 'Website',
|
||||||
|
@ -201,6 +201,8 @@ const message = {
|
|||||||
disabled: '已停止',
|
disabled: '已停止',
|
||||||
normal: '正常',
|
normal: '正常',
|
||||||
building: '制作镜像中',
|
building: '制作镜像中',
|
||||||
|
downloaderr: '下载失败',
|
||||||
|
upgrading: '升级中',
|
||||||
},
|
},
|
||||||
units: {
|
units: {
|
||||||
second: '秒',
|
second: '秒',
|
||||||
@ -1089,6 +1091,7 @@ const message = {
|
|||||||
allowPort: '端口外部访问',
|
allowPort: '端口外部访问',
|
||||||
allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开',
|
allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开',
|
||||||
appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开',
|
appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开',
|
||||||
|
upgradeStart: '开始升级!请稍后刷新页面',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '网站',
|
website: '网站',
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
float: right;
|
float: right;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
.msg {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.d-description {
|
.d-description {
|
||||||
|
@ -179,7 +179,7 @@ const get = async () => {
|
|||||||
}
|
}
|
||||||
paramModel.value.memoryLimit = res.data.memoryLimit;
|
paramModel.value.memoryLimit = res.data.memoryLimit;
|
||||||
paramModel.value.cpuQuota = res.data.cpuQuota;
|
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.allowPort = res.data.allowPort;
|
||||||
paramModel.value.containerName = res.data.containerName;
|
paramModel.value.containerName = res.data.containerName;
|
||||||
paramModel.value.advanced = false;
|
paramModel.value.advanced = false;
|
||||||
|
@ -81,25 +81,25 @@
|
|||||||
<el-col :xs="21" :sm="21" :md="21" :lg="20" :xl="20">
|
<el-col :xs="21" :sm="21" :md="21" :lg="20" :xl="20">
|
||||||
<div class="a-detail">
|
<div class="a-detail">
|
||||||
<div class="d-name">
|
<div class="d-name">
|
||||||
|
<el-button link type="info">
|
||||||
<span class="name">{{ installed.name }}</span>
|
<span class="name">{{ installed.name }}</span>
|
||||||
|
</el-button>
|
||||||
|
|
||||||
<span class="status">
|
<span class="status">
|
||||||
|
<Status :key="installed.status" :status="installed.status"></Status>
|
||||||
|
</span>
|
||||||
|
<span class="msg">
|
||||||
<el-popover
|
<el-popover
|
||||||
v-if="installed.status === 'Error'"
|
v-if="isAppErr(installed)"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
:width="400"
|
:width="400"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
:content="installed.message"
|
:content="installed.message"
|
||||||
>
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<Status
|
<el-button link type="primary">详情</el-button>
|
||||||
:key="installed.status"
|
|
||||||
:status="installed.status"
|
|
||||||
></Status>
|
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<span v-else>
|
|
||||||
<Status :key="installed.status" :status="installed.status"></Status>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
@ -132,6 +132,7 @@
|
|||||||
plain
|
plain
|
||||||
round
|
round
|
||||||
size="small"
|
size="small"
|
||||||
|
:disabled="installed.status === 'Upgrading'"
|
||||||
@click="openOperate(installed, 'upgrade')"
|
@click="openOperate(installed, 'upgrade')"
|
||||||
v-if="mode === 'upgrade'"
|
v-if="mode === 'upgrade'"
|
||||||
>
|
>
|
||||||
@ -337,18 +338,27 @@ const buttons = [
|
|||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
openOperate(row, 'sync');
|
openOperate(row, 'sync');
|
||||||
},
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.rebuild'),
|
label: i18n.global.t('app.rebuild'),
|
||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
openOperate(row, 'rebuild');
|
openOperate(row, 'rebuild');
|
||||||
},
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.restart'),
|
label: i18n.global.t('app.restart'),
|
||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
openOperate(row, 'restart');
|
openOperate(row, 'restart');
|
||||||
},
|
},
|
||||||
|
disabled: (row: any) => {
|
||||||
|
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.global.t('app.start'),
|
label: i18n.global.t('app.start'),
|
||||||
@ -356,7 +366,12 @@ const buttons = [
|
|||||||
openOperate(row, 'start');
|
openOperate(row, 'start');
|
||||||
},
|
},
|
||||||
disabled: (row: any) => {
|
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');
|
openOperate(row, 'stop');
|
||||||
},
|
},
|
||||||
disabled: (row: any) => {
|
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) => {
|
click: (row: any) => {
|
||||||
openParam(row.id);
|
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 });
|
appParamRef.value.acceptParams({ id: installId });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isAppErr = (row: any) => {
|
||||||
|
return row.status.includes('Err') || row.status.includes('Error');
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const path = router.currentRoute.value.path;
|
const path = router.currentRoute.value.path;
|
||||||
if (path == '/apps/upgrade') {
|
if (path == '/apps/upgrade') {
|
||||||
|
@ -83,7 +83,7 @@ const operate = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
await InstalledOp(operateReq)
|
await InstalledOp(operateReq)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('app.upgradeStart'));
|
||||||
bus.emit('upgrade', true);
|
bus.emit('upgrade', true);
|
||||||
handleClose();
|
handleClose();
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user