Files
goyomi/internal/httpclient/graphql.go
T
achmad 85d2ea6143 feat: initial Phase 1 implementation — core framework + Docker
- Data types (SManga, SChapter, Page, MangasPage, all Filter variants)
- Source interfaces (Source, CatalogueSource) with MD5-based ID generation matching Tachiyomi/Suwayomi
- HTTP client with per-host rate limiting, cookie jar, and 429 retry
- FlareSolverr v1 client (FLARESOLVERR_URL env)
- Generic GraphQL POST helper
- goquery HTML parser wrappers
- Source registry (panics on duplicate ID)
- Multi-stage Dockerfile (golang:1.26-alpine + distroless) and compose.yml (postgres, flaresolverr, app)
2026-05-10 21:24:38 +07:00

56 lines
1.2 KiB
Go

package httpclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
)
type GraphQLRequest struct {
Query string `json:"query"`
Variables any `json:"variables,omitempty"`
}
type graphQLResponse[T any] struct {
Data T `json:"data"`
Errors []struct {
Message string `json:"message"`
} `json:"errors"`
}
// Post sends a GraphQL request and unmarshals the `data` field into T.
func Post[T any](ctx context.Context, client *http.Client, url string, req GraphQLRequest, headers map[string]string) (T, error) {
var zero T
body, err := json.Marshal(req)
if err != nil {
return zero, err
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return zero, err
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Accept", "application/json")
for k, v := range headers {
httpReq.Header.Set(k, v)
}
resp, err := client.Do(httpReq)
if err != nil {
return zero, err
}
defer resp.Body.Close()
var gqlResp graphQLResponse[T]
if err := json.NewDecoder(resp.Body).Decode(&gqlResp); err != nil {
return zero, err
}
if len(gqlResp.Errors) > 0 {
return zero, fmt.Errorf("graphql: %s", gqlResp.Errors[0].Message)
}
return gqlResp.Data, nil
}