package services import ( "encoding/json" "strings" "server/models" beego "github.com/beego/beego/v2/server/web" ) // PublicRequestBaseURL 根据请求拼出对外访问的根(用于把 /uploads/... 拼成完整下载地址) func PublicRequestBaseURL(c *beego.Controller) (scheme, host string) { scheme = "http" if c.Ctx.Request.TLS != nil { scheme = "https" } if strings.EqualFold(strings.TrimSpace(c.Ctx.Request.Header.Get("X-Forwarded-Proto")), "https") { scheme = "https" } host = strings.TrimSpace(c.Ctx.Request.Host) return scheme, host } // ResolveSoftwareDownloadURL 优先使用自定义 download_url;否则根据 file_id 读附件 src 拼完整 URL func ResolveSoftwareDownloadURLs(scheme, host string, downloadURLs *string) map[string]string { result := map[string]string{} if downloadURLs == nil || strings.TrimSpace(*downloadURLs) == "" { return result } raw := strings.TrimSpace(*downloadURLs) var values map[string]string if err := json.Unmarshal([]byte(raw), &values); err != nil { return result } for k, v := range values { platform := strings.ToLower(strings.TrimSpace(k)) url := strings.TrimSpace(v) if platform == "" || url == "" { continue } result[platform] = ResolvePublicURL(scheme, host, url) } return result } // legacyStorageHosts 历史七牛 CDN 域名:这两个域名只支持 HTTP,HTTPS 页面直接引用会被 // 浏览器以 Mixed Content 拦截,需在返回给前端前改写为当前存储配置的访问入口。 var legacyStorageHosts = []string{"7colud.yunzer.cn", "7cloud.yunzer.cn"} // ResolvePublicURL 把相对路径拼成公开 URL;http(s) 地址原样返回, // 但历史七牛域名会改写为当前存储配置的访问基址(如 https://yunzer.com.cn/qiniu)。 func ResolvePublicURL(scheme, host, src string) string { src = strings.TrimSpace(src) if src == "" { return "" } lowerSrc := strings.ToLower(src) if strings.HasPrefix(lowerSrc, "http://") || strings.HasPrefix(lowerSrc, "https://") { return rewriteLegacyStorageURL(src) } if host == "" { return src } if !strings.HasPrefix(src, "/") { src = "/" + src } if scheme == "" { scheme = "http" } return scheme + "://" + host + src } // rewriteLegacyStorageURL 把历史七牛域名的 scheme+host 换成当前存储配置域名, // 保留对象路径不变;非历史域名或配置缺失时原样返回。 func rewriteLegacyStorageURL(src string) string { cfg, err := models.GetStorageConfig() if err != nil { return src } base := strings.TrimRight(strings.TrimSpace(cfg.QiniuDomain), "/") if base == "" { return src } if !strings.HasPrefix(base, "http://") && !strings.HasPrefix(base, "https://") { base = "https://" + base } lowerSrc := strings.ToLower(src) for _, legacyHost := range legacyStorageHosts { for _, scheme := range []string{"http://", "https://"} { prefix := scheme + legacyHost if !strings.HasPrefix(lowerSrc, prefix) { continue } rest := src[len(prefix):] // 防止 7colud.yunzer.cn.example.com 这类前缀误匹配 if rest == "" || strings.HasPrefix(rest, "/") { return base + rest } } } return src } // ResolveSoftwareDownloadURL 优先使用自定义 download_url;否则根据 file_id 读附件 src 拼完整 URL func ResolveSoftwareDownloadURL(scheme, host string, downloadURL *string, fileID *uint64) string { if downloadURL != nil { u := strings.TrimSpace(*downloadURL) if u != "" { return u } } if fileID == nil || *fileID == 0 { return "" } var f models.SystemFile err := models.Orm.QueryTable(new(models.SystemFile)). Filter("id", *fileID). Filter("delete_time__isnull", true). One(&f) if err != nil { return "" } return ResolvePublicURL(scheme, host, f.Src) }