package remnawave import ( "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) type Client struct { baseURL string token string caddyToken string http *http.Client } func NewClient(baseURL, apiToken, caddyToken string) *Client { return &Client{ baseURL: strings.TrimRight(baseURL, "/"), token: apiToken, caddyToken: caddyToken, http: &http.Client{ Timeout: 15 * time.Second, }, } } func (c *Client) countFromEndpoint(ctx context.Context, path, arrayKey string) (int, error) { resp, body, err := c.get(ctx, path) if err != nil { return 0, err } if resp.StatusCode != http.StatusOK { return 0, fmt.Errorf("HTTP %d", resp.StatusCode) } return parseCount(body, arrayKey), nil } func (c *Client) get(ctx context.Context, path string) (*http.Response, []byte, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, nil) if err != nil { return nil, nil, err } req.Header.Set("Authorization", "Bearer "+c.token) req.Header.Set("Accept", "application/json") if c.caddyToken != "" { req.Header.Set("X-Api-Key", c.caddyToken) } resp, err := c.http.Do(req) if err != nil { return nil, nil, fmt.Errorf("нет связи с панелью: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { return resp, nil, err } return resp, body, nil } func parseCount(body []byte, arrayKey string) int { var raw map[string]json.RawMessage if err := json.Unmarshal(body, &raw); err != nil { return 0 } if n := countInRaw(raw["response"], arrayKey); n > 0 { return n } return countInRaw(json.RawMessage(body), arrayKey) } func countInRaw(data json.RawMessage, arrayKey string) int { if len(data) == 0 { return 0 } var obj map[string]json.RawMessage if err := json.Unmarshal(data, &obj); err != nil { var arr []json.RawMessage if err := json.Unmarshal(data, &arr); err == nil { return len(arr) } return 0 } if totalRaw, ok := obj["total"]; ok { var total int if err := json.Unmarshal(totalRaw, &total); err == nil && total > 0 { return total } } if items, ok := obj[arrayKey]; ok { var arr []json.RawMessage if err := json.Unmarshal(items, &arr); err == nil { return len(arr) } } for _, v := range obj { var arr []json.RawMessage if err := json.Unmarshal(v, &arr); err == nil && len(arr) > 0 { return len(arr) } } return 0 } func trimBody(b []byte, max int) string { s := strings.TrimSpace(string(b)) if len(s) > max { return s[:max] + "…" } return s }