generated from NezuChan/template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(structures): Separate modules and implementation (#33)
* feat(deps): install resty and govips * rename image-proxy -> github.com/nezuchan/image-proxy * feat(domain): add image resizer domain * feat(impl): implementation for image_resizer * feat(main): initialize image_resizer * feat(Docker): install vips deps * fix(Docker): install deps on build stage * fix(Docker): install libc * fix(Docker): install g++ and make * fix(Docker): install vips-dev * ci(test): pass TARGET_FILE * chore: remove this binary file * ci(build): install libvips-dev * fix(dotenv): don't load specific file and supress error
- Loading branch information
1 parent
e8ee142
commit dbe9e0b
Showing
10 changed files
with
317 additions
and
117 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/davidbyttow/govips/v2/vips" | ||
"github.com/gofiber/fiber/v2" | ||
"github.com/gofiber/fiber/v2/middleware/logger" | ||
"github.com/joho/godotenv" | ||
"github.com/nezuchan/image-proxy/impl/image_resizer/client" | ||
i_http "github.com/nezuchan/image-proxy/impl/image_resizer/handler/http" | ||
i_uc "github.com/nezuchan/image-proxy/impl/image_resizer/usecase" | ||
"log" | ||
"os" | ||
"strconv" | ||
) | ||
|
||
func main() { | ||
// Initialize vips | ||
vips.LoggingSettings(func(domain string, level vips.LogLevel, msg string) { | ||
fmt.Println(domain, level, msg) | ||
}, vips.LogLevelInfo) | ||
|
||
vips.Startup(nil) | ||
defer vips.Shutdown() | ||
|
||
_ = godotenv.Load() | ||
app := fiber.New() | ||
|
||
app.Use(logger.New(logger.Config{ | ||
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", | ||
})) | ||
|
||
host := os.Getenv("HOST") | ||
port := os.Getenv("PORT") | ||
|
||
imageResizerClient := client.NewImageResizerClient(os.Getenv("IV"), os.Getenv("KEY")) | ||
maxWidth, err := strconv.Atoi(os.Getenv("MAX_WIDTH")) | ||
maxHeight, err := strconv.Atoi(os.Getenv("MAX_HEIGHT")) | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
iUsecase := i_uc.NewImageResizerUsecase(imageResizerClient, maxWidth, maxHeight) | ||
i_http.ConfigureImageResizerHandler(app, iUsecase) | ||
log.Fatal(app.Listen(fmt.Sprintf("%s:%s", host, port))) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package domain | ||
|
||
type ImageResizerUsecase interface { | ||
GetMaxResolution() (int, int) | ||
ResolveImage(imageHex string, width, height int) ([]byte, error) | ||
} | ||
|
||
type ImageResizerHTTPResponse struct { | ||
StatusCode int `json:"statusCode"` | ||
Message string `json:"message"` | ||
} |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package client | ||
|
||
import ( | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"github.com/davidbyttow/govips/v2/vips" | ||
resty "github.com/go-resty/resty/v2" | ||
) | ||
|
||
type ImageResizerClient interface { | ||
DecryptImage(imageHex string) (string, error) | ||
GetOriginImage(url string) ([]byte, error) | ||
ResizeImage(image []byte, width, height int) ([]byte, error) | ||
} | ||
|
||
type imageResizerClient struct { | ||
httpClient resty.Client | ||
crypter cipher.BlockMode | ||
iv []byte | ||
} | ||
|
||
func (i imageResizerClient) ResizeImage(image []byte, width, height int) (result []byte, err error) { | ||
img, err := vips.NewImageFromBuffer(image) | ||
defer img.Close() | ||
if err != nil { | ||
err = errors.New(fmt.Sprintf("unable to read image: %v", err.Error())) | ||
return | ||
} | ||
|
||
// Get original size of image | ||
originalWidth := float64(img.Width()) | ||
originalHeight := float64(img.Height()) | ||
|
||
if err = img.ResizeWithVScale(float64(width)/originalWidth, float64(height)/originalHeight, vips.KernelNearest); err != nil { | ||
return | ||
} | ||
|
||
options := vips.NewJpegExportParams() | ||
options.Quality = 100 | ||
result, _, err = img.ExportJpeg(options) | ||
if err != nil { | ||
err = errors.New(fmt.Sprintf("unable to export image: %v", err)) | ||
return | ||
} | ||
|
||
return | ||
} | ||
|
||
func (i imageResizerClient) GetOriginImage(url string) (result []byte, err error) { | ||
response, err := i.httpClient.R().Get(url) | ||
if err != nil { | ||
return | ||
} | ||
|
||
result = response.Body() | ||
return | ||
} | ||
|
||
func (i imageResizerClient) DecryptImage(imageHex string) (result string, err error) { | ||
image, err := hex.DecodeString(imageHex) | ||
if err != nil { | ||
err = errors.New("couldn't decode image hex") | ||
return | ||
} | ||
|
||
decryptedImage := make([]byte, len(image)) | ||
i.crypter.CryptBlocks(decryptedImage, image) | ||
i.crypter.(interface{ SetIV([]byte) }).SetIV(i.iv) // Reset the IV | ||
|
||
result = string(decryptedImage[:len(decryptedImage)-int(decryptedImage[len(decryptedImage)-1])]) | ||
return | ||
} | ||
|
||
func NewImageResizerClient(rawIV string, rawKey string) ImageResizerClient { | ||
key := []byte(rawKey) | ||
iv := []byte(rawIV) | ||
|
||
block, err := aes.NewCipher(key) | ||
if err != nil { | ||
panic(fmt.Sprintf("couldn't initialize new cipher: %v", err)) | ||
} | ||
return &imageResizerClient{ | ||
httpClient: *resty.New(), | ||
crypter: cipher.NewCBCDecrypter(block, iv), | ||
iv: iv, | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package http | ||
|
||
import ( | ||
"github.com/gofiber/fiber/v2" | ||
"github.com/nezuchan/image-proxy/domain" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
type httpMemberHandler struct { | ||
iUsecase domain.ImageResizerUsecase | ||
} | ||
|
||
func ConfigureImageResizerHandler(app *fiber.App, iUsecase domain.ImageResizerUsecase) { | ||
h := httpMemberHandler{ | ||
iUsecase: iUsecase, | ||
} | ||
|
||
app.Add(http.MethodGet, "/:size/:image", h.GetImage) | ||
} | ||
|
||
func (h *httpMemberHandler) GetImage(c *fiber.Ctx) (err error) { | ||
size := strings.Split(c.Params("size", "512x512"), "x") | ||
|
||
width, err := strconv.Atoi(size[0]) | ||
if err != nil { | ||
return c.Status(400).JSON(domain.ImageResizerHTTPResponse{ | ||
StatusCode: 400, | ||
Message: "invalid width size", | ||
}) | ||
} | ||
|
||
height, err := strconv.Atoi(size[1]) | ||
if err != nil { | ||
return c.Status(400).JSON(domain.ImageResizerHTTPResponse{ | ||
StatusCode: 400, | ||
Message: "invalid height size", | ||
}) | ||
} | ||
|
||
maxWidth, maxHeight := h.iUsecase.GetMaxResolution() | ||
if width > maxWidth || height > maxHeight { | ||
return c.Status(400).JSON(domain.ImageResizerHTTPResponse{ | ||
StatusCode: 400, | ||
Message: "width or height size too large", | ||
}) | ||
} | ||
|
||
image, err := h.iUsecase.ResolveImage(c.Params("image"), width, height) | ||
if err != nil { | ||
return c.Status(500).JSON(domain.ImageResizerHTTPResponse{ | ||
StatusCode: 500, | ||
Message: err.Error(), | ||
}) | ||
} | ||
|
||
c.Set("Content-Type", "image/jpeg") | ||
return c.Send(image) | ||
} |
Oops, something went wrong.