Files
simplegit/gitrpc/client.go
T
2026-07-15 11:06:48 -04:00

103 lines
2.6 KiB
Go

package gitrpc
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"time"
"connectrpc.com/connect"
"simplegit/gitrpc/v1/v1connect"
)
type GitRpcClient struct {
baseURL string
http *http.Client
svc v1connect.GitServiceClient
}
func NewClient(baseURL string) *GitRpcClient {
httpClient := &http.Client{Timeout: 30 * time.Second}
svc := v1connect.NewGitServiceClient(
httpClient,
baseURL,
connect.WithInterceptors(tokenInterceptor()),
)
return &GitRpcClient{
baseURL: baseURL,
http: httpClient,
svc: svc,
}
}
func (c *GitRpcClient) GitService() v1connect.GitServiceClient { return c.svc }
type tokenCtxKey struct{}
func WithToken(ctx context.Context, token string) context.Context {
return context.WithValue(ctx, tokenCtxKey{}, token)
}
func tokenFromContext(ctx context.Context) (string, bool) {
t, ok := ctx.Value(tokenCtxKey{}).(string)
return t, ok
}
func tokenInterceptor() connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
if token, ok := tokenFromContext(ctx); ok {
req.Header().Set("Authorization", "Bearer "+token)
}
return next(ctx, req)
}
}
}
func IsNotFound(err error) bool {
return err != nil && connect.CodeOf(err) == connect.CodeNotFound
}
func (c *GitRpcClient) GetRaw(ctx context.Context, repo, ref, path string) (io.ReadCloser, error) {
return c.getBinary(ctx, "/raw", repo, url.Values{"ref": {ref}, "file": {path}})
}
func (c *GitRpcClient) GetPatch(ctx context.Context, repo, sha string) (io.ReadCloser, error) {
return c.getBinary(ctx, "/patch", repo, url.Values{"sha": {sha}})
}
func (c *GitRpcClient) GetArchive(ctx context.Context, repo, ref, format string) (io.ReadCloser, error) {
v := url.Values{"ref": {ref}}
if format != "" {
v.Set("format", format)
}
return c.getBinary(ctx, "/archive", repo, v)
}
func (c *GitRpcClient) getBinary(ctx context.Context, endpoint, repo string, extra url.Values) (io.ReadCloser, error) {
v := url.Values{"repo": {repo}}
for k, vs := range extra {
v[k] = vs
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+endpoint+"?"+v.Encode(), nil)
if err != nil {
return nil, err
}
if token, ok := tokenFromContext(ctx); ok {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return nil, fmt.Errorf("gitrpc %s: %s: %s", endpoint, resp.Status, body)
}
return resp.Body, nil
}