This commit is contained in:
yml2213
2026-07-23 13:03:47 +08:00
commit 2a67d074a4
12 changed files with 999 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
package hfb
import (
"bytes"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
const uploadPath = "/api/open/listing-uploads"
type Client struct {
Server string
Secret string
Timeout time.Duration
HTTPClient *http.Client
}
func (c Client) Upload(ctx context.Context, req ExternalUploadRequest) (string, error) {
body, err := json.Marshal(req)
if err != nil {
return "", fmt.Errorf("生成上传 JSON 失败:%w", err)
}
endpoint, err := normalizeEndpoint(c.Server)
if err != nil {
return "", err
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
return "", fmt.Errorf("创建上传请求失败:%w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("User-Agent", "updata_hfb/1.0")
if strings.TrimSpace(c.Secret) != "" {
timestamp := strconvFormatUnix(time.Now())
httpReq.Header.Set("X-HFB-Timestamp", timestamp)
httpReq.Header.Set("X-HFB-Signature", signBody(c.Secret, timestamp, body))
}
httpClient := c.HTTPClient
if httpClient == nil {
timeout := c.Timeout
if timeout <= 0 {
timeout = 15 * time.Second
}
httpClient = &http.Client{Timeout: timeout}
}
resp, err := httpClient.Do(httpReq)
if err != nil {
return "", fmt.Errorf("请求服务器失败:%w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return "", fmt.Errorf("读取服务器响应失败:%w", err)
}
text := strings.TrimSpace(string(respBody))
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
if text == "" {
text = resp.Status
}
return "", fmt.Errorf("服务器返回失败状态 %s%s", resp.Status, text)
}
if text == "" {
return resp.Status, nil
}
return text, nil
}
func normalizeEndpoint(raw string) (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("服务器地址不正确:%s", raw)
}
if strings.Contains(parsed.Path, uploadPath) {
return parsed.String(), nil
}
basePath := strings.TrimRight(parsed.Path, "/")
parsed.Path = basePath + uploadPath
return parsed.String(), nil
}
func signBody(secret, timestamp string, body []byte) string {
mac := hmac.New(sha256.New, []byte(strings.TrimSpace(secret)))
mac.Write([]byte(timestamp))
mac.Write([]byte("."))
mac.Write(body)
return hex.EncodeToString(mac.Sum(nil))
}
func strconvFormatUnix(t time.Time) string {
return fmt.Sprintf("%d", t.Unix())
}