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"` 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) 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) }