forked from imgproxy/imgproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpushd_plugin.go
155 lines (136 loc) · 3.92 KB
/
pushd_plugin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main
import (
"bytes"
"crypto/md5"
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
log "github.com/sirupsen/logrus"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/google/uuid"
)
var pushdPath = "/pushd"
var s3ImagesBucket = os.Getenv("PUSH_S3_IMAGES_BUCKET")
var s3RenderBucket = os.Getenv("PUSH_S3_RENDER_BUCKET")
// called in main.go
func panicIfConfigMissing() {
if s3ImagesBucket == "" {
panic("PUSH_S3_IMAGES_BUCKET environment variable not set")
}
if s3RenderBucket == "" {
panic("PUSH_S3_RENDER_BUCKET environment variable not set")
}
}
func fileNameToParams(requestURI string, needsSig bool) string {
imgParams := make(map[string]string)
splitFilename := strings.Split(path.Base(requestURI), "__")
for _, param := range splitFilename {
splitParam := strings.Split(param, "_")
if len(splitParam) < 2 {
continue
}
imgParams[splitParam[0]] = splitParam[1]
}
s3Path := getS3SourcePath(requestURI)
urlParam := fmt.Sprintf("/plain/s3://%s/%s@jpg", s3Path, splitFilename[len(splitFilename)-1])
var pathStr string
for param, value := range imgParams {
paramString := fmt.Sprintf("/%s:%s", param, value)
pathStr += paramString
}
// if no signature required, add a filler signature
var paramPath string
if needsSig {
paramPath = pathStr + urlParam
} else {
paramPath = "/nosig" + pathStr + urlParam
}
log.Infof("parsed pushd file name to: %s", paramPath)
return paramPath
}
// Gets s3 path of source file
// removes pushdPath and uuid from path if they exist
func getS3SourcePath(requestURI string) string {
pathDirs := strings.Split(path.Dir(requestURI), "/")
s3PathDirs := []string{s3ImagesBucket}
for _, pathDir := range pathDirs {
if len(pathDir) < 1 || isValidUUID(pathDir) || pathDir == pushdPath[1:] {
continue
} else {
s3PathDirs = append(s3PathDirs, pathDir)
}
}
return strings.Join(s3PathDirs, "/")
}
// creates s3 path for cached generated file
func getS3CachePath(requestURI string) string {
pathBase := path.Base(requestURI)
pathDirs := strings.Split(path.Dir(requestURI), "/")
// only add last pathDir if length > 2, we expect at least /pushd in the path
if len(pathDirs) <= 2 {
return pathBase
} else {
return fmt.Sprintf("%s/%s", strings.Join(pathDirs[2:], "/"), pathBase)
}
}
func beforeProcessing(r *http.Request, needsSig bool) (*http.Request, string) {
// process pushd filename if path starts with pushdPath
var cachePath string
if strings.HasPrefix(r.RequestURI, pushdPath) {
cachePath = getS3CachePath(r.RequestURI)
r.RequestURI = fileNameToParams(r.RequestURI, needsSig)
}
return r, cachePath
}
func createMD5Hash(data []byte) string {
hash := md5.New()
reader := bytes.NewReader(data)
if _, err := io.Copy(hash, reader); err != nil {
log.Error(err.Error())
}
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}
func uploadToS3(data []byte, s3Key string, uploaded chan bool) {
md5Hash := createMD5Hash(data)
log.Infof("Uploading rendered image to: %s with md5 hash: %s", s3Key, md5Hash)
awsSession, err := session.NewSession()
if err != nil {
log.Error(err.Error())
return
}
svc := s3.New(awsSession)
input := &s3.PutObjectInput{
Body: aws.ReadSeekCloser(bytes.NewReader(data)),
Bucket: aws.String(s3RenderBucket),
Key: aws.String(s3Key),
ContentType: aws.String("image/jpeg"),
ContentMD5: aws.String(md5Hash),
}
_, err = svc.PutObject(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
log.Error(aerr.Error())
} else {
log.Error(err.Error())
}
return
}
log.Infof("Upload complete for: %s", s3Key)
uploaded <- true
}
func beforeResponse(imageData []byte, cachePath string) chan bool {
uploaded := make(chan bool)
go uploadToS3(imageData, cachePath, uploaded)
return uploaded
}
func isValidUUID(u string) bool {
_, err := uuid.Parse(u)
return err == nil
}