package service import ( "context" "fmt" "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/minio/minio-go/v7" "github.com/rs/zerolog" "io" "log" "math/rand" "mime" "path/filepath" "strconv" "strings" "time" "web-qudo-be/app/database/entity" magazineFilesRepository "web-qudo-be/app/module/magazine_files/repository" "web-qudo-be/app/module/magazines/mapper" "web-qudo-be/app/module/magazines/repository" "web-qudo-be/app/module/magazines/request" "web-qudo-be/app/module/magazines/response" usersRepository "web-qudo-be/app/module/users/repository" config "web-qudo-be/config/config" minioStorage "web-qudo-be/config/config" "web-qudo-be/utils/paginator" utilSvc "web-qudo-be/utils/service" ) // MagazinesService type magazinesService struct { Repo repository.MagazinesRepository UsersRepo usersRepository.UsersRepository MagazineFilesRepo magazineFilesRepository.MagazineFilesRepository MinioStorage *minioStorage.MinioStorage Log zerolog.Logger Cfg *config.Config } // MagazinesService define interface of IMagazinesService type MagazinesService interface { All(clientId *uuid.UUID, req request.MagazinesQueryRequest) (magazines []*response.MagazinesResponse, paging paginator.Pagination, err error) Show(clientId *uuid.UUID, id uint) (magazines *response.MagazinesResponse, err error) Save(clientId *uuid.UUID, req request.MagazinesCreateRequest, authToken string) (magazines *entity.Magazines, err error) Update(clientId *uuid.UUID, id uint, req request.MagazinesUpdateRequest) (err error) SaveThumbnail(clientId *uuid.UUID, c *fiber.Ctx) (err error) Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) Delete(clientId *uuid.UUID, id uint) error } // NewMagazinesService init MagazinesService func NewMagazinesService(repo repository.MagazinesRepository, magazineFilesRepo magazineFilesRepository.MagazineFilesRepository, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, log zerolog.Logger, cfg *config.Config) MagazinesService { return &magazinesService{ Repo: repo, MagazineFilesRepo: magazineFilesRepo, UsersRepo: usersRepo, MinioStorage: minioStorage, Log: log, Cfg: cfg, } } // All implement interface of MagazinesService func (_i *magazinesService) All(clientId *uuid.UUID, req request.MagazinesQueryRequest) (magaziness []*response.MagazinesResponse, paging paginator.Pagination, err error) { results, paging, err := _i.Repo.GetAll(clientId, req) if err != nil { return } host := _i.Cfg.App.Domain for _, result := range results { magaziness = append(magaziness, mapper.MagazinesResponseMapper(result, _i.MagazineFilesRepo, host)) } return } func (_i *magazinesService) Show(clientId *uuid.UUID, id uint) (magazines *response.MagazinesResponse, err error) { result, err := _i.Repo.FindOne(clientId, id) if err != nil { return nil, err } host := _i.Cfg.App.Domain return mapper.MagazinesResponseMapper(result, _i.MagazineFilesRepo, host), nil } func (_i *magazinesService) Save(clientId *uuid.UUID, req request.MagazinesCreateRequest, authToken string) (magazines *entity.Magazines, err error) { _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) newReq.CreatedById = &createdBy.ID // Set ClientId on entity newReq.ClientId = clientId saveMagazineResponse, err := _i.Repo.Create(newReq) if err != nil { return nil, err } return saveMagazineResponse, nil } func (_i *magazinesService) SaveThumbnail(clientId *uuid.UUID, c *fiber.Ctx) (err error) { id, err := strconv.ParseUint(c.Params("id"), 10, 0) if err != nil { return err } _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:magazinesService", "Methods:SaveThumbnail"). Interface("id", id).Msg("") bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName form, err := c.MultipartForm() if err != nil { return err } files := form.File["files"] // Create minio connection. minioClient, err := _i.MinioStorage.ConnectMinio() if err != nil { // Return status 500 and minio connection error. return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": true, "msg": err.Error(), }) } // Iterasi semua file yang diunggah for _, file := range files { _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop1"). Interface("data", file).Msg("") src, err := file.Open() if err != nil { return err } defer src.Close() filename := filepath.Base(file.Filename) filename = strings.ReplaceAll(filename, " ", "") filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) extension := filepath.Ext(file.Filename)[1:] now := time.Now() rand.New(rand.NewSource(now.UnixNano())) randUniqueId := rand.Intn(1000000) newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) newFilename := newFilenameWithoutExt + "." + extension objectName := fmt.Sprintf("magazines/thumbnail/%d/%d/%s", now.Year(), now.Month(), newFilename) findCategory, err := _i.Repo.FindOne(clientId, uint(id)) findCategory.ThumbnailName = &newFilename findCategory.ThumbnailPath = &objectName err = _i.Repo.Update(clientId, uint(id), findCategory) if err != nil { return err } // Upload file ke MinIO _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, file.Size, minio.PutObjectOptions{}) if err != nil { return err } } return } func (_i *magazinesService) Update(clientId *uuid.UUID, id uint, req request.MagazinesUpdateRequest) (err error) { _i.Log.Info().Interface("data", req).Msg("") // Set ClientId on entity entity := req.ToEntity() entity.ClientId = clientId return _i.Repo.Update(clientId, id, entity) } func (_i *magazinesService) Delete(clientId *uuid.UUID, id uint) error { return _i.Repo.Delete(clientId, id) } func (_i *magazinesService) Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) { thumbnailName := c.Params("thumbnailName") emptyImage := "empty-image.jpg" searchThumbnail := emptyImage if thumbnailName != emptyImage { result, err := _i.Repo.FindByFilename(clientId, thumbnailName) if err != nil { return err } _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "magazinesService:Viewer"). Interface("resultThumbnail", result.ThumbnailPath).Msg("") if result.ThumbnailPath != nil { searchThumbnail = *result.ThumbnailPath } else { searchThumbnail = "magazines/thumbnail/" + emptyImage } } else { searchThumbnail = "magazines/thumbnail/" + emptyImage } ctx := context.Background() bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName objectName := searchThumbnail // Create minio connection. minioClient, err := _i.MinioStorage.ConnectMinio() if err != nil { // Return status 500 and minio connection error. return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": true, "msg": err.Error(), }) } fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) if err != nil { log.Fatalln(err) } defer fileContent.Close() contentType := mime.TypeByExtension("." + getFileExtension(objectName)) if contentType == "" { contentType = "application/octet-stream" } c.Set("Content-Type", contentType) if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { return err } return } func getFileExtension(filename string) string { // split file name parts := strings.Split(filename, ".") // jika tidak ada ekstensi, kembalikan string kosong if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { return "" } // ambil ekstensi terakhir return parts[len(parts)-1] }