termcloud/internal/cli/client.go

253 lines
7.1 KiB
Go

package cli
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type Client struct {
baseURL string
httpClient *http.Client
token string
}
type Account struct {
ID int64 `json:"id"`
AccountNumber string `json:"accountNumber"`
AccessToken string `json:"accessToken,omitempty"`
BalanceUSD float64 `json:"balanceUsd"`
IsActive bool `json:"isActive"`
CreatedAt time.Time `json:"createdAt"`
ActivatedAt *time.Time `json:"activatedAt,omitempty"`
LastBillingDate time.Time `json:"lastBillingDate"`
}
type Payment struct {
ID int64 `json:"id"`
AccountID int64 `json:"accountId"`
PaymentType string `json:"paymentType"`
BTCAddress string `json:"btcAddress,omitempty"`
BTCAmount float64 `json:"btcAmount,omitempty"`
USDAmount float64 `json:"usdAmount"`
Confirmations int `json:"confirmations"`
TxHash string `json:"txHash,omitempty"`
Status string `json:"status"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
ConfirmedAt *time.Time `json:"confirmedAt,omitempty"`
}
type Bucket struct {
ID int64 `json:"id"`
Name string `json:"name"`
OwnerID int64 `json:"ownerId"`
StorageUsedBytes int64 `json:"storageUsedBytes"`
CreatedAt time.Time `json:"createdAt"`
}
type Object struct {
ID int64 `json:"id"`
BucketID int64 `json:"bucketId"`
Key string `json:"key"`
SizeBytes int64 `json:"sizeBytes"`
ContentType string `json:"contentType"`
LastModified time.Time `json:"lastModified"`
VersionID string `json:"versionId"`
MD5Checksum string `json:"md5Checksum"`
CustomMetadata map[string]interface{} `json:"customMetadata"`
}
func NewClient(baseURL, token string) *Client {
return &Client{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
token: token,
}
}
func (c *Client) makeRequest(ctx context.Context, method, path string, body interface{}, result interface{}) error {
var reqBody io.Reader
if body != nil {
jsonData, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, reqBody)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
if c.token != "" {
req.Header.Set("X-Access-Token", c.token)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("server error %d: %s", resp.StatusCode, string(body))
}
if result != nil && resp.StatusCode != http.StatusNoContent {
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
}
return nil
}
func (c *Client) CreateAccount(ctx context.Context) (*Account, error) {
var account Account
err := c.makeRequest(ctx, "POST", "/api/accounts", nil, &account)
return &account, err
}
func (c *Client) GetAccount(ctx context.Context) (*Account, error) {
var account Account
err := c.makeRequest(ctx, "GET", "/api/account", nil, &account)
return &account, err
}
func (c *Client) CreatePayment(ctx context.Context, usdAmount float64) (*Payment, error) {
req := map[string]interface{}{
"usd_amount": usdAmount,
}
var payment Payment
err := c.makeRequest(ctx, "POST", "/api/payments", req, &payment)
return &payment, err
}
func (c *Client) CreatePaymentByAccountNumber(ctx context.Context, accountNumber string, usdAmount float64) (*Payment, error) {
req := map[string]interface{}{
"account_number": accountNumber,
"usd_amount": usdAmount,
}
var payment Payment
err := c.makeRequest(ctx, "POST", "/api/payments/by-account", req, &payment)
return &payment, err
}
func (c *Client) GetPayment(ctx context.Context, paymentID int64) (*Payment, error) {
var payment Payment
path := fmt.Sprintf("/api/payments/%d", paymentID)
err := c.makeRequest(ctx, "GET", path, nil, &payment)
return &payment, err
}
func (c *Client) GetPayments(ctx context.Context) ([]Payment, error) {
var payments []Payment
err := c.makeRequest(ctx, "GET", "/api/payments", nil, &payments)
return payments, err
}
func (c *Client) CreateBucket(ctx context.Context, name string) (*Bucket, error) {
req := map[string]interface{}{
"name": name,
}
var bucket Bucket
err := c.makeRequest(ctx, "PUT", "/api/buckets/"+name, req, &bucket)
return &bucket, err
}
func (c *Client) ListBuckets(ctx context.Context) ([]Bucket, error) {
var buckets []Bucket
err := c.makeRequest(ctx, "GET", "/api/buckets", nil, &buckets)
return buckets, err
}
func (c *Client) GetBucket(ctx context.Context, name string) (*Bucket, error) {
var bucket Bucket
path := fmt.Sprintf("/api/buckets/%s", name)
err := c.makeRequest(ctx, "GET", path, nil, &bucket)
return &bucket, err
}
func (c *Client) DeleteBucket(ctx context.Context, name string) error {
path := fmt.Sprintf("/api/buckets/%s", name)
return c.makeRequest(ctx, "DELETE", path, nil, nil)
}
func (c *Client) ListObjects(ctx context.Context, bucketName string) ([]Object, error) {
var objects []Object
path := fmt.Sprintf("/api/buckets/%s/objects", bucketName)
err := c.makeRequest(ctx, "GET", path, nil, &objects)
return objects, err
}
func (c *Client) PutObject(ctx context.Context, bucketName, key string, data []byte, contentType string) error {
req, err := http.NewRequestWithContext(ctx, "PUT",
fmt.Sprintf("%s/api/buckets/%s/objects/%s", c.baseURL, bucketName, key),
bytes.NewReader(data))
if err != nil {
return err
}
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
if c.token != "" {
req.Header.Set("X-Access-Token", c.token)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("upload failed %d: %s", resp.StatusCode, string(body))
}
return nil
}
func (c *Client) GetObject(ctx context.Context, bucketName, key string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET",
fmt.Sprintf("%s/api/buckets/%s/objects/%s", c.baseURL, bucketName, key), nil)
if err != nil {
return nil, err
}
if c.token != "" {
req.Header.Set("X-Access-Token", c.token)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("download failed %d: %s", resp.StatusCode, string(body))
}
return io.ReadAll(resp.Body)
}
func (c *Client) DeleteObject(ctx context.Context, bucketName, key string) error {
path := fmt.Sprintf("/api/buckets/%s/objects/%s", bucketName, key)
return c.makeRequest(ctx, "DELETE", path, nil, nil)
}