Files
ufile/cmd/ufile-cli/client.go
T

120 lines
3.3 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
const sessionCookieName = "ufile_session"
type apiClient struct {
host string
session string
http *http.Client
}
type apiError struct {
Status int
Code string
Message string
}
func (e *apiError) Error() string { return e.Message }
type userResponse struct {
ID uint `json:"id"`
Username string `json:"username"`
CanManageUsers bool `json:"canManageUsers"`
CanManageFiles bool `json:"canManageFiles"`
CanUpload bool `json:"canUpload"`
CanAdvancedUpload bool `json:"canAdvancedUpload"`
MustChangePassword bool `json:"mustChangePassword"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type fileResponse struct {
ID uint `json:"id"`
Hash string `json:"hash"`
Name string `json:"name"`
MimeType string `json:"mimeType"`
Size int64 `json:"size"`
ExpiresAt *time.Time `json:"expiresAt"`
MaxDownloads *int `json:"maxDownloads"`
DownloadCount int `json:"downloadCount"`
}
func newAPIClient(host, session string, timeout time.Duration) *apiClient {
return &apiClient{host: host, session: session, http: &http.Client{Timeout: timeout}}
}
func (c *apiClient) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) {
request, err := http.NewRequestWithContext(ctx, method, c.host+path, body)
if err != nil {
return nil, err
}
request.Header.Set("Accept", "application/json")
request.Header.Set("User-Agent", "ufile-cli/"+version)
if c.session != "" {
request.AddCookie(&http.Cookie{Name: sessionCookieName, Value: c.session})
}
return request, nil
}
func (c *apiClient) doJSON(ctx context.Context, method, path string, requestBody, responseBody any) (*http.Response, error) {
var body io.Reader
if requestBody != nil {
data, err := json.Marshal(requestBody)
if err != nil {
return nil, err
}
body = bytes.NewReader(data)
}
request, err := c.newRequest(ctx, method, path, body)
if err != nil {
return nil, err
}
if requestBody != nil {
request.Header.Set("Content-Type", "application/json")
}
response, err := c.http.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return response, decodeAPIError(response)
}
if responseBody == nil || response.StatusCode == http.StatusNoContent {
_, _ = io.Copy(io.Discard, response.Body)
return response, nil
}
if err := json.NewDecoder(io.LimitReader(response.Body, 2<<20)).Decode(responseBody); err != nil {
return response, fmt.Errorf("服务端返回了无效的 JSON: %w", err)
}
return response, nil
}
func decodeAPIError(response *http.Response) error {
payload := struct {
Error string `json:"error"`
Code string `json:"code"`
}{}
data, _ := io.ReadAll(io.LimitReader(response.Body, 1<<20))
_ = json.Unmarshal(data, &payload)
message := strings.TrimSpace(payload.Error)
if message == "" {
message = strings.TrimSpace(string(data))
}
if message == "" {
message = response.Status
}
return &apiError{Status: response.StatusCode, Code: payload.Code, Message: message}
}