package gitea import ( "encoding/json" "fmt" "io" "net/http" "strings" "time" ) type Client struct { BaseURL string Token string Client *http.Client } func NewClient(baseURL, token string) *Client { baseURL = strings.TrimRight(baseURL, "/") if !strings.HasPrefix(baseURL, "http://") && !strings.HasPrefix(baseURL, "https://") { baseURL = "https://" + baseURL } if strings.HasSuffix(baseURL, "/api/v1") { baseURL = strings.TrimSuffix(baseURL, "/api/v1") } if strings.HasSuffix(baseURL, "/api") { baseURL = strings.TrimSuffix(baseURL, "/api") } return &Client{ BaseURL: baseURL, Token: token, Client: &http.Client{Timeout: 60 * time.Second}, } } type GiteaRepo struct { ID int64 `json:"id"` Name string `json:"name"` FullName string `json:"full_name"` HTMLURL string `json:"html_url"` Language string `json:"language"` Topics []string `json:"topics"` } type GiteaCommit struct { SHA string `json:"sha"` Commit struct { Author struct { Name string `json:"name"` Email string `json:"email"` Date time.Time `json:"date"` } `json:"author"` Message string `json:"message"` } `json:"commit"` } // do makes an authenticated HTTP request to Gitea API func (c *Client) do(method, path string) ([]byte, error) { url := fmt.Sprintf("%s/api/v1%s", c.BaseURL, path) req, err := http.NewRequest(method, url, nil) if err != nil { return nil, fmt.Errorf("failed to create request for %s: %w", url, err) } req.Header.Set("Authorization", fmt.Sprintf("token %s", c.Token)) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", "gitea-wrapped/1.0") resp, err := c.Client.Do(req) if err != nil { return nil, fmt.Errorf("API request failed (%s %s): %w", method, url, err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { errMsg := string(body) if len(errMsg) > 500 { errMsg = errMsg[:500] } return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, errMsg) } return body, nil } func (c *Client) GetUserRepos() ([]GiteaRepo, error) { var repos []GiteaRepo page := 1 pageSize := 50 maxPages := 100 // Safety limit to prevent infinite loops for page <= maxPages { path := fmt.Sprintf("/user/repos?page=%d&limit=%d", page, pageSize) body, err := c.do("GET", path) if err != nil { if len(repos) > 0 { return repos, nil } return nil, err } var pageRepos []GiteaRepo if err := json.Unmarshal(body, &pageRepos); err != nil { return nil, err } if len(pageRepos) == 0 { break } repos = append(repos, pageRepos...) if len(pageRepos) < pageSize { break } page++ } return repos, nil } func (c *Client) GetRepoCommits(owner, repo string) ([]GiteaCommit, error) { var commits []GiteaCommit page := 1 pageSize := 50 maxPages := 1000 // Safety limit to prevent infinite loops for page <= maxPages { path := fmt.Sprintf("/repos/%s/%s/commits?page=%d&limit=%d", owner, repo, page, pageSize) body, err := c.do("GET", path) if err != nil { if len(commits) > 0 { return commits, nil } return nil, err } var pageCommits []GiteaCommit if err := json.Unmarshal(body, &pageCommits); err != nil { return nil, err } if len(pageCommits) == 0 { break } commits = append(commits, pageCommits...) if len(pageCommits) < pageSize { break } page++ } return commits, nil } func (c *Client) TestConnection() error { _, err := c.do("GET", "/user") if err != nil { return err } return nil }