Skip to content

Instantly share code, notes, and snippets.

@TACIXAT
Created February 11, 2022 05:33
Show Gist options
  • Save TACIXAT/9f6b91790e39a5e66996aaf229247d71 to your computer and use it in GitHub Desktop.
Save TACIXAT/9f6b91790e39a5e66996aaf229247d71 to your computer and use it in GitHub Desktop.
Golang Cloudflare Images Boilerplate Code
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
// "mime/multipart"
"errors"
"fmt"
_ "golang.org/x/image/webp"
imagego "image"
_ "image/jpeg"
_ "image/png"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
"time"
)
var cfAccount string
var cfAccountPub string
var cfSigningToken string
var cfKey string
var cfEmail string
var cfApiBase string = "https://api.cloudflare.com/client/v4"
var cfDeliveryBase string = "https://imagedelivery.net"
var codes map[int]string = map[int]string{
5400: "Bad Request",
5401: "Variant not found",
5403: "The given account is not valid or is not authorized to access this service",
5404: "Image not found",
5408: "Client was sending upload too slowly",
5413: "Maximum image size of 10 Mb is reached",
5415: "Images must be uploaded as a form, not as raw image data. Please use multipart/form-data format",
5433: "Request has been aborted by the client",
5450: "Error while receiving upload",
5453: "The given account has reached a service limit",
5455: "Unsupported image format",
5500: "Internal Server Error",
5503: "Server Unavailable",
5540: "Error received from the storage",
5541: "Error while purging cache",
5542: "Error while loading account",
5543: "Error during audit",
5544: "Error during abuse operation",
5550: "Internal Server Error",
}
func init() {
cfAccount = os.Getenv("CF_ACCOUNT")
if len(cfAccount) == 0 {
log.Fatal("missing environment variable: CF_ACCOUNT")
}
cfAccountPub = os.Getenv("CF_ACCOUNT_HASH")
if len(cfAccountPub) == 0 {
log.Fatal("missing environment variable: CF_ACCOUNT_HASH")
}
cfSigningToken = os.Getenv("CF_SIGNING_TOKEN")
if len(cfSigningToken) == 0 {
log.Fatal("missing environment variable: CF_SIGNING_TOKEN")
}
cfKey = os.Getenv("CF_SECRET")
if len(cfKey) == 0 {
log.Fatal("missing environment variable: CF_SECRET")
}
cfEmail = os.Getenv("CF_EMAIL")
if len(cfEmail) == 0 {
log.Fatal("missing environment variable: CF_EMAIL")
}
}
func signUrl(cfImageId string) string {
expiry := time.Now().Add(1 * time.Hour).Unix()
endpoint := fmt.Sprintf(
"/%s/%s/public?exp=%d", cfAccountPub, cfImageId, expiry)
mac := hmac.New(sha256.New, []byte(cfSigningToken))
mac.Write([]byte(endpoint))
sig := hex.EncodeToString(mac.Sum(nil))
return fmt.Sprintf("%s%s&sig=%s", cfDeliveryBase, endpoint, sig)
}
type cfResult struct {
Id string `json:"id"`
Filename string `json:"filename"`
Uploaded time.Time `json:"uploaded"`
RequireSignedURLs bool `json:"requireSignedURLs"`
Variants []string `json:"variants"`
}
type cfResponse struct {
Result cfResult `json:"result"`
Success bool `json:"success"`
}
func cfImageUpload(name string, bs []byte) (string, error) {
client := &http.Client{}
endpoint := fmt.Sprintf(
"%s/accounts/%s/images/v1", cfApiBase, cfAccount)
var b bytes.Buffer
w := multipart.NewWriter(&b)
fw, err := w.CreateFormField("requireSignedURLs")
if err != nil {
log.Println(err)
return "", err
}
_, err = fw.Write([]byte("true"))
if err != nil {
log.Println(err)
return "", err
}
fw, err = w.CreateFormFile("file", name)
if err != nil {
log.Println(err)
return "", err
}
_, err = fw.Write(bs)
if err != nil {
log.Println(err)
return "", err
}
w.Close()
req, err := http.NewRequest("POST", endpoint, &b)
if err != nil {
log.Println(err)
return "", err
}
req.Header.Add("X-Auth-Key", cfKey)
req.Header.Add("X-Auth-Email", cfEmail)
req.Header.Add("Content-Type", w.FormDataContentType())
resp, err := client.Do(req)
if err != nil {
log.Println(err)
return "", err
}
defer resp.Body.Close()
log.Println(resp.Status)
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return "", err
}
log.Println(string(body))
return "", errors.New("Unsuccessful status code.")
}
dec := json.NewDecoder(resp.Body)
cfResp := cfResponse{}
err = dec.Decode(&cfResp)
if err != nil {
log.Println(resp.Body)
log.Println(err)
return "", err
}
if !cfResp.Success {
return "", errors.New("Unsuccessful response.")
}
return cfResp.Result.Id, nil
}
func cfImagesDelete(ids []string) []error {
var errs []error
for _, id := range ids {
err := cfImageDelete(id)
if err != nil {
log.Println(id, err)
errs = append(errs, err)
}
}
return errs
}
func cfImageDelete(id string) error {
client := &http.Client{}
endpoint := fmt.Sprintf(
"%s/accounts/%s/images/v1/%s", cfApiBase, cfAccount, id)
req, err := http.NewRequest("DELETE", endpoint, nil)
if err != nil {
return err
}
req.Header.Add("X-Auth-Key", cfKey)
req.Header.Add("X-Auth-Email", cfEmail)
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
} else {
log.Println(string(body))
}
return errors.New(
fmt.Sprintf(
"Delete for id %s unsuccessful. (%s)", id, resp.Status))
}
dec := json.NewDecoder(resp.Body)
cfResp := cfResponse{}
err = dec.Decode(&cfResp)
if err != nil {
return err
}
if !cfResp.Success {
return errors.New(
fmt.Sprintf("Delete for id %s unsuccessful.", id))
}
return nil
}
func cfImageGet(cdnId string) (img imagego.Image, err error) {
url := signUrl(cdnId)
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return
}
req.Header.Add("Accept", "image/png")
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
img, _, err = imagego.Decode(resp.Body)
return
}
@TACIXAT
Copy link
Author

TACIXAT commented Feb 11, 2022

Stopped using this, figured I'd share the code before deleting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment