From 1e01761ba3ac84c3cf8d185aa6f7033820f147e8 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:53:36 +0800 Subject: [PATCH] fix(appstore): refresh cached resources and PHP versions Version official app package and compose downloads using the synchronized store revision. Refresh outdated local resource caches and use synchronized database parameters for official PHP version options. --- agent/app/service/app.go | 51 ++++++++++++++++++-------- agent/app/service/app_install.go | 6 ++++ agent/app/service/app_sync_task.go | 6 +++- agent/app/service/app_utils.go | 57 +++++++++++++++++++++++------- 4 files changed, 91 insertions(+), 29 deletions(-) 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