back knowledge base

This commit is contained in:
Rama Priyanto 2026-02-10 11:02:55 +07:00
parent e68b01a25a
commit e205e5ab2e
3 changed files with 72 additions and 56 deletions

View File

@ -10,7 +10,6 @@ func KnowledgeBaseResponseMapper(kb *entity.KnowledgeBase, host string) (out *re
return nil return nil
} }
out = &res.KnowledgeBaseResponse{ out = &res.KnowledgeBaseResponse{
ID: kb.ID, ID: kb.ID,
AgentId: kb.AgentId, AgentId: kb.AgentId,

View File

@ -118,26 +118,31 @@ func (r *KnowledgeBaseRepository) Delete(id uint) (err error) {
return r.DB.DB.Delete(&entity.KnowledgeBase{}, id).Error return r.DB.DB.Delete(&entity.KnowledgeBase{}, id).Error
} }
func (r *KnowledgeBaseRepository) FindByFilename(filename string) (*entity.KnowledgeBase, string, error) { func (r *KnowledgeBaseRepository) FindByFilename(filename string) (data *entity.KnowledgeBase, fileType string, err error) {
query := r.DB.DB.Model(&entity.KnowledgeBase{}).Where("is_active = ?", true)
// cari berdasarkan url yang mengandung filename
like := "%" + filename
// journal
var kb entity.KnowledgeBase var kb entity.KnowledgeBase
if err := query.Where("file_journal_url LIKE ?", like).First(&kb).Error; err == nil {
if err := r.DB.DB.Where("file_journal_url LIKE ?", "%"+filename).First(&kb).Error; err == nil { return &kb, "journal", nil
return &kb, *kb.FileJournalUrl, nil
} }
if err := r.DB.DB.Where("file_audio_url LIKE ?", "%"+filename).First(&kb).Error; err == nil { // audio
return &kb, *kb.FileAudioUrl, nil if err := query.Where("file_audio_url LIKE ?", like).First(&kb).Error; err == nil {
return &kb, "audio", nil
} }
if err := r.DB.DB.Where("file_video_url LIKE ?", "%"+filename).First(&kb).Error; err == nil { // video
return &kb, *kb.FileVideoUrl, nil if err := query.Where("file_video_url LIKE ?", like).First(&kb).Error; err == nil {
return &kb, "video", nil
} }
return nil, "", fmt.Errorf("file not found") return nil, "", fmt.Errorf("file not found")
} }
func (r *KnowledgeBaseRepository) UpdateDocumentId(id uint, documentId int) error { func (r *KnowledgeBaseRepository) UpdateDocumentId(id uint, documentId int) error {
return r.DB.DB.Model(&entity.KnowledgeBase{}). return r.DB.DB.Model(&entity.KnowledgeBase{}).
Where("id = ?", id). Where("id = ?", id).

View File

@ -3,6 +3,7 @@ package service
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"mime" "mime"
"mime/multipart" "mime/multipart"
"narasi-ahli-be/app/module/knowledge_base/mapper" "narasi-ahli-be/app/module/knowledge_base/mapper"
@ -63,6 +64,7 @@ func (s *KnowledgeBaseService) uploadFileToMinio(
minioClient *minio.Client, minioClient *minio.Client,
bucketName string, bucketName string,
agentId string, agentId string,
folder string,
fileHeader *multipart.FileHeader, fileHeader *multipart.FileHeader,
) (*string, error) { ) (*string, error) {
@ -85,10 +87,10 @@ func (s *KnowledgeBaseService) uploadFileToMinio(
now := time.Now() now := time.Now()
newFilename := fmt.Sprintf("%s_%d%s", filenameWithoutExt, now.UnixNano(), ext) newFilename := fmt.Sprintf("%s_%d%s", filenameWithoutExt, now.UnixNano(), ext)
// ⭐ INI PATH ASLI MINIO
objectName := fmt.Sprintf( objectName := fmt.Sprintf(
"knowledge-base/%s/%s", "knowledge-base/%s/%s/%s",
agentId, agentId,
folder,
newFilename, newFilename,
) )
@ -98,19 +100,15 @@ func (s *KnowledgeBaseService) uploadFileToMinio(
objectName, objectName,
src, src,
fileHeader.Size, fileHeader.Size,
minio.PutObjectOptions{ minio.PutObjectOptions{},
ContentType: fileHeader.Header.Get("Content-Type"),
},
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
// ⭐ KEMBALIKAN OBJECT PATH return &newFilename, nil
return &objectName, nil
} }
func (s *KnowledgeBaseService) All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error) { func (s *KnowledgeBaseService) All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error) {
results, paging, err := s.Repo.GetAll(req) results, paging, err := s.Repo.GetAll(req)
@ -136,55 +134,52 @@ func getFileExtension(filename string) string {
return parts[len(parts)-1] return parts[len(parts)-1]
} }
func (s *KnowledgeBaseService) Viewer(c *fiber.Ctx) error { func (s *KnowledgeBaseService) Viewer(c *fiber.Ctx) (err error) {
filename := c.Params("filename") filename := c.Params("filename")
_, objectName, err := s.Repo.FindByFilename(filename) result, folder, err := s.Repo.FindByFilename(filename)
if err != nil { if err != nil {
return c.Status(404).SendString("file not found") return err
} }
ctx := context.Background() ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
objectName := fmt.Sprintf("knowledge-base/%s/%s/%s", *result.AgentId, folder, filename)
s.Log.Info().
Str("timestamp", time.Now().Format(time.RFC3339)).
Str("Service:Resource", "KnowledgeBase:Viewer").
Interface("objectName", objectName).
Msg("")
minioClient, err := s.MinioStorage.ConnectMinio() minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil { if err != nil {
return c.Status(500).SendString(err.Error()) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": true,
"msg": err.Error(),
})
} }
opts := minio.GetObjectOptions{} fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
// 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)
if err != nil { if err != nil {
return c.Status(404).SendString("file not found") return err
} }
defer object.Close() defer fileContent.Close()
stat, err := object.Stat() contentType := mime.TypeByExtension("." + getFileExtension(objectName))
if err != nil {
return c.Status(404).SendString("file not found")
}
contentType := stat.ContentType
if contentType == "" { if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(objectName)) contentType = "application/octet-stream"
}
c.Set("Content-Type", contentType)
if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil {
return err
} }
c.Set("Content-Type", contentType) return nil
c.Set("Content-Disposition", "inline")
return c.SendStream(object)
} }
func (s *KnowledgeBaseService) Show(id uint) (data *response.KnowledgeBaseResponse, err error) { func (s *KnowledgeBaseService) Show(id uint) (data *response.KnowledgeBaseResponse, err error) {
result, err := s.Repo.FindOne(id) result, err := s.Repo.FindOne(id)
if err != nil { if err != nil {
@ -223,18 +218,35 @@ func (s *KnowledgeBaseService) Create(c *fiber.Ctx) (data *response.KnowledgeBas
ctx := context.Background() ctx := context.Background()
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
journalPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileJournal) journalFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "journal", fileJournal)
audioPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileAudio) if err != nil {
videoPath, _ := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, fileVideo) return nil, err
}
entity := req.ToEntity() audioFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "audio", fileAudio)
if err != nil {
entity.FileJournalUrl = journalPath return nil, err
entity.FileAudioUrl = audioPath }
entity.FileVideoUrl = videoPath videoFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "video", fileVideo)
if err != nil {
return nil, err
}
entity := req.ToEntity()
viewerBase := s.Cfg.App.Domain + "/knowledge-base/viewer/"
if journalFilename != nil {
journalUrl := viewerBase + *journalFilename
entity.FileJournalUrl = &journalUrl
}
if audioFilename != nil {
audioUrl := viewerBase + *audioFilename
entity.FileAudioUrl = &audioUrl
}
if videoFilename != nil {
videoUrl := viewerBase + *videoFilename
entity.FileVideoUrl = &videoUrl
}
if req.Status < 0 { if req.Status < 0 {
entity.Status = 0 entity.Status = 0