package client import ( "encoding/json" "fmt" "io" "net/http" "time" "code.linberg.su/linberg/awesome-back/pkg/errors" ) type APIClient struct { baseURL string httpClient *http.Client } func NewAPIClient(baseURL string) *APIClient { return &APIClient{ baseURL: baseURL, httpClient: &http.Client{ Timeout: 30 * time.Second, }, } } type APIResponse struct { Success bool `json:"success"` Data json.RawMessage `json:"data,omitempty"` Error string `json:"error,omitempty"` } func (c *APIClient) doRequest(method, path string) (*APIResponse, error) { url := fmt.Sprintf("%s%s", c.baseURL, path) req, err := http.NewRequest(method, url, nil) if err != nil { return nil, errors.NewNetworkError("failed to create request").Wrap(err) } req.Header.Set("Accept", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return nil, errors.NewNetworkError("request failed").Wrap(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, errors.NewNetworkError("failed to read response").Wrap(err) } var apiResponse APIResponse if err := json.Unmarshal(body, &apiResponse); err != nil { return nil, errors.NewInternalError("failed to parse response").Wrap(err) } // Обработка HTTP ошибок if resp.StatusCode >= 400 { switch resp.StatusCode { case http.StatusNotFound: return nil, errors.NewNotFoundError(apiResponse.Error) case http.StatusBadRequest: return nil, errors.NewInvalidError(apiResponse.Error) default: return nil, errors.NewInternalError(fmt.Sprintf("server error: %s", apiResponse.Error)) } } return &apiResponse, nil } // Get выполняет GET запрос и парсит ответ в target func (c *APIClient) Get(path string, target interface{}) error { response, err := c.doRequest("GET", path) if err != nil { return err } if !response.Success { return errors.NewInternalError("API request failed") } if err := json.Unmarshal(response.Data, target); err != nil { return errors.NewInternalError("failed to parse response data").Wrap(err) } return nil }