package client import ( "bufio" "bytes" "encoding/json" "fmt" "git.db.org.ai/dborg/internal/models" "io" "net/http" "net/url" "strings" ) func parseSSEResponse(data []byte) ([]byte, error) { scanner := bufio.NewScanner(bytes.NewReader(data)) const maxScanTokenSize = 10 * 1024 * 1024 buf := make([]byte, maxScanTokenSize) scanner.Buffer(buf, maxScanTokenSize) var resultData []byte var foundResult bool for scanner.Scan() { line := scanner.Text() if line == "event: result" { foundResult = true continue } if foundResult && strings.HasPrefix(line, "data: ") { resultData = []byte(strings.TrimPrefix(line, "data: ")) break } } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error reading SSE response: %w", err) } if resultData == nil { return nil, fmt.Errorf("no result event found in SSE response") } trimmed := strings.TrimSpace(string(resultData)) if !strings.HasPrefix(trimmed, "{") && !strings.HasPrefix(trimmed, "[") { return nil, fmt.Errorf("API returned: %s", trimmed) } return resultData, nil } func (c *Client) getSSE(path string, params url.Values) ([]byte, error) { fullURL := c.config.BaseURL + path if params != nil && len(params) > 0 { fullURL += "?" + params.Encode() } req, err := http.NewRequest("GET", fullURL, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("X-API-Key", c.config.APIKey) req.Header.Set("User-Agent", c.config.UserAgent) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) switch resp.StatusCode { case http.StatusPaymentRequired: return nil, fmt.Errorf("insufficient credits (402): %s - Please add credits to your account", string(bodyBytes)) case http.StatusForbidden: return nil, fmt.Errorf("access denied (403): %s - This endpoint requires premium access", string(bodyBytes)) case http.StatusUnauthorized: return nil, fmt.Errorf("unauthorized (401): %s - Check your API key", string(bodyBytes)) case http.StatusTooManyRequests: return nil, fmt.Errorf("rate limit exceeded (429): %s", string(bodyBytes)) case http.StatusBadRequest: return nil, fmt.Errorf("bad request (400): %s", string(bodyBytes)) default: return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(bodyBytes)) } } data, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } contentType := resp.Header.Get("Content-Type") if strings.HasPrefix(string(data), "event:") || strings.Contains(contentType, "text/event-stream") { return parseSSEResponse(data) } return data, nil } func (c *Client) SearchPeople(params *models.SkiptraceParams) (*models.SkiptraceResponse, error) { queryParams := url.Values{} queryParams.Set("first_name", params.FirstName) queryParams.Set("last_name", params.LastName) if params.City != "" { queryParams.Set("city", params.City) } if params.State != "" { queryParams.Set("state", params.State) } if params.Age != "" { queryParams.Set("age", params.Age) } data, err := c.getSSE("/prem/skiptrace/people/search", queryParams) if err != nil { return nil, err } var response models.SkiptraceResponse if err := json.Unmarshal(data, &response); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &response, nil } func (c *Client) GetPersonReport(sxKey string, selection int) (*models.SkiptraceReportResponse, error) { path := fmt.Sprintf("/prem/skiptrace/people/report/%s/%d", sxKey, selection) data, err := c.getSSE(path, nil) if err != nil { return nil, err } var response models.SkiptraceReportResponse if err := json.Unmarshal(data, &response); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &response, nil } func (c *Client) SearchPhone(phone string) (*models.SkiptracePhoneResponse, error) { path := fmt.Sprintf("/prem/skiptrace/phone/%s", phone) data, err := c.getSSE(path, nil) if err != nil { return nil, err } var response models.SkiptracePhoneResponse if err := json.Unmarshal(data, &response); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &response, nil } func (c *Client) SearchEmail(email string) (*models.SkiptraceEmailResponse, error) { path := fmt.Sprintf("/prem/skiptrace/email/%s", email) data, err := c.getSSE(path, nil) if err != nil { return nil, err } var response models.SkiptraceEmailResponse if err := json.Unmarshal(data, &response); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &response, nil }