// client.go // Package gitea provides a client for interacting with the Gitea API package gitea import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "time" ) // Client handles communication with the Gitea API type Client struct { baseURL *url.URL httpClient *http.Client token string } // VersionResponse represents the Gitea version response type VersionResponse struct { Version string `json:"version"` } // FetchCSRFToken retrieves a CSRF token from Gitea func (c *Client) FetchCSRFToken() error { // Create a request to the Gitea login page to get a CSRF token u, err := c.baseURL.Parse("/") if err != nil { return fmt.Errorf("invalid URL: %w", err) } // Use a normal HTTP client without our custom transport for this request // to avoid circular dependency (we need the token for the transport) httpClient := &http.Client{} req, err := http.NewRequest("GET", u.String(), nil) if err != nil { return fmt.Errorf("failed to create request: %w", err) } resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() // Extract CSRF token from Set-Cookie header for _, cookie := range resp.Cookies() { if cookie.Name == "_csrf" { // Update the transport with the CSRF token transport, ok := c.httpClient.Transport.(*CSRFTokenTransport) if ok { transport.CSRFToken = cookie.Value return nil } return fmt.Errorf("transport is not a CSRFTokenTransport") } } // If we couldn't find a CSRF token, try to extract it from the response body bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %w", err) } // Look for in the HTML body := string(bodyBytes) csrfMetaStart := strings.Index(body, `= 300 { return resp, fmt.Errorf("API returned error: %s - %s", resp.Status, string(bodyBytes)) } if result != nil && len(bodyBytes) > 0 { if err := json.NewDecoder(bytes.NewBuffer(bodyBytes)).Decode(result); err != nil { return resp, fmt.Errorf("failed to decode response: %w", err) } } return resp, nil }