package service import ( "context" "github.com/gofiber/fiber/v2" "github.com/minio/minio-go/v7" "github.com/rs/zerolog" "go-humas-be/app/database/entity" articleFilesRepository "go-humas-be/app/module/article_files/repository" "go-humas-be/app/module/articles/mapper" "go-humas-be/app/module/articles/repository" "go-humas-be/app/module/articles/request" "go-humas-be/app/module/articles/response" usersRepository "go-humas-be/app/module/users/repository" minioStorage "go-humas-be/config/config" "go-humas-be/utils/paginator" utilSvc "go-humas-be/utils/service" "io" "log" "math/rand" "mime" "path/filepath" "strconv" "strings" "time" ) // ArticlesService type articlesService struct { Repo repository.ArticlesRepository ArticleFilesRepo articleFilesRepository.ArticleFilesRepository Log zerolog.Logger UsersRepo usersRepository.UsersRepository MinioStorage *minioStorage.MinioStorage } // ArticlesService define interface of IArticlesService type ArticlesService interface { All(req request.ArticlesQueryRequest) (articles []*response.ArticlesResponse, paging paginator.Pagination, err error) Show(id uint) (articles *response.ArticlesResponse, err error) Save(req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) SaveThumbnail(c *fiber.Ctx) (err error) Update(id uint, req request.ArticlesUpdateRequest) (err error) Delete(id uint) error Viewer(c *fiber.Ctx) error } // NewArticlesService init ArticlesService func NewArticlesService( repo repository.ArticlesRepository, articleFilesRepo articleFilesRepository.ArticleFilesRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, ) ArticlesService { return &articlesService{ Repo: repo, ArticleFilesRepo: articleFilesRepo, Log: log, UsersRepo: usersRepo, MinioStorage: minioStorage, } } // All implement interface of ArticlesService func (_i *articlesService) All(req request.ArticlesQueryRequest) (articless []*response.ArticlesResponse, paging paginator.Pagination, err error) { results, paging, err := _i.Repo.GetAll(req) if err != nil { return } _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:articlesService", "Methods:All"). Interface("results", results).Msg("") for _, result := range results { articleRes := mapper.ArticlesResponseMapper(_i.Log, result, _i.ArticleFilesRepo, _i.UsersRepo) articless = append(articless, articleRes) } return } func (_i *articlesService) Show(id uint) (articles *response.ArticlesResponse, err error) { result, err := _i.Repo.FindOne(id) if err != nil { return nil, err } return mapper.ArticlesResponseMapper(_i.Log, result, _i.ArticleFilesRepo, _i.UsersRepo), nil } func (_i *articlesService) Save(req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) { _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) newReq.CreatedById = &createdBy.ID return _i.Repo.Create(newReq) } func (_i *articlesService) SaveThumbnail(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:articlesService", "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) filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) extension := filepath.Ext(file.Filename)[1:] rand.New(rand.NewSource(time.Now().UnixNano())) randUniqueId := rand.Intn(1000000) newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) newFilename := newFilenameWithoutExt + "." + extension objectName := "articles/thumbnail/" + newFilename findCategory, err := _i.Repo.FindOne(uint(id)) findCategory.ThumbnailName = &newFilename findCategory.ThumbnailPath = &objectName err = _i.Repo.Update(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 *articlesService) Update(id uint, req request.ArticlesUpdateRequest) (err error) { _i.Log.Info().Interface("data", req).Msg("") return _i.Repo.Update(id, req.ToEntity()) } func (_i *articlesService) Delete(id uint) error { result, err := _i.Repo.FindOne(id) if err != nil { return err } isActive := false result.IsActive = &isActive return _i.Repo.Update(id, result) } func (_i *articlesService) Viewer(c *fiber.Ctx) (err error) { thumbnailName := c.Params("thumbnailName") result, err := _i.Repo.FindByFilename(thumbnailName) if err != nil { return err } if result.ThumbnailPath == nil { return nil } ctx := context.Background() bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName objectName := result.ThumbnailPath // 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] }