diff --git a/agent/app/service/app.go b/agent/app/service/app.go index d33678ae4..f919235e5 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -258,26 +258,35 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response. fileOp := files.NewFileOp() versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version) - if !fileOp.Stat(versionPath) || detail.Update { - if err = downloadApp(app, detail, nil, nil); err != nil && !fileOp.Stat(versionPath) { - return appDetailDTO, err - } + if err = downloadApp(app, detail, nil, nil); err != nil && !fileOp.Stat(versionPath) { + return appDetailDTO, err + } + if err == nil && app.Resource == constant.AppResourceRemote { + detail.Update = false + appDetailDTO.Update = false } switch app.Type { case constant.RuntimePHP: - paramsPath := filepath.Join(versionPath, "data.yml") - if !fileOp.Stat(paramsPath) { - return appDetailDTO, buserr.WithDetail("ErrFileNotExist", paramsPath, nil) - } - param, err := fileOp.GetContent(paramsPath) - if err != nil { - return appDetailDTO, err - } paramMap := make(map[string]interface{}) - if err = yaml.Unmarshal(param, ¶mMap); err != nil { - return appDetailDTO, err + if app.Resource == constant.AppResourceRemote { + if err = json.Unmarshal([]byte(detail.Params), ¶mMap); err != nil { + return appDetailDTO, err + } + appDetailDTO.Params = paramMap + } else { + paramsPath := filepath.Join(versionPath, "data.yml") + if !fileOp.Stat(paramsPath) { + return appDetailDTO, buserr.WithDetail("ErrFileNotExist", paramsPath, nil) + } + param, err := fileOp.GetContent(paramsPath) + if err != nil { + return appDetailDTO, err + } + if err = yaml.Unmarshal(param, ¶mMap); err != nil { + return appDetailDTO, err + } + appDetailDTO.Params = paramMap["additionalProperties"] } - appDetailDTO.Params = paramMap["additionalProperties"] composePath := filepath.Join(versionPath, "docker-compose.yml") if !fileOp.Stat(composePath) { return appDetailDTO, buserr.WithDetail("ErrFileNotExist", composePath, nil) @@ -308,6 +317,12 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response. if appDetailDTO.DockerCompose == "" { filename := filepath.Base(appDetailDTO.DownloadUrl) dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml") + if app.Resource == constant.AppResourceRemote { + dockerComposeUrl, err = currentAppDownloadURL(dockerComposeUrl) + if err != nil { + return appDetailDTO, err + } + } statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s) if statusCode == http.StatusNotFound { return appDetailDTO, buserr.New("ErrAppVersionUnavailable") @@ -434,6 +449,12 @@ func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript } else { if appDetail.DockerCompose == "" { dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.AppRepoURL(), global.CONF.Base.Mode, app.Key, appDetail.Version) + if app.Resource == constant.AppResourceRemote { + dockerComposeUrl, err = currentAppDownloadURL(dockerComposeUrl) + if err != nil { + return + } + } var statusCode int statusCode, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s) if statusCode == http.StatusNotFound { diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index d60faa1fb..c4bac8d1d 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -707,6 +707,12 @@ func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]d if req.UpdateVersion != "" && req.UpdateVersion == detail.Version && detail.DockerCompose == "" && !app.IsLocalApp() { filename := filepath.Base(detail.DownloadUrl) dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(detail.DownloadUrl, filename), "docker-compose.yml") + if app.Resource == constant.AppResourceRemote { + dockerComposeUrl, err = currentAppDownloadURL(dockerComposeUrl) + if err != nil { + return versions, err + } + } statusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s) if statusCode == http.StatusNotFound { return versions, buserr.New("ErrAppVersionUnavailable") diff --git a/agent/app/service/app_sync_task.go b/agent/app/service/app_sync_task.go index b05c07f56..1639a9e44 100644 --- a/agent/app/service/app_sync_task.go +++ b/agent/app/service/app_sync_task.go @@ -191,7 +191,11 @@ func (c *appSyncContext) processOneApp(item appWorkItem) appWorkResult { if _, ok := InitTypes[app.Type]; ok { dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml") - _, composeRes, err := req_helper.HandleRequestWithClient(&c.httpClient, dockerComposeUrl, http.MethodGet, constant.TimeOut20s) + dockerComposeUrl, err := appDownloadURL(dockerComposeUrl, strconv.Itoa(c.list.LastModified)) + var composeRes []byte + if err == nil { + _, composeRes, err = req_helper.HandleRequestWithClient(&c.httpClient, dockerComposeUrl, http.MethodGet, constant.TimeOut20s) + } if err == nil { detail.DockerCompose = string(composeRes) } else { diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 68b24e2a7..a8b5db48b 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -8,6 +8,7 @@ import ( "maps" "math" "net/http" + "net/url" "os" "path" "path/filepath" @@ -891,16 +892,52 @@ func handleMap(params map[string]interface{}, envParams map[string]string) { } } +func appDownloadURL(rawURL, version string) (string, error) { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return "", err + } + query := parsedURL.Query() + query.Set("v", version) + parsedURL.RawQuery = query.Encode() + return parsedURL.String(), nil +} + +func currentAppDownloadURL(rawURL string) (string, error) { + storeVersion, err := settingRepo.GetValueByKey("AppStoreLastModified") + if err != nil { + return "", err + } + return appDownloadURL(rawURL, storeVersion) +} + func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *logrus.Logger) (err error) { if app.IsLocalApp() || app.IsCustomApp() { return nil } + defer func() { + if err != nil && appInstall != nil { + appInstall.Status = constant.StatusDownloadErr + appInstall.Message = err.Error() + } + }() appResourceDir := path.Join(global.Dir.AppResourceDir, app.Resource) appDownloadDir := app.GetAppResourcePath() appVersionDir := path.Join(appDownloadDir, appDetail.Version) fileOp := files.NewFileOp() + storeVersion, err := settingRepo.GetValueByKey("AppStoreLastModified") + if err != nil { + return err + } + cacheVersionPath := path.Join(appVersionDir, ".1panel-appstore-version") if !appDetail.Update && fileOp.Stat(appVersionDir) { - return + if cachedVersion, readErr := os.ReadFile(cacheVersionPath); readErr == nil && string(cachedVersion) == storeVersion { + return nil + } + } + downloadURL, err := appDownloadURL(appDetail.DownloadUrl, storeVersion) + if err != nil { + return err } if !fileOp.Stat(appDownloadDir) { _ = fileOp.CreateDir(appDownloadDir, constant.DirPerm) @@ -909,23 +946,14 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App _ = fileOp.CreateDir(appVersionDir, constant.DirPerm) } if logger == nil { - global.LOG.Infof("download app [%s] from %s", app.Name, appDetail.DownloadUrl) + global.LOG.Infof("download app [%s] from %s", app.Name, downloadURL) } else { - logger.Printf("download app [%s] from %s", app.Name, appDetail.DownloadUrl) + logger.Printf("download app [%s] from %s", app.Name, downloadURL) } filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz") - defer func() { - if err != nil { - if appInstall != nil { - appInstall.Status = constant.StatusDownloadErr - appInstall.Message = err.Error() - } - } - }() - - if err = files.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil { + if err = files.DownloadFileWithProxy(downloadURL, filePath); err != nil { if logger == nil { global.LOG.Errorf("download app [%s] error %v", app.Name, err) } else { @@ -942,6 +970,9 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App return } _ = fileOp.DeleteFile(filePath) + if err = os.WriteFile(cacheVersionPath, []byte(storeVersion), constant.FilePerm); err != nil { + return err + } appDetail.Update = false _ = appDetailRepo.Update(context.Background(), appDetail) return