crud implemented
This commit is contained in:
parent
78cb709bd7
commit
8bc961e1be
8 changed files with 149 additions and 13 deletions
10
cmd/main.go
10
cmd/main.go
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/davidbyttow/govips/v2/vips"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"imageStorage/config"
|
"imageStorage/config"
|
||||||
"imageStorage/internal/app"
|
"imageStorage/internal/app"
|
||||||
|
|
@ -13,11 +14,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := config.NewConfig()
|
//c := config.NewConfig()
|
||||||
//c := config.DevConfig()
|
c := config.DevConfig()
|
||||||
config.LogSetup(c.App.LogLevel)
|
config.LogSetup(c.App.LogLevel)
|
||||||
log.Infof("Log level: %s", c.App.LogLevel)
|
log.Infof("Log level: %s", c.App.LogLevel)
|
||||||
|
|
||||||
|
vips.Startup(&vips.Config{
|
||||||
|
MaxCacheMem: 100 << 20,
|
||||||
|
})
|
||||||
|
defer vips.Shutdown()
|
||||||
|
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ HOST=
|
||||||
HTTP_PORT=
|
HTTP_PORT=
|
||||||
GRPC_PORT=
|
GRPC_PORT=
|
||||||
DOMAIN=
|
DOMAIN=
|
||||||
|
VOLUME=
|
||||||
LOG_LEVEL=Info
|
LOG_LEVEL=Info
|
||||||
|
|
@ -9,6 +9,7 @@ type AppConfig struct {
|
||||||
HttpPort string
|
HttpPort string
|
||||||
GrpcPort string
|
GrpcPort string
|
||||||
Domain string
|
Domain string
|
||||||
|
Volume string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ func NewConfig() *Config {
|
||||||
HttpPort: getEnv("HTTP_PORT", ""),
|
HttpPort: getEnv("HTTP_PORT", ""),
|
||||||
GrpcPort: getEnv("GRPC_PORT", ""),
|
GrpcPort: getEnv("GRPC_PORT", ""),
|
||||||
Domain: getEnv("DOMAIN", ""),
|
Domain: getEnv("DOMAIN", ""),
|
||||||
|
Volume: getEnv("VOLUME", ""),
|
||||||
LogLevel: getEnv("LOG_LEVEL", ""),
|
LogLevel: getEnv("LOG_LEVEL", ""),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ func DevConfig() *Config {
|
||||||
HttpPort: getEnv("HTTP_PORT", "9280"),
|
HttpPort: getEnv("HTTP_PORT", "9280"),
|
||||||
GrpcPort: getEnv("GRPC_PORT", "9200"),
|
GrpcPort: getEnv("GRPC_PORT", "9200"),
|
||||||
Domain: getEnv("DOMAIN", "http://localhost:9280"),
|
Domain: getEnv("DOMAIN", "http://localhost:9280"),
|
||||||
|
Volume: getEnv("VOLUME", ""),
|
||||||
LogLevel: getEnv("LOG_LEVEL", "Debug"),
|
LogLevel: getEnv("LOG_LEVEL", "Debug"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,48 @@
|
||||||
package convert
|
package convert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/davidbyttow/govips/v2/vips"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
type service struct{}
|
type service struct{}
|
||||||
|
|
||||||
func newService() *service {
|
func newService() *service {
|
||||||
return &service{}
|
return &service{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ConvertToJpeg() error {
|
func (s *service) ConvertAndSave(imageData []byte, fullWriter, thumbWriter io.Writer) error {
|
||||||
return nil
|
img, err := vips.NewImageFromBuffer(imageData)
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer img.Close()
|
||||||
|
|
||||||
|
//force convert even it is already a jpeg image
|
||||||
|
converted, _, err := img.ExportJpeg(&vips.JpegExportParams{
|
||||||
|
StripMetadata: true,
|
||||||
|
Quality: 90,
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err = fullWriter.Write(converted); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = img.Thumbnail(300, 300, vips.InterestingNone); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnail, _, err := img.ExportJpeg(&vips.JpegExportParams{
|
||||||
|
StripMetadata: true,
|
||||||
|
Quality: 80,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = thumbWriter.Write(thumbnail); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) MakeThumbnail() error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package interfaces
|
package interfaces
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
type Converter interface {
|
type Converter interface {
|
||||||
MakeThumbnail() error
|
ConvertAndSave(imageData []byte, fullWriter, thumbWriter io.Writer) error
|
||||||
ConvertToJpeg() error
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,22 @@ import (
|
||||||
type ImageHandler struct {
|
type ImageHandler struct {
|
||||||
pb.UnimplementedImageStorageServer
|
pb.UnimplementedImageStorageServer
|
||||||
converter interfaces.Converter
|
converter interfaces.Converter
|
||||||
|
domain string
|
||||||
|
volume string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
Converter interfaces.Converter
|
Converter interfaces.Converter
|
||||||
|
Domain string
|
||||||
|
Volume string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(deps Deps) *grpc.Server {
|
func NewHandler(deps Deps) *grpc.Server {
|
||||||
srv := grpc.NewServer()
|
srv := grpc.NewServer()
|
||||||
imgSrv := ImageHandler{
|
imgSrv := ImageHandler{
|
||||||
converter: deps.Converter,
|
converter: deps.Converter,
|
||||||
|
domain: deps.Domain,
|
||||||
|
volume: deps.Volume,
|
||||||
}
|
}
|
||||||
|
|
||||||
pb.RegisterImageStorageServer(srv, &imgSrv)
|
pb.RegisterImageStorageServer(srv, &imgSrv)
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,103 @@ package mainHandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
pb "imageStorage/pkg/proto/imageStorage"
|
pb "imageStorage/pkg/proto/imageStorage"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *ImageHandler) UploadImage(ctx context.Context, req *pb.UploadImageRequest) (*pb.UploadImageResponse, error) {
|
func (i *ImageHandler) UploadImage(ctx context.Context, req *pb.UploadMerchImageRequest) (*pb.UploadMerchImageResponse, error) {
|
||||||
return nil, nil
|
if len(req.ImageData) == 0 {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "image data is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := i._makeMerchImagePath(req.UserUuid, req.MerchUuid)
|
||||||
|
if err := os.MkdirAll(path, 0777); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
full := filepath.Join(path, "full.jpg")
|
||||||
|
thumbnail := filepath.Join(path, "thumbnail.jpg")
|
||||||
|
|
||||||
|
fullFile, err := os.Create(full)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer fullFile.Close()
|
||||||
|
|
||||||
|
thumbnailFile, err := os.Create(thumbnail)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer thumbnailFile.Close()
|
||||||
|
|
||||||
|
if err = i.converter.ConvertAndSave(req.ImageData, fullFile, thumbnailFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &pb.UploadMerchImageResponse{
|
||||||
|
FullImage: i._makeMerchImageURL(req.UserUuid, req.MerchUuid, full),
|
||||||
|
Thumbnail: i._makeMerchImageURL(req.UserUuid, req.MerchUuid, thumbnail),
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ImageHandler) GetImage(ctx context.Context, req *pb.GetImageRequest) (*pb.GetImageResponse, error) {
|
func (i *ImageHandler) GetImage(ctx context.Context, req *pb.GetImageRequest) (*pb.GetImageResponse, error) {
|
||||||
return nil, nil
|
path := i._makeMerchImagePath(req.UserUuid, req.MerchUuid)
|
||||||
|
unk := status.Error(codes.InvalidArgument, "unknown image type")
|
||||||
|
|
||||||
|
switch req.ImgType {
|
||||||
|
case pb.ImageType_UNKNOWN:
|
||||||
|
return nil, unk
|
||||||
|
case pb.ImageType_FULL:
|
||||||
|
return &pb.GetImageResponse{
|
||||||
|
Url: fmt.Sprintf("%s/%s", i.domain, filepath.Join(path, "full.jpg")),
|
||||||
|
Etag: "",
|
||||||
|
}, nil
|
||||||
|
case pb.ImageType_THUMBNAIL:
|
||||||
|
return &pb.GetImageResponse{
|
||||||
|
Url: fmt.Sprintf("%s/%s", i.domain, filepath.Join(path, "thumbnail.jpg")),
|
||||||
|
Etag: "",
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, unk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ImageHandler) DeleteImage(ctx context.Context, req *pb.DeleteImageRequest) (*pb.GetImageResponse, error) {
|
func (i *ImageHandler) DeleteImage(ctx context.Context, req *pb.DeleteImageRequest) (*emptypb.Empty, error) {
|
||||||
return nil, nil
|
path := i._makeMerchImagePath(req.UserUuid, req.MerchUuid)
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
filePath := filepath.Join(path, entry.Name())
|
||||||
|
if err := os.Remove(filePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImageHandler) _makeMerchImagePath(u, m string) string {
|
||||||
|
return fmt.Sprintf("%s/merchImages/%s/%s", i.volume, u, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImageHandler) _makeMerchImageURL(u, m, n string) string {
|
||||||
|
d := strings.TrimSuffix(i.domain, "/")
|
||||||
|
return fmt.Sprintf("%s/merchImages/%s/%s/%s", d, u, m, n)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue