632 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			632 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package service
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/buserr"
 | |
| 	"io/ioutil"
 | |
| 	"math"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"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/cmd"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/utils/common"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/utils/compose"
 | |
| 	"github.com/1Panel-dev/1Panel/backend/utils/files"
 | |
| 	"github.com/joho/godotenv"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"gopkg.in/yaml.v3"
 | |
| )
 | |
| 
 | |
| type DatabaseOp string
 | |
| 
 | |
| var (
 | |
| 	Add    DatabaseOp = "add"
 | |
| 	Delete DatabaseOp = "delete"
 | |
| )
 | |
| 
 | |
| func execDockerCommand(database model.DatabaseMysql, dbInstall model.AppInstall, op DatabaseOp) error {
 | |
| 	var auth dto.AuthParam
 | |
| 	var dbConfig dto.AppDatabase
 | |
| 	dbConfig.Password = database.Password
 | |
| 	dbConfig.DbUser = database.Username
 | |
| 	dbConfig.DbName = database.Name
 | |
| 	_ = json.Unmarshal([]byte(dbInstall.Param), &auth)
 | |
| 	execConfig := dto.ContainerExec{
 | |
| 		ContainerName: dbInstall.ContainerName,
 | |
| 		Auth:          auth,
 | |
| 		DbParam:       dbConfig,
 | |
| 	}
 | |
| 	out, err := cmd.Exec(getSqlStr(dbInstall.Version, op, execConfig))
 | |
| 	if err != nil {
 | |
| 		return errors.New(out)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func getSqlStr(version string, operate DatabaseOp, exec dto.ContainerExec) string {
 | |
| 	var str string
 | |
| 	param := exec.DbParam
 | |
| 
 | |
| 	if strings.Contains(version, "5.7") {
 | |
| 		if operate == Add {
 | |
| 			str = fmt.Sprintf("docker exec -i  %s  mysql -uroot -p%s  -e \"CREATE USER IF NOT EXISTS '%s'@'%%' IDENTIFIED BY '%s';\" -e \"create database %s;\" -e \"GRANT ALL ON %s.* TO '%s'@'%%' IDENTIFIED BY '%s';\" -e \"FLUSH PRIVILEGES;\"",
 | |
| 				exec.ContainerName, exec.Auth.RootPassword, param.DbUser, param.Password, param.DbName, param.DbName, param.DbUser, param.Password)
 | |
| 		}
 | |
| 		if operate == Delete {
 | |
| 			str = fmt.Sprintf("docker exec -i  %s  mysql -uroot -p%s   -e \"drop database %s;\"  -e \"drop user %s;\" ",
 | |
| 				exec.ContainerName, exec.Auth.RootPassword, param.DbName, param.DbUser)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if strings.Contains(version, "8.0") {
 | |
| 		if operate == Add {
 | |
| 			str = fmt.Sprintf("docker exec -i  %s  mysql -uroot -p%s  -e \"CREATE USER IF NOT EXISTS '%s'@'%%' IDENTIFIED BY '%s';\" -e \"create database %s;\" -e \"GRANT ALL ON %s.* TO '%s'@'%%';\" -e \"FLUSH PRIVILEGES;\"",
 | |
| 				exec.ContainerName, exec.Auth.RootPassword, param.DbUser, param.Password, param.DbName, param.DbName, param.DbUser)
 | |
| 		}
 | |
| 		if operate == Delete {
 | |
| 			str = fmt.Sprintf("docker exec -i  %s  mysql -uroot -p%s   -e \"drop database %s;\"  -e \"drop user %s;\" ",
 | |
| 				exec.ContainerName, exec.Auth.RootPassword, param.DbName, param.DbUser)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return str
 | |
| }
 | |
| 
 | |
| func checkPort(key string, params map[string]interface{}) (int, error) {
 | |
| 
 | |
| 	port, ok := params[key]
 | |
| 	if ok {
 | |
| 		portN := int(math.Ceil(port.(float64)))
 | |
| 		if common.ScanPort(portN) {
 | |
| 			return portN, buserr.New(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
 | |
| 		}
 | |
| 		authByte, err := json.Marshal(authParam)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		appInstall.Param = string(authByte)
 | |
| 	}
 | |
| 	if app.Type == "website" {
 | |
| 		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{}) {
 | |
| 		dbInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(dbConfig.ServiceName))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		var database model.DatabaseMysql
 | |
| 		database.Name = dbConfig.DbName
 | |
| 		database.Username = dbConfig.DbUser
 | |
| 		database.Password = dbConfig.Password
 | |
| 		database.MysqlName = dbInstall.Name
 | |
| 		database.Format = "utf8mb4"
 | |
| 		database.Permission = "127.0.0.1"
 | |
| 		if err := mysqlRepo.Create(ctx, &database); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		var installResource model.AppInstallResource
 | |
| 		installResource.ResourceId = database.ID
 | |
| 		installResource.AppInstallId = appInstall.ID
 | |
| 		installResource.LinkId = dbInstall.ID
 | |
| 		installResource.Key = dbInstall.App.Key
 | |
| 		if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := execDockerCommand(database, dbInstall, Add); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func deleteAppInstall(ctx context.Context, install model.AppInstall) error {
 | |
| 	op := files.NewFileOp()
 | |
| 	appDir := install.GetPath()
 | |
| 	dir, _ := os.Stat(appDir)
 | |
| 	if dir != nil {
 | |
| 		out, err := compose.Down(install.GetComposePath())
 | |
| 		if err != nil {
 | |
| 			return handleErr(install, err, out)
 | |
| 		}
 | |
| 		if err := op.DeleteDir(appDir); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err := appInstallRepo.Delete(ctx, install); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := deleteLink(ctx, &install); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	backups, _ := appInstallBackupRepo.GetBy(appInstallBackupRepo.WithAppInstallID(install.ID))
 | |
| 	for _, backup := range backups {
 | |
| 		_ = op.DeleteDir(backup.Path)
 | |
| 	}
 | |
| 	if err := appInstallBackupRepo.Delete(ctx, appInstallBackupRepo.WithAppInstallID(install.ID)); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func deleteLink(ctx context.Context, install *model.AppInstall) error {
 | |
| 	resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
 | |
| 	if len(resources) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	for _, re := range resources {
 | |
| 		if re.Key == "mysql" {
 | |
| 			database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
 | |
| 			if reflect.DeepEqual(database, model.DatabaseMysql{}) {
 | |
| 				continue
 | |
| 			}
 | |
| 			appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByName(database.MysqlName))
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			if err := execDockerCommand(database, appInstall, Delete); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			if err := mysqlRepo.Delete(ctx, commonRepo.WithByID(database.ID)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
 | |
| }
 | |
| 
 | |
| func updateInstall(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")
 | |
| 	}
 | |
| 	tx, ctx := getTxAndContext()
 | |
| 	if err := backupInstall(ctx, install); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	tx.Commit()
 | |
| 	if _, err = compose.Down(install.GetComposePath()); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	install.DockerCompose = detail.DockerCompose
 | |
| 	install.Version = detail.Version
 | |
| 
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if _, err = compose.Up(install.GetComposePath()); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return appInstallRepo.Save(&install)
 | |
| }
 | |
| 
 | |
| func backupInstall(ctx context.Context, install model.AppInstall) error {
 | |
| 	var backup model.AppInstallBackup
 | |
| 	appPath := install.GetPath()
 | |
| 
 | |
| 	backupAccount, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	varMap := make(map[string]interface{})
 | |
| 	if err := json.Unmarshal([]byte(backupAccount.Vars), &varMap); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	dir, ok := varMap["dir"]
 | |
| 	if !ok {
 | |
| 		return errors.New("load local backup dir failed")
 | |
| 	}
 | |
| 	baseDir, ok := dir.(string)
 | |
| 	if !ok {
 | |
| 		return errors.New("load local backup dir failed")
 | |
| 	}
 | |
| 	backupDir := path.Join(baseDir, "apps", install.App.Key, install.Name)
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	if !fileOp.Stat(backupDir) {
 | |
| 		_ = fileOp.CreateDir(backupDir, 0775)
 | |
| 	}
 | |
| 	now := time.Now()
 | |
| 	day := now.Format("20060102150405")
 | |
| 	fileName := fmt.Sprintf("%s_%s%s", install.Name, day, ".tar.gz")
 | |
| 	if err := fileOp.Compress([]string{appPath}, backupDir, fileName, files.TarGz); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	backup.Name = fileName
 | |
| 	backup.Path = backupDir
 | |
| 	backup.AppInstallId = install.ID
 | |
| 	backup.AppDetailId = install.AppDetailId
 | |
| 	backup.Param = install.Param
 | |
| 
 | |
| 	return appInstallBackupRepo.Create(ctx, backup)
 | |
| }
 | |
| 
 | |
| func restoreInstall(install model.AppInstall, backupId uint) error {
 | |
| 	backup, err := appInstallBackupRepo.GetFirst(commonRepo.WithByID(backupId))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if _, err := compose.Down(install.GetComposePath()); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	installKeyDir := path.Join(constant.AppInstallDir, install.App.Key)
 | |
| 	installDir := path.Join(installKeyDir, install.Name)
 | |
| 	backupFile := path.Join(backup.Path, backup.Name)
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	if !fileOp.Stat(backupFile) {
 | |
| 		return errors.New(fmt.Sprintf("%s file is not exist", backup.Name))
 | |
| 	}
 | |
| 
 | |
| 	backupDir, err := fileOp.Backup(installDir)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := fileOp.Decompress(backupFile, installKeyDir, files.TarGz); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	composeContent, err := os.ReadFile(install.GetComposePath())
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	install.DockerCompose = string(composeContent)
 | |
| 	envContent, err := os.ReadFile(path.Join(installDir, ".env"))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	install.Env = string(envContent)
 | |
| 	envMaps, err := godotenv.Unmarshal(string(envContent))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	install.HttpPort = 0
 | |
| 	httpPort, ok := envMaps["PANEL_APP_PORT_HTTP"]
 | |
| 	if ok {
 | |
| 		httpPortN, _ := strconv.Atoi(httpPort)
 | |
| 		install.HttpPort = httpPortN
 | |
| 	}
 | |
| 	install.HttpsPort = 0
 | |
| 	httpsPort, ok := envMaps["PANEL_APP_PORT_HTTPS"]
 | |
| 	if ok {
 | |
| 		httpsPortN, _ := strconv.Atoi(httpsPort)
 | |
| 		install.HttpsPort = httpsPortN
 | |
| 	}
 | |
| 
 | |
| 	composeMap := make(map[string]interface{})
 | |
| 	if err := yaml.Unmarshal(composeContent, &composeMap); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	servicesMap := composeMap["services"].(map[string]interface{})
 | |
| 	for k, v := range servicesMap {
 | |
| 		install.ServiceName = k
 | |
| 		value := v.(map[string]interface{})
 | |
| 		install.ContainerName = value["container_name"].(string)
 | |
| 	}
 | |
| 
 | |
| 	install.Param = backup.Param
 | |
| 	_ = fileOp.DeleteDir(backupDir)
 | |
| 	if out, err := compose.Up(install.GetComposePath()); err != nil {
 | |
| 		return handleErr(install, err, out)
 | |
| 	}
 | |
| 	install.AppDetailId = backup.AppDetailId
 | |
| 	install.Version = backup.AppDetail.Version
 | |
| 	install.Status = constant.Running
 | |
| 	return appInstallRepo.Save(&install)
 | |
| }
 | |
| 
 | |
| func getContainerNames(install model.AppInstall) ([]string, error) {
 | |
| 	composeMap := install.DockerCompose
 | |
| 	envMap := make(map[string]string)
 | |
| 	_ = json.Unmarshal([]byte(install.Env), &envMap)
 | |
| 	project, err := compose.GetComposeProject([]byte(composeMap), envMap)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	var containerNames []string
 | |
| 	for _, service := range project.AllServices() {
 | |
| 		containerNames = append(containerNames, service.ContainerName)
 | |
| 	}
 | |
| 	return containerNames, nil
 | |
| }
 | |
| 
 | |
| func checkLimit(app model.App) error {
 | |
| 	if app.Limit > 0 {
 | |
| 		installs, err := appInstallRepo.GetBy(appInstallRepo.WithAppId(app.ID))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if len(installs) >= app.Limit {
 | |
| 			return buserr.New(constant.ErrAppLimit, "", nil)
 | |
| 		}
 | |
| 	}
 | |
| 	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.New(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{}) (err error) {
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	resourceDir := path.Join(constant.AppResourceDir, key, "versions", version)
 | |
| 	installAppDir := path.Join(constant.AppInstallDir, key)
 | |
| 	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, appDir); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	envPath := path.Join(appDir, ".env")
 | |
| 
 | |
| 	envParams := make(map[string]string, len(params))
 | |
| 	handleMap(params, envParams)
 | |
| 	if err = godotenv.Write(envParams, envPath); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func upApp(composeFilePath string, appInstall model.AppInstall) {
 | |
| 	out, err := compose.Up(composeFilePath)
 | |
| 	if err != nil {
 | |
| 		if out != "" {
 | |
| 			appInstall.Message = out
 | |
| 		} else {
 | |
| 			appInstall.Message = err.Error()
 | |
| 		}
 | |
| 		appInstall.Status = constant.Error
 | |
| 		_ = appInstallRepo.Save(&appInstall)
 | |
| 	} else {
 | |
| 		appInstall.Status = constant.Running
 | |
| 		_ = appInstallRepo.Save(&appInstall)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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) 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 {
 | |
| 		app, ok := apps[item.Key]
 | |
| 		if !ok {
 | |
| 			app = model.App{}
 | |
| 		}
 | |
| 		app.Name = item.Name
 | |
| 		app.Limit = item.Limit
 | |
| 		app.Key = item.Key
 | |
| 		app.ShortDesc = item.ShortDesc
 | |
| 		app.Author = item.Author
 | |
| 		app.Source = item.Source
 | |
| 		app.Type = item.Type
 | |
| 		app.CrossVersionUpdate = item.CrossVersionUpdate
 | |
| 		app.Required = item.GetRequired()
 | |
| 		app.Status = constant.AppNormal
 | |
| 		apps[item.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)
 | |
| 	}
 | |
| 	_ = appInstallRepo.Save(&install)
 | |
| 	return reErr
 | |
| }
 | |
| 
 | |
| func getAppFromOss() error {
 | |
| 	res, err := http.Get(global.CONF.System.AppOss)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	appByte, err := ioutil.ReadAll(res.Body)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	var ossConfig dto.AppOssConfig
 | |
| 	if err := json.Unmarshal(appByte, &ossConfig); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	appDir := constant.AppResourceDir
 | |
| 	content, _ := os.ReadFile(path.Join(appDir, "apps.json"))
 | |
| 
 | |
| 	if content != nil {
 | |
| 		oldConfig := &dto.AppOssConfig{}
 | |
| 		if err := json.Unmarshal(content, oldConfig); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if oldConfig.Version == ossConfig.Version {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fileOp := files.NewFileOp()
 | |
| 	if _, err := fileOp.Backup(appDir); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	packagePath := path.Join(constant.ResourceDir, path.Base(ossConfig.Package))
 | |
| 	if err := fileOp.DownloadFile(ossConfig.Package, packagePath); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	defer func() {
 | |
| 		_ = fileOp.DeleteFile(packagePath)
 | |
| 	}()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func handleInstalled(installed []model.AppInstall) ([]dto.AppInstalled, error) {
 | |
| 	var res []dto.AppInstalled
 | |
| 
 | |
| 	for _, installed := range installed {
 | |
| 
 | |
| 		installDTO := dto.AppInstalled{
 | |
| 			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)
 | |
| 		}
 | |
| 		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
 | |
| }
 | 
