-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
initial implementation #1
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
763bf48
init
gravityblast 2e50dd6
add http router and port/public flags
gravityblast 629d77c
add initial tests
gravityblast e0ce439
test served static files
gravityblast 2b64060
add pinJSONHandler
gravityblast e39ee0a
generate real ipfs cid
gravityblast 1f67189
add pinFileHandler
gravityblast dee1c00
add handleUpload
gravityblast d357475
add authentication
gravityblast a8ead83
add README
gravityblast 13fa9af
add Docker files
gravityblast 79d7fc8
update readme
gravityblast 89ad1a6
add docker action
gravityblast 86a3af7
build
gravityblast a04ad83
add default CORS handler
gravityblast fb8b55a
add logger
gravityblast 57e8c66
log errors and written file paths
gravityblast bc6ffa7
pinata responses starts with uppercase char
gravityblast File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/public | ||
/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
name: Create and publish a Docker image | ||
|
||
on: | ||
push: | ||
branches: | ||
- '**' | ||
pull_request: | ||
branches: | ||
- "**" | ||
|
||
|
||
env: | ||
REGISTRY: ghcr.io | ||
IMAGE_NAME: ${{ github.repository }} | ||
|
||
jobs: | ||
build-and-push-image: | ||
runs-on: ubuntu-latest | ||
|
||
permissions: | ||
contents: read | ||
packages: write | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Log in to the Container registry | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ${{ env.REGISTRY }} | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Extract metadata (tags, labels) for Docker | ||
id: meta | ||
uses: docker/metadata-action@v5 | ||
with: | ||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
|
||
|
||
- name: Build and push Docker image | ||
uses: docker/build-push-action@v5 | ||
with: | ||
context: . | ||
push: true | ||
tags: ${{ steps.meta.outputs.tags }} | ||
labels: ${{ steps.meta.outputs.labels }} | ||
platforms: linux/amd64,linux/arm64 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/pina | ||
/public |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
FROM golang:1.21.3-alpine3.18 | ||
|
||
COPY . /app | ||
WORKDIR /app | ||
|
||
RUN mkdir /app/bin | ||
RUN mkdir /app/public | ||
|
||
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/🍍 | ||
|
||
EXPOSE 8000 | ||
|
||
CMD /app/bin/🍍 -port 8000 -public /app/public |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.PHONY: docker-build docker-run docker-kill docker-stop docker-logs docker-deploy-contracts docker-all | ||
|
||
IMAGE_NAME=pina | ||
CONTAINER_NAME=pina | ||
|
||
docker-all: docker-kill docker-build docker-run | ||
|
||
docker-build: | ||
docker build . -t $(IMAGE_NAME) --no-cache --progress=plain | ||
|
||
docker-run: | ||
docker run --name $(CONTAINER_NAME) --rm -d -p 127.0.0.1:8000:8000/tcp $(IMAGE_NAME) | ||
|
||
docker-kill: | ||
-docker kill $(CONTAINER_NAME) | ||
|
||
docker-stop: | ||
docker stop $(CONTAINER_NAME) | ||
|
||
docker-logs: | ||
docker logs -f $(CONTAINER_NAME) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Piña 🍍 | ||
|
||
Piña is a clone of the [Pinata](https://www.pinata.cloud/) API, | ||
**exclusively designed for local development environments** to streamline your development process and minimize | ||
the need for direct usage of Pinata's services during development. | ||
|
||
## API | ||
|
||
``` | ||
get /ipfs/{CID} | ||
post /pinning/pinJSONToIPFS | ||
post /pinning/pinFileToIPFS | ||
``` | ||
|
||
### Test and Build | ||
|
||
``` | ||
git clone [email protected]:gitcoinco/pina.git | ||
go test | ||
go build | ||
``` | ||
|
||
### Run | ||
|
||
`go build && ./pina -port 8000 -public ./public` | ||
|
||
### Run in docker | ||
|
||
``` | ||
make docker-build | ||
make docker-run | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module github.com/gitcoinco/pina | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/gravityblast/miniassert v0.0.0-20140522125902-bee63581261a | ||
github.com/julienschmidt/httprouter v1.3.0 | ||
) | ||
|
||
require ( | ||
github.com/ipfs/go-cid v0.4.1 // indirect | ||
github.com/klauspost/cpuid/v2 v2.0.4 // indirect | ||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect | ||
github.com/minio/sha256-simd v1.0.0 // indirect | ||
github.com/mr-tron/base58 v1.2.0 // indirect | ||
github.com/multiformats/go-base32 v0.0.3 // indirect | ||
github.com/multiformats/go-base36 v0.1.0 // indirect | ||
github.com/multiformats/go-multibase v0.0.3 // indirect | ||
github.com/multiformats/go-multicodec v0.9.0 // indirect | ||
github.com/multiformats/go-multihash v0.0.15 // indirect | ||
github.com/multiformats/go-varint v0.0.6 // indirect | ||
github.com/rs/cors v1.10.1 // indirect | ||
golang.org/x/crypto v0.1.0 // indirect | ||
golang.org/x/sys v0.1.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
github.com/gravityblast/miniassert v0.0.0-20140522125902-bee63581261a h1:3xcty4gUzOHD4pqUZQDbS/Xb34susIRg9nmjKR3XM94= | ||
github.com/gravityblast/miniassert v0.0.0-20140522125902-bee63581261a/go.mod h1:tVdF3zmaPraSn1jEeaVFIw4GYDX6fezNB3P9Gk4UpPA= | ||
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= | ||
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= | ||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= | ||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= | ||
github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= | ||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= | ||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= | ||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= | ||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= | ||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= | ||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= | ||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= | ||
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= | ||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= | ||
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= | ||
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= | ||
github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= | ||
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= | ||
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= | ||
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= | ||
github.com/multiformats/go-multihash v0.0.15 h1:hWOPdrNqDjwHDx82vsYGSDZNyktOJJ2dzZJzFkOV1jM= | ||
github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= | ||
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= | ||
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= | ||
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= | ||
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= | ||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= | ||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= | ||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"mime/multipart" | ||
"net/http" | ||
"net/http/httptest" | ||
"net/textproto" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
|
||
assert "github.com/gravityblast/miniassert" | ||
"github.com/julienschmidt/httprouter" | ||
) | ||
|
||
func newTestRouter(t *testing.T) (*httprouter.Router, string) { | ||
publicPath := t.TempDir() | ||
router, err := newRouter(publicPath) | ||
if err != nil { | ||
t.Error(err) | ||
t.FailNow() | ||
} | ||
|
||
return router, filepath.Join(publicPath, "ipfs") | ||
} | ||
|
||
func TestIndexHandler(t *testing.T) { | ||
router, _ := newTestRouter(t) | ||
|
||
w := httptest.NewRecorder() | ||
req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
router.ServeHTTP(w, req) | ||
|
||
resp := w.Result() | ||
body, _ := io.ReadAll(resp.Body) | ||
|
||
assert.Equal(t, 200, resp.StatusCode) | ||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) | ||
assert.Equal(t, "Hello World", string(body)) | ||
} | ||
|
||
func TestStaticFiles(t *testing.T) { | ||
router, ipfsPath := newTestRouter(t) | ||
filePath := filepath.Join(ipfsPath, "test.txt") | ||
err := os.WriteFile(filePath, []byte("Static file example"), 0755) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
w := httptest.NewRecorder() | ||
req := httptest.NewRequest(http.MethodGet, "/ipfs/test.txt", nil) | ||
router.ServeHTTP(w, req) | ||
|
||
resp := w.Result() | ||
body, _ := io.ReadAll(resp.Body) | ||
|
||
assert.Equal(t, 200, resp.StatusCode) | ||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) | ||
assert.Equal(t, "Static file example", string(body)) | ||
} | ||
|
||
func TestPinJSONHandler(t *testing.T) { | ||
router, ipfsPath := newTestRouter(t) | ||
|
||
w := httptest.NewRecorder() | ||
reqBody := bytes.NewBufferString(`{"pinataContent": {"foo": {"bar": "baz"}}}`) | ||
req := httptest.NewRequest(http.MethodPost, "/pinning/pinJSONToIPFS", reqBody) | ||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", AUTH_TOKEN)) | ||
router.ServeHTTP(w, req) | ||
|
||
resp := w.Result() | ||
|
||
assert.Equal(t, 200, resp.StatusCode) | ||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) | ||
|
||
var responseBody PinJSONResponseBody | ||
err := json.NewDecoder(resp.Body).Decode(&responseBody) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
cid := "bafkreihktyturq4bzrikdjylvvjbrgh5rfigzlydmjoyri3ip6fjbcqddu" | ||
assert.Equal(t, cid, responseBody.IpfsHash) | ||
assert.Equal(t, 10, responseBody.PinSize) | ||
assert.NotEqual(t, "", responseBody.Timestamp) | ||
|
||
fileName := filepath.Join(ipfsPath, cid) | ||
f, err := os.Open(fileName) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
content, err := ioutil.ReadAll(f) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
assert.Equal(t, `{"foo":{"bar":"baz"}}`, strings.TrimSpace(string(content))) | ||
} | ||
|
||
func TestPinFileHandler(t *testing.T) { | ||
router, _ := newTestRouter(t) | ||
|
||
body := new(bytes.Buffer) | ||
writer := multipart.NewWriter(body) | ||
// writer.WriteField("file", "Uploaded content") | ||
|
||
// Create the file part with an invalid file extension | ||
h := make(textproto.MIMEHeader) | ||
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, "file", "uploaded.txt")) | ||
h.Set("Content-Type", "text/plain") | ||
part, _ := writer.CreatePart(h) | ||
part.Write([]byte("Uploaded content")) | ||
writer.Close() | ||
// Prepare and send the HTTP request | ||
|
||
w := httptest.NewRecorder() | ||
req := httptest.NewRequest(http.MethodPost, "/pinning/pinFileToIPFS", body) | ||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", AUTH_TOKEN)) | ||
req.Header.Add("Content-Type", writer.FormDataContentType()) | ||
router.ServeHTTP(w, req) | ||
|
||
resp := w.Result() | ||
|
||
assert.Equal(t, 200, resp.StatusCode) | ||
assert.Equal(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) | ||
|
||
var responseBody PinJSONResponseBody | ||
err := json.NewDecoder(resp.Body).Decode(&responseBody) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
cid := "bafkreifqrpvngn6k2q6qahrm2oawbtrsoucxi7xtxehydodbzgnky6eiem" | ||
assert.Equal(t, cid, responseBody.IpfsHash) | ||
assert.Equal(t, 10, responseBody.PinSize) | ||
assert.NotEqual(t, "", responseBody.Timestamp) | ||
|
||
fileName := filepath.Join(ipfsPath, cid) | ||
f, err := os.Open(fileName) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
content, err := ioutil.ReadAll(f) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
assert.Equal(t, "Uploaded content", strings.TrimSpace(string(content))) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I wanted to persist the uploaded files between restarts, would i have to mount a volume here? maybe a
VOLUME
directive would be a good idea hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@boudra you should be able to do it anyway when you start the container right?
but I didn't set it as our standard way to do it just because we don't have a way to persist the full state of the system including the blockchain state until anvil doesn't dump events foundry-rs/foundry#5906