package storage import ( "fmt" "net" "net/url" "strings" ) // ValidateThreadsFetchableURL ensures Meta servers can download an image for publish. func ValidateThreadsFetchableURL(raw string) error { raw = strings.TrimSpace(raw) if raw == "" { return fmt.Errorf("圖片網址不可為空") } parsed, err := url.Parse(raw) if err != nil || parsed.Scheme == "" || parsed.Host == "" { return fmt.Errorf("圖片網址格式無效") } scheme := strings.ToLower(parsed.Scheme) if scheme != "https" && scheme != "http" { return fmt.Errorf("圖片網址必須是 http 或 https") } host := strings.ToLower(parsed.Hostname()) if host == "" { return fmt.Errorf("圖片網址缺少主機名稱") } if isPrivateOrLoopbackHost(host) { return fmt.Errorf( "圖片網址 %s 無法被 Threads 伺服器存取(localhost / 內網)。請將 HAIXUN_STORAGE_S3_PUBLIC_BASE_URL 設為公開 HTTPS,例如 https://你的網域/api/v1/public/assets", raw, ) } if scheme != "https" { return fmt.Errorf("Threads 附圖網址建議使用 HTTPS(目前為 %s)", scheme) } return nil } func isPrivateOrLoopbackHost(host string) bool { if host == "localhost" { return true } ip := net.ParseIP(host) if ip == nil { return false } return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() }