package gitea import ( "context" "encoding/json" "fmt" "net/http" "time" "github.com/rs/zerolog" "github.com/vincentc-afk/gitea-notification-hub/internal/config" ) // GiteaUser represents a user from Gitea API type GiteaUser struct { ID int64 `json:"id"` Login string `json:"login"` FullName string `json:"full_name"` Email string `json:"email"` AvatarURL string `json:"avatar_url"` } // Provider implements email lookup via Gitea API type Provider struct { baseURL string token string httpClient *http.Client logger zerolog.Logger } // New creates a new Gitea API provider func New(cfg *config.GiteaConfig, logger zerolog.Logger) *Provider { return &Provider{ baseURL: cfg.URL, token: cfg.Token, httpClient: &http.Client{ Timeout: 10 * time.Second, }, logger: logger.With().Str("component", "gitea-provider").Logger(), } } // LookupEmail fetches the real email for a Gitea username via API func (p *Provider) LookupEmail(ctx context.Context, username string) (string, error) { url := fmt.Sprintf("%s/api/v1/users/%s", p.baseURL, username) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return "", fmt.Errorf("creating request: %w", err) } // Add authorization header if token is provided if p.token != "" { req.Header.Set("Authorization", "token "+p.token) } req.Header.Set("Accept", "application/json") resp, err := p.httpClient.Do(req) if err != nil { return "", fmt.Errorf("fetching user from Gitea: %w", err) } defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { return "", fmt.Errorf("user %s not found in Gitea", username) } if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("Gitea API returned status %d", resp.StatusCode) } var user GiteaUser if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return "", fmt.Errorf("decoding Gitea response: %w", err) } if user.Email == "" { return "", fmt.Errorf("user %s has no email in Gitea", username) } p.logger.Debug(). Str("username", username). Str("email", user.Email). Str("full_name", user.FullName). Msg("found user email via Gitea API") return user.Email, nil } // GetUser fetches full user info from Gitea API func (p *Provider) GetUser(ctx context.Context, username string) (*GiteaUser, error) { url := fmt.Sprintf("%s/api/v1/users/%s", p.baseURL, username) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("creating request: %w", err) } if p.token != "" { req.Header.Set("Authorization", "token "+p.token) } req.Header.Set("Accept", "application/json") resp, err := p.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("fetching user from Gitea: %w", err) } defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("user %s not found in Gitea", username) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Gitea API returned status %d", resp.StatusCode) } var user GiteaUser if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return nil, fmt.Errorf("decoding Gitea response: %w", err) } return &user, nil }