Created
February 11, 2022 05:33
-
-
Save TACIXAT/9f6b91790e39a5e66996aaf229247d71 to your computer and use it in GitHub Desktop.
Golang Cloudflare Images Boilerplate Code
This file contains hidden or 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
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Stopped using this, figured I'd share the code before deleting.