Skip to content

Commit

Permalink
Merge pull request #25 from go-park-mail-ru/TP-87a_filtration
Browse files Browse the repository at this point in the history
Tp 87a filtration
  • Loading branch information
wonderf00l authored Dec 19, 2023
2 parents e3d1477 + ff13346 commit 1b8f327
Show file tree
Hide file tree
Showing 10 changed files with 716 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ cert/
.env
redis.conf
inventory
keyVision.json
script*
18 changes: 16 additions & 2 deletions configs/playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@
- name: "Provide .env file"
copy:
src: ../.env
dest: /home/ond_team/go/src/github.com/go-park-mail-ru/ci-cd/.env
dest: /home/ond_team/go/src/github.com/go-park-mail-ru/{{ item }}/.env
with_items:
- ci-cd
- 2023_2_OND_team
- name: "Provide redis config"
copy:
src: ../redis.conf
dest: /home/ond_team/go/src/github.com/go-park-mail-ru/ci-cd/redis.conf
dest: /home/ond_team/go/src/github.com/go-park-mail-ru/{{ item }}/redis.conf
with_items:
- ci-cd
- 2023_2_OND_team
- name: "Provide key for Google Cloud Vision API"
copy:
src: ../keyVision.json
dest: /home/ond_team/go/src/github.com/go-park-mail-ru/{{ item }}/keyVision.json
with_items:
- ci-cd
- 2023_2_OND_team

24 changes: 20 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ module github.com/go-park-mail-ru/2023_2_OND_team
go 1.19

require (
cloud.google.com/go/vision v1.2.0
cloud.google.com/go/vision/v2 v2.7.5
github.com/IBM/sarama v1.42.1
github.com/Masterminds/squirrel v1.5.4
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/go-chi/chi/v5 v5.0.10
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.3
github.com/google/uuid v1.3.1
github.com/google/uuid v1.4.0
github.com/jackc/pgx/v5 v5.4.3
github.com/joho/godotenv v1.5.1
github.com/mailru/easyjson v0.7.7
Expand All @@ -27,12 +29,16 @@ require (
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.14.0
golang.org/x/image v0.13.0
google.golang.org/grpc v1.59.0
google.golang.org/grpc v1.60.0
google.golang.org/protobuf v1.31.0
nhooyr.io/websocket v1.8.10
)

require (
cloud.google.com/go v0.111.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
Expand All @@ -53,7 +59,11 @@ require (
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-text/typesetting v0.0.0-20231013144250-6cc35dbfae7d // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
Expand Down Expand Up @@ -81,14 +91,20 @@ require (
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
github.com/tdewolff/minify/v2 v2.20.5 // indirect
github.com/tdewolff/parse/v2 v2.7.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/api v0.149.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
star-tex.org/x/tex v0.4.0 // indirect
Expand Down
569 changes: 552 additions & 17 deletions go.sum

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

vision "cloud.google.com/go/vision/v2/apiv1"
authProto "github.com/go-park-mail-ru/2023_2_OND_team/internal/api/auth"
"github.com/go-park-mail-ru/2023_2_OND_team/internal/api/messenger"
rt "github.com/go-park-mail-ru/2023_2_OND_team/internal/api/realtime"
Expand Down Expand Up @@ -41,7 +42,10 @@ import (
log "github.com/go-park-mail-ru/2023_2_OND_team/pkg/logger"
)

var _timeoutForConnPG = 5 * time.Second
var (
_timeoutForConnPG = 5 * time.Second
timeoutCloudVisionAPI = 10 * time.Second
)

const uploadFiles = "upload/"

Expand Down Expand Up @@ -83,7 +87,16 @@ func Run(ctx context.Context, log *log.Logger, cfg ConfigFiles) {

commentRepository := commentRepo.NewCommentRepoPG(pool)

imgCase := image.New(log, imgRepo.NewImageRepoFS(uploadFiles))
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "keyVision.json")
visionCtx, cancel := context.WithTimeout(ctx, timeoutCloudVisionAPI)
defer cancel()
visionClient, err := vision.NewImageAnnotatorClient(visionCtx)
if err != nil {
log.Error(err.Error())
return
}

imgCase := image.New(log, imgRepo.NewImageRepoFS(uploadFiles), visionClient)
messageCase := message.New(log, messenger.NewMessengerClient(connMessMS), chat.New(realtime.NewRealTimeChatClient(rtClient), log))
pinCase := pin.New(log, imgCase, pinRepo.NewPinRepoPG(pool))

Expand Down
7 changes: 6 additions & 1 deletion internal/pkg/delivery/http/v1/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
entity "github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/entity/pin"
"github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/entity/user"
"github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/middleware/auth"
img "github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/usecase/image"
usecase "github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/usecase/pin"
)

Expand Down Expand Up @@ -73,7 +74,11 @@ func (h *HandlerHTTP) CreateNewPin(w http.ResponseWriter, r *http.Request) {
err = h.pinCase.CreateNewPin(r.Context(), newPin, mime.Header.Get("Content-Type"), mime.Size, picture)
if err != nil {
logger.Error(err.Error())
err = responseError(w, "add_pin", "failed to create pin")
if err == img.ErrExplicitImage {
err = responseError(w, "explicit_pin", err.Error())
} else {
err = responseError(w, "add_pin", "failed to create pin")
}
} else {
err = responseOk(http.StatusCreated, w, "pin successfully created", nil)
}
Expand Down
80 changes: 80 additions & 0 deletions internal/pkg/usecase/image/filtration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package image

import (
"context"
"errors"
"strings"

pb "cloud.google.com/go/vision/v2/apiv1/visionpb"
)

var (
maxAnnotationsNumber int32 = 15
explicitLabels = []string{"goose", "duck"}
ErrExplicitImage = errors.New("Image content doesn't comply with service policy")
)

func CheckAnnotations(annotation *pb.SafeSearchAnnotation) bool {
if annotation.GetAdult() >= pb.Likelihood_LIKELY ||
annotation.GetMedical() >= pb.Likelihood_LIKELY ||
annotation.GetRacy() >= pb.Likelihood_LIKELY ||
annotation.GetViolence() >= pb.Likelihood_LIKELY ||
annotation.GetSpoof() >= pb.Likelihood_LIKELY {
return true
}
return false
}

func GetImageLabels(annotations []*pb.EntityAnnotation) []string {
imgLabels := make([]string, 0, len(annotations))
for _, label := range annotations {
imgLabels = append(imgLabels, label.GetDescription())
}
return imgLabels
}

func CheckCertainLabels(explicitLabels, imgLabels []string) bool {
for _, label := range explicitLabels {
if HasExplicitLabel(label, imgLabels) {
return true
}
}
return false
}

func HasExplicitLabel(explicitLabel string, imgLabels []string) bool {
for _, label := range imgLabels {
if strings.Contains(strings.ToLower(label), strings.ToLower(explicitLabel)) {
return true
}
}
return false
}

func CheckExplicit(resp *pb.AnnotateImageResponse, explicitLabels []string) error {
if CheckCertainLabels(explicitLabels, GetImageLabels(resp.GetLabelAnnotations())) ||
CheckAnnotations(resp.GetSafeSearchAnnotation()) {
return ErrExplicitImage
}
return nil
}

func (img *imageCase) FilterImage(ctx context.Context, imgBytes []byte, explicitLabels []string) error {
req := &pb.BatchAnnotateImagesRequest{
Requests: []*pb.AnnotateImageRequest{
{
Image: &pb.Image{Content: imgBytes},
Features: []*pb.Feature{
{Type: pb.Feature_LABEL_DETECTION, MaxResults: maxAnnotationsNumber},
{Type: pb.Feature_SAFE_SEARCH_DETECTION, MaxResults: maxAnnotationsNumber},
},
},
},
}
resp, err := img.visionClient.BatchAnnotateImages(ctx, req)
if err != nil {
return err
}

return CheckExplicit(resp.GetResponses()[0], explicitLabels)
}
30 changes: 21 additions & 9 deletions internal/pkg/usecase/image/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package image

import (
"bytes"
"context"
"errors"
"fmt"
"io"

vision "cloud.google.com/go/vision/v2/apiv1"
repo "github.com/go-park-mail-ru/2023_2_OND_team/internal/pkg/repository/image"
log "github.com/go-park-mail-ru/2023_2_OND_team/pkg/logger"
valid "github.com/go-park-mail-ru/2023_2_OND_team/pkg/validator/image"
Expand All @@ -14,33 +16,43 @@ import (

const PrefixURLImage = "https://pinspire.online:8081/"

var ErrInvalidImage = errors.New("invalid images")
var ErrUploadFile = errors.New("file upload failed")
var (
ErrInvalidImage = errors.New("invalid images")
ErrUploadFile = errors.New("file upload failed")
)

//go:generate mockgen -destination=./mock/image_mock.go -package=mock -source=usecase.go Usecase
type Usecase interface {
UploadImage(path string, mimeType string, size int64, image io.Reader, check check.CheckSize) (string, error)
UploadImage(ctx context.Context, path string, mimeType string, size int64, image io.Reader, check check.CheckSize) (string, error)
}

type imageCase struct {
log *log.Logger
repo repo.Repository
log *log.Logger
repo repo.Repository
visionClient *vision.ImageAnnotatorClient
}

func New(log *log.Logger, repo repo.Repository) *imageCase {
return &imageCase{log, repo}
func New(log *log.Logger, repo repo.Repository, visionClient *vision.ImageAnnotatorClient) *imageCase {
return &imageCase{log, repo, visionClient}
}

func (img *imageCase) UploadImage(path string, mimeType string, size int64, image io.Reader, check check.CheckSize) (string, error) {
func (img *imageCase) UploadImage(ctx context.Context, path string, mimeType string, size int64, image io.Reader, check check.CheckSize) (string, error) {
buf := bytes.NewBuffer(nil)

extension, ok := valid.IsValidImage(io.TeeReader(image, buf), mimeType, check)
if !ok {
return "", ErrInvalidImage
}

io.Copy(buf, image)

err := img.FilterImage(ctx, buf.Bytes(), explicitLabels)
if err != nil {
if err == ErrExplicitImage {
return "", err
}
return "", fmt.Errorf("upload image: %w", err)
}

filename, written, err := img.repo.SaveImage(path, extension, buf)
if err != nil {
return "", fmt.Errorf("upload image: %w", err)
Expand Down
5 changes: 4 additions & 1 deletion internal/pkg/usecase/pin/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ func New(log *log.Logger, imgCase image.Usecase, repo repo.Repository) *pinCase
}

func (p *pinCase) CreateNewPin(ctx context.Context, pin *entity.Pin, mimeTypePicture string, sizePicture int64, picture io.Reader) error {
picturePin, err := p.UploadImage("pins/", mimeTypePicture, sizePicture, picture, check.BothSidesFallIntoRange(100, 6000))
picturePin, err := p.UploadImage(ctx, "pins/", mimeTypePicture, sizePicture, picture, check.BothSidesFallIntoRange(100, 6000))
if err != nil {
if err == image.ErrExplicitImage {
return err
}
return fmt.Errorf("uploading an avatar when creating pin: %w", err)
}
pin.Picture = picturePin
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/usecase/user/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
var ErrBadBody = errors.New("bad body avatar")

func (u *userCase) UpdateUserAvatar(ctx context.Context, userID int, mimeTypeAvatar string, sizeAvatar int64, avatar io.Reader) error {
avatarProfile, err := u.UploadImage("avatars/", mimeTypeAvatar, sizeAvatar, avatar, check.BothSidesFallIntoRange(200, 1800))
avatarProfile, err := u.UploadImage(ctx, "avatars/", mimeTypeAvatar, sizeAvatar, avatar, check.BothSidesFallIntoRange(200, 1800))
if err != nil {
return fmt.Errorf("uploading an avatar when updating avatar profile: %w", err)
}
Expand Down

0 comments on commit 1b8f327

Please sign in to comment.