Skip to content

Commit b42ae61

Browse files
feat: add arweave vith iExec API
1 parent e1e9968 commit b42ae61

File tree

3 files changed

+154
-9
lines changed

3 files changed

+154
-9
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ web3datacli [command]
4646

4747
### Available Commands
4848

49-
| Command | Description |
50-
|--------------|----------------------------------------|
49+
| Command | Description |
50+
| ------------ | --------------------------------------- |
51+
| `arweave` | 🕸️ Interact with Arweave |
5152
| `encryption` | 🔐 Manage data encryption and decryption |
52-
| `ipfs` | 📤 Interact with IPFS |
53-
| `version` | Show the CLI version |
54-
| `completion` | Generate autocompletion for your shell |
55-
| `help` | Help about any command |
53+
| `ipfs` | 📤 Interact with IPFS |
54+
| `version` | Show the CLI version |
55+
| `completion` | Generate autocompletion for your shell |
56+
| `help` | Help about any command |
5657

5758
---
5859

@@ -61,8 +62,8 @@ web3datacli [command]
6162
Enable autocompletion for your shell (e.g., bash, zsh):
6263

6364
```bash
64-
web3datacli completion bash > /etc/bash_completion.d/web3datacli
65-
source /etc/bash_completion.d/web3datacli
65+
web3datacli completion zsh > ~/.zsh_completions/_web3datacli
66+
source ~/.zsh_completions/_web3datacli
6667
```
6768

6869
---
@@ -75,4 +76,4 @@ MIT License © 2025 F.CORDIER
7576

7677
## 🤝 Contributions
7778

78-
Feel free to open issues or submit PRs — contributions are welcome!
79+
Feel free to open issues or submit PRs — contributions are welcome!

cmd/arweave/arweave.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package arweave
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"mime/multipart"
9+
"net/http"
10+
"os"
11+
"path/filepath"
12+
13+
"github.com/spf13/cobra"
14+
)
15+
16+
var (
17+
apiBaseURL string
18+
)
19+
20+
var ArweaveCmd = &cobra.Command{
21+
Use: "arweave",
22+
Short: "🕸️ Interact with Arweave",
23+
}
24+
25+
func init() {
26+
ArweaveCmd.AddCommand(arweaveUploadCommand())
27+
ArweaveCmd.AddCommand(arweaveDownloadCommand())
28+
}
29+
30+
func arweaveUploadCommand() *cobra.Command {
31+
var filePath string
32+
33+
cmd := &cobra.Command{
34+
Use: "upload",
35+
Short: "Upload a file to Arweave through a relay API",
36+
RunE: func(cmd *cobra.Command, args []string) error {
37+
if filePath == "" {
38+
return fmt.Errorf("❌ you must provide a file path with --file")
39+
}
40+
41+
file, err := os.Open(filePath)
42+
if err != nil {
43+
return fmt.Errorf("cannot open file: %w", err)
44+
}
45+
defer file.Close()
46+
47+
var buf bytes.Buffer
48+
writer := multipart.NewWriter(&buf)
49+
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
50+
if err != nil {
51+
return fmt.Errorf("cannot create form file: %w", err)
52+
}
53+
54+
if _, errCopy := io.Copy(part, file); errCopy != nil {
55+
return fmt.Errorf("failed to copy file data: %w", errCopy)
56+
}
57+
58+
if errClose := writer.Close(); errClose != nil {
59+
return fmt.Errorf("failed to close multipart writer: %w", errClose)
60+
}
61+
62+
resp, err := http.Post(apiBaseURL+"/upload", writer.FormDataContentType(), &buf)
63+
if err != nil {
64+
return fmt.Errorf("upload request failed: %w", err)
65+
}
66+
defer resp.Body.Close()
67+
68+
body, _ := io.ReadAll(resp.Body)
69+
if resp.StatusCode != http.StatusOK {
70+
return fmt.Errorf("arweave upload failed: %s", string(body))
71+
}
72+
73+
var res struct {
74+
ArweaveID string `json:"arweaveId"`
75+
URL string `json:"url"`
76+
}
77+
if err := json.Unmarshal(body, &res); err != nil {
78+
return fmt.Errorf("invalid response: %w", err)
79+
}
80+
81+
fmt.Println("✅ Upload successful")
82+
fmt.Printf("🆔 Arweave ID: %s\n🔗 URL: %s\n", res.ArweaveID, res.URL)
83+
return nil
84+
},
85+
}
86+
87+
cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the local file to upload")
88+
cmd.Flags().StringVarP(&apiBaseURL, "api", "a", "http://localhost:3000", "Base URL of the Arweave relay API (without /upload)")
89+
90+
return cmd
91+
}
92+
93+
func arweaveDownloadCommand() *cobra.Command {
94+
var id string
95+
var output string
96+
97+
cmd := &cobra.Command{
98+
Use: "download",
99+
Short: "Download a file from Arweave using its transaction ID",
100+
RunE: func(cmd *cobra.Command, args []string) error {
101+
if id == "" {
102+
return fmt.Errorf("❌ you must provide an Arweave transaction ID with --id")
103+
}
104+
if output == "" {
105+
output = id
106+
}
107+
108+
url := fmt.Sprintf("https://arweave.net/%s", id)
109+
110+
fmt.Printf("🔗 Downloading from: %s\n📁 Saving to: %s\n", url, output)
111+
// #nosec G107 -- ID is validated and used to build a known domain URL
112+
resp, err := http.Get(url)
113+
if err != nil {
114+
return fmt.Errorf("failed to download from Arweave: %w", err)
115+
}
116+
defer resp.Body.Close()
117+
118+
if resp.StatusCode != http.StatusOK {
119+
body, _ := io.ReadAll(resp.Body)
120+
return fmt.Errorf("arweave download failed: %s", string(body))
121+
}
122+
123+
outFile, err := os.Create(output)
124+
if err != nil {
125+
return fmt.Errorf("cannot create output file: %w", err)
126+
}
127+
defer outFile.Close()
128+
129+
if _, err := io.Copy(outFile, resp.Body); err != nil {
130+
return fmt.Errorf("error saving file: %w", err)
131+
}
132+
133+
fmt.Println("✅ Download complete")
134+
return nil
135+
},
136+
}
137+
138+
cmd.Flags().StringVarP(&id, "id", "i", "", "Arweave transaction ID to download (required)")
139+
cmd.Flags().StringVarP(&output, "out", "o", "", "Output file path (optional)")
140+
141+
return cmd
142+
}

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"github.com/thewhitewizard/web3data-cli/cmd/arweave"
45
"github.com/thewhitewizard/web3data-cli/cmd/encryption"
56
"github.com/thewhitewizard/web3data-cli/cmd/ipfs"
67
"github.com/thewhitewizard/web3data-cli/cmd/version"
@@ -20,5 +21,6 @@ func Execute() {
2021
func init() {
2122
rootCmd.AddCommand(encryption.EncryptionCmd)
2223
rootCmd.AddCommand(ipfs.IPFSCmd)
24+
rootCmd.AddCommand(arweave.ArweaveCmd)
2325
rootCmd.AddCommand(version.VersionCmd)
2426
}

0 commit comments

Comments
 (0)