package service import ( "context" "fmt" "io" "log" "math/rand" "mime" "narasi-ahli-be/app/database/entity" "narasi-ahli-be/app/module/advertisement/mapper" "narasi-ahli-be/app/module/advertisement/repository" "narasi-ahli-be/app/module/advertisement/request" "narasi-ahli-be/app/module/advertisement/response" usersRepository "narasi-ahli-be/app/module/users/repository" config "narasi-ahli-be/config/config" minioStorage "narasi-ahli-be/config/config" "narasi-ahli-be/utils/paginator" "path/filepath" "strconv" "strings" "time" "github.com/gofiber/fiber/v2" "github.com/minio/minio-go/v7" "github.com/rs/zerolog" ) // AdvertisementService type advertisementService struct { Repo repository.AdvertisementRepository UsersRepo usersRepository.UsersRepository Log zerolog.Logger Cfg *config.Config MinioStorage *minioStorage.MinioStorage } // AdvertisementService define interface of IAdvertisementService type AdvertisementService interface { All(req request.AdvertisementQueryRequest) (advertisement []*response.AdvertisementResponse, paging paginator.Pagination, err error) Show(id uint) (advertisement *response.AdvertisementResponse, err error) Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) Upload(c *fiber.Ctx, id uint) (err error) Update(id uint, req request.AdvertisementUpdateRequest) (err error) UpdatePublish(id uint, isPublish bool) (err error) Delete(id uint) error Viewer(c *fiber.Ctx) (err error) } // NewAdvertisementService init AdvertisementService func NewAdvertisementService(repo repository.AdvertisementRepository, minioStorage *minioStorage.MinioStorage, usersRepo usersRepository.UsersRepository, log zerolog.Logger, cfg *config.Config) AdvertisementService { return &advertisementService{ Repo: repo, UsersRepo: usersRepo, MinioStorage: minioStorage, Log: log, Cfg: cfg, } } // All implement interface of AdvertisementService func (_i *advertisementService) All(req request.AdvertisementQueryRequest) (advertisements []*response.AdvertisementResponse, paging paginator.Pagination, err error) { results, paging, err := _i.Repo.GetAll(req) if err != nil { return } host := _i.Cfg.App.Domain for _, result := range results { advertisements = append(advertisements, mapper.AdvertisementResponseMapper(result, host)) } return } func (_i *advertisementService) Show(id uint) (advertisement *response.AdvertisementResponse, err error) { result, err := _i.Repo.FindOne(id) if err != nil { return nil, err } host := _i.Cfg.App.Domain return mapper.AdvertisementResponseMapper(result, host), nil } func (_i *advertisementService) Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) { _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() return _i.Repo.Create(newReq) } func (_i *advertisementService) Upload(c *fiber.Ctx, id uint) (err error) { bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName form, err := c.MultipartForm() if err != nil { return err } //filess := form.File["files"] // Create minio connection. minioClient, err := _i.MinioStorage.ConnectMinio() result, err := _i.Repo.FindOne(id) if result == nil { // Return status 400. Id not found. return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": true, "msg": err.Error(), }) } if err != nil { // Return status 500 and minio connection error. return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": true, "msg": err.Error(), }) } for _, files := range form.File { _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). Interface("files", files).Msg("") for _, fileHeader := range files { _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop"). Interface("data", fileHeader).Msg("") src, err := fileHeader.Open() if err != nil { return err } defer src.Close() filename := filepath.Base(fileHeader.Filename) filename = strings.ReplaceAll(filename, " ", "") filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) extension := filepath.Ext(fileHeader.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("advertisement/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) result.ContentFileName = &newFilename result.ContentFilePath = &objectName err = _i.Repo.Update(id, result) if err != nil { return err } // Upload file ke MinIO _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{}) if err != nil { return err } } } return } func (_i *advertisementService) Update(id uint, req request.AdvertisementUpdateRequest) (err error) { _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() return _i.Repo.Update(id, newReq) } func (_i *advertisementService) UpdatePublish(id uint, isPublish bool) (err error) { _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Interface("ids", id).Msg("") _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Interface("isPublish", isPublish).Msg("") result, err := _i.Repo.FindOne(id) if err != nil { return err } result.IsPublish = isPublish _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Interface("result", result).Msg("") return _i.Repo.Update(id, result) } func (_i *advertisementService) Delete(id uint) error { result, err := _i.Repo.FindOne(id) if err != nil { return err } result.IsActive = false return _i.Repo.Update(id, result) } func (_i *advertisementService) Viewer(c *fiber.Ctx) (err error) { filename := c.Params("filename") result, err := _i.Repo.FindByFilename(filename) if err != nil { return err } ctx := context.Background() bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName objectName := *result.ContentFilePath // 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() // Tentukan Content-Type berdasarkan ekstensi file contentType := mime.TypeByExtension("." + getFileExtension(objectName)) if contentType == "" { contentType = "application/octet-stream" // fallback jika tidak ada tipe MIME yang cocok } 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] }