diff --git a/go.mod b/go.mod index 20de185..566e396 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.24.1 require ( github.com/99designs/gqlgen v0.17.72 + github.com/aws/aws-sdk-go v1.55.7 github.com/caarlos0/env/v8 v8.0.0 github.com/go-chi/chi/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.2 @@ -23,6 +24,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect diff --git a/go.sum b/go.sum index 2b1ecff..5366519 100644 --- a/go.sum +++ b/go.sum @@ -10,12 +10,15 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -34,6 +37,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +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.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -58,6 +65,7 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8= @@ -72,7 +80,11 @@ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9x google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/service/post_service.go b/internal/service/post_service.go index e0cfc5b..4be8b91 100644 --- a/internal/service/post_service.go +++ b/internal/service/post_service.go @@ -1,17 +1,24 @@ package service import ( + "bytes" "context" "encoding/base64" "errors" "fmt" + "github.com/aws/aws-sdk-go/aws/credentials" + "image" + "image/jpeg" "log" - "os" "strings" "tailly_back_v2/internal/domain" "tailly_back_v2/internal/repository" "tailly_back_v2/internal/utils" "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" ) // Интерфейс сервиса постов @@ -56,27 +63,59 @@ func (s *postService) Create(ctx context.Context, authorID int, title, content s log.Println("failed to decode image base64", op, err) return nil, err } - randName := utils.GenerateId() - upPath := fmt.Sprintf("./uploads/posts/images/%d", authorID) - - err = os.MkdirAll(upPath, os.ModePerm) + // Проверяем, что это изображение + img, format, err := image.Decode(bytes.NewReader(imgBase64)) if err != nil { - log.Println("failed to create directory", op, err) return nil, err } - fileName := fmt.Sprintf("%s/%s.jpg", upPath, randName) + // Проверяем формат (например, только JPEG/PNG) + if format != "jpeg" && format != "png" { + return nil, errors.New("only JPEG/PNG allowed") + } - err = os.WriteFile(fileName, imgBase64, os.ModePerm) - if err != nil { - log.Println("failed to write file", op, err) + // Проверяем размер (например, не больше 10MB) + if len(imgBase64) > 10_000_000 { + return nil, errors.New("image too large (max 10MB)") + } + + randName := utils.GenerateId() + ".jpg" + + buf := new(bytes.Buffer) + if err := jpeg.Encode(buf, img, nil); err != nil { return nil, err } + sess := session.Must(session.NewSession(&aws.Config{ + Region: aws.String("ru-central1"), + Endpoint: aws.String("https://s3.regru.cloud"), + S3ForcePathStyle: aws.Bool(true), + Credentials: credentials.NewStaticCredentials( + "TJ946G2S1Z5FEI3I7DQQ", + "C2H2aITHRDpek8H921yhnrINZwDoADsjW3F6HURl", + "", + ), + })) + uploader := s3manager.NewUploader(sess) + + s3Key := fmt.Sprintf("posts/%d/%s", authorID, randName) + + _, err = uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String("tailly"), + Key: aws.String(s3Key), + Body: bytes.NewReader(buf.Bytes()), + ContentType: aws.String("image/jpeg"), + }) + if err != nil { + return nil, fmt.Errorf("failed to upload to S3: %v", err) + } + + imageURL := fmt.Sprintf("https://s3.regru.cloud/%s/%s", "tailly", s3Key) + post := &domain.Post{ Title: title, - Content: fileName, + Content: imageURL, AuthorID: authorID, CreatedAt: time.Now(), UpdatedAt: time.Now(), diff --git a/uploads/posts/images/1/8eec541e6474071aa848b3ecdd992d6d.jpg b/uploads/posts/images/1/8eec541e6474071aa848b3ecdd992d6d.jpg deleted file mode 100755 index 4f7d212..0000000 Binary files a/uploads/posts/images/1/8eec541e6474071aa848b3ecdd992d6d.jpg and /dev/null differ diff --git a/uploads/posts/images/1/d59b870db2a935ca6970181e46fbdbef.jpg b/uploads/posts/images/1/d59b870db2a935ca6970181e46fbdbef.jpg deleted file mode 100755 index 8b1b106..0000000 Binary files a/uploads/posts/images/1/d59b870db2a935ca6970181e46fbdbef.jpg and /dev/null differ