Add Infra
This commit is contained in:
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
categoryrepository "legalgo-BE-go/internal/accessor/category"
|
||||
newsrepository "legalgo-BE-go/internal/accessor/news"
|
||||
"legalgo-BE-go/internal/accessor/oss"
|
||||
redisaccessor "legalgo-BE-go/internal/accessor/redis"
|
||||
staffrepository "legalgo-BE-go/internal/accessor/staff"
|
||||
subscriberepository "legalgo-BE-go/internal/accessor/subscribe"
|
||||
@@ -22,4 +23,5 @@ var Module = fx.Module("repository", fx.Provide(
|
||||
tagrepository.New,
|
||||
categoryrepository.New,
|
||||
newsrepository.New,
|
||||
oss.New,
|
||||
))
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"legalgo-BE-go/config"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
type OSSConfig interface {
|
||||
GetAccessKeyID() string
|
||||
GetAccessKeySecret() string
|
||||
GetEndpoint() string
|
||||
GetBucketName() string
|
||||
GetHostURL() string
|
||||
GetPublicURL() string
|
||||
}
|
||||
|
||||
const _awsRegion = "us-east-1"
|
||||
const _s3ACL = "public-read"
|
||||
|
||||
type OssRepositoryImpl struct {
|
||||
s3 *s3.S3
|
||||
cfg OSSConfig
|
||||
}
|
||||
|
||||
func New(cfg *config.Config) OSSRepository {
|
||||
ossCfg := cfg.OSSConfig
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
S3ForcePathStyle: aws.Bool(true),
|
||||
Endpoint: aws.String(ossCfg.GetEndpoint()),
|
||||
Region: aws.String(_awsRegion),
|
||||
Credentials: credentials.NewStaticCredentials(ossCfg.GetAccessKeyID(), ossCfg.GetAccessKeySecret(), ""),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Failed to create AWS session:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &OssRepositoryImpl{
|
||||
s3: s3.New(sess),
|
||||
cfg: ossCfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *OssRepositoryImpl) UploadFile(ctx context.Context, fileName string, fileContent []byte) (fileUrl string, err error) {
|
||||
reader := bytes.NewReader(fileContent)
|
||||
|
||||
_, err = r.s3.PutObject(&s3.PutObjectInput{
|
||||
Bucket: aws.String(r.cfg.GetBucketName()),
|
||||
Key: aws.String(fileName),
|
||||
Body: reader,
|
||||
ACL: aws.String(_s3ACL),
|
||||
})
|
||||
|
||||
return r.GetPublicURL(fileName), err
|
||||
}
|
||||
|
||||
func (r *OssRepositoryImpl) GetPublicURL(fileName string) string {
|
||||
if fileName == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s:%s%s", r.cfg.GetPublicURL(), r.cfg.GetBucketName(), fileName)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type OSSRepository interface {
|
||||
UploadFile(ctx context.Context, fileName string, fileContent []byte) (fileUrl string, err error)
|
||||
GetPublicURL(fileName string) string
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package osshttp
|
||||
|
||||
import (
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
var Module = fx.Module("upload",
|
||||
fx.Invoke(
|
||||
RegisterUploadFile,
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
package osshttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
oss2 "legalgo-BE-go/internal/domain/oss"
|
||||
"legalgo-BE-go/internal/services/oss"
|
||||
"legalgo-BE-go/internal/utilities/response"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
const _oneMB = 1 << 20
|
||||
const _maxUploadSize = 2 * _oneMB
|
||||
const _folderName = "/file"
|
||||
|
||||
func RegisterUploadFile(ossService oss.OSSService, router chi.Router) {
|
||||
router.Post("/file", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Limit the size of the request body to prevent very large uploads.
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(_maxUploadSize))
|
||||
if err := r.ParseMultipartForm(int64(_maxUploadSize)); err != nil {
|
||||
response.ResponseWithErrorCode(
|
||||
ctx,
|
||||
w,
|
||||
err,
|
||||
response.ErrBadRequest.Code,
|
||||
response.ErrBadRequest.HttpCode,
|
||||
"Failed to parse multipart form",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve the file from the form.
|
||||
file, header, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
response.ResponseWithErrorCode(
|
||||
ctx,
|
||||
w,
|
||||
err,
|
||||
response.ErrBadRequest.Code,
|
||||
response.ErrBadRequest.HttpCode,
|
||||
"Failed to retrieve file",
|
||||
)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Check if file size exceeds the maximum limit.
|
||||
if header.Size > int64(_maxUploadSize) {
|
||||
response.ResponseWithErrorCode(
|
||||
ctx,
|
||||
w,
|
||||
fmt.Errorf("file too big"),
|
||||
response.ErrBadRequest.Code,
|
||||
response.ErrBadRequest.HttpCode,
|
||||
fmt.Sprintf("The file is too big. The maximum size is %d MB", _maxUploadSize/_oneMB),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare the request for the OSS service.
|
||||
uploadReq := &oss2.UploadFileRequest{
|
||||
FileHeader: header,
|
||||
FolderName: _folderName,
|
||||
}
|
||||
|
||||
// Call the OSS service to handle the file upload.
|
||||
result, err := ossService.UploadFile(ctx, uploadReq)
|
||||
if err != nil {
|
||||
response.ResponseWithErrorCode(
|
||||
ctx,
|
||||
w,
|
||||
err,
|
||||
response.ErrBadRequest.Code,
|
||||
response.ErrBadRequest.HttpCode,
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Return a successful JSON response.
|
||||
response.RespondJsonSuccess(ctx, w, struct {
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}{
|
||||
Message: "File uploaded successfully.",
|
||||
Data: result,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
authhttp "legalgo-BE-go/internal/api/http/auth"
|
||||
categoryhttp "legalgo-BE-go/internal/api/http/category"
|
||||
newshttp "legalgo-BE-go/internal/api/http/news"
|
||||
osshttp "legalgo-BE-go/internal/api/http/oss"
|
||||
subscribeplanhttp "legalgo-BE-go/internal/api/http/subscribe_plan"
|
||||
taghttp "legalgo-BE-go/internal/api/http/tag"
|
||||
|
||||
@@ -25,6 +26,7 @@ var Module = fx.Module("router",
|
||||
taghttp.Module,
|
||||
categoryhttp.Module,
|
||||
newshttp.Module,
|
||||
osshttp.Module,
|
||||
)
|
||||
|
||||
func initRouter() chi.Router {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package oss
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
type UploadFileRequest struct {
|
||||
FileHeader *multipart.FileHeader
|
||||
FolderName string
|
||||
FileSize int64 `validate:"max=10000000"` // 10Mb = 10000000 byte
|
||||
Ext string `validate:"oneof=.png .jpeg .jpg .pdf .xlsx .csv"`
|
||||
}
|
||||
|
||||
type DownloadFileRequest struct {
|
||||
FileName string `query:"file_name" validate:"required"`
|
||||
FolderName string `query:"folder_name" validate:"required"`
|
||||
}
|
||||
|
||||
type UploadFileResponse struct {
|
||||
FilePath string `json:"file_path"`
|
||||
FileUrl string `json:"file_url"`
|
||||
}
|
||||
|
||||
type DownloadFileResponse struct {
|
||||
FileUrl string `json:"file_url"`
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
serviceauth "legalgo-BE-go/internal/services/auth"
|
||||
categorysvc "legalgo-BE-go/internal/services/category"
|
||||
newssvc "legalgo-BE-go/internal/services/news"
|
||||
"legalgo-BE-go/internal/services/oss"
|
||||
subscribesvc "legalgo-BE-go/internal/services/subscribe"
|
||||
subscribeplansvc "legalgo-BE-go/internal/services/subscribe_plan"
|
||||
tagsvc "legalgo-BE-go/internal/services/tag"
|
||||
@@ -19,5 +20,6 @@ var Module = fx.Module("services",
|
||||
tagsvc.New,
|
||||
categorysvc.New,
|
||||
newssvc.New,
|
||||
oss.NewOSSService,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
oss2 "legalgo-BE-go/internal/accessor/oss"
|
||||
"legalgo-BE-go/internal/domain/oss"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type OssService struct {
|
||||
ossRepo oss2.OSSRepository
|
||||
}
|
||||
|
||||
func NewOSSService(ossRepo oss2.OSSRepository) OSSService {
|
||||
return &OssService{
|
||||
ossRepo: ossRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OssService) UploadFile(ctx context.Context, req *oss.UploadFileRequest) (*oss.UploadFileResponse, error) {
|
||||
file := req.FileHeader
|
||||
req.FileSize = file.Size
|
||||
req.Ext = path.Ext(file.Filename)
|
||||
validate := validator.New()
|
||||
if err := validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Open the file and read its content
|
||||
srcFile, err := file.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
fileContent := make([]byte, file.Size)
|
||||
_, err = srcFile.Read(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("%v/%v%v", req.FolderName, s.GenerateFileName(), req.Ext)
|
||||
fileUrl, err := s.ossRepo.UploadFile(ctx, filePath, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oss.UploadFileResponse{
|
||||
FilePath: filePath,
|
||||
FileUrl: fileUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *OssService) GenerateFileName() string {
|
||||
return fmt.Sprintf("%v-%v", uuid.New(), strconv.Itoa(int(time.Now().Unix())))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"context"
|
||||
"legalgo-BE-go/internal/domain/oss"
|
||||
)
|
||||
|
||||
type OSSService interface {
|
||||
UploadFile(ctx context.Context, req *oss.UploadFileRequest) (*oss.UploadFileResponse, error)
|
||||
}
|
||||
Reference in New Issue
Block a user