diff --git a/backend/app/api/v1/setting.go b/backend/app/api/v1/setting.go index ff0ac9913..146b22788 100644 --- a/backend/app/api/v1/setting.go +++ b/backend/app/api/v1/setting.go @@ -58,7 +58,7 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) { return } - if err := settingService.Update(c, req.Key, req.Value); err != nil { + if err := settingService.Update(req.Key, req.Value); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } @@ -240,12 +240,12 @@ func (b *BaseApi) MFABind(c *gin.Context) { return } - if err := settingService.Update(c, "MFAStatus", "enable"); err != nil { + if err := settingService.Update("MFAStatus", "enable"); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - if err := settingService.Update(c, "MFASecret", req.Secret); err != nil { + if err := settingService.Update("MFASecret", req.Secret); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index b87bc603e..74c60d8eb 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -30,6 +30,8 @@ type SettingInfo struct { EmailVars string `json:"emailVars"` WeChatVars string `json:"weChatVars"` DingVars string `json:"dingVars"` + + AppStoreVersion string `json:"appStoreVersion"` } type SettingUpdate struct { diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 84b9552e4..352a075d3 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -333,14 +333,12 @@ func (a AppService) SyncInstalled(installId uint) error { } func (a AppService) SyncAppList() error { - if err := getAppFromOss(); err != nil { + if err := getAppFromRepo(); err != nil { global.LOG.Errorf("get app from oss error: %s", err.Error()) return err } - appDir := constant.AppResourceDir listFile := path.Join(appDir, "list.json") - content, err := os.ReadFile(listFile) if err != nil { return err @@ -354,22 +352,18 @@ func (a AppService) SyncAppList() error { tags []*model.Tag appTags []*model.AppTag ) - for _, t := range list.Tags { tags = append(tags, &model.Tag{ Key: t.Key, Name: t.Name, }) } - oldApps, err := appRepo.GetBy() if err != nil { return err } appsMap := getApps(oldApps, list.Items) - for _, l := range list.Items { - app := appsMap[l.Key] icon, err := os.ReadFile(path.Join(appDir, l.Key, "metadata", "logo.png")) if err != nil { @@ -433,9 +427,7 @@ func (a AppService) SyncAppList() error { updateArray = append(updateArray, v) } } - tx, ctx := getTxAndContext() - if len(addAppArray) > 0 { if err := appRepo.BatchCreate(ctx, addAppArray); err != nil { tx.Rollback() @@ -461,7 +453,6 @@ func (a AppService) SyncAppList() error { return err } } - apps := append(addAppArray, updateArray...) var ( @@ -478,7 +469,6 @@ func (a AppService) SyncAppList() error { }) } } - for _, d := range a.Details { d.AppId = a.ID if d.ID == 0 { @@ -488,7 +478,6 @@ func (a AppService) SyncAppList() error { } } } - if len(addDetails) > 0 { if err := appDetailRepo.BatchCreate(ctx, addDetails); err != nil { tx.Rollback() diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go index a1c923cf1..af234ae62 100644 --- a/backend/app/service/app_utils.go +++ b/backend/app/service/app_utils.go @@ -4,9 +4,8 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "github.com/1Panel-dev/1Panel/backend/utils/git" "math" - "net/http" "os" "path" "reflect" @@ -500,46 +499,32 @@ func handleErr(install model.AppInstall, err error, out string) error { return reErr } -func getAppFromOss() error { - res, err := http.Get(global.CONF.System.AppOss + "/apps/apps.json") +func getAppFromRepo() error { + repoInfo, err := git.CheckAndGetInfo(global.CONF.System.AppRepoOwner, global.CONF.System.AppRepoName) 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, "list.json")) - - if content != nil { - oldConfig := &dto.AppOssConfig{} - if err := json.Unmarshal(content, oldConfig); err != nil { - return err - } - if oldConfig.Version == ossConfig.Version { - return nil - } + setting, err := NewISettingService().GetSettingInfo() + if err != nil { + return err } - + if !common.CompareVersion(repoInfo.Version, setting.AppStoreVersion) { + return nil + } + downloadUrl := fmt.Sprintf("%sapps-%s.tar.gz", repoInfo.DownloadPath, repoInfo.Version) fileOp := files.NewFileOp() - if _, err := fileOp.CopyAndBackup(appDir); err != nil { return err } - - packagePath := path.Join(constant.ResourceDir, path.Base(ossConfig.Package)) - if err := fileOp.DownloadFile(ossConfig.Package, packagePath); err != nil { + 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.Zip); err != nil { + if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil { return err } - + _ = NewISettingService().Update("AppStoreVersion", repoInfo.Version) defer func() { _ = fileOp.DeleteFile(packagePath) }() diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go index 40a179ada..bbb97c641 100644 --- a/backend/app/service/setting.go +++ b/backend/app/service/setting.go @@ -17,7 +17,7 @@ type SettingService struct{} type ISettingService interface { GetSettingInfo() (*dto.SettingInfo, error) - Update(c *gin.Context, key, value string) error + Update(key, value string) error UpdatePassword(c *gin.Context, old, new string) error UpdatePort(port uint) error HandlePasswordExpired(c *gin.Context, old, new string) error @@ -48,7 +48,7 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { return &info, err } -func (u *SettingService) Update(c *gin.Context, key, value string) error { +func (u *SettingService) Update(key, value string) error { if key == "ExpirationDays" { timeout, _ := strconv.Atoi(value) if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil { diff --git a/backend/app/service/upgrade.go b/backend/app/service/upgrade.go index 0f2ac3879..628021d9c 100644 --- a/backend/app/service/upgrade.go +++ b/backend/app/service/upgrade.go @@ -33,7 +33,7 @@ func NewIUpgradeService() IUpgradeService { } func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) { - currentVerion, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion")) + currentVersion, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion")) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) { } } if len(releaseInfo.NewVersion) != 0 { - isNew, err := compareVersion(currentVerion.Value, releaseInfo.NewVersion) + isNew, err := compareVersion(currentVersion.Value, releaseInfo.NewVersion) if !isNew && err != nil { return nil, err } @@ -183,8 +183,8 @@ func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string, func (u *UpgradeService) loadLatestFromGithub() (dto.UpgradeInfo, error) { var info dto.UpgradeInfo client := github.NewClient(nil) - ctx, cancle := context.WithTimeout(context.Background(), 3*time.Second) - defer cancle() + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() stats, res, err := client.Repositories.GetLatestRelease(ctx, "wanghe-fit2cloud", "1Panel") if res.StatusCode != 200 || err != nil { return info, fmt.Errorf("load upgrade info from github failed, err: %v", err) @@ -198,8 +198,8 @@ func (u *UpgradeService) loadLatestFromGithub() (dto.UpgradeInfo, error) { func (u *UpgradeService) loadLatestFromGitee() (dto.UpgradeInfo, error) { var info dto.UpgradeInfo client := gitee.NewAPIClient(gitee.NewConfiguration()) - ctx, cancle := context.WithTimeout(context.Background(), 3*time.Second) - defer cancle() + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() stats, res, err := client.RepositoriesApi.GetV5ReposOwnerRepoReleasesLatest(ctx, "wanghe-fit2cloud", "1Panel", &gitee.GetV5ReposOwnerRepoReleasesLatestOpts{}) if res.StatusCode != 200 || err != nil { return info, fmt.Errorf("load upgrade info from gitee failed, err: %v", err) diff --git a/backend/configs/system.go b/backend/configs/system.go index 9ad65bd41..d6fc84588 100644 --- a/backend/configs/system.go +++ b/backend/configs/system.go @@ -1,12 +1,14 @@ package configs type System struct { - Port string `mapstructure:"port"` - DbFile string `mapstructure:"db_file"` - DbPath string `mapstructure:"db_path"` - LogPath string `mapstructure:"log_path"` - DataDir string `mapstructure:"data_dir"` - Cache string `mapstructure:"cache"` - Backup string `mapstructure:"backup"` - AppOss string `mapstructure:"app_oss"` + Port string `mapstructure:"port"` + DbFile string `mapstructure:"db_file"` + DbPath string `mapstructure:"db_path"` + LogPath string `mapstructure:"log_path"` + DataDir string `mapstructure:"data_dir"` + Cache string `mapstructure:"cache"` + Backup string `mapstructure:"backup"` + AppOss string `mapstructure:"app_oss"` + AppRepoOwner string `mapstructure:"app_repo_owner"` + AppRepoName string `mapstructure:"app_repo_name"` } diff --git a/backend/constant/errs.go b/backend/constant/errs.go index a4a58ce4b..0a94ef0fb 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -32,6 +32,7 @@ var ( ErrTokenParse = errors.New("ErrTokenParse") ErrPageGenerate = errors.New("generate page info failed") + ErrRepoNotValid = "ErrRepoNotValid" ) // api diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 0198e8eba..cb0d1bf64 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -11,7 +11,7 @@ ErrNotLogin: "User is not Login: {{ .detail }}" ErrNotSafety: "The login status of the current user is unsafe: {{ .detail }}" ErrPasswordExpired: "The current password has expired: {{ .detail }}" ErrNotSupportType: "The system does not support the current type: {{ .detail }}" - +ErrRepoNotValid: "Remote repository verification failed!" #common ErrNameIsExist: "Name is already exist" diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 22ce0905a..5859a9d75 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -11,7 +11,7 @@ ErrNotLogin: "用户未登录: {{ .detail }}" ErrNotSafety: "当前用户登录状态不安全: {{ .detail }}" ErrPasswordExpired: "当前密码已过期: {{ .detail }}" ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}" - +ErrRepoNotValid: "远程仓库校验失败!" #common ErrNameIsExist: "名称已存在" diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 810f53ebc..485c72cd6 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -139,6 +139,9 @@ var AddTableSetting = &gormigrate.Migration{ if err := tx.Create(&model.Setting{Key: "SystemStatus", Value: "Free"}).Error; err != nil { return err } + if err := tx.Create(&model.Setting{Key: "AppStoreVersion", Value: "0"}).Error; err != nil { + return err + } return nil }, } diff --git a/backend/init/viper/viper.go b/backend/init/viper/viper.go index 7e7fee8d4..fe8ebcc8d 100644 --- a/backend/init/viper/viper.go +++ b/backend/init/viper/viper.go @@ -23,7 +23,7 @@ func Init() { v.SetConfigType("yaml") if fileOp.Stat("/opt/1panel/conf/app.yaml") { v.SetConfigName("app") - v.AddConfigPath(path.Join("/opt/1pane/conf")) + v.AddConfigPath(path.Join("/opt/1panel/conf")) if err := v.ReadInConfig(); err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) } diff --git a/backend/utils/git/git.go b/backend/utils/git/git.go new file mode 100644 index 000000000..32ff46a4a --- /dev/null +++ b/backend/utils/git/git.go @@ -0,0 +1,79 @@ +package git + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "gitee.com/openeuler/go-gitee/gitee" + "github.com/google/go-github/github" + "net/http" + "time" +) + +type RepoInfo struct { + RepoType string + Version string + ReleaseNote string + CreatedAt string + DownloadPath string +} + +var gitRepoTypes = []string{"gitee", "github"} + +func CheckAndGetInfo(owner, repoName string) (*RepoInfo, error) { + for _, repoType := range gitRepoTypes { + url := fmt.Sprintf("https://%s.com/%s/%s", repoType, owner, repoName) + if checkValid(url) { + res, err := getLatestRepoInfo(repoType, owner, repoName) + if err == nil { + return res, nil + } + } + } + return nil, errors.New("remote repo get failed") +} + +func checkValid(addr string) bool { + timeout := 2 * time.Second + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := http.Client{ + Transport: tr, + Timeout: timeout, + } + if _, err := client.Get(addr); err != nil { + return false + } + return true +} + +func getLatestRepoInfo(repoType, owner, repoName string) (*RepoInfo, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + var repoInfo RepoInfo + repoInfo.RepoType = repoType + if repoType == "gitee" { + client := gitee.NewAPIClient(gitee.NewConfiguration()) + stats, res, err := client.RepositoriesApi.GetV5ReposOwnerRepoReleasesLatest(ctx, owner, repoName, &gitee.GetV5ReposOwnerRepoReleasesLatestOpts{}) + if res.StatusCode != 200 || err != nil { + return nil, err + } + repoInfo.Version = stats.Name + repoInfo.ReleaseNote = stats.Body + repoInfo.CreatedAt = stats.CreatedAt.Format("2006-01-02 15:04:05") + repoInfo.DownloadPath = fmt.Sprintf("https://gitee.com/%s/%s/releases/download/%s/", owner, repoName, repoInfo.Version) + } else { + client := github.NewClient(nil) + stats, res, err := client.Repositories.GetLatestRelease(ctx, owner, repoName) + if res.StatusCode != 200 || err != nil { + return nil, err + } + repoInfo.Version = *stats.Name + repoInfo.ReleaseNote = *stats.Body + repoInfo.CreatedAt = stats.PublishedAt.Add(8 * time.Hour).Format("2006-01-02 15:04:05") + repoInfo.DownloadPath = fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/", owner, repoName, repoInfo.Version) + } + return &repoInfo, nil +} diff --git a/cmd/server/conf/app.yaml b/cmd/server/conf/app.yaml index 846f7a3f3..b407ee20d 100644 --- a/cmd/server/conf/app.yaml +++ b/cmd/server/conf/app.yaml @@ -1,6 +1,8 @@ system: db_file: 1Panel.db app_oss: "https://1panel.oss-cn-hangzhou.aliyuncs.com" + app_repo_owner: "1Panel-dev" + app_repo_name: 'appstore' log: level: debug