142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
package ipfs
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
shell "github.com/ipfs/go-ipfs-api"
|
|
)
|
|
|
|
// Client wraps IPFS shell for easier use across services
|
|
type Client struct {
|
|
shell *shell.Shell
|
|
}
|
|
|
|
// Config holds IPFS client configuration
|
|
type Config struct {
|
|
Host string
|
|
Port string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// NewClient creates a new IPFS client
|
|
func NewClient(cfg Config) *Client {
|
|
if cfg.Timeout == 0 {
|
|
cfg.Timeout = 30 * time.Second
|
|
}
|
|
|
|
// Create IPFS shell connection
|
|
sh := shell.NewShell(fmt.Sprintf("%s:%s", cfg.Host, cfg.Port))
|
|
sh.SetTimeout(cfg.Timeout)
|
|
|
|
return &Client{
|
|
shell: sh,
|
|
}
|
|
}
|
|
|
|
// Add uploads data to IPFS and returns the CID
|
|
func (c *Client) Add(ctx context.Context, data []byte) (string, error) {
|
|
reader := bytes.NewReader(data)
|
|
cid, err := c.shell.Add(reader)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to add to IPFS: %w", err)
|
|
}
|
|
return cid, nil
|
|
}
|
|
|
|
// AddFile uploads a file to IPFS and returns the CID
|
|
func (c *Client) AddFile(ctx context.Context, reader io.Reader) (string, error) {
|
|
cid, err := c.shell.Add(reader)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to add file to IPFS: %w", err)
|
|
}
|
|
return cid, nil
|
|
}
|
|
|
|
// Get retrieves data from IPFS by CID
|
|
func (c *Client) Get(ctx context.Context, cid string) ([]byte, error) {
|
|
reader, err := c.shell.Cat(cid)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get from IPFS: %w", err)
|
|
}
|
|
defer reader.Close()
|
|
|
|
data, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read IPFS data: %w", err)
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// GetFile retrieves a file from IPFS by CID and returns a reader
|
|
func (c *Client) GetFile(ctx context.Context, cid string) (io.ReadCloser, error) {
|
|
reader, err := c.shell.Cat(cid)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get file from IPFS: %w", err)
|
|
}
|
|
return reader, nil
|
|
}
|
|
|
|
// Pin pins a CID to ensure it's not garbage collected
|
|
func (c *Client) Pin(ctx context.Context, cid string) error {
|
|
err := c.shell.Pin(cid)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to pin CID: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Unpin removes a pin from a CID
|
|
func (c *Client) Unpin(ctx context.Context, cid string) error {
|
|
err := c.shell.Unpin(cid)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to unpin CID: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsAvailable checks if the IPFS node is reachable
|
|
func (c *Client) IsAvailable() bool {
|
|
_, err := c.shell.ID()
|
|
return err == nil
|
|
}
|
|
|
|
// GetGatewayURL returns the IPFS gateway URL for a CID
|
|
func (c *Client) GetGatewayURL(cid string, gateway string) string {
|
|
if gateway == "" {
|
|
gateway = "https://ipfs.io"
|
|
}
|
|
return fmt.Sprintf("%s/ipfs/%s", gateway, cid)
|
|
}
|
|
|
|
// AddJSON uploads JSON data to IPFS
|
|
func (c *Client) AddJSON(ctx context.Context, jsonData []byte) (string, error) {
|
|
return c.Add(ctx, jsonData)
|
|
}
|
|
|
|
// VerifyHTTP checks if a CID is accessible via HTTP gateway
|
|
func (c *Client) VerifyHTTP(cid string, gateway string) error {
|
|
url := c.GetGatewayURL(cid, gateway)
|
|
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
|
|
resp, err := client.Head(url)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to verify CID via HTTP: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("CID not accessible, status: %d", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|