package gitea import ( "encoding/json" "fmt" "io" "net/http" "net/url" "time" ) type Client struct { baseURL *url.URL httpClient *http.Client token string } func NewClient(serverURL, token string) (*Client, error) { u, err := url.Parse(serverURL) if err != nil { return nil, fmt.Errorf("invalid server URL: %w", err) } return &Client{ baseURL: u, httpClient: &http.Client{ Timeout: 30 * time.Second, }, token: token, }, nil } func (c *Client) do(path string) (*http.Response, error) { reqURL := c.baseURL.JoinPath(path) q := reqURL.Query() q.Set("token", c.token) reqURL.RawQuery = q.Encode() resp, err := c.httpClient.Get(reqURL.String()) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } return resp, nil } func (c *Client) doSearch(path string, params map[string]string) (*http.Response, error) { reqURL := c.baseURL.JoinPath(path) q := reqURL.Query() q.Set("token", c.token) for k, v := range params { q.Set(k, v) } reqURL.RawQuery = q.Encode() resp, err := c.httpClient.Get(reqURL.String()) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } return resp, nil } func (c *Client) ValidateToken() (*GiteaUser, error) { resp, err := c.do("/api/v1/user") if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("authentication failed: %s - %s", resp.Status, string(body)) } var user GiteaUser if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } return &user, nil } func (c *Client) SearchRepos(page, limit int) ([]GiteaRepo, error) { resp, err := c.doSearch("/api/v1/repos/search", map[string]string{ "page": fmt.Sprintf("%d", page), "limit": fmt.Sprintf("%d", limit), }) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("search failed: %s - %s", resp.Status, string(body)) } var searchResp GiteaSearchResponse if err := json.NewDecoder(resp.Body).Decode(&searchResp); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } return searchResp.Data, nil } func (c *Client) GetAllRepos() ([]GiteaRepo, error) { var allRepos []GiteaRepo page := 1 limit := 50 for { repos, err := c.SearchRepos(page, limit) if err != nil { return nil, err } if len(repos) == 0 { break } allRepos = append(allRepos, repos...) if len(repos) < limit { break } page++ } return allRepos, nil }