Files
ttstd-infinite-canvas/backend/internal/updaterclient/client.go

90 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package updaterclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
"infinite-canvas/backend/internal/hostupdate"
)
type Client struct {
baseURL string
token string
http *http.Client
}
func New(socketPath, token string) *Client {
dialer := &net.Dialer{Timeout: 3 * time.Second}
transport := &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return dialer.DialContext(ctx, "unix", socketPath)
},
DisableKeepAlives: true,
}
return &Client{baseURL: "http://unix", token: strings.TrimSpace(token), http: &http.Client{Transport: transport, Timeout: 15 * time.Second}}
}
func (c *Client) Status(ctx context.Context) (hostupdate.Status, error) {
return c.request(ctx, http.MethodGet, "/v1/status", nil)
}
func (c *Client) Check(ctx context.Context) (hostupdate.Status, error) {
return c.request(ctx, http.MethodPost, "/v1/check", struct{}{})
}
func (c *Client) Start(ctx context.Context, targetVersion string) (hostupdate.Status, error) {
return c.request(ctx, http.MethodPost, "/v1/update", hostupdate.StartRequest{TargetVersion: targetVersion})
}
func (c *Client) Rollback(ctx context.Context, reason string) (hostupdate.Status, error) {
return c.request(ctx, http.MethodPost, "/v1/rollback", hostupdate.RollbackRequest{Reason: reason})
}
func (c *Client) request(ctx context.Context, method, path string, payload any) (hostupdate.Status, error) {
var body io.Reader
if payload != nil {
encoded, err := json.Marshal(payload)
if err != nil {
return hostupdate.Status{}, err
}
body = bytes.NewReader(encoded)
}
request, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, body)
if err != nil {
return hostupdate.Status{}, err
}
request.Header.Set("Authorization", "Bearer "+c.token)
request.Header.Set("Accept", "application/json")
if payload != nil {
request.Header.Set("Content-Type", "application/json")
}
response, err := c.http.Do(request)
if err != nil {
return hostupdate.Status{}, fmt.Errorf("连接 Host Updater:%w", err)
}
defer response.Body.Close()
limited := io.LimitReader(response.Body, 2<<20)
if response.StatusCode < 200 || response.StatusCode >= 300 {
var failure struct {
Error string `json:"error"`
Data hostupdate.Status `json:"data"`
}
if err := json.NewDecoder(limited).Decode(&failure); err == nil && failure.Error != "" {
return failure.Data, fmt.Errorf("Host Updater:%s", failure.Error)
}
return hostupdate.Status{}, fmt.Errorf("Host Updater 返回 HTTP %d", response.StatusCode)
}
var status hostupdate.Status
if err := json.NewDecoder(limited).Decode(&status); err != nil {
return hostupdate.Status{}, fmt.Errorf("解析 Host Updater 响应:%w", err)
}
return status, nil
}