feat: 为应用版本查询增加appCode参数支持

新增应用版本类型的AppCode字段,修改相关接口和实现以支持通过appCode查询指定应用的最新版本,增加appCode合法性校验,完善缓存键以区分不同应用的版本信息
This commit is contained in:
Austin
2026-10-12 14:59:23 +08:00
parent 0a33d21eb8
commit 6be7796049
4 changed files with 32 additions and 8 deletions
+2 -1
View File
@@ -276,8 +276,9 @@ func (c *adminInternalClient) TenantLoginIPWhitelist(id types.TenantID) (*tenant
return &result, nil 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 := url.Values{}
query.Set("app_code", strings.ToLower(strings.TrimSpace(appCode)))
query.Set("platform", strings.ToLower(strings.TrimSpace(platform))) query.Set("platform", strings.ToLower(strings.TrimSpace(platform)))
query.Set("channel", strings.ToLower(strings.TrimSpace(channel))) query.Set("channel", strings.ToLower(strings.TrimSpace(channel)))
var dto struct { var dto struct {
+22 -1
View File
@@ -2287,10 +2287,18 @@ func serveIMAppReleaseLatest(wrt http.ResponseWriter, req *http.Request) {
} }
platform := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("platform"))) platform := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("platform")))
channel := strings.ToLower(strings.TrimSpace(req.URL.Query().Get("channel"))) 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")) currentVersion := strings.TrimSpace(req.URL.Query().Get("current_version"))
if channel == "" { if channel == "" {
channel = "stable" 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" { if platform != "ios" && platform != "android" && platform != "web" {
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "platform"}) writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "platform"})
return 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"}) writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "current_version"})
return return
} }
release, err := store.AppReleases.Latest(platform, channel) release, err := store.AppReleases.Latest(appCode, platform, channel)
if err != nil { if err != nil {
if platform == "web" { if platform == "web" {
logs.Warn.Println("im app release latest: using web fallback", err) 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) { func parseNumericAppVersion(raw string) ([]uint64, error) {
raw = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(raw)), "v") raw = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(raw)), "v")
parts := strings.Split(raw, ".") parts := strings.Split(raw, ".")
+7 -6
View File
@@ -171,7 +171,7 @@ type AdminInternalConfigProvider interface {
ListActiveTenants(limit int) ([]types.Tenant, error) ListActiveTenants(limit int) ([]types.Tenant, error)
RegistrationSMSEnabled() (bool, error) RegistrationSMSEnabled() (bool, error)
ThirdPartyConfig(group string, keys ...string) (map[string]string, 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) ModerationProvider() (*types.ModerationProviderConfig, error)
ModerationPolicy(tenantCode string) (*types.ModerationPolicy, error) ModerationPolicy(tenantCode string) (*types.ModerationPolicy, error)
SensitiveWords(tenantCode string) (*types.SensitiveWordSet, error) SensitiveWords(tenantCode string) (*types.SensitiveWordSet, error)
@@ -2654,20 +2654,21 @@ type profileMapper struct{}
var Profiles ProfilePersistenceInterface var Profiles ProfilePersistenceInterface
type AppReleasePersistenceInterface interface { type AppReleasePersistenceInterface interface {
Latest(platform, channel string) (*types.AppRelease, error) Latest(appCode, platform, channel string) (*types.AppRelease, error)
} }
type appReleaseMapper struct{} type appReleaseMapper struct{}
var AppReleases AppReleasePersistenceInterface 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)) platform = strings.ToLower(strings.TrimSpace(platform))
channel = strings.ToLower(strings.TrimSpace(channel)) channel = strings.ToLower(strings.TrimSpace(channel))
if platform == "" || channel == "" { appCode = strings.ToLower(strings.TrimSpace(appCode))
if platform == "" || channel == "" || appCode == "" {
return nil, types.ErrMalformed return nil, types.ErrMalformed
} }
cacheKey := platform + ":" + channel cacheKey := appCode + ":" + platform + ":" + channel
if cached, ok := appReleaseConfigCache.get(cacheKey); ok { if cached, ok := appReleaseConfigCache.get(cacheKey); ok {
copy := *cached copy := *cached
return &copy, nil return &copy, nil
@@ -2675,7 +2676,7 @@ func (appReleaseMapper) Latest(platform, channel string) (*types.AppRelease, err
if adminInternalConfigProvider == nil { if adminInternalConfigProvider == nil {
return nil, types.ErrUnsupported return nil, types.ErrUnsupported
} }
release, err := adminInternalConfigProvider.LatestAppRelease(platform, channel) release, err := adminInternalConfigProvider.LatestAppRelease(appCode, platform, channel)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+1
View File
@@ -56,6 +56,7 @@ type UserPrivacyPatch struct {
type AppRelease struct { type AppRelease struct {
ID int64 ID int64
AppCode string
Platform string Platform string
Channel string Channel string
LatestVersion string LatestVersion string