From 6be779604959595d35e2473ea7516bafa52c251a Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 12 Oct 2026 14:59:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BA=E5=BA=94=E7=94=A8=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=9F=A5=E8=AF=A2=E5=A2=9E=E5=8A=A0appCode=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增应用版本类型的AppCode字段,修改相关接口和实现以支持通过appCode查询指定应用的最新版本,增加appCode合法性校验,完善缓存键以区分不同应用的版本信息 --- server/admin_internal_config.go | 3 ++- server/im_http.go | 23 ++++++++++++++++++++++- server/store/store.go | 13 +++++++------ server/store/types/profile.go | 1 + 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/server/admin_internal_config.go b/server/admin_internal_config.go index f4e827c..8c97333 100644 --- a/server/admin_internal_config.go +++ b/server/admin_internal_config.go @@ -276,8 +276,9 @@ func (c *adminInternalClient) TenantLoginIPWhitelist(id types.TenantID) (*tenant return &result, nil } -func (c *adminInternalClient) LatestAppRelease(platform, channel string) (*types.AppRelease, error) { +func (c *adminInternalClient) LatestAppRelease(appCode, platform, channel string) (*types.AppRelease, error) { query := url.Values{} + query.Set("app_code", strings.ToLower(strings.TrimSpace(appCode))) query.Set("platform", strings.ToLower(strings.TrimSpace(platform))) query.Set("channel", strings.ToLower(strings.TrimSpace(channel))) var dto struct { diff --git a/server/im_http.go b/server/im_http.go index a0bbeb0..c4eb21e 100644 --- a/server/im_http.go +++ b/server/im_http.go @@ -2287,10 +2287,18 @@ func serveIMAppReleaseLatest(wrt http.ResponseWriter, req *http.Request) { } platform := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("platform"))) channel := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("channel"))) + appCode := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("app_code"))) currentVersion := strings.TrimSpace(req.URL.Query().Get("current_version")) if channel == "" { channel = "stable" } + if appCode == "" { + appCode = "im" + } + if !isValidAppCode(appCode) { + writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "app_code"}) + return + } if platform != "ios" && platform != "android" && platform != "web" { writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "platform"}) return @@ -2300,7 +2308,7 @@ func serveIMAppReleaseLatest(wrt http.ResponseWriter, req *http.Request) { writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "current_version"}) return } - release, err := store.AppReleases.Latest(platform, channel) + release, err := store.AppReleases.Latest(appCode, platform, channel) if err != nil { if platform == "web" { logs.Warn.Println("im app release latest: using web fallback", err) @@ -8782,6 +8790,19 @@ func appReleasePayload(release *types.AppRelease, currentVersion string, hasUpda } } +func isValidAppCode(raw string) bool { + if raw == "" || len(raw) > 32 { + return false + } + for _, r := range raw { + if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' { + continue + } + return false + } + return true +} + func parseNumericAppVersion(raw string) ([]uint64, error) { raw = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(raw)), "v") parts := strings.Split(raw, ".") diff --git a/server/store/store.go b/server/store/store.go index bb5b613..2002c12 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -171,7 +171,7 @@ type AdminInternalConfigProvider interface { ListActiveTenants(limit int) ([]types.Tenant, error) RegistrationSMSEnabled() (bool, error) ThirdPartyConfig(group string, keys ...string) (map[string]string, error) - LatestAppRelease(platform, channel string) (*types.AppRelease, error) + LatestAppRelease(appCode, platform, channel string) (*types.AppRelease, error) ModerationProvider() (*types.ModerationProviderConfig, error) ModerationPolicy(tenantCode string) (*types.ModerationPolicy, error) SensitiveWords(tenantCode string) (*types.SensitiveWordSet, error) @@ -2654,20 +2654,21 @@ type profileMapper struct{} var Profiles ProfilePersistenceInterface type AppReleasePersistenceInterface interface { - Latest(platform, channel string) (*types.AppRelease, error) + Latest(appCode, platform, channel string) (*types.AppRelease, error) } type appReleaseMapper struct{} var AppReleases AppReleasePersistenceInterface -func (appReleaseMapper) Latest(platform, channel string) (*types.AppRelease, error) { +func (appReleaseMapper) Latest(appCode, platform, channel string) (*types.AppRelease, error) { platform = strings.ToLower(strings.TrimSpace(platform)) channel = strings.ToLower(strings.TrimSpace(channel)) - if platform == "" || channel == "" { + appCode = strings.ToLower(strings.TrimSpace(appCode)) + if platform == "" || channel == "" || appCode == "" { return nil, types.ErrMalformed } - cacheKey := platform + ":" + channel + cacheKey := appCode + ":" + platform + ":" + channel if cached, ok := appReleaseConfigCache.get(cacheKey); ok { copy := *cached return ©, nil @@ -2675,7 +2676,7 @@ func (appReleaseMapper) Latest(platform, channel string) (*types.AppRelease, err if adminInternalConfigProvider == nil { return nil, types.ErrUnsupported } - release, err := adminInternalConfigProvider.LatestAppRelease(platform, channel) + release, err := adminInternalConfigProvider.LatestAppRelease(appCode, platform, channel) if err != nil { return nil, err } diff --git a/server/store/types/profile.go b/server/store/types/profile.go index a502af1..ef3e3fe 100644 --- a/server/store/types/profile.go +++ b/server/store/types/profile.go @@ -56,6 +56,7 @@ type UserPrivacyPatch struct { type AppRelease struct { ID int64 + AppCode string Platform string Channel string LatestVersion string