From b39bc78cb03ddde76ff5bdeb2afb365405472308 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Wed, 4 Mar 2020 20:55:10 -0700
Subject: [PATCH] Early support for putting files into IPFS (not wired)

---
 go.mod                           |  1 +
 ipfs_proxy/api.go                |  6 ++++++
 ipfs_proxy/iface.go              |  3 +++
 ipfs_proxy/ipfs_embedded/impl.go | 32 +++++++++++++++++++++++++-------
 ipfs_proxy/ipfs_local/impl.go    | 11 +++++++++++
 5 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/go.mod b/go.mod
index 5b1ff211..34ffdba5 100644
--- a/go.mod
+++ b/go.mod
@@ -29,6 +29,7 @@ require (
 	github.com/ipfs/go-cid v0.0.4
 	github.com/ipfs/go-ipfs v0.4.22-0.20191119151441-b8ec598d5801
 	github.com/ipfs/go-ipfs-config v0.0.11
+	github.com/ipfs/go-ipfs-files v0.0.4
 	github.com/ipfs/go-ipfs-http-client v0.0.5
 	github.com/ipfs/interface-go-ipfs-core v0.2.6
 	github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
diff --git a/ipfs_proxy/api.go b/ipfs_proxy/api.go
index 0be12796..ba61094b 100644
--- a/ipfs_proxy/api.go
+++ b/ipfs_proxy/api.go
@@ -1,6 +1,8 @@
 package ipfs_proxy
 
 import (
+	"io"
+
 	"github.com/sirupsen/logrus"
 	"github.com/turt2live/matrix-media-repo/common/config"
 	"github.com/turt2live/matrix-media-repo/common/rcontext"
@@ -59,3 +61,7 @@ func getImpl() IPFSImplementation {
 func GetObject(contentId string, ctx rcontext.RequestContext) (*ipfs_models.IPFSObject, error) {
 	return getImpl().GetObject(contentId, ctx)
 }
+
+func PutObject(data io.Reader, ctx rcontext.RequestContext) (string, error) {
+	return getImpl().PutObject(data, ctx)
+}
diff --git a/ipfs_proxy/iface.go b/ipfs_proxy/iface.go
index 6fa26de4..e80a5518 100644
--- a/ipfs_proxy/iface.go
+++ b/ipfs_proxy/iface.go
@@ -1,11 +1,14 @@
 package ipfs_proxy
 
 import (
+	"io"
+
 	"github.com/turt2live/matrix-media-repo/common/rcontext"
 	"github.com/turt2live/matrix-media-repo/ipfs_proxy/ipfs_models"
 )
 
 type IPFSImplementation interface {
 	GetObject(contentId string, ctx rcontext.RequestContext) (*ipfs_models.IPFSObject, error)
+	PutObject(data io.Reader, ctx rcontext.RequestContext) (string, error)
 	Stop()
 }
diff --git a/ipfs_proxy/ipfs_embedded/impl.go b/ipfs_proxy/ipfs_embedded/impl.go
index 24b59f7a..d348f4f1 100644
--- a/ipfs_proxy/ipfs_embedded/impl.go
+++ b/ipfs_proxy/ipfs_embedded/impl.go
@@ -3,6 +3,7 @@ package ipfs_embedded
 import (
 	"bytes"
 	"context"
+	"io"
 	"io/ioutil"
 	"path/filepath"
 	"sync"
@@ -10,6 +11,7 @@ import (
 
 	"github.com/ipfs/go-cid"
 	ipfsConfig "github.com/ipfs/go-ipfs-config"
+	files "github.com/ipfs/go-ipfs-files"
 	"github.com/ipfs/go-ipfs/core"
 	"github.com/ipfs/go-ipfs/core/coreapi"
 	"github.com/ipfs/go-ipfs/core/node/libp2p"
@@ -27,14 +29,18 @@ import (
 )
 
 type IPFSEmbedded struct {
-	api  icore.CoreAPI
-	node *core.IpfsNode
+	api         icore.CoreAPI
+	node        *core.IpfsNode
+	ctx         context.Context
+	cancelCtxFn context.CancelFunc
 }
 
 func NewEmbeddedIPFSNode() (IPFSEmbedded, error) {
 	// Startup routine modified from:
 	// https://github.com/ipfs/go-ipfs/blob/083ef47ce84a5bd9a93f0ce0afaf668881dc1f35/docs/examples/go-ipfs-as-a-library/main.go
 
+	ctx, cancel := context.WithCancel(context.Background())
+
 	blank := IPFSEmbedded{}
 
 	// Load plugins
@@ -85,7 +91,7 @@ func NewEmbeddedIPFSNode() (IPFSEmbedded, error) {
 	}
 
 	logrus.Info("Building IPFS embedded node")
-	node, err := core.NewNode(context.Background(), nodeOptions)
+	node, err := core.NewNode(ctx, nodeOptions)
 	if err != nil {
 		return blank, err
 	}
@@ -135,7 +141,7 @@ func NewEmbeddedIPFSNode() (IPFSEmbedded, error) {
 	for _, peerInfo := range peerInfos {
 		go func(peerInfo *peerstore.PeerInfo) {
 			defer wg.Done()
-			err := api.Swarm().Connect(context.Background(), *peerInfo)
+			err := api.Swarm().Connect(ctx, *peerInfo)
 			if err != nil {
 				logrus.Error(err)
 			} else {
@@ -147,8 +153,10 @@ func NewEmbeddedIPFSNode() (IPFSEmbedded, error) {
 
 	logrus.Info("Done building IPFS embedded node")
 	return IPFSEmbedded{
-		api:  api,
-		node: node,
+		api:         api,
+		node:        node,
+		ctx:         ctx,
+		cancelCtxFn: cancel,
 	}, nil
 }
 
@@ -160,7 +168,7 @@ func (i IPFSEmbedded) GetObject(contentId string, ctx rcontext.RequestContext) (
 	}
 
 	ctx.Log.Info("Resolving path and node")
-	timeoutCtx, cancel := context.WithTimeout(ctx.Context, 10 * time.Second)
+	timeoutCtx, cancel := context.WithTimeout(ctx.Context, 10*time.Second)
 	defer cancel()
 	ipfsPath := icorepath.IpfsPath(ipfsCid)
 	node, err := i.api.ResolveNode(timeoutCtx, ipfsPath)
@@ -177,6 +185,16 @@ func (i IPFSEmbedded) GetObject(contentId string, ctx rcontext.RequestContext) (
 	}, nil
 }
 
+func (i IPFSEmbedded) PutObject(data io.Reader, ctx rcontext.RequestContext) (string, error) {
+	ipfsFile := files.NewReaderFile(data)
+	p, err := i.api.Unixfs().Add(ctx.Context, ipfsFile)
+	if err != nil {
+		return "", err
+	}
+	return p.Cid().String(), nil
+}
+
 func (i IPFSEmbedded) Stop() {
+	i.cancelCtxFn()
 	i.node.Close()
 }
diff --git a/ipfs_proxy/ipfs_local/impl.go b/ipfs_proxy/ipfs_local/impl.go
index f37cb699..25c38dec 100644
--- a/ipfs_proxy/ipfs_local/impl.go
+++ b/ipfs_proxy/ipfs_local/impl.go
@@ -2,8 +2,10 @@ package ipfs_local
 
 import (
 	"bytes"
+	"io"
 
 	"github.com/ipfs/go-cid"
+	files "github.com/ipfs/go-ipfs-files"
 	httpapi "github.com/ipfs/go-ipfs-http-client"
 	"github.com/ipfs/interface-go-ipfs-core/path"
 	"github.com/turt2live/matrix-media-repo/common/rcontext"
@@ -42,6 +44,15 @@ func (i IPFSLocal) GetObject(contentId string, ctx rcontext.RequestContext) (*ip
 	}, nil
 }
 
+func (i IPFSLocal) PutObject(data io.Reader, ctx rcontext.RequestContext) (string, error) {
+	ipfsFile := files.NewReaderFile(data)
+	p, err := i.client.Unixfs().Add(ctx.Context, ipfsFile)
+	if err != nil {
+		return "", err
+	}
+	return p.Cid().String(), nil
+}
+
 func (i IPFSLocal) Stop() {
 	// Nothing to do
 }
-- 
GitLab