From e205e5ab2e9a3e688fdcbae544477947e4e7d64c Mon Sep 17 00:00:00 2001 From: Rama Priyanto Date: Tue, 10 Feb 2026 11:02:55 +0700 Subject: [PATCH] back knowledge base --- .../mapper/knowledge_base.mapper.go | 1 - .../repository/knowledge_base.repository.go | 23 ++-- .../service/knowledge_base.service.go | 104 ++++++++++-------- 3 files changed, 72 insertions(+), 56 deletions(-) diff --git a/app/module/knowledge_base/mapper/knowledge_base.mapper.go b/app/module/knowledge_base/mapper/knowledge_base.mapper.go index 6d99cea..1b7d6da 100644 --- a/app/module/knowledge_base/mapper/knowledge_base.mapper.go +++ b/app/module/knowledge_base/mapper/knowledge_base.mapper.go @@ -9,7 +9,6 @@ func KnowledgeBaseResponseMapper(kb *entity.KnowledgeBase, host string) (out *re if kb == nil { return nil } - out = &res.KnowledgeBaseResponse{ ID: kb.ID, diff --git a/app/module/knowledge_base/repository/knowledge_base.repository.go b/app/module/knowledge_base/repository/knowledge_base.repository.go index 73f4c25..d29bea0 100644 --- a/app/module/knowledge_base/repository/knowledge_base.repository.go +++ b/app/module/knowledge_base/repository/knowledge_base.repository.go @@ -118,26 +118,31 @@ func (r *KnowledgeBaseRepository) Delete(id uint) (err 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 - - if err := r.DB.DB.Where("file_journal_url LIKE ?", "%"+filename).First(&kb).Error; err == nil { - return &kb, *kb.FileJournalUrl, nil + if err := query.Where("file_journal_url LIKE ?", like).First(&kb).Error; err == nil { + return &kb, "journal", nil } - if err := r.DB.DB.Where("file_audio_url LIKE ?", "%"+filename).First(&kb).Error; err == nil { - return &kb, *kb.FileAudioUrl, nil + // audio + 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 { - return &kb, *kb.FileVideoUrl, nil + // video + 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") } - func (r *KnowledgeBaseRepository) UpdateDocumentId(id uint, documentId int) error { return r.DB.DB.Model(&entity.KnowledgeBase{}). Where("id = ?", id). diff --git a/app/module/knowledge_base/service/knowledge_base.service.go b/app/module/knowledge_base/service/knowledge_base.service.go index 109330e..e5d555f 100644 --- a/app/module/knowledge_base/service/knowledge_base.service.go +++ b/app/module/knowledge_base/service/knowledge_base.service.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "io" "mime" "mime/multipart" "narasi-ahli-be/app/module/knowledge_base/mapper" @@ -63,6 +64,7 @@ func (s *KnowledgeBaseService) uploadFileToMinio( minioClient *minio.Client, bucketName string, agentId string, + folder string, fileHeader *multipart.FileHeader, ) (*string, error) { @@ -85,10 +87,10 @@ func (s *KnowledgeBaseService) uploadFileToMinio( now := time.Now() newFilename := fmt.Sprintf("%s_%d%s", filenameWithoutExt, now.UnixNano(), ext) - // ⭐ INI PATH ASLI MINIO objectName := fmt.Sprintf( - "knowledge-base/%s/%s", + "knowledge-base/%s/%s/%s", agentId, + folder, newFilename, ) @@ -98,19 +100,15 @@ func (s *KnowledgeBaseService) uploadFileToMinio( objectName, src, fileHeader.Size, - minio.PutObjectOptions{ - ContentType: fileHeader.Header.Get("Content-Type"), - }, + minio.PutObjectOptions{}, ) if err != nil { return nil, err } - // ⭐ KEMBALIKAN OBJECT PATH - return &objectName, nil + return &newFilename, nil } - func (s *KnowledgeBaseService) All(req request.KnowledgeBaseQueryRequest) (data []*response.KnowledgeBaseResponse, paging paginator.Pagination, err error) { results, paging, err := s.Repo.GetAll(req) @@ -136,55 +134,52 @@ func getFileExtension(filename string) string { 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") - _, objectName, err := s.Repo.FindByFilename(filename) + result, folder, err := s.Repo.FindByFilename(filename) if err != nil { - return c.Status(404).SendString("file not found") + return err } ctx := context.Background() 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() 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{} - - // 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) + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) if err != nil { - return c.Status(404).SendString("file not found") + return err } - defer object.Close() + defer fileContent.Close() - stat, err := object.Stat() - if err != nil { - return c.Status(404).SendString("file not found") - } - - contentType := stat.ContentType + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) 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) - c.Set("Content-Disposition", "inline") - - return c.SendStream(object) + return nil } - - func (s *KnowledgeBaseService) Show(id uint) (data *response.KnowledgeBaseResponse, err error) { result, err := s.Repo.FindOne(id) if err != nil { @@ -223,18 +218,35 @@ func (s *KnowledgeBaseService) Create(c *fiber.Ctx) (data *response.KnowledgeBas ctx := context.Background() bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName - 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) + journalFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "journal", fileJournal) + if err != nil { + return nil, err + } + audioFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "audio", fileAudio) + if err != nil { + return nil, err + } + videoFilename, err := s.uploadFileToMinio(ctx, minioClient, bucketName, *req.AgentId, "video", fileVideo) + if err != nil { + return nil, err + } -entity := req.ToEntity() + entity := req.ToEntity() -entity.FileJournalUrl = journalPath -entity.FileAudioUrl = audioPath -entity.FileVideoUrl = videoPath + 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 { entity.Status = 0