diff --git a/internal/gitea/client.go b/internal/gitea/client.go new file mode 100644 index 0000000..bfa6822 --- /dev/null +++ b/internal/gitea/client.go @@ -0,0 +1,98 @@ +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) { + apiURL := c.baseURL.JoinPath(path) + q := apiURL.Query() + q.Set("token", c.token) + apiURL.RawQuery = q.Encode() + resp, err := c.httpClient.Get(apiURL.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) { + path := fmt.Sprintf("/api/v1/repos/search?page=%d&limit=%d", page, limit) + resp, err := c.do(path) + 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 +} diff --git a/internal/gitea/types.go b/internal/gitea/types.go new file mode 100644 index 0000000..b831fc2 --- /dev/null +++ b/internal/gitea/types.go @@ -0,0 +1,25 @@ +package gitea + +type GiteaUser struct { + ID int64 `json:"id"` + Login string `json:"login"` + FullName string `json:"full_name"` + Email string `json:"email"` +} + +type GiteaRepo struct { + ID int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"full_name"` + CloneURL string `json:"clone_url"` + SSHURL string `json:"ssh_url"` + Private bool `json:"private"` + Size int64 `json:"size"` + Updated string `json:"updated_at"` + Owner *GiteaUser `json:"owner"` +} + +type GiteaSearchResponse struct { + OK bool `json:"ok"` + Data []GiteaRepo `json:"data"` +}