crud implemented

This commit is contained in:
nquidox 2025-10-22 21:34:17 +03:00
parent 78cb709bd7
commit 8bc961e1be
8 changed files with 149 additions and 13 deletions

View file

@ -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()

View file

@ -2,4 +2,5 @@ HOST=
HTTP_PORT= HTTP_PORT=
GRPC_PORT= GRPC_PORT=
DOMAIN= DOMAIN=
VOLUME=
LOG_LEVEL=Info LOG_LEVEL=Info

View file

@ -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", ""),
}, },
} }

View file

@ -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"),
}, },
} }

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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)

View file

@ -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)
} }