mirror of https://github.com/1Panel-dev/1Panel
feat: 优化应用商店同步 (#2795)
parent
5ff23f44d5
commit
a55d571bc9
|
@ -44,8 +44,13 @@ func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !res.CanUpdate {
|
if !res.CanUpdate {
|
||||||
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
|
if res.IsSyncing {
|
||||||
|
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsSyncing"))
|
||||||
|
} else {
|
||||||
|
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsUpToDate"))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ type AppRes struct {
|
||||||
|
|
||||||
type AppUpdateRes struct {
|
type AppUpdateRes struct {
|
||||||
CanUpdate bool `json:"canUpdate"`
|
CanUpdate bool `json:"canUpdate"`
|
||||||
|
IsSyncing bool `json:"isSyncing"`
|
||||||
AppStoreLastModified int `json:"appStoreLastModified"`
|
AppStoreLastModified int `json:"appStoreLastModified"`
|
||||||
AppList *dto.AppList `json:"appList"`
|
AppList *dto.AppList `json:"appList"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ type SettingInfo struct {
|
||||||
|
|
||||||
AppStoreVersion string `json:"appStoreVersion"`
|
AppStoreVersion string `json:"appStoreVersion"`
|
||||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||||
|
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingUpdate struct {
|
type SettingUpdate struct {
|
||||||
|
|
|
@ -222,6 +222,25 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
|
||||||
appDetailDTO.Params = paramMap
|
appDetailDTO.Params = paramMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if appDetailDTO.DockerCompose == "" {
|
||||||
|
filename := path.Base(appDetailDTO.DownloadUrl)
|
||||||
|
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
|
||||||
|
composeRes, err := http.Get(dockerComposeUrl)
|
||||||
|
if err != nil {
|
||||||
|
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
||||||
|
}
|
||||||
|
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||||
|
if err != nil {
|
||||||
|
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
|
||||||
|
}
|
||||||
|
if composeRes.StatusCode > 200 {
|
||||||
|
return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(bodyContent), err)
|
||||||
|
}
|
||||||
|
detail.DockerCompose = string(bodyContent)
|
||||||
|
_ = appDetailRepo.Update(context.Background(), detail)
|
||||||
|
appDetailDTO.DockerCompose = string(bodyContent)
|
||||||
|
}
|
||||||
|
|
||||||
appDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose)
|
appDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose)
|
||||||
|
|
||||||
app, err := appRepo.GetFirst(commonRepo.WithByID(detail.AppId))
|
app, err := appRepo.GetFirst(commonRepo.WithByID(detail.AppId))
|
||||||
|
@ -696,6 +715,11 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if setting.AppStoreSyncStatus == constant.Syncing {
|
||||||
|
res.IsSyncing = true
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
appStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)
|
appStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)
|
||||||
res.AppStoreLastModified = appStoreLastModified
|
res.AppStoreLastModified = appStoreLastModified
|
||||||
if setting.AppStoreLastModified == "" || lastModified != appStoreLastModified {
|
if setting.AppStoreLastModified == "" || lastModified != appStoreLastModified {
|
||||||
|
@ -722,7 +746,7 @@ func getAppFromRepo(downloadPath string) error {
|
||||||
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
|
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.SdkZip); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -747,6 +771,12 @@ func getAppList() (*dto.AppList, error) {
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var InitTypes = map[string]struct{}{
|
||||||
|
"runtime": {},
|
||||||
|
"php": {},
|
||||||
|
"node": {},
|
||||||
|
}
|
||||||
|
|
||||||
func (a AppService) SyncAppListFromRemote() (err error) {
|
func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
global.LOG.Infof("Starting synchronization with App Store...")
|
global.LOG.Infof("Starting synchronization with App Store...")
|
||||||
updateRes, err := a.GetAppUpdate()
|
updateRes, err := a.GetAppUpdate()
|
||||||
|
@ -754,9 +784,14 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !updateRes.CanUpdate {
|
if !updateRes.CanUpdate {
|
||||||
|
if updateRes.IsSyncing {
|
||||||
|
global.LOG.Infof("AppStore is Syncing!")
|
||||||
|
return
|
||||||
|
}
|
||||||
global.LOG.Infof("The App Store is at the latest version")
|
global.LOG.Infof("The App Store is at the latest version")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
list := &dto.AppList{}
|
list := &dto.AppList{}
|
||||||
if updateRes.AppList == nil {
|
if updateRes.AppList == nil {
|
||||||
list, err = getAppList()
|
list, err = getAppList()
|
||||||
|
@ -766,16 +801,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
} else {
|
} else {
|
||||||
list = updateRes.AppList
|
list = updateRes.AppList
|
||||||
}
|
}
|
||||||
|
settingService := NewISettingService()
|
||||||
if err = NewISettingService().Update("AppStoreLastModified", strconv.Itoa(list.LastModified)); err != nil {
|
_ = settingService.Update("AppStoreSyncStatus", constant.Syncing)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
_ = NewISettingService().Update("AppStoreLastModified", strconv.Itoa(updateRes.AppStoreLastModified))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tags []*model.Tag
|
tags []*model.Tag
|
||||||
|
@ -799,6 +826,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
|
|
||||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||||
appsMap := getApps(oldApps, list.Apps)
|
appsMap := getApps(oldApps, list.Apps)
|
||||||
|
|
||||||
|
global.LOG.Infof("Starting synchronization of application details...")
|
||||||
for _, l := range list.Apps {
|
for _, l := range list.Apps {
|
||||||
app := appsMap[l.AppProperty.Key]
|
app := appsMap[l.AppProperty.Key]
|
||||||
iconRes, err := http.Get(l.Icon)
|
iconRes, err := http.Get(l.Icon)
|
||||||
|
@ -825,16 +854,21 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
version := v.Name
|
version := v.Name
|
||||||
detail := detailsMap[version]
|
detail := detailsMap[version]
|
||||||
versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
|
versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
|
||||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
|
||||||
composeRes, err := http.Get(dockerComposeUrl)
|
if _, ok := InitTypes[app.Type]; ok {
|
||||||
if err != nil {
|
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||||
return err
|
composeRes, err := http.Get(dockerComposeUrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bodyContent, err := io.ReadAll(composeRes.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
detail.DockerCompose = string(bodyContent)
|
||||||
|
} else {
|
||||||
|
detail.DockerCompose = ""
|
||||||
}
|
}
|
||||||
bodyContent, err := io.ReadAll(composeRes.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
detail.DockerCompose = string(bodyContent)
|
|
||||||
|
|
||||||
paramByte, _ := json.Marshal(v.AppForm)
|
paramByte, _ := json.Marshal(v.AppForm)
|
||||||
detail.Params = string(paramByte)
|
detail.Params = string(paramByte)
|
||||||
|
@ -852,6 +886,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
appsMap[l.AppProperty.Key] = app
|
appsMap[l.AppProperty.Key] = app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.LOG.Infof("Synchronization of application details Success")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
addAppArray []model.App
|
addAppArray []model.App
|
||||||
updateAppArray []model.App
|
updateAppArray []model.App
|
||||||
|
@ -976,6 +1012,10 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
|
|
||||||
|
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||||
|
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
|
||||||
|
|
||||||
global.LOG.Infof("Synchronization with the App Store was successful!")
|
global.LOG.Infof("Synchronization with the App Store was successful!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -649,7 +649,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
||||||
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, appResourceDir, files.TarGz); err != nil {
|
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz); err != nil {
|
||||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -679,7 +679,7 @@ func changeServiceName(composePath, newServiceName string) (composeByte []byte,
|
||||||
|
|
||||||
func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
|
func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
|
||||||
var (
|
var (
|
||||||
ports map[int]struct{}
|
ports = make(map[int]struct{})
|
||||||
)
|
)
|
||||||
domainArray := strings.Split(domains, "\n")
|
domainArray := strings.Split(domains, "\n")
|
||||||
for _, domain := range domainArray {
|
for _, domain := range domainArray {
|
||||||
|
|
|
@ -6,13 +6,13 @@ const (
|
||||||
Error = "Error"
|
Error = "Error"
|
||||||
Stopped = "Stopped"
|
Stopped = "Stopped"
|
||||||
Installing = "Installing"
|
Installing = "Installing"
|
||||||
Syncing = "Syncing"
|
|
||||||
DownloadErr = "DownloadErr"
|
DownloadErr = "DownloadErr"
|
||||||
DirNotFound = "DirNotFound"
|
|
||||||
Upgrading = "Upgrading"
|
Upgrading = "Upgrading"
|
||||||
UpgradeErr = "UpgradeErr"
|
UpgradeErr = "UpgradeErr"
|
||||||
PullErr = "PullErr"
|
|
||||||
Rebuilding = "Rebuilding"
|
Rebuilding = "Rebuilding"
|
||||||
|
Syncing = "Syncing"
|
||||||
|
SyncSuccess = "SyncSuccess"
|
||||||
|
SyncErr = "SyncErr"
|
||||||
|
|
||||||
ContainerPrefix = "1Panel-"
|
ContainerPrefix = "1Panel-"
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
|
||||||
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
|
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
|
||||||
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
|
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
|
||||||
ErrAppNameExist: 'App name is already exist'
|
ErrAppNameExist: 'App name is already exist'
|
||||||
|
AppStoreIsSyncing: 'The App Store is syncing, please try again later'
|
||||||
|
ErrGetCompose: "Failed to obtain docker-compose.yml file! {{ .detail }}"
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "File can not read"
|
ErrFileCanNotRead: "File can not read"
|
||||||
|
|
|
@ -52,6 +52,8 @@ ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
|
||||||
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
|
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
|
||||||
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
|
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
|
||||||
ErrAppNameExist: '應用名稱已存在'
|
ErrAppNameExist: '應用名稱已存在'
|
||||||
|
AppStoreIsSyncing: '應用程式商店正在同步中,請稍後再試'
|
||||||
|
ErrGetCompose: "docker-compose.yml 檔案取得失敗!{{ .detail }}"
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "此文件不支持預覽"
|
ErrFileCanNotRead: "此文件不支持預覽"
|
||||||
|
|
|
@ -52,6 +52,8 @@ ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
|
||||||
ErrImagePull: '镜像拉取失败 {{.err}}'
|
ErrImagePull: '镜像拉取失败 {{.err}}'
|
||||||
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
|
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
|
||||||
ErrAppNameExist: '应用名称已存在'
|
ErrAppNameExist: '应用名称已存在'
|
||||||
|
AppStoreIsSyncing: '应用商店正在同步中,请稍后再试'
|
||||||
|
ErrGetCompose: "docker-compose.yml 文件获取失败!{{ .detail }}"
|
||||||
|
|
||||||
#file
|
#file
|
||||||
ErrFileCanNotRead: "此文件不支持预览"
|
ErrFileCanNotRead: "此文件不支持预览"
|
||||||
|
|
|
@ -2,6 +2,7 @@ package business
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ func Init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncApp() {
|
func syncApp() {
|
||||||
|
_ = service.NewISettingService().Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||||
global.LOG.Errorf("App Store synchronization failed")
|
global.LOG.Errorf("App Store synchronization failed")
|
||||||
return
|
return
|
||||||
|
|
|
@ -51,6 +51,7 @@ func Init() {
|
||||||
migrations.AddFavorite,
|
migrations.AddFavorite,
|
||||||
migrations.AddBindAddress,
|
migrations.AddBindAddress,
|
||||||
migrations.AddCommandGroup,
|
migrations.AddCommandGroup,
|
||||||
|
migrations.AddAppSyncStatus,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
|
|
@ -45,3 +45,13 @@ var AddCommandGroup = &gormigrate.Migration{
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddAppSyncStatus = &gormigrate.Migration{
|
||||||
|
ID: "20231103-update-table-setting",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.Create(&model.Setting{Key: "AppStoreSyncStatus", Value: "SyncSuccess"}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -402,7 +402,10 @@ func getFormat(cType CompressType) archiver.CompressedArchive {
|
||||||
case TarGz, Gz:
|
case TarGz, Gz:
|
||||||
format.Compression = archiver.Gz{}
|
format.Compression = archiver.Gz{}
|
||||||
format.Archival = archiver.Tar{}
|
format.Archival = archiver.Tar{}
|
||||||
case Zip:
|
case SdkTarGz:
|
||||||
|
format.Compression = archiver.Gz{}
|
||||||
|
format.Archival = archiver.Tar{}
|
||||||
|
case SdkZip:
|
||||||
format.Archival = archiver.Zip{
|
format.Archival = archiver.Zip{
|
||||||
Compression: zip.Deflate,
|
Compression: zip.Deflate,
|
||||||
}
|
}
|
||||||
|
|
|
@ -351,10 +351,12 @@ func min(x, y int) int {
|
||||||
type CompressType string
|
type CompressType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Zip CompressType = "zip"
|
Zip CompressType = "zip"
|
||||||
Gz CompressType = "gz"
|
Gz CompressType = "gz"
|
||||||
Bz2 CompressType = "bz2"
|
Bz2 CompressType = "bz2"
|
||||||
Tar CompressType = "tar"
|
Tar CompressType = "tar"
|
||||||
TarGz CompressType = "tar.gz"
|
TarGz CompressType = "tar.gz"
|
||||||
Xz CompressType = "xz"
|
Xz CompressType = "xz"
|
||||||
|
SdkZip CompressType = "sdkZip"
|
||||||
|
SdkTarGz CompressType = "sdkTarGz"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue