Skip to content

Commit

Permalink
feat: upload image
Browse files Browse the repository at this point in the history
  • Loading branch information
shani-asad committed May 19, 2024
1 parent 810b1ee commit 935898e
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 1 deletion.
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,32 @@ module health-record
go 1.19

require (
github.com/aws/aws-sdk-go-v2 v1.27.0
github.com/aws/aws-sdk-go-v2/config v1.27.15
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2
github.com/docker/distribution v2.8.3+incompatible
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/joho/godotenv v1.5.1
github.com/relvacode/iso8601 v1.4.0
)

require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
Expand Down
38 changes: 38 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo=
github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg=
github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc=
github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M=
github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo=
github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 h1:/FUtT3xsoHO3cfh+I/kCbcMCN98QZRsiFet/V8QkWSs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7/go.mod h1:MaCAgWpGooQoCWZnMur97rGn5dp350w2+CeiV5406wE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 h1:UXqEWQI0n+q0QixzU0yUUQBZXRd5037qdInTIHFTl98=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9/go.mod h1:xP6Gq6fzGZT8w/ZN+XvGMZ2RU1LeEs7b2yUP5DN8NY4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 h1:uO5XR6QGBcmPyo2gxofYJLFkcVQ4izOoGDNenlZhTEk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7/go.mod h1:feeeAYfAcwTReM6vbwjEyDmiGho+YgBhaFULuXDW8kc=
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2 h1:gYSJhNiOF6J9xaYxu2NFNstoiNELwt0T9w29FxSfN+Y=
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2/go.mod h1:739CllldowZiPPsDFcJHNF4FXrVxaSGVnZ9Ez9Iz9hc=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
Expand All @@ -9,6 +45,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
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/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down
6 changes: 5 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func main() {
authHandler := handler.NewAuthHandler(authUsecase)
nurseHandler := handler.NewNurseHandler(nurseUscase)
patientHandler := handler.NewPatientHandler(patientUsecase)
imageHandler := handler.NewImageHandler()

// ROUTE
r.GET("/health", func(c *gin.Context) {
Expand All @@ -105,9 +106,12 @@ func main() {
itAuthorized.DELETE("/v1/user/nurse/:userId", nurseHandler.DeleteNurse)
itAuthorized.PUT("/v1/user/nurse/:userId/access", nurseHandler.AddAccess)


// Manage medical records
authorized.POST("/v1/medical/patient", patientHandler.CreatePatient)
authorized.GET("/v1/medical/patient", patientHandler.GetPatients)

// upload image
authorized.POST("/v1/image", imageHandler.UploadImage)

r.Run()
}
4 changes: 4 additions & 0 deletions src/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ type NurseHandlerInterface interface {
type PatientHandlerInterface interface {
CreatePatient(c *gin.Context)
GetPatients(c *gin.Context)
}

type ImageHandlerInterface interface {
UploadImage(c *gin.Context)
}
118 changes: 118 additions & 0 deletions src/handler/image_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package handler

import (
"bytes"
"context"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"os"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/docker/distribution/uuid"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
)

type ImageHandler struct {}

func NewImageHandler() ImageHandlerInterface {
return &ImageHandler{}
}

const (
maxUploadSize = 2 * 1024 * 1024 // 2MB
minUploadSize = 10 * 1024 // 10KB

)

var s3Client *s3.Client

func init() {
err := godotenv.Load()
if err != nil {
log.Println("Error loading .env file:", err)
return
}

// Load the AWS configuration
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(os.Getenv("AWS_REGION")))
if err != nil {
log.Fatalf("unable to load SDK config, %v", err)
}

// Create an S3 client
s3Client = s3.NewFromConfig(cfg)
}

func (h *ImageHandler) UploadImage(c *gin.Context) {
err := c.Request.ParseMultipartForm(maxUploadSize)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Unable to parse form"})
return
}

file, handler, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "File is required"})
return
}
defer file.Close()

// Validate file size
if handler.Size > maxUploadSize {
c.JSON(http.StatusBadRequest, gin.H{"error": "File size exceeds 2MB"})
return
}
if handler.Size < minUploadSize {
c.JSON(http.StatusBadRequest, gin.H{"error": "File size is less than 10KB"})
return
}

// Validate file type
if !strings.HasSuffix(handler.Filename, ".jpg") && !strings.HasSuffix(handler.Filename, ".jpeg") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file type, only .jpg and .jpeg are allowed"})
return
}

// Generate a unique file name
fileName := fmt.Sprintf("%s%s", uuid.Generate().String(), ".jpeg")

// Upload the file to S3
err = uploadFileToS3(file, fileName)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to upload file"})
return
}

// Respond with the URL of the uploaded file
imageURL := fmt.Sprintf("https://%s.s3.amazonaws.com/%s", os.Getenv("AWS_S3_BUCKET_NAME"), fileName)
c.JSON(http.StatusCreated, gin.H{
"message": "File uploaded successfully",
"data": gin.H{
"imageUrl": imageURL,
},
})
}

func uploadFileToS3(file multipart.File, fileName string) error {
// Read the file content into a buffer
buf := bytes.NewBuffer(nil)
if _, err := io.Copy(buf, file); err != nil {
return err
}

// Upload the file to S3
_, err := s3Client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(os.Getenv("AWS_S3_BUCKET_NAME")),
Key: aws.String(fileName),
Body: bytes.NewReader(buf.Bytes()),
ACL: "public-read",
})
return err
}

0 comments on commit 935898e

Please sign in to comment.