Add image uploads

This commit is contained in:
ekzyis 2024-09-23 02:11:11 +02:00
parent 7048c2ec9b
commit f85af6abe3
2 changed files with 140 additions and 3 deletions

View File

@ -10,9 +10,10 @@ import (
)
type Client struct {
BaseUrl string
ApiUrl string
ApiKey string
BaseUrl string
ApiUrl string
ApiKey string
MediaUrl string
}
func NewClient(options ...func(*Client)) *Client {
@ -28,6 +29,9 @@ func NewClient(options ...func(*Client)) *Client {
if c.ApiKey == "" {
c.ApiKey = os.Getenv("SN_API_KEY")
}
if c.MediaUrl == "" {
c.MediaUrl = "https://m.stacker.news"
}
c.ApiUrl = fmt.Sprintf("%s/api/graphql", c.BaseUrl)
return c
@ -45,6 +49,12 @@ func WithBaseUrl(baseUrl string) func(*Client) {
}
}
func WithMediaUrl(mediaUrl string) func(*Client) {
return func(c *Client) {
c.MediaUrl = mediaUrl
}
}
type GqlBody struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`

127
upload.go Normal file
View File

@ -0,0 +1,127 @@
package sn
import (
"bytes"
"encoding/json"
"fmt"
"image"
"image/png"
"io"
"mime/multipart"
"net/http"
)
type GetSignedPOST struct {
Url string `json:"url"`
Fields map[string]string `json:"fields"`
}
type GetSignedPOSTResponse struct {
Errors []GqlError `json:"errors"`
Data struct {
GetSignedPOST GetSignedPOST `json:"getSignedPOST"`
} `json:"data"`
}
func (c *Client) UploadImage(img *image.RGBA) (string, error) {
var (
b = img.Bounds()
width = b.Max.X
height = b.Max.Y
size = width * height
type_ = "image/png"
)
// get signed URL for S3 upload
body := GqlBody{
Query: `
mutation getSignedPOST($type: String!, $size: Int!, $width: Int!, $height: Int!, $avatar: Boolean) {
getSignedPOST(type: $type, size: $size, width: $width, height: $height, avatar: $avatar) {
url
fields
}
}`,
Variables: map[string]interface{}{
"type": type_,
"size": size,
"width": width,
"height": height,
"avatar": false,
},
}
resp, err := c.callApi(body)
if err != nil {
return "", err
}
defer resp.Body.Close()
var respBody GetSignedPOSTResponse
err = json.NewDecoder(resp.Body).Decode(&respBody)
if err != nil {
err = fmt.Errorf("error decoding getSignedPOST: %w", err)
return "", err
}
err = c.checkForErrors(respBody.Errors)
if err != nil {
return "", err
}
s3Url := respBody.Data.GetSignedPOST.Url
fields := respBody.Data.GetSignedPOST.Fields
// create multipart form
var (
buf bytes.Buffer
w = multipart.NewWriter(&buf)
fw io.Writer
)
for k, v := range fields {
if fw, err = w.CreateFormField(k); err != nil {
return "", err
}
fw.Write([]byte(v))
}
for k, v := range map[string]string{
"Content-type": type_,
"Cache-Control": "max-age=31536000",
"acl": "public-read",
} {
if fw, err = w.CreateFormField(k); err != nil {
return "", err
}
fw.Write([]byte(v))
}
if fw, err = w.CreateFormFile("file", "image.png"); err != nil {
return "", err
}
if err = png.Encode(fw, img); err != nil {
return "", err
}
if err = w.Close(); err != nil {
return "", err
}
// upload to S3
var req *http.Request
if req, err = http.NewRequest("POST", s3Url, &buf); err != nil {
return "", err
}
req.Header.Set("Content-Type", w.FormDataContentType())
client := http.DefaultClient
if resp, err = client.Do(req); err != nil {
return "", err
}
defer resp.Body.Close()
imgId := respBody.Data.GetSignedPOST.Fields["key"]
imgUrl := fmt.Sprintf("%s/%s", c.MediaUrl, imgId)
return imgUrl, nil
}