ipfs-node-pin/main.go

180 lines
4.3 KiB
Go
Raw Normal View History

2024-01-04 19:36:26 +00:00
package main
import (
2024-01-06 12:51:26 +00:00
"encoding/json"
"fmt"
"io"
"io/fs"
"mime/multipart"
"net/http"
2024-01-07 12:20:52 +00:00
"net/textproto"
2024-01-07 20:46:25 +00:00
"net/url"
2024-01-06 12:51:26 +00:00
"os"
"path/filepath"
2024-01-07 12:54:14 +00:00
"strings"
2024-01-06 12:51:26 +00:00
"github.com/sethvargo/go-githubactions"
2024-01-04 19:36:26 +00:00
)
2024-01-06 12:51:26 +00:00
type AddResponse struct {
Bytes int64
Hash string
Name string
Size string
}
2024-01-07 12:54:14 +00:00
type IpfsMultipartWriter struct {
multipart.Writer
}
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}
func NewIpfsMultipartWriter(w io.Writer) *IpfsMultipartWriter {
return &IpfsMultipartWriter{
Writer: *multipart.NewWriter(w),
}
}
func (w *IpfsMultipartWriter) CreateIpfsDirectoryPart(name string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
2024-01-08 07:01:04 +00:00
encodedName := url.QueryEscape(name)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, encodedName))
2024-01-07 12:54:14 +00:00
h.Set("Content-Type", "application/x-directory")
return w.CreatePart(h)
}
2024-01-07 17:08:00 +00:00
func (w *IpfsMultipartWriter) CreateIpfsFilePart(name string) (io.Writer, error) {
2024-01-07 12:54:14 +00:00
h := make(textproto.MIMEHeader)
2024-01-08 07:01:04 +00:00
encodedName := url.QueryEscape(name)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(encodedName)))
2024-01-07 12:54:14 +00:00
h.Set("Content-Type", "application/octet-stream")
return w.CreatePart(h)
}
2024-01-07 17:08:00 +00:00
func (w *IpfsMultipartWriter) CreateIpfsAbsFilePart(name, absPath string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
2024-01-08 07:01:04 +00:00
encodedName := url.QueryEscape(name)
2024-01-07 17:08:00 +00:00
h.Set("AbsPath", absPath)
2024-01-08 07:01:04 +00:00
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(encodedName)))
2024-01-07 17:08:00 +00:00
h.Set("Content-Type", "application/octet-stream")
return w.CreatePart(h)
}
2024-01-07 18:56:56 +00:00
2024-01-04 19:36:26 +00:00
func main() {
2024-01-08 07:01:04 +00:00
githubactions.Debugf("Checking inputs")
2024-01-06 18:54:00 +00:00
2024-01-06 12:51:26 +00:00
path := githubactions.GetInput("path_to_add")
if path == "" {
githubactions.Fatalf("Missing: path_to_add")
}
ipfsHost := githubactions.GetInput("ipfs_host")
if ipfsHost == "" {
githubactions.Fatalf("Missing: ipfs_host")
}
ipfsPort := githubactions.GetInput("ipfs_port")
if ipfsPort == "" {
githubactions.Fatalf("Missing: ipfs_port")
}
targetPath, err := os.Open(path)
if err != nil {
githubactions.Fatalf("Unable to access path_to_add: %v", err.Error())
}
defer targetPath.Close()
targetPathInfo, err := targetPath.Stat()
if err != nil {
githubactions.Fatalf("Unable to access to access path_to_add info: %v", fmt.Errorf("%w", err))
}
if !targetPathInfo.IsDir() {
githubactions.Fatalf("%v is not a directory", path)
}
2024-01-08 07:01:04 +00:00
githubactions.Debugf("Inputs OK")
2024-01-06 18:54:00 +00:00
2024-01-06 12:51:26 +00:00
body, writer := io.Pipe()
2024-01-07 20:46:25 +00:00
targetUrl := fmt.Sprintf("http://%v:%v/api/v0/add", ipfsHost, ipfsPort)
req, err := http.NewRequest(http.MethodPost, targetUrl, body)
2024-01-06 12:51:26 +00:00
if err != nil {
githubactions.Fatalf("Unable to create request: %v", err.Error())
}
2024-01-07 16:56:10 +00:00
q := req.URL.Query()
2024-01-07 20:46:25 +00:00
q.Add("progress", "false")
2024-01-07 21:19:37 +00:00
q.Add("stream-true", "false")
2024-01-07 16:56:10 +00:00
req.URL.RawQuery = q.Encode()
2024-01-07 12:54:14 +00:00
mwriter := NewIpfsMultipartWriter(writer)
2024-01-06 20:36:09 +00:00
req.Header.Add("Content-Type", mwriter.FormDataContentType())
2024-01-06 12:51:26 +00:00
2024-01-06 20:49:47 +00:00
go func() {
2024-01-06 21:17:31 +00:00
defer writer.Close()
2024-01-07 21:19:37 +00:00
defer mwriter.Close()
2024-01-06 21:17:31 +00:00
2024-01-06 20:49:47 +00:00
err = filepath.Walk(path, func(innerPath string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
2024-01-07 18:51:18 +00:00
if info.IsDir() {
2024-01-07 21:19:37 +00:00
return nil
}
2024-01-08 07:01:04 +00:00
w, err := mwriter.CreateIpfsFilePart(innerPath)
2024-01-07 21:19:37 +00:00
if err != nil {
return err
}
fileReader, err := os.Open(innerPath)
if err != nil {
return err
}
defer fileReader.Close()
written, err := io.Copy(w, fileReader)
if err != nil {
return fmt.Errorf("error copying %s (%d bytes written): %v", innerPath, written, err)
2024-01-06 20:49:47 +00:00
}
return nil
})
2024-01-06 20:05:26 +00:00
2024-01-06 20:36:09 +00:00
if err != nil {
2024-01-06 20:57:37 +00:00
githubactions.Fatalf("Unable to create request body: %v", fmt.Errorf("%w", err))
}
2024-01-06 20:49:47 +00:00
}()
2024-01-06 12:51:26 +00:00
2024-01-07 19:15:22 +00:00
githubactions.Infof("Calling node API...")
2024-01-06 12:51:26 +00:00
client := &http.Client{}
res, err := client.Do(req)
2024-01-06 18:46:27 +00:00
if err != nil {
githubactions.Fatalf(err.Error())
}
2024-01-06 12:51:26 +00:00
2024-01-07 19:15:22 +00:00
githubactions.Infof("Reading response...")
2024-01-06 12:51:26 +00:00
resBody, err := io.ReadAll(res.Body)
if err != nil {
2024-01-06 18:46:27 +00:00
githubactions.Fatalf(err.Error())
2024-01-06 12:51:26 +00:00
}
2024-01-07 20:46:25 +00:00
githubactions.Infof("Response: %v", string(resBody))
2024-01-07 20:50:02 +00:00
d := json.NewDecoder(res.Body)
for {
var ipfsAddResponse []AddResponse
if err := d.Decode(&ipfsAddResponse); err == io.EOF {
break
} else if err != nil {
githubactions.Warningf("Failed to unmarshal response: %v", fmt.Errorf("%w", err))
}
githubactions.Infof("Received response: %v", ipfsAddResponse)
2024-01-07 19:18:22 +00:00
}
2024-01-04 19:36:26 +00:00
}