github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/main/shared/ipfs.go (about)

     1  package shared
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"mime/multipart"
    10  	"net/http"
    11  	"time"
    12  )
    13  
    14  const (
    15  	baseUrl = "https://api.pinata.cloud"
    16  )
    17  
    18  type IpfsClient struct {
    19  	BaseURL    string
    20  	apiKey     string
    21  	apiSecret  string
    22  	HTTPClient *http.Client
    23  }
    24  
    25  type ipfsErrorResponse struct {
    26  	Code    int    `json:"code"`
    27  	Message string `json:"message"`
    28  }
    29  
    30  type ipfsSuccessResponse struct {
    31  	Code int         `json:"code"`
    32  	Data interface{} `json:"data"`
    33  }
    34  
    35  type Pin struct {
    36  	IpfsHash    string    `json:"IpfsHash"`
    37  	PinSize     int       `json:"PinSize"`
    38  	Timestamp   time.Time `json:"Timestamp"`
    39  	IsDuplicate bool      `json:"isDuplicate"`
    40  }
    41  
    42  func NewIpfsClient(apiKey string, apiSecret string) *IpfsClient {
    43  	return &IpfsClient{
    44  		BaseURL:   baseUrl,
    45  		apiKey:    apiKey,
    46  		apiSecret: apiSecret,
    47  		HTTPClient: &http.Client{
    48  			Timeout: time.Second * 10,
    49  		},
    50  	}
    51  }
    52  
    53  func (c *IpfsClient) sendRequest(req *http.Request, v interface{}) error {
    54  	req.Header.Set("pinata_api_key", c.apiKey)
    55  	req.Header.Set("pinata_secret_api_key", c.apiSecret)
    56  
    57  	res, err := c.HTTPClient.Do(req)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	defer res.Body.Close()
    63  
    64  	if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusBadRequest {
    65  		var errRes ipfsErrorResponse
    66  		if err = json.NewDecoder(res.Body).Decode(&errRes); err == nil {
    67  			return errors.New(errRes.Message)
    68  		}
    69  		return fmt.Errorf("unknown error, status code: %d", res.StatusCode)
    70  	}
    71  
    72  	fullResponse := ipfsSuccessResponse{
    73  		Data: v,
    74  	}
    75  
    76  	if err = json.NewDecoder(res.Body).Decode(&fullResponse.Data); err != nil {
    77  		return err
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func (c *IpfsClient) PinJson(data interface{}) (*Pin, error) {
    84  	url := c.BaseURL + "/pinning/pinJSONToIPFS"
    85  	json_data, err := json.Marshal(data)
    86  
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(json_data))
    92  	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
    93  
    94  	res := Pin{}
    95  
    96  	if err := c.sendRequest(req, &res); err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	return &res, nil
   101  }
   102  
   103  func (c *IpfsClient) PinFile(file multipart.File, fileName string) (*Pin, error) {
   104  	url := c.BaseURL + "/pinning/pinFileToIPFS"
   105  
   106  	body := &bytes.Buffer{}
   107  	writer := multipart.NewWriter(body)
   108  	part, _ := writer.CreateFormFile("file", fileName)
   109  	io.Copy(part, file)
   110  	writer.Close()
   111  
   112  	req, _ := http.NewRequest("POST", url, body)
   113  	req.Header.Add("Content-Type", writer.FormDataContentType())
   114  
   115  	res := Pin{}
   116  
   117  	if err := c.sendRequest(req, &res); err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	return &res, nil
   122  }