mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-10-28 01:36:56 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			662 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			662 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package service
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
 | |
| 	"github.com/compose-spec/compose-go/types"
 | |
| 	"github.com/subosito/gotenv"
 | |
| 	"math"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"path"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/1Panel-dev/1Panel/backend/app/repo"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/utils/env"
 | |
| 
 | |
| 	"github.com/1Panel-dev/1Panel/backend/app/dto/response"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/buserr"
 | |
| 
 | |
| 	"github.com/1Panel-dev/1Panel/backend/app/dto"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/app/model"
 | |
| 	"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/compose"
 | |
| 	composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/utils/files"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| type DatabaseOp string
 | |
| 
 | |
| var (
 | |
| 	Add    DatabaseOp = "add"
 | |
| 	Delete DatabaseOp = "delete"
 | |
| )
 | |
| 
 | |
| func checkPort(key string, params map[string]interface{}) (int, error) {
 | |
| 	port, ok := params[key]
 | |
| 	if ok {
 | |
| 		portN := int(math.Ceil(port.(float64)))
 | |
| 
 | |
| 		oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN))
 | |
| 		if len(oldInstalled) > 0 {
 | |
| 			var apps []string
 | |
| 			for _, install := range oldInstalled {
 | |
| 				apps = append(apps, install.App.Name)
 | |
| 			}
 | |
| 			return portN, buserr.WithMap(constant.ErrPortInOtherApp, map[string]interface{}{"port": portN, "apps": apps}, nil)
 | |
| 		}
 | |
| 		if common.ScanPort(portN) {
 | |
| 			return portN, buserr.WithDetail(constant.ErrPortInUsed, portN, nil)
 | |
| 		} else {
 | |
| 			return portN, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, nil
 | |
| }
 | |
| 
 | |
| func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
 | |
| 	var dbConfig dto.AppDatabase
 | |
| 	if app.Type == "runtime" {
 | |
| 		var authParam dto.AuthParam
 | |
| 		paramByte, err := json.Marshal(params)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := json.Unmarshal(paramByte, &authParam); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if authParam.RootPassword != "" {
 | |
| 			authByte, err := json.Marshal(authParam)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			appInstall.Param = string(authByte)
 | |
| 		}
 | |
| 	}
 | |
| 	if app.Type == "website" || app.Type == "tool" {
 | |
| 		paramByte, err := json.Marshal(params)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := json.Unmarshal(paramByte, &dbConfig); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) && dbConfig.ServiceName != "" {
 | |
| 		dbInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(dbConfig.ServiceName))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		var resourceId uint
 | |
| 		if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" {
 | |
| 			iMysqlRepo := repo.NewIMysqlRepo()
 | |
| 			oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName))
 | |
| 			resourceId = oldMysqlDb.ID
 | |
| 			if oldMysqlDb.ID > 0 {
 | |
| 				if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {
 | |
| 					return buserr.New(constant.ErrDbUserNotValid)
 | |
| 				}
 | |
| 			} else {
 | |
| 				var createMysql dto.MysqlDBCreate
 | |
| 				createMysql.Name = dbConfig.DbName
 | |
| 				createMysql.Username = dbConfig.DbUser
 | |
| 				createMysql.Format = "utf8mb4"
 | |
| 				createMysql.Permission = "%"
 | |
| 				createMysql.Password = dbConfig.Password
 | |
| 				mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				resourceId = mysqldb.ID
 | |
| 			}
 | |
| 		}
 | |
| 		var installResource model.AppInstallResource
 | |
| 		installResource.ResourceId = resourceId
 | |
| 		installResource.AppInstallId = appInstall.ID
 | |
| 		installResource.LinkId = dbInstall.ID
 | |
| 		installResource.Key = dbInstall.App.Key
 | |
| 		if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func handleAppInstallErr(ctx context.Context, install *model.AppInstall) error {
 | |
| 	op := files.NewFileOp()
 | |
| 	appDir := install.GetPath()
 | |
| 	dir, _ := os.Stat(appDir)
 | |
| 	if dir != nil {
 | |
| 		_, _ = compose.Down(install.GetComposePath())
 | |
| 		if err := op.DeleteDir(appDir); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	if err := deleteLink(ctx, install, true, true); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete bool, deleteDB bool) error {
 | |
| 	op := files.NewFileOp()
 | |
| 	appDir := install.GetPath()
 | |
| 	dir, _ := os.Stat(appDir)
 | |
| 	if dir != nil {
 | |
| 		out, err := compose.Down(install.GetComposePath())
 | |
| 		if err != nil && !forceDelete {
 | |
| 			return handleErr(install, err, out)
 | |
| 		}
 | |
| 	}
 | |
| 	tx, ctx := helper.GetTxAndContext()
 | |
| 	defer tx.Rollback()
 | |
| 	if err := appInstallRepo.Delete(ctx, install); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := deleteLink(ctx, &install, deleteDB, forceDelete); err != nil && !forceDelete {
 | |
| 		return err
 | |
| 	}
 | |
| 	_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
 | |
| 	_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(install.App.Key))
 | |
| 	if install.App.Key == constant.AppMysql {
 | |
| 		_ = mysqlRepo.DeleteAll(ctx)
 | |
| 	}
 | |
| 	uploadDir := fmt.Sprintf("%s/1panel/uploads/app/%s/%s", global.CONF.System.BaseDir, install.App.Key, install.Name)
 | |
| 	if _, err := os.Stat(uploadDir); err == nil {
 | |
| 		_ = os.RemoveAll(uploadDir)
 | |
| 	}
 | |
| 	if deleteBackup {
 | |
| 		localDir, _ := loadLocalDir()
 | |
| 		backupDir := fmt.Sprintf("%s/app/%s/%s", localDir, install.App.Key, install.Name)
 | |
| 		if _, err := os.Stat(backupDir); err == nil {
 | |
| 			_ = os.RemoveAll(backupDir)
 | |
| 		}
 | |
| 		global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name)
 | |
| 	}
 | |
| 	_ = op.DeleteDir(appDir)
 | |
| 	tx.Commit()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool) error {
 | |
| 	resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
 | |
| 	if len(resources) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	for _, re := range resources {
 | |
| 		mysqlService := NewIMysqlService()
 | |
| 		if re.Key == "mysql" && deleteDB {
 | |
| 			database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
 | |
| 			if reflect.DeepEqual(database, model.DatabaseMysql{}) {
 | |
| 				continue
 | |
| 			}
 | |
| 			if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
 | |
| 				ID:          database.ID,
 | |
| 				ForceDelete: forceDelete,
 | |
| 			}); err != nil && !forceDelete {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
 | |
| }
 | |
| 
 | |
| func upgradeInstall(installId uint, detailId uint) error {
 | |
| 	install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if install.Version == detail.Version {
 | |
| 		return errors.New("two version is same")
 | |
| 	}
 | |
| 	if err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
 | |
| 	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
 | |
| 	}
 | |
| 
 | |
| 	if out, err := compose.Down(install.GetComposePath()); err != nil {
 | |
| 		if out != "" {
 | |
| 			return errors.New(out)
 | |
| 		}
 | |
| 		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)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	return appInstallRepo.Save(context.Background(), &install)
 | |
| }
 | |
| 
 | |
| func getContainerNames(install model.AppInstall) ([]string, error) {
 | |
| 	envStr, err := coverEnvJsonToStr(install.Env)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	containerMap := make(map[string]struct{})
 | |
| 	containerMap[install.ContainerName] = struct{}{}
 | |
| 	for _, service := range project.AllServices() {
 | |
| 		if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
 | |
| 			continue
 | |
| 		}
 | |
| 		containerMap[service.ContainerName] = struct{}{}
 | |
| 	}
 | |
| 	var containerNames []string
 | |
| 	for k := range containerMap {
 | |
| 		containerNames = append(containerNames, k)
 | |
| 	}
 | |
| 	return containerNames, nil
 | |
| }
 | |
| 
 | |
| func coverEnvJsonToStr(envJson string) (string, error) {
 | |
| 	envMap := make(map[string]interface{})
 | |
| 	_ = json.Unmarshal([]byte(envJson), &envMap)
 | |
| 	newEnvMap := make(map[string]string, len(envMap))
 | |
| 	handleMap(envMap, newEnvMap)
 | |
| 	envStr, err := gotenv.Marshal(newEnvMap)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return envStr, nil
 | |
| }
 | |
| 
 | |
| func checkLimit(app model.App) error {
 | |
| 	if app.Limit > 0 {
 | |
| 		installs, err := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if len(installs) >= app.Limit {
 | |
| 			return buserr.New(constant.ErrAppLimit)
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func checkRequiredAndLimit(app model.App) error {
 | |
| 	if err := checkLimit(app); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if app.Required != "" {
 | |
| 		var requiredArray []string
 | |
| 		if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		for _, key := range requiredArray {
 | |
| 			if key == "" {
 | |
| 				continue
 | |
| 			}
 | |
| 			requireApp, err := appRepo.GetFirst(appRepo.WithKey(key))
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(requireApp.ID))
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			var detailIds []uint
 | |
| 			for _, d := range details {
 | |
| 				detailIds = append(detailIds, d.ID)
 | |
| 			}
 | |
| 
 | |
| 			_, err = appInstallRepo.GetFirst(appInstallRepo.WithDetailIdsIn(detailIds))
 | |
| 			if err != nil {
 | |
| 				return buserr.WithDetail(constant.ErrAppRequired, requireApp.Name, nil)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func handleMap(params map[string]interface{}, envParams map[string]string) {
 | |
| 	for k, v := range params {
 | |
| 		switch t := v.(type) {
 | |
| 		case string:
 | |
| 			envParams[k] = t
 | |
| 		case float64:
 | |
| 			envParams[k] = strconv.FormatFloat(t, 'f', -1, 32)
 | |
| 		default:
 | |
| 			envParams[k] = t.(string)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) {
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	appResourceDir := constant.AppResourceDir
 | |
| 	installAppDir := path.Join(constant.AppInstallDir, key)
 | |
| 	appKey := key
 | |
| 	if isLocal {
 | |
| 		appResourceDir = constant.LocalAppResourceDir
 | |
| 		appKey = strings.TrimPrefix(key, "local")
 | |
| 		installAppDir = path.Join(constant.LocalAppInstallDir, appKey)
 | |
| 	}
 | |
| 	resourceDir := path.Join(appResourceDir, appKey, "versions", version)
 | |
| 
 | |
| 	if !fileOp.Stat(installAppDir) {
 | |
| 		if err = fileOp.CreateDir(installAppDir, 0755); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	appDir := path.Join(installAppDir, installName)
 | |
| 	if fileOp.Stat(appDir) {
 | |
| 		if err = fileOp.DeleteDir(appDir); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	if err = fileOp.Copy(resourceDir, installAppDir); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	versionDir := path.Join(installAppDir, version)
 | |
| 	if err = fileOp.Rename(versionDir, appDir); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	envPath := path.Join(appDir, ".env")
 | |
| 
 | |
| 	envParams := make(map[string]string, len(params))
 | |
| 	handleMap(params, envParams)
 | |
| 	if err = env.Write(envParams, envPath); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // 处理文件夹权限等问题
 | |
| func upAppPre(app model.App, appInstall *model.AppInstall) error {
 | |
| 	if app.Key == "nexus" {
 | |
| 		dataPath := path.Join(appInstall.GetPath(), "data")
 | |
| 		if err := files.NewFileOp().Chown(dataPath, 200, 0); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.ComposeService, err error) {
 | |
| 	var (
 | |
| 		project *types.Project
 | |
| 		envStr  string
 | |
| 	)
 | |
| 	envStr, err = coverEnvJsonToStr(appInstall.Env)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	project, err = composeV2.GetComposeProject(appInstall.Name, appInstall.GetPath(), []byte(appInstall.DockerCompose), []byte(envStr), true)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	service, err = composeV2.NewComposeService()
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	service.SetProject(project)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func upApp(appInstall *model.AppInstall) {
 | |
| 	upProject := func(appInstall *model.AppInstall) (err error) {
 | |
| 		if err == nil {
 | |
| 			var composeService *composeV2.ComposeService
 | |
| 			composeService, err = getServiceFromInstall(appInstall)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			err = composeService.ComposeUp()
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			return
 | |
| 		} else {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	if err := upProject(appInstall); err != nil {
 | |
| 		appInstall.Status = constant.Error
 | |
| 		appInstall.Message = err.Error()
 | |
| 	} else {
 | |
| 		appInstall.Status = constant.Running
 | |
| 	}
 | |
| 	exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
 | |
| 	if exist.ID > 0 {
 | |
| 		_ = appInstallRepo.Save(context.Background(), appInstall)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func rebuildApp(appInstall model.AppInstall) error {
 | |
| 	dockerComposePath := appInstall.GetComposePath()
 | |
| 	out, err := compose.Down(dockerComposePath)
 | |
| 	if err != nil {
 | |
| 		return handleErr(appInstall, err, out)
 | |
| 	}
 | |
| 	out, err = compose.Up(dockerComposePath)
 | |
| 	if err != nil {
 | |
| 		return handleErr(appInstall, err, out)
 | |
| 	}
 | |
| 	return syncById(appInstall.ID)
 | |
| }
 | |
| 
 | |
| func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
 | |
| 	appDetails := make(map[string]model.AppDetail, len(details))
 | |
| 	for _, old := range details {
 | |
| 		old.Status = constant.AppTakeDown
 | |
| 		appDetails[old.Version] = old
 | |
| 	}
 | |
| 
 | |
| 	for _, v := range versions {
 | |
| 		detail, ok := appDetails[v]
 | |
| 		if ok {
 | |
| 			detail.Status = constant.AppNormal
 | |
| 			appDetails[v] = detail
 | |
| 		} else {
 | |
| 			appDetails[v] = model.AppDetail{
 | |
| 				Version: v,
 | |
| 				Status:  constant.AppNormal,
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return appDetails
 | |
| }
 | |
| 
 | |
| func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App {
 | |
| 	apps := make(map[string]model.App, len(oldApps))
 | |
| 	for _, old := range oldApps {
 | |
| 		old.Status = constant.AppTakeDown
 | |
| 		apps[old.Key] = old
 | |
| 	}
 | |
| 	for _, item := range items {
 | |
| 		key := item.Key
 | |
| 		if isLocal {
 | |
| 			key = "local" + key
 | |
| 		}
 | |
| 		app, ok := apps[key]
 | |
| 		if !ok {
 | |
| 			app = model.App{}
 | |
| 		}
 | |
| 		if isLocal {
 | |
| 			app.Resource = constant.AppResourceLocal
 | |
| 		} else {
 | |
| 			app.Resource = constant.AppResourceRemote
 | |
| 		}
 | |
| 		app.Name = item.Name
 | |
| 		app.Limit = item.Limit
 | |
| 		app.Key = key
 | |
| 		app.ShortDescZh = item.ShortDescZh
 | |
| 		app.ShortDescEn = item.ShortDescEn
 | |
| 		app.Website = item.Website
 | |
| 		app.Document = item.Document
 | |
| 		app.Github = item.Github
 | |
| 		app.Type = item.Type
 | |
| 		app.CrossVersionUpdate = item.CrossVersionUpdate
 | |
| 		app.Required = item.GetRequired()
 | |
| 		app.Status = constant.AppNormal
 | |
| 		apps[key] = app
 | |
| 	}
 | |
| 	return apps
 | |
| }
 | |
| 
 | |
| func handleErr(install model.AppInstall, err error, out string) error {
 | |
| 	reErr := err
 | |
| 	install.Message = err.Error()
 | |
| 	if out != "" {
 | |
| 		install.Message = out
 | |
| 		reErr = errors.New(out)
 | |
| 		install.Status = constant.Error
 | |
| 	}
 | |
| 	_ = appInstallRepo.Save(context.Background(), &install)
 | |
| 	return reErr
 | |
| }
 | |
| 
 | |
| func getAppFromRepo(downloadPath, version string) error {
 | |
| 	downloadUrl := downloadPath
 | |
| 	appDir := constant.AppResourceDir
 | |
| 
 | |
| 	global.LOG.Infof("download file from %s", downloadUrl)
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	if _, err := fileOp.CopyAndBackup(appDir); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl))
 | |
| 	if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	_ = NewISettingService().Update("AppStoreVersion", version)
 | |
| 	defer func() {
 | |
| 		_ = fileOp.DeleteFile(packagePath)
 | |
| 	}()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
 | |
| 	var res []response.AppInstalledDTO
 | |
| 	for _, installed := range appInstallList {
 | |
| 		installDTO := response.AppInstalledDTO{
 | |
| 			AppInstall: installed,
 | |
| 		}
 | |
| 		app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		var versions []string
 | |
| 		for _, detail := range details {
 | |
| 			versions = append(versions, detail.Version)
 | |
| 		}
 | |
| 		versions = common.GetSortedVersions(versions)
 | |
| 		lastVersion := versions[0]
 | |
| 		if common.IsCrossVersion(installed.Version, lastVersion) {
 | |
| 			installDTO.CanUpdate = app.CrossVersionUpdate
 | |
| 		} else {
 | |
| 			installDTO.CanUpdate = common.CompareVersion(lastVersion, installed.Version)
 | |
| 		}
 | |
| 		if updated {
 | |
| 			if installDTO.CanUpdate {
 | |
| 				res = append(res, installDTO)
 | |
| 			}
 | |
| 		} else {
 | |
| 			res = append(res, installDTO)
 | |
| 		}
 | |
| 	}
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| func getAppInstallByKey(key string) (model.AppInstall, error) {
 | |
| 	app, err := appRepo.GetFirst(appRepo.WithKey(key))
 | |
| 	if err != nil {
 | |
| 		return model.AppInstall{}, err
 | |
| 	}
 | |
| 	appInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))
 | |
| 	if err != nil {
 | |
| 		return model.AppInstall{}, err
 | |
| 	}
 | |
| 	return appInstall, nil
 | |
| }
 | |
| 
 | |
| func updateToolApp(installed *model.AppInstall) {
 | |
| 	tooKey, ok := dto.AppToolMap[installed.App.Key]
 | |
| 	if !ok {
 | |
| 		return
 | |
| 	}
 | |
| 	toolInstall, _ := getAppInstallByKey(tooKey)
 | |
| 	if reflect.DeepEqual(toolInstall, model.AppInstall{}) {
 | |
| 		return
 | |
| 	}
 | |
| 	paramMap := make(map[string]string)
 | |
| 	_ = json.Unmarshal([]byte(installed.Param), ¶mMap)
 | |
| 	envMap := make(map[string]interface{})
 | |
| 	_ = json.Unmarshal([]byte(toolInstall.Env), &envMap)
 | |
| 	if password, ok := paramMap["PANEL_DB_ROOT_PASSWORD"]; ok {
 | |
| 		envMap["PANEL_DB_ROOT_PASSWORD"] = password
 | |
| 	}
 | |
| 	if _, ok := envMap["PANEL_REDIS_HOST"]; ok {
 | |
| 		envMap["PANEL_REDIS_HOST"] = installed.ServiceName
 | |
| 	}
 | |
| 	if _, ok := envMap["PANEL_DB_HOST"]; ok {
 | |
| 		envMap["PANEL_DB_HOST"] = installed.ServiceName
 | |
| 	}
 | |
| 
 | |
| 	envPath := path.Join(toolInstall.GetPath(), ".env")
 | |
| 	contentByte, err := json.Marshal(envMap)
 | |
| 	if err != nil {
 | |
| 		global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	envFileMap := make(map[string]string)
 | |
| 	handleMap(envMap, envFileMap)
 | |
| 	if err = env.Write(envFileMap, envPath); err != nil {
 | |
| 		global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	toolInstall.Env = string(contentByte)
 | |
| 	if err := appInstallRepo.Save(context.Background(), &toolInstall); err != nil {
 | |
| 		global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if out, err := compose.Down(toolInstall.GetComposePath()); err != nil {
 | |
| 		global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, out)
 | |
| 		return
 | |
| 	}
 | |
| 	if out, err := compose.Up(toolInstall.GetComposePath()); err != nil {
 | |
| 		global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, out)
 | |
| 		return
 | |
| 	}
 | |
| }
 |