From a5d41ae1f722c2be129fae61e38a0ff24b56ef17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20Rajkovi=C4=87?= Date: Mon, 13 Jun 2022 08:37:16 +0200 Subject: [PATCH] Implemented resizer --- cache/impl.go | 73 ----------- cache/manager.go | 24 ---- go.mod | 17 ++- go.sum | 19 ++- handlers.go | 109 ----------------- image.go | 57 --------- image/image.go | 52 ++++++++ image/image_test.go | 31 +++++ main.go | 83 ++++++------- resource/impl.go | 288 -------------------------------------------- resource/manager.go | 35 ------ 11 files changed, 148 insertions(+), 640 deletions(-) delete mode 100644 cache/impl.go delete mode 100644 cache/manager.go delete mode 100644 handlers.go delete mode 100644 image.go create mode 100644 image/image.go create mode 100644 image/image_test.go delete mode 100644 resource/impl.go delete mode 100644 resource/manager.go diff --git a/cache/impl.go b/cache/impl.go deleted file mode 100644 index 80e1e91..0000000 --- a/cache/impl.go +++ /dev/null @@ -1,73 +0,0 @@ -package cache - -import ( - "context" - "errors" - "github.com/go-redis/redis/v8" - "os" - "time" -) - -func newRedisCache() Manager { - redisClient := redis.NewClient(&redis.Options{ - Addr: os.Getenv("REDIS_URL"), - Username: os.Getenv("REDIS_USERNAME"), - Password: os.Getenv("REDIS_PASSWORD"), - }) - return &redisCache{redisClient} -} - -type redisCache struct { - client *redis.Client -} - -func (r *redisCache) Set(key, value string, expiration time.Duration) error { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - return r.client.Set(ctx, key, value, expiration).Err() -} -func (r *redisCache) Get(key string) (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - cacheValue := r.client.Get(ctx, key) - return cacheValue.Val(), cacheValue.Err() -} -func (r *redisCache) Remove(key string) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - r.client.Del(ctx, key) -} - -func newInMemCache() Manager { - return &inMemCache{make(map[string]inMemEntry)} -} - -type inMemEntry struct { - value string - expiration time.Time -} - -type inMemCache struct { - entries map[string]inMemEntry -} - -func (i *inMemCache) Set(key, value string, expiration time.Duration) error { - i.entries[key] = inMemEntry{ - value: value, - expiration: time.Now().Add(expiration), - } - return nil -} -func (i *inMemCache) Get(key string) (string, error) { - if val, exists := i.entries[key]; exists { - if val.expiration.Before(time.Now()) { - delete(i.entries, key) - } else { - return val.value, nil - } - } - return "", errors.New("no value for given key") -} -func (i *inMemCache) Remove(key string) { - delete(i.entries, key) -} diff --git a/cache/manager.go b/cache/manager.go deleted file mode 100644 index 18d85a8..0000000 --- a/cache/manager.go +++ /dev/null @@ -1,24 +0,0 @@ -package cache - -import ( - "log" - "os" - "strings" - "time" -) - -func NewManager() Manager { - if strings.Contains(os.Getenv("PROFILE"), "redis") { - log.Println("Cache | using redis cache") - return newRedisCache() - } else { - log.Println("Cache | using in memory cache") - return newInMemCache() - } -} - -type Manager interface { - Set(key, value string, expiration time.Duration) error - Get(key string) (string, error) - Remove(key string) -} diff --git a/go.mod b/go.mod index 2b6c483..b9b762f 100644 --- a/go.mod +++ b/go.mod @@ -3,30 +3,29 @@ module git.bbr-dev.info/brajkovic/resource_manager go 1.17 require ( - github.com/anthonynsimon/bild v0.13.0 // indirect - github.com/aws/aws-sdk-go v1.42.7 // indirect + github.com/anthonynsimon/bild v0.13.0 + github.com/aws/aws-sdk-go v1.42.7 + github.com/gin-gonic/gin v1.7.4 + github.com/go-redis/redis/v8 v8.11.4 + github.com/joho/godotenv v1.4.0 +) + +require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.7.4 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/validator/v10 v10.4.1 // indirect - github.com/go-redis/redis/v8 v8.11.4 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/joho/godotenv v1.4.0 // indirect github.com/json-iterator/go v1.1.9 // indirect github.com/leodido/go-urn v1.2.0 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect - github.com/spf13/cobra v0.0.5 // indirect - github.com/spf13/pflag v1.0.3 // indirect github.com/ugorji/go/codec v1.1.7 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect - golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 // indirect golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect google.golang.org/protobuf v1.26.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 9f89301..41fd7ea 100644 --- a/go.sum +++ b/go.sum @@ -11,15 +11,18 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= @@ -45,14 +48,15 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -70,29 +74,32 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -106,7 +113,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 h1:uc17S921SPw5F2gJo7slQ3aqvr2RwpL7eb3+DZncu3s= golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -115,6 +121,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -137,6 +144,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -144,6 +152,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -154,8 +163,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/handlers.go b/handlers.go deleted file mode 100644 index b36a025..0000000 --- a/handlers.go +++ /dev/null @@ -1,109 +0,0 @@ -package main - -import ( - "bytes" - "encoding/base64" - "git.bbr-dev.info/brajkovic/resource_manager/resource" - "github.com/gin-gonic/gin" - "log" - "net/http" - "strings" -) - -type LegacySave struct { - Content string `json:"content"` - Path string `json:"path"` - Properties struct { - Height int `json:"height"` - Overwrite bool `json:"overwrite"` - MimeType string `json:"mimeType"` - } `json:"properties"` -} - -func HandleLegacySave(resourceManager resource.Manager) gin.HandlerFunc { - return func(c *gin.Context) { - var legacySave LegacySave - if err := c.ShouldBindJSON(&legacySave); err == nil { - // removing image/(png/jpeg/...); start - if strings.HasPrefix(legacySave.Content, "data:") { - legacySave.Content = strings.Split(legacySave.Content, ";")[1] - } - if imageBytes, err := base64.StdEncoding.DecodeString(legacySave.Content); err == nil { - if legacySave.Properties.Height > 0 { - imageBytes, err = resizeImage(imageBytes, legacySave.Properties.Height) - } - mimeType := readMimeType(legacySave.Path, legacySave.Properties.MimeType) - if err == nil { - // request is sent to uplader service after which it is being uploaded - resourceManager.Upload(resource.UploadRequest{ - Buffer: bytes.NewBuffer(imageBytes), - Path: legacySave.Path, - MimeType: mimeType, - }) - // we return this as success - c.Status(http.StatusNoContent) - } else { - c.AbortWithStatusJSON(400, gin.H{"error": "bad request"}) - } - } else { - c.AbortWithStatusJSON(400, gin.H{"error": "bad request"}) - } - } else { - c.AbortWithStatusJSON(400, gin.H{"error": "bad request"}) - } - } -} - -func HandleCopy(resourceManager resource.Manager) gin.HandlerFunc { - return func(c *gin.Context) { - from := c.Query("from") - to := c.Query("to") - overwrite := c.Query("overwrite") == "true" - if err := resourceManager.Copy(from, to, overwrite); err != nil { - log.Println(err) - c.AbortWithStatus(500) - } else { - c.Status(201) - } - - } -} - -func HandlePresign(resourceManager resource.Manager) gin.HandlerFunc { - return func(c *gin.Context) { - path := c.Query("path") - url, err := resourceManager.Presign(c, path) - if err != nil { - c.AbortWithStatus(404) - return - } - if c.Query("redirect") == "true" { - c.Redirect(http.StatusTemporaryRedirect, url) - } else { - c.JSON(200, url) - } - } -} - -func HandleGet(resourceManager resource.Manager) gin.HandlerFunc { - return func(c *gin.Context) { - path := c.Query("path") - data, err := resourceManager.Download(c, path) - if err == nil { - c.Data(200, readMimeType(path, ""), data) - } else { - c.AbortWithStatus(http.StatusNotFound) - } - } -} - -func HandleDelete(resourceManager resource.Manager) gin.HandlerFunc { - return func(c *gin.Context) { - path := c.Query("path") - if err := resourceManager.Delete(path); err != nil { - c.AbortWithError(400, err) - } else { - c.Status(204) - } - } -} diff --git a/image.go b/image.go deleted file mode 100644 index 09436cf..0000000 --- a/image.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "bytes" - "github.com/anthonynsimon/bild/transform" - "image" - "image/jpeg" - "image/png" - "io" - "strings" -) - -var mimeTypes = map[string]string{ - "jpg": "image/jpg", - "jpeg": "image/jpeg", - "png": "image/png", - "json": "application/json", - "html": "text/html", - "xml": "application/xml", -} - -func readMimeType(path string, mimeType string) string { - if mimeType != "" { - return mimeType - } - parts := strings.Split(path, ".") - fileType := parts[len(parts)-1] - if value, exists := mimeTypes[fileType]; exists { - return value - } - return "application/octet-stream" -} - -func resizeImage(imageBytes []byte, resizeHeight int) ([]byte, error) { - img, format, err := image.Decode(bytes.NewReader(imageBytes)) - if err != nil { - return nil, err - } - var buffer bytes.Buffer - writer := io.Writer(&buffer) - resizeWidth := img.Bounds().Dx() * resizeHeight / img.Bounds().Dy() - img = transform.Resize(img, resizeWidth, resizeHeight, transform.Gaussian) - switch format { - case "png": - err = png.Encode(writer, img) - break - case "jpeg": - err = jpeg.Encode(writer, img, nil) - break - default: - err = jpeg.Encode(writer, img, nil) - } - if err != nil { - return nil, err - } - return buffer.Bytes(), nil -} diff --git a/image/image.go b/image/image.go new file mode 100644 index 0000000..7e9662d --- /dev/null +++ b/image/image.go @@ -0,0 +1,52 @@ +package image + +import ( + "bytes" + "github.com/anthonynsimon/bild/transform" + "image" + "image/jpeg" + "io" +) + +func ResizeImage(imageBytes []byte, minWidth, minHeight int) ([]byte, error) { + img, _, err := image.Decode(bytes.NewReader(imageBytes)) + if err != nil { + return nil, err + } + var buffer bytes.Buffer + writer := io.Writer(&buffer) + + rX, rY := Resize(minWidth, minHeight, img.Bounds().Dx(), img.Bounds().Dy()) + + img = transform.Resize(img, rX, rY, transform.Gaussian) + + if err = jpeg.Encode(writer, img, nil); err != nil { + return nil, err + } + return buffer.Bytes(), nil +} + +func Resize(minX, minY, x, y int) (int, int) { + return resize(float32(minX), float32(minY), float32(x), float32(y)) +} + +func resize(minX, minY, x, y float32) (int, int) { + if x <= minX && y <= minY { + return int(x), int(y) + } + + // try by width + rX := minX + rY := y * (rX / x) + if rX >= minX && rY >= minY && rX <= x && rY <= y { + return int(rX), int(rY) + } + // try by height + rY = minY + rX = x * (rY / y) + if rX >= minX && rY >= minY && rX <= x && rY <= y { + return int(rX), int(rY) + } + + return int(x), int(y) +} diff --git a/image/image_test.go b/image/image_test.go new file mode 100644 index 0000000..12bff8b --- /dev/null +++ b/image/image_test.go @@ -0,0 +1,31 @@ +package image_test + +import ( + "git.bbr-dev.info/brajkovic/resource_manager/image" + "testing" +) + +func TestResize(t *testing.T) { + + cases := [][]int{ + // minX, minY, x, y, newX, newY + {400, 300, 400, 800, 400, 800}, + {400, 300, 800, 400, 600, 300}, + {1024, 1280, 800, 400, 800, 400}, + {400, 300, 800, 100, 800, 100}, + {1200, 900, 1600, 1400, 1200, 1050}, + } + + for _, testCase := range cases { + minX, minY, x, y, newX, newY := destructure(testCase) + + rX, rY := image.Resize(minX, minY, x, y) + if rX != newX || rY != newY { + t.Errorf("for (%d, %d, %d, %d)expected (%d, %d) received (%d, %d)", minX, minY, x, y, newX, newY, rX, rY) + } + } +} + +func destructure(testCase []int) (minX, minY, x, y, newX, newY int) { + return testCase[0], testCase[1], testCase[2], testCase[3], testCase[4], testCase[5] +} diff --git a/main.go b/main.go index 3452e37..f526710 100644 --- a/main.go +++ b/main.go @@ -1,59 +1,60 @@ package main import ( - "git.bbr-dev.info/brajkovic/resource_manager/cache" - "git.bbr-dev.info/brajkovic/resource_manager/resource" - "github.com/gin-gonic/gin" - "github.com/joho/godotenv" + "flag" + "git.bbr-dev.info/brajkovic/resource_manager/image" + "io/fs" + "io/ioutil" "log" - "net/http" - "os" + "path/filepath" "strings" - "time" ) -func init() { - log.SetPrefix("[ReM] ") - godotenv.Load() -} - func main() { - cacheManager := cache.NewManager() - expiration := loadExpiration() - log.Println("Presign | expiration set to " + expiration.String()) + folder := flag.Bool("folder", false, "copying folders instead of files") + in := flag.String("in", "", "input file/folder") + out := flag.String("out", "", "output file/folder") + x := flag.Int("x", 400, "min width") + y := flag.Int("y", 300, "min height") - resourceManager := resource.NewManager(cacheManager, expiration) + flag.Parse() - server := gin.Default() - if strings.Contains(os.Getenv("PROFILE"), "legacy") { - setupLegacyEndpoints(server, resourceManager) + if *folder { + resizeFolder(*in, *out, *x, *y) + } else { + resize(*in, *out, *x, *y) } - setupV1Endpoints(server, resourceManager) - log.Fatalln(http.ListenAndServe(":5201", server)) + } -func loadExpiration() time.Duration { - if value := os.Getenv("PRESIGN_DURATION"); value != "" { - duration, err := time.ParseDuration(value) - if err != nil { - return duration +func resizeFolder(src, dest string, x, y int) { + filepath.Walk(src, func(path string, info fs.FileInfo, err error) error { + if !info.IsDir() { + destination := strings.ReplaceAll(path, src, dest) + log.Printf("moving %s to %s", path, destination) + resize(path, destination, x, y) } + return err + }) +} + +func resize(src, dest string, x, y int) { + if src == "" || dest == "" { + log.Fatalln("Missing src or dest") } - // default duration - return 1 * time.Hour -} -func setupLegacyEndpoints(server *gin.Engine, resourceManager resource.Manager) { - server.POST("/save", HandleLegacySave(resourceManager)) - server.GET("/get", HandleGet(resourceManager)) - server.GET("/presign", HandlePresign(resourceManager)) - server.PUT("/copy", HandleCopy(resourceManager)) -} + source, err := ioutil.ReadFile(src) + if err != nil { + log.Panic(err) + } -func setupV1Endpoints(server *gin.Engine, resourceManager resource.Manager) { - server.POST("/api/v1/save", HandleLegacySave(resourceManager)) - server.GET("/api/v1/get", HandleGet(resourceManager)) - server.GET("/api/v1/presign", HandlePresign(resourceManager)) - server.PUT("/api/v1/copy", HandleCopy(resourceManager)) - server.DELETE("/api/v1/delete", HandleDelete(resourceManager)) + destination, err := image.ResizeImage(source, x, y) + if err != nil { + log.Panic(err) + } + + err = ioutil.WriteFile(dest, destination, 0644) + if err != nil { + log.Panic(err) + } } diff --git a/resource/impl.go b/resource/impl.go deleted file mode 100644 index 2d43fdf..0000000 --- a/resource/impl.go +++ /dev/null @@ -1,288 +0,0 @@ -package resource - -import ( - "bytes" - "context" - "errors" - "git.bbr-dev.info/brajkovic/resource_manager/cache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - "io/ioutil" - "log" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" -) - -func newLocalFolder(cacheManager cache.Manager, expiration time.Duration) Manager { - path := os.Getenv("UPLOAD_PATH") - // if path isn't set use local - if path == "" { - path, _ = os.Getwd() - } - log.Println("Manager | using local file system for data storage (" + path + ")") - manager := fileManager{cacheManager, path} - return &manager -} - -type fileManager struct { - cache cache.Manager - path string -} - -func (f *fileManager) Upload(request UploadRequest) { - fullPath := filepath.Join(f.path, request.Path) - createFolder(fullPath) - log.Println("Manager | uploading to (" + request.Path + ")") - if err := ioutil.WriteFile(fullPath, request.Buffer.Bytes(), 0o644); err != nil { - log.Println("Manager | failed uploading (" + request.Path + ") cause: " + err.Error()) - } -} -func (f *fileManager) Download(ctx context.Context, path string) (file []byte, err error) { - fullPath := filepath.Join(f.path, path) - file, err = ioutil.ReadFile(fullPath) - if err != nil { - log.Println("Manager | failed downloading (" + path + ") cause: " + err.Error()) - } - return -} -func (f *fileManager) Presign(_ context.Context, path string) (string, error) { - return "/api/v1/get?path=" + path, nil -} -func (f *fileManager) Copy(from string, to string, overwrite bool) error { - fromPath := filepath.Join(f.path, from) - toPath := filepath.Join(f.path, to) - log.Println("Manager | copying from (" + fromPath + ") to (" + toPath + ")") - createFolder(filepath.Join(toPath, "file")) - return copyFolder(fromPath, toPath, overwrite) -} -func (f *fileManager) Delete(path string) error { - log.Println("Manager | deleting " + path) - fullPath := filepath.Join(f.path, path) - if err := os.Remove(fullPath); err != nil { - log.Println("Manager | failed deleting (" + path + ") cause: " + err.Error()) - return err - } - f.cache.Remove(path) - return nil -} -func createFolder(path string) { - paths := strings.Split(path, "/") - folderPath := "/" + filepath.Join(paths[:len(paths)-1]...) - if err := os.MkdirAll(folderPath, 0o755); err != nil { - log.Println("[error] ", err) - } -} -func copyFolder(source, destination string, overwrite bool) error { - var err = filepath.WalkDir(source, func(path string, d os.DirEntry, err error) error { - relPath := strings.Replace(path, source, "", 1) - if relPath == "" { - return nil - } - if d.IsDir() { - return os.Mkdir(filepath.Join(destination, relPath), 0755) - } else { - doesExist := false - if _, err := os.Stat(filepath.Join(destination, relPath)); !os.IsNotExist(err) { - doesExist = true - } - if !doesExist || overwrite { - var data, err1 = ioutil.ReadFile(filepath.Join(source, relPath)) - if err1 != nil { - return err1 - } - return ioutil.WriteFile(filepath.Join(destination, relPath), data, 0777) - } else { - return nil - } - } - }) - return err -} - -func newS3Bucket(cacheManager cache.Manager, expiration time.Duration) Manager { - sess, err := session.NewSession(&aws.Config{ - Region: aws.String(os.Getenv("AWS_REGION_NAME"))}, - ) - if err != nil { - panic(err) - } - uploader := s3manager.NewUploader(sess) - downloader := s3manager.NewDownloader(sess) - upload := make(chan UploadRequest, 20) - manager := awsManager{ - cacheManager, - upload, - make(map[string][]onUpload), - sync.Mutex{}, - expiration, - uploader, - downloader, - } - go manager.uploadRunner() - return &manager -} - -type onUpload chan bool - -type awsManager struct { - cache cache.Manager - requests chan UploadRequest - currentlyUploading map[string][]onUpload - uploadSync sync.Mutex - expiration time.Duration - uploader *s3manager.Uploader - downloader *s3manager.Downloader -} - -func (a *awsManager) uploadRunner() { - for req := range a.requests { - status := true - if !a.doesFileExist(req.Path) { - status = a.upload(req.Path, req.Buffer.Bytes(), req.MimeType) - } - a.notifyOnUploadComplete(req.Path, status) - } -} -func (a *awsManager) Upload(request UploadRequest) { - a.addToCurrentlyUploading(request.Path) - a.requests <- request -} -func (a *awsManager) Presign(ctx context.Context, path string) (string, error) { - if !a.waitForUploadComplete(path) { - return "", errors.New("failed upload") - } - // ask cache for latest url - if url, err := a.cache.Get(path); err == nil { - return url, nil - } - // if there is no value in cache, presign url and add it to cache - url, err := a.presign(path, a.expiration) - if err != nil { - a.cache.Set(path, url, a.expiration) - } - return url, err -} -func (a *awsManager) Download(ctx context.Context, path string) (file []byte, err error) { - return a.download(path) -} -func (a *awsManager) Copy(from string, to string, overwrite bool) error { - fromPath := strings.SplitN(from, "/", 2) - toPath := strings.SplitN(to, "/", 2) - output, err := a.downloader.S3.ListObjectsV2(&s3.ListObjectsV2Input{ - Bucket: aws.String(fromPath[0]), - Prefix: aws.String(fromPath[1]), - }) - if err != nil { - return err - } - for _, obj := range output.Contents { - if overwrite || !a.doesFileExist(strings.Replace(*obj.Key, fromPath[1], toPath[1], 1)) { - _, err = a.downloader.S3.CopyObject(&s3.CopyObjectInput{ - Bucket: aws.String(toPath[0]), - CopySource: aws.String(url.PathEscape(from)), - Key: aws.String(toPath[1]), - }) - if err != nil { - return err - } - } - } - return nil -} -func (a *awsManager) Delete(path string) error { - paths := strings.SplitN(path, "/", 2) - _, err := a.uploader.S3.DeleteObject(&s3.DeleteObjectInput{ - Bucket: aws.String(paths[0]), - Key: aws.String(paths[1]), - }) - if err != nil { - return err - } - a.cache.Remove(path) - return nil -} - -func (a *awsManager) addToCurrentlyUploading(path string) { - a.uploadSync.Lock() - log.Println("Manager | (" + path + ") added to upload list") - a.currentlyUploading[path] = make([]onUpload, 0) - a.uploadSync.Unlock() -} -func (a *awsManager) waitForUploadComplete(path string) bool { - a.uploadSync.Lock() - defer a.uploadSync.Unlock() - if _, contains := a.currentlyUploading[path]; contains { - upload := make(onUpload) - a.currentlyUploading[path] = append(a.currentlyUploading[path], upload) - return <-upload - } - return true -} -func (a *awsManager) notifyOnUploadComplete(path string, status bool) { - a.uploadSync.Lock() - defer a.uploadSync.Unlock() - - log.Println("Manager | (" + path + ") finished uploading, success: " + strconv.FormatBool(status)) - - for _, upload := range a.currentlyUploading[path] { - upload <- status - close(upload) - } - delete(a.currentlyUploading, path) -} - -func (a *awsManager) upload(path string, data []byte, mimeType string) bool { - log.Println("Manager | (" + path + ") uploading") - paths := strings.SplitN(path, "/", 2) - _, err := a.uploader.Upload(&s3manager.UploadInput{ - Bucket: aws.String(paths[0]), - ContentType: aws.String(mimeType), - Key: aws.String(paths[1]), - Body: bytes.NewReader(data), - }) - return err == nil -} -func (a *awsManager) presign(path string, expiration time.Duration) (string, error) { - paths := strings.SplitN(path, "/", 2) - requestInput := s3.GetObjectInput{ - Bucket: aws.String(paths[0]), - Key: aws.String(paths[1]), - } - request, _ := a.downloader.S3.GetObjectRequest(&requestInput) - if request.Error != nil { - log.Println("Manager | (" + path + ") failed presiging, cause: " + request.Error.Error()) - } - return request.Presign(expiration) -} -func (a *awsManager) download(path string) ([]byte, error) { - paths := strings.SplitN(path, "/", 2) - requestInput := s3.GetObjectInput{ - Bucket: aws.String(paths[0]), - Key: aws.String(paths[1]), - } - buf := aws.NewWriteAtBuffer([]byte{}) - _, err := a.downloader.Download(buf, &requestInput) - if err != nil { - log.Println("Manager | (" + path + ") failed downloading, cause: " + err.Error()) - } - return buf.Bytes(), err -} -func (a *awsManager) doesFileExist(path string) bool { - paths := strings.SplitN(path, "/", 2) - requestInput := s3.HeadObjectInput{ - Bucket: aws.String(paths[0]), - Key: aws.String(paths[1]), - } - _, err := a.downloader.S3.HeadObject(&requestInput) - if err != nil && err.Error() == s3.ErrCodeNoSuchKey { - return true - } - return false -} diff --git a/resource/manager.go b/resource/manager.go deleted file mode 100644 index e2f8b33..0000000 --- a/resource/manager.go +++ /dev/null @@ -1,35 +0,0 @@ -package resource - -import ( - "bytes" - "context" - "git.bbr-dev.info/brajkovic/resource_manager/cache" - "log" - "os" - "strings" - "time" -) - -type UploadRequest struct { - Buffer *bytes.Buffer - Path string - MimeType string - Overwrite bool -} - -func NewManager(cacheManager cache.Manager, expiration time.Duration) Manager { - if strings.Contains(os.Getenv("PROFILE"), "s3") { - log.Println("Manager | using S3 for data storage") - return newS3Bucket(cacheManager, expiration) - } else { - return newLocalFolder(cacheManager, expiration) - } -} - -type Manager interface { - Upload(request UploadRequest) - Download(ctx context.Context, path string) (file []byte, err error) - Presign(ctx context.Context, path string) (string, error) - Copy(from string, to string, overwrite bool) error - Delete(path string) error -}