Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #91 from swibly/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
devkcud authored Oct 28, 2024
2 parents 385f136 + 9d25bd7 commit 82c341e
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 52 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.1
github.com/joho/godotenv v1.5.1
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
golang.org/x/crypto v0.28.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/postgres v1.5.9
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
5 changes: 3 additions & 2 deletions internal/service/repository/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ func (pr *projectRepository) baseProjectQuery(issuerID uint) *gorm.DB {
) AS total_favorites
`, issuerID).
Joins("JOIN project_owners po ON po.project_id = p.id").
Joins("JOIN users u ON po.user_id = u.id")
Joins("JOIN users u ON po.user_id = u.id").
Order("created_at DESC")
}

func (pr *projectRepository) paginateProjects(query *gorm.DB, page, perPage int) (*dto.Pagination[dto.ProjectInfo], error) {
Expand Down Expand Up @@ -644,7 +645,7 @@ func (pr *projectRepository) GetPublic(issuerID uint, page, perPage int) (*dto.P
}

func (pr *projectRepository) GetFavorited(issuerID, userID uint, onlyPublic bool, page, perPage int) (*dto.Pagination[dto.ProjectInfo], error) {
query := pr.baseProjectQuery(issuerID).Where("deleted_at IS NULL").Joins("JOIN project_user_favorites f ON f.project_id = p.id AND f.user_id = ?", userID)
query := pr.baseProjectQuery(issuerID).Where("deleted_at IS NULL").Joins("JOIN project_user_favorites f ON f.project_id = p.id AND f.user_id = ?", userID).Order("f.created_at ASC")

if onlyPublic {
query = query.Joins("JOIN project_publications pp ON pp.project_id = p.id")
Expand Down
118 changes: 68 additions & 50 deletions pkg/aws/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@ import (
"bytes"
"context"
"fmt"
"image"
"io"
"mime/multipart"
"path"
"path/filepath"
"slices"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/chai2010/webp"
"github.com/disintegration/imaging"
"github.com/rwcarlsen/goexif/exif"
"github.com/swibly/swibly-api/config"
)

Expand All @@ -29,14 +28,38 @@ var (
ErrFileTooLarge = fmt.Errorf("file too large")
)

func (svc *AWSService) UploadFile(key string, file io.Reader) (string, error) {
newKey := fmt.Sprintf("%s/%s", config.Router.Environment, key)
func (svc *AWSService) UploadFile(key string, file multipart.File) (string, error) {
newKey := fmt.Sprintf("%s/%s-%d.webp", config.Router.Environment, key, time.Now().Unix())

buf := new(bytes.Buffer)
_, err := io.Copy(buf, file)
if err != nil {
return "", fmt.Errorf("unable to read file: %v", err)
}

_, err := svc.s3.PutObject(context.TODO(), &s3.PutObjectInput{
imgData := bytes.NewReader(buf.Bytes())
img, _, err := image.Decode(imgData)
if err != nil {
return "", fmt.Errorf("unable to decode image: %v", err)
}

imgData.Seek(0, io.SeekStart)

img = adjustOrientation(imgData, img)

processedImgBuf := new(bytes.Buffer)
err = webp.Encode(processedImgBuf, img, &webp.Options{
Lossless: true,
})
if err != nil {
return "", fmt.Errorf("unable to encode image to WebP: %v", err)
}

_, err = svc.s3.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(config.S3.Bucket),
Key: aws.String(newKey),
Body: file,
ContentType: aws.String("application/octet-stream"),
Body: bytes.NewReader(processedImgBuf.Bytes()),
ContentType: aws.String("image/webp"),
ACL: types.ObjectCannedACLPublicRead,
})
if err != nil {
Expand Down Expand Up @@ -69,83 +92,78 @@ func UploadProjectImage(projectID uint, file *multipart.FileHeader) (string, err
return "", ErrFileTooLarge
}

ext := strings.ToLower(path.Ext(file.Filename))

if !slices.Contains([]string{".png", ".jpg", ".jpeg"}, ext) {
return "", ErrUnsupportedFileType
}

src, err := file.Open()
if err != nil {
return "", ErrUnableToOpenFile
}
defer src.Close()

img, err := imaging.Decode(src)
if err != nil {
return "", ErrUnableToDecode
}

outputPath := fmt.Sprintf("projects/%d-%d.webp", time.Now().Unix(), projectID)
var buf bytes.Buffer

err = webp.Encode(&buf, img, nil)
if err != nil {
return "", ErrUnableToEncode
}
outputPath := fmt.Sprintf("projects/%d", projectID)

url, err := AWS.UploadFile(outputPath, &buf)
url, err := AWS.UploadFile(outputPath, src)
if err != nil {
return "", ErrUnableToUploadFile
}

return url, nil
}

func DeleteProjectImage(filename string) error {
return AWS.DeleteFile(fmt.Sprintf("projects/%s", filepath.Base(filename)))
}

func UploadUserImage(userID uint, file *multipart.FileHeader) (string, error) {
const maxFileSize = 5 * 1024 * 1024

if file.Size > maxFileSize {
return "", ErrFileTooLarge
}

ext := strings.ToLower(path.Ext(file.Filename))

if !slices.Contains([]string{".png", ".jpg", ".jpeg"}, ext) {
return "", ErrUnsupportedFileType
}

src, err := file.Open()
if err != nil {
return "", ErrUnableToOpenFile
}
defer src.Close()

img, err := imaging.Decode(src)
if err != nil {
return "", ErrUnableToDecode
}

outputPath := fmt.Sprintf("users/%d-%d.webp", time.Now().Unix(), userID)
var buf bytes.Buffer
outputPath := fmt.Sprintf("users/%d", userID)

err = webp.Encode(&buf, img, nil)
if err != nil {
return "", ErrUnableToEncode
}

url, err := AWS.UploadFile(outputPath, &buf)
url, err := AWS.UploadFile(outputPath, src)
if err != nil {
return "", ErrUnableToUploadFile
}

return url, nil
}

func DeleteProjectImage(filename string) error {
return AWS.DeleteFile(fmt.Sprintf("projects/%s", filepath.Base(filename)))
}

func DeleteUserImage(filename string) error {
return AWS.DeleteFile(fmt.Sprintf("users/%s", filepath.Base(filename)))
}

func adjustOrientation(reader io.Reader, img image.Image) image.Image {
exifData, err := exif.Decode(reader)
if err == nil {
orientTag, err := exifData.Get(exif.Orientation)
if err == nil {
orient, err := orientTag.Int(0)
if err == nil {
switch orient {
case 2:
img = imaging.FlipH(img)
case 3:
img = imaging.Rotate180(img)
case 4:
img = imaging.FlipV(img)
case 5:
img = imaging.Transpose(img)
case 6:
img = imaging.Rotate90(img)
case 7:
img = imaging.Transverse(img)
case 8:
img = imaging.Rotate270(img)
}
}
}
}
return img
}

0 comments on commit 82c341e

Please sign in to comment.