// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package api import ( "context" "crypto/tls" "encoding/json" "fmt" "io" "net/http" "code.gitea.io/tea/modules/config" ) // Client provides methods for making raw API calls to Gitea type Client struct { login *config.Login httpClient *http.Client } // NewClient creates a new API client from a login func NewClient(login *config.Login) *Client { httpClient := &http.Client{} if login.Insecure { httpClient = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, } } return &Client{ login: login, httpClient: httpClient, } } // Get makes an authenticated GET request to the API and decodes the JSON response func (c *Client) Get(ctx context.Context, path string, result interface{}) (*http.Response, error) { url := fmt.Sprintf("%s/api/v1%s", c.login.URL, path) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "token "+c.login.Token) req.Header.Set("Accept", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to execute request: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { body, _ := io.ReadAll(resp.Body) return resp, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) } if result != nil { if err := json.NewDecoder(resp.Body).Decode(result); err != nil { return resp, fmt.Errorf("failed to decode response: %w", err) } } return resp, nil } // GetRaw makes an authenticated GET request and returns the raw response body func (c *Client) GetRaw(ctx context.Context, path string) ([]byte, error) { url := fmt.Sprintf("%s/api/v1%s", c.login.URL, path) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "token "+c.login.Token) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to execute request: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } return body, nil } // Post makes an authenticated POST request to the API and decodes the JSON response func (c *Client) Post(ctx context.Context, path string, body io.Reader, result interface{}) (*http.Response, error) { return c.doRequest(ctx, "POST", path, body, result) } // Delete makes an authenticated DELETE request to the API func (c *Client) Delete(ctx context.Context, path string, body io.Reader) (*http.Response, error) { return c.doRequest(ctx, "DELETE", path, body, nil) } // doRequest performs an HTTP request with the given method func (c *Client) doRequest(ctx context.Context, method, path string, body io.Reader, result interface{}) (*http.Response, error) { url := fmt.Sprintf("%s/api/v1%s", c.login.URL, path) req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "token "+c.login.Token) req.Header.Set("Accept", "application/json") if body != nil { req.Header.Set("Content-Type", "application/json") } resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to execute request: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(resp.Body) return resp, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody)) } if result != nil { if err := json.NewDecoder(resp.Body).Decode(result); err != nil { return resp, fmt.Errorf("failed to decode response: %w", err) } } return resp, nil }