|
- package region_sync
-
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "strings"
- "time"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/QuantumNous/new-api/logger"
- )
-
- // SyncClient 调用远程节点的 HTTP 客户端
- type SyncClient struct {
- endpoint string
- apiKey string
- httpClient *http.Client
- }
-
- func NewSyncClient(endpoint, apiKey string) *SyncClient {
- return &SyncClient{
- endpoint: endpoint,
- apiKey: apiKey,
- httpClient: &http.Client{Timeout: 30 * time.Second},
- }
- }
-
- func (c *SyncClient) doRequest(method, path string, body interface{}) ([]byte, error) {
- var reqBody io.Reader
- if body != nil {
- data, err := json.Marshal(body)
- if err != nil {
- return nil, fmt.Errorf("marshal request: %w", err)
- }
- reqBody = bytes.NewReader(data)
- }
-
- // 处理 URL 拼接,避免双斜杠问题
- endpoint := strings.TrimRight(c.endpoint, "/")
- path = strings.TrimLeft(path, "/")
- url := endpoint + "/" + path
-
- common.SysLog(fmt.Sprintf("[RegionSync] HTTP %s %s", method, url))
- logger.LogDebug(nil, "[RegionSync] HTTP %s %s", method, url)
-
- req, err := http.NewRequest(method, url, reqBody)
- if err != nil {
- return nil, fmt.Errorf("create request: %w", err)
- }
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("X-Sync-API-Key", c.apiKey)
- req.Header.Set("X-Sync-Node", c.endpoint)
-
- resp, err := c.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("http request: %w", err)
- }
- defer resp.Body.Close()
-
- data, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("read response: %w", err)
- }
-
- // 如果返回状态码不是 200,记录错误信息
- if resp.StatusCode != http.StatusOK {
- common.SysError(fmt.Sprintf("[RegionSync] HTTP error %d from %s: %s", resp.StatusCode, url, string(data)))
- return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(data))
- }
-
- return data, nil
- }
-
- func (c *SyncClient) SyncUserCreate(req *SyncUserRequest) (*SyncUserResponse, error) {
- data, err := c.doRequest("POST", "/api/internal/sync/user/create", req)
- if err != nil {
- return nil, err
- }
- var resp SyncUserResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- return &resp, nil
- }
-
- func (c *SyncClient) QueryQuota(userId int) (*QueryQuotaResponse, error) {
- data, err := c.doRequest("POST", "/api/internal/sync/quota/query", &QueryQuotaRequest{UserId: userId})
- if err != nil {
- return nil, err
- }
- var resp QueryQuotaResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- logger.LogDebug(nil, "[RegionSync] QueryQuota: userId=%d, quota=%d, success=%v", userId, resp.Quota, resp.Success)
- return &resp, nil
- }
-
- func (c *SyncClient) BatchDeduct(req *BatchDeductRequest) (*BatchDeductResponse, error) {
- logger.LogDebug(nil, "[RegionSync] BatchDeduct: sending %d records", len(req.Records))
- data, err := c.doRequest("POST", "/api/internal/sync/quota/batch-deduct", req)
- if err != nil {
- return nil, err
- }
- var resp BatchDeductResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- successCount := 0
- for _, r := range resp.Results {
- if r.Success {
- successCount++
- }
- }
- logger.LogDebug(nil, "[RegionSync] BatchDeduct: response success=%d, total=%d", successCount, len(resp.Results))
- return &resp, nil
- }
-
- func (c *SyncClient) UpdateQuota(remoteUserId, quota int) (*UpdateQuotaResponse, error) {
- data, err := c.doRequest("POST", "/api/internal/sync/quota/update", &UpdateQuotaRequest{RemoteUserId: remoteUserId, Quota: quota})
- if err != nil {
- return nil, err
- }
- var resp UpdateQuotaResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- return &resp, nil
- }
-
- func (c *SyncClient) FetchConfig() (*SyncConfigResponse, error) {
- data, err := c.doRequest("GET", "/api/internal/sync/config", nil)
- if err != nil {
- return nil, err
- }
- var resp SyncConfigResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- return &resp, nil
- }
-
- func (c *SyncClient) BatchQueryQuota(userIds []int) (*BatchQueryQuotaResponse, error) {
- data, err := c.doRequest("POST", "/api/internal/sync/quota/batch-query", &BatchQueryQuotaRequest{UserIds: userIds})
- if err != nil {
- return nil, err
- }
- var resp BatchQueryQuotaResponse
- if err := json.Unmarshal(data, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- return &resp, nil
- }
|