Files
web-hosts/domains/coppertone.tech/backend/pkg/ipfs/client.go
2025-12-26 13:38:04 +01:00

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
}