narasiahli-be/app/module/knowledge_base/service/knowledge_base.service.go

350 lines
8.1 KiB
Go
Raw Normal View History

2026-01-14 01:29:35 +00:00
package service
import (
"context"
"fmt"
"mime"
"mime/multipart"
"narasi-ahli-be/app/module/knowledge_base/mapper"
"narasi-ahli-be/app/module/knowledge_base/repository"
"narasi-ahli-be/app/module/knowledge_base/request"
"narasi-ahli-be/app/module/knowledge_base/response"
config "narasi-ahli-be/config/config"
minioStorage "narasi-ahli-be/config/config"
"narasi-ahli-be/utils/paginator"
"path/filepath"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
"github.com/rs/zerolog"
)
type KnowledgeBaseService struct {
Repo repository.KnowledgeBaseRepositoryInterface
Log zerolog.Logger
Cfg *config.Config
MinioStorage *minioStorage.MinioStorage
}
type KnowledgeBaseServiceInterface interface {
All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error)
Show(id uint) (data *response.KnowledgeBaseResponse, err error)
Create(c *fiber.Ctx) (data *response.KnowledgeBaseResponse, err error)
Update(id uint, req request.KnowledgeBaseUpdateRequest) (data *response.KnowledgeBaseResponse, err error)
Delete(id uint) error
Viewer(c *fiber.Ctx) error
UpdateStatus(id uint, status int) (data *response.KnowledgeBaseResponse, err error)
2026-01-15 06:59:03 +00:00
UpdateDocumentId(id uint, documentId int) (data *response.KnowledgeBaseResponse, err error)
2026-01-14 01:29:35 +00:00
}
func NewKnowledgeBaseService(
repo repository.KnowledgeBaseRepositoryInterface,
log zerolog.Logger,
cfg *config.Config,
minioStorage *minioStorage.MinioStorage,
) KnowledgeBaseServiceInterface {
return &KnowledgeBaseService{
Repo: repo,
Log: log,
Cfg: cfg,
MinioStorage: minioStorage,
}
}
func (s *KnowledgeBaseService) uploadFileToMinio(
ctx context.Context,
minioClient *minio.Client,
bucketName string,
agentId string,
fileHeader *multipart.FileHeader,
) (*string, error) {
if fileHeader == nil {
return nil, nil
}
src, err := fileHeader.Open()
if err != nil {
return nil, err
}
defer src.Close()
filename := filepath.Base(fileHeader.Filename)
filename = strings.ReplaceAll(filename, " ", "")
ext := filepath.Ext(filename)
filenameWithoutExt := strings.TrimSuffix(filename, ext)
now := time.Now()
newFilename := fmt.Sprintf("%s_%d%s", filenameWithoutExt, now.UnixNano(), ext)
2026-02-09 08:44:31 +00:00
// ⭐ INI PATH ASLI MINIO
2026-01-14 01:29:35 +00:00
objectName := fmt.Sprintf(
2026-02-09 08:44:31 +00:00
"knowledge-base/%s/%s",
2026-01-14 01:29:35 +00:00
agentId,
newFilename,
)
_, err = minioClient.PutObject(
ctx,
bucketName,
objectName,
src,
fileHeader.Size,
2026-02-09 08:44:31 +00:00
minio.PutObjectOptions{
ContentType: fileHeader.Header.Get("Content-Type"),
},
2026-01-14 01:29:35 +00:00
)
if err != nil {
return nil, err
}
2026-02-09 08:44:31 +00:00
// ⭐ KEMBALIKAN OBJECT PATH
return &objectName, nil
2026-01-14 01:29:35 +00:00
}
2026-02-09 08:44:31 +00:00
2026-01-14 01:29:35 +00:00
func (s *KnowledgeBaseService) All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error) {
results, paging, err := s.Repo.GetAll(req)
if err != nil {
return nil, paging, err
}
host := s.Cfg.App.Domain
for _, item := range results {
data = append(data, mapper.KnowledgeBaseResponseMapper(item, host))
}
return data, paging, nil
}
func getFileExtension(filename string) string {
parts := strings.Split(filename, ".")
if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") {
return ""
}
return parts[len(parts)-1]
}
2026-02-09 08:44:31 +00:00
func (s *KnowledgeBaseService) Viewer(c *fiber.Ctx) error {
2026-01-14 01:29:35 +00:00
filename := c.Params("filename")
2026-02-09 08:44:31 +00:00
_, objectName, err := s.Repo.FindByFilename(filename)
2026-01-14 01:29:35 +00:00
if err != nil {
2026-02-09 08:44:31 +00:00
return c.Status(404).SendString("file not found")
2026-01-14 01:29:35 +00:00
}
ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
2026-02-09 08:44:31 +00:00
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return c.Status(500).SendString(err.Error())
}
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
opts := minio.GetObjectOptions{}
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
// support video streaming
if rangeHeader := c.Get("Range"); rangeHeader != "" {
opts.Set("Range", rangeHeader)
c.Status(fiber.StatusPartialContent)
c.Set("Accept-Ranges", "bytes")
}
object, err := minioClient.GetObject(ctx, bucketName, objectName, opts)
2026-01-14 01:29:35 +00:00
if err != nil {
2026-02-09 08:44:31 +00:00
return c.Status(404).SendString("file not found")
2026-01-14 01:29:35 +00:00
}
2026-02-09 08:44:31 +00:00
defer object.Close()
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
stat, err := object.Stat()
2026-01-14 01:29:35 +00:00
if err != nil {
2026-02-09 08:44:31 +00:00
return c.Status(404).SendString("file not found")
2026-01-14 01:29:35 +00:00
}
2026-02-09 08:44:31 +00:00
contentType := stat.ContentType
2026-01-14 01:29:35 +00:00
if contentType == "" {
2026-02-09 08:44:31 +00:00
contentType = mime.TypeByExtension(filepath.Ext(objectName))
2026-01-14 01:29:35 +00:00
}
2026-02-09 08:44:31 +00:00
c.Set("Content-Type", contentType)
c.Set("Content-Disposition", "inline")
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
return c.SendStream(object)
2026-01-14 01:29:35 +00:00
}
2026-02-09 08:44:31 +00:00
2026-01-14 01:29:35 +00:00
func (s *KnowledgeBaseService) Show(id uint) (data *response.KnowledgeBaseResponse, err error) {
result, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(result, host), nil
}
func (s *KnowledgeBaseService) Create(c *fiber.Ctx) (data *response.KnowledgeBaseResponse, err error) {
req := new(request.KnowledgeBaseCreateRequest)
if err := c.BodyParser(req); err != nil {
return nil, err
}
if req.AgentId == nil || *req.AgentId == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "agentId is required")
}
if req.AgentName == nil || *req.AgentName == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "agentName is required")
}
if req.Title == nil || *req.Title == "" {
return nil, fiber.NewError(fiber.StatusBadRequest, "title is required")
}
fileJournal, _ := c.FormFile("fileJournal")
fileAudio, _ := c.FormFile("fileAudio")
fileVideo, _ := c.FormFile("fileVideo")
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return nil, err
}
ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
2026-02-09 08:44:31 +00:00
journalPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileJournal)
audioPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileAudio)
videoPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileVideo)
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
entity := req.ToEntity()
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
entity.FileJournalUrl = journalPath
entity.FileAudioUrl = audioPath
entity.FileVideoUrl = videoPath
2026-01-14 01:29:35 +00:00
2026-02-09 08:44:31 +00:00
2026-01-14 01:29:35 +00:00
if req.Status < 0 {
entity.Status = 0
}
err = s.Repo.Create(entity)
if err != nil {
return nil, err
}
result, err := s.Repo.FindOne(entity.ID)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(result, host), nil
}
func (s *KnowledgeBaseService) Update(id uint, req request.KnowledgeBaseUpdateRequest) (data *response.KnowledgeBaseResponse, err error) {
old, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
old.AgentId = req.AgentId
old.AgentName = req.AgentName
old.Title = req.Title
old.Status = req.Status
old.FileJournalUrl = req.FileJournalUrl
old.FileAudioUrl = req.FileAudioUrl
old.FileVideoUrl = req.FileVideoUrl
if req.IsActive != nil {
old.IsActive = *req.IsActive
}
old.UpdatedAt = time.Now()
err = s.Repo.Update(id, old)
if err != nil {
return nil, err
}
updated, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(updated, host), nil
}
func (s *KnowledgeBaseService) Delete(id uint) error {
result, err := s.Repo.FindOne(id)
if err != nil {
return err
}
result.IsActive = false
result.UpdatedAt = time.Now()
2026-02-09 00:25:27 +00:00
return s.Repo.Delete(id)
2026-01-14 01:29:35 +00:00
}
func (s *KnowledgeBaseService) UpdateStatus(id uint, status int) (data *response.KnowledgeBaseResponse, err error) {
old, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
old.Status = status
old.UpdatedAt = time.Now()
err = s.Repo.Update(id, old)
if err != nil {
return nil, err
}
updated, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(updated, host), nil
}
2026-01-15 06:59:03 +00:00
func (s *KnowledgeBaseService) UpdateDocumentId(id uint, documentId int) (data *response.KnowledgeBaseResponse, err error) {
// cek data exist
_, err = s.Repo.FindOne(id)
if err != nil {
return nil, err
}
err = s.Repo.UpdateDocumentId(id, documentId)
if err != nil {
return nil, err
}
updated, err := s.Repo.FindOne(id)
if err != nil {
return nil, err
}
host := s.Cfg.App.Domain
return mapper.KnowledgeBaseResponseMapper(updated, host), nil
}