fix:toml, yaml, temp upload file
This commit is contained in:
parent
38a72b74c6
commit
52b6432430
|
|
@ -13,10 +13,10 @@ stages:
|
|||
|
||||
build-2:
|
||||
stage: build-image
|
||||
image: docker/compose:latest
|
||||
image: docker/compose:25.0.3-cli
|
||||
services:
|
||||
- name: docker:dind
|
||||
command: [ "--insecure-registry=103.82.242.92:8900" ]
|
||||
- name: docker:25.0.3-dind
|
||||
command: ["--insecure-registry=103.82.242.92:8900"]
|
||||
script:
|
||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900
|
||||
- docker-compose build
|
||||
|
|
@ -30,4 +30,4 @@ deploy:
|
|||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- curl --user $JENKINS_USER:$JENKINS_PWD http://38.47.180.165:8080/job/autodeploy-narasiahli-be/build?token=autodeploynarasiahli
|
||||
- curl --user $JENKINS_USER:$JENKINS_PWD http://103.31.38.120:8080/job/autodeploy-narasiahli-be/build?token=autodeploynarasiahli
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type AiChatFiles struct {
|
||||
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
|
||||
MessageId uint `json:"message_id" gorm:"type:int4"`
|
||||
UploadID *string `json:"upload_id" gorm:"type:varchar"`
|
||||
Type string `json:"type" gorm:"type:varchar"`
|
||||
FilePath *string `json:"file_path" gorm:"type:varchar"`
|
||||
FileUrl *string `json:"file_url" gorm:"type:varchar"`
|
||||
FileName *string `json:"file_name" gorm:"type:varchar"`
|
||||
FileThumbnail *string `json:"file_thumbnail" gorm:"type:varchar"`
|
||||
FileAlt *string `json:"file_alt" gorm:"type:varchar"`
|
||||
Size *string `json:"size" gorm:"type:varchar"`
|
||||
FileType *string `json:"file_type" gorm:"type:varchar"`
|
||||
IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
|
||||
}
|
||||
|
|
@ -129,6 +129,7 @@ func Models() []interface{} {
|
|||
entity.AIChatSessions{},
|
||||
entity.AIChatMessages{},
|
||||
entity.AIChatLogs{},
|
||||
entity.AiChatFiles{},
|
||||
|
||||
// Ebook entities
|
||||
entity.Ebooks{},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package ai_chat_files
|
||||
|
||||
import (
|
||||
"narasi-ahli-be/app/module/ai_chat_files/controller"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/repository"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
// struct of ArticleFilesRouter
|
||||
type AiChatFilesRouter struct {
|
||||
App fiber.Router
|
||||
Controller *controller.Controller
|
||||
}
|
||||
|
||||
// register bulky of AiChatFiles module
|
||||
var NewAiChatFilesModule = fx.Options(
|
||||
// register repository of AiChatFiles module
|
||||
fx.Provide(repository.NewAiChatFilesRepository),
|
||||
|
||||
// register service of AiChatFiles module
|
||||
fx.Provide(service.NewAiChatFilesService),
|
||||
fx.Provide(service.NewUploadService),
|
||||
fx.Provide(service.NewUploadManager),
|
||||
|
||||
// register controller of AiChatFiles module
|
||||
fx.Provide(controller.NewController),
|
||||
|
||||
// register router of AiChatFiles module
|
||||
fx.Provide(NewAiChatFilesRouter),
|
||||
)
|
||||
|
||||
// init AiChatFilesRouter
|
||||
func NewAiChatFilesRouter(fiber *fiber.App, controller *controller.Controller) *AiChatFilesRouter {
|
||||
return &AiChatFilesRouter{
|
||||
App: fiber,
|
||||
Controller: controller,
|
||||
}
|
||||
}
|
||||
|
||||
// register routes of AiChatFiles module
|
||||
func (_i *AiChatFilesRouter) RegisterAiChatFilesRoutes() {
|
||||
// define controllers
|
||||
aiChatFilesController := _i.Controller.AiChatFiles
|
||||
|
||||
// define routes
|
||||
_i.App.Route("/ai-chat-files", func(router fiber.Router) {
|
||||
router.Get("/", aiChatFilesController.All)
|
||||
router.Get("/:id", aiChatFilesController.Show)
|
||||
router.Post("/:messageId", aiChatFilesController.Save)
|
||||
router.Put("/:id", aiChatFilesController.Update)
|
||||
router.Delete("/:id", aiChatFilesController.Delete)
|
||||
router.Get("/viewer/:filename", aiChatFilesController.Viewer)
|
||||
router.Get("/upload-status/:uploadId", aiChatFilesController.GetUploadStatus)
|
||||
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/request"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/service"
|
||||
"narasi-ahli-be/utils/paginator"
|
||||
utilRes "narasi-ahli-be/utils/response"
|
||||
utilVal "narasi-ahli-be/utils/validator"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type aiChatFilesController struct {
|
||||
aiChatFilesService service.AiChatFilesService
|
||||
}
|
||||
|
||||
type AiChatFilesController interface {
|
||||
All(c *fiber.Ctx) error
|
||||
Show(c *fiber.Ctx) error
|
||||
Save(c *fiber.Ctx) error
|
||||
Update(c *fiber.Ctx) error
|
||||
Delete(c *fiber.Ctx) error
|
||||
Viewer(c *fiber.Ctx) error
|
||||
GetUploadStatus(c *fiber.Ctx) error
|
||||
|
||||
}
|
||||
|
||||
func NewAiChatFilesController(aiChatFilesService service.AiChatFilesService) AiChatFilesController {
|
||||
return &aiChatFilesController{
|
||||
aiChatFilesService: aiChatFilesService,
|
||||
}
|
||||
}
|
||||
|
||||
// All AIChatFiles
|
||||
// @Summary Get all AiChatFiles
|
||||
// @Description API for getting all AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param req query request.AiChatFilesQueryRequest false "query parameters"
|
||||
// @Param req query paginator.Pagination false "pagination parameters"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /ai-chat-files [get]
|
||||
func (_i *aiChatFilesController) All(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
paginate, err := paginator.Paginate(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reqContext := request.AiChatFilesQueryRequestContext{
|
||||
MessageId: c.Query("messageId"),
|
||||
FileName: c.Query("fileName"),
|
||||
}
|
||||
req := reqContext.ToParamRequest()
|
||||
req.Pagination = paginate
|
||||
|
||||
aiChatFilesData, paging, err := _i.aiChatFilesService.All(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"AiChatFiles list successfully retrieved"},
|
||||
Data: aiChatFilesData,
|
||||
Meta: paging,
|
||||
})
|
||||
}
|
||||
|
||||
// Show AiChatFiles
|
||||
// @Summary Get one AiChatFiles
|
||||
// @Description API for getting one AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param id path int true "AiChatFiles ID"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /ai-chat-files/{id} [get]
|
||||
func (_i *aiChatFilesController) Show(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aiChatFilesData, err := _i.aiChatFilesService.Show(uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"AiChatFiles successfully retrieved"},
|
||||
Data: aiChatFilesData,
|
||||
})
|
||||
}
|
||||
|
||||
// Save AiChatFiles
|
||||
// @Summary Upload AiChatFiles
|
||||
// @Description API for create AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Produce json
|
||||
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||
// @Param files formData file true "Upload file" multiple true
|
||||
// @Param aiChatId path int true "AiChat ID"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /ai-chat-files/{messageId} [post]
|
||||
func (_i *aiChatFilesController) Save(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
id, err := strconv.ParseUint(c.Params("messageId"), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = _i.aiChatFilesService.Save(c, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"AiChatFiles successfully upload"},
|
||||
})
|
||||
}
|
||||
|
||||
// Update AiChatFiles
|
||||
// @Summary Update AiChatFiles
|
||||
// @Description API for update AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||
// @Param payload body request.AiChatFilesUpdateRequest true "Required payload"
|
||||
// @Param id path int true "AiChatFiles ID"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /aiChat-files/{id} [put]
|
||||
func (_i *aiChatFilesController) Update(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := new(request.AiChatFilesUpdateRequest)
|
||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = _i.aiChatFilesService.Update(uint(id), *req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"AiChatFiles successfully updated"},
|
||||
})
|
||||
}
|
||||
|
||||
// Delete AiChatFiles
|
||||
// @Summary Delete AiChatFiles
|
||||
// @Description API for delete AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||
// @Param id path int true "AiChatFiles ID"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /aiChat-files/{id} [delete]
|
||||
func (_i *aiChatFilesController) Delete(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = _i.aiChatFilesService.Delete(uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"AiChatFiles successfully deleted"},
|
||||
})
|
||||
}
|
||||
|
||||
// Viewer AiChatFiles
|
||||
// @Summary Viewer AiChatFiles
|
||||
// @Description API for Viewer AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param filename path string true "AiChat File Name"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /aiChat-files/viewer/{filename} [get]
|
||||
func (_i *aiChatFilesController) Viewer(c *fiber.Ctx) error {
|
||||
// Get from context
|
||||
return _i.aiChatFilesService.Viewer(c)
|
||||
}
|
||||
|
||||
// GetUploadStatus AiChatFiles
|
||||
// @Summary GetUploadStatus AiChatFiles
|
||||
// @Description API for GetUploadStatus AiChatFiles
|
||||
// @Tags AiChat Files
|
||||
// @Security Bearer
|
||||
// @Param uploadId path string true "Upload ID of AiChatFiles"
|
||||
// @Success 200 {object} response.Response
|
||||
// @Failure 400 {object} response.BadRequestError
|
||||
// @Failure 401 {object} response.UnauthorizedError
|
||||
// @Failure 500 {object} response.InternalServerError
|
||||
// @Router /aiChat-files/upload-status/{uploadId} [get]
|
||||
func (_i *aiChatFilesController) GetUploadStatus(c *fiber.Ctx) error {
|
||||
progress, _ := _i.aiChatFilesService.GetUploadStatus(c)
|
||||
progressMessage := fmt.Sprintf("Upload Progress: %d%%", progress)
|
||||
|
||||
return utilRes.Resp(c, utilRes.Response{
|
||||
Success: true,
|
||||
Messages: utilRes.Messages{"Upload Status Retrieve"},
|
||||
Data: progressMessage,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package controller
|
||||
|
||||
import "narasi-ahli-be/app/module/ai_chat_files/service"
|
||||
|
||||
type Controller struct {
|
||||
AiChatFiles AiChatFilesController
|
||||
}
|
||||
|
||||
func NewController(AiChatFilesService service.AiChatFilesService) *Controller {
|
||||
return &Controller{
|
||||
AiChatFiles: NewAiChatFilesController(AiChatFilesService),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package mapper
|
||||
|
||||
import (
|
||||
"narasi-ahli-be/app/database/entity"
|
||||
res "narasi-ahli-be/app/module/ai_chat_files/response"
|
||||
)
|
||||
|
||||
func AiChatFilesResponseMapper(aiChatFilesReq *entity.AiChatFiles, host string) (aiChatFilesRes *res.AiChatFilesResponse) {
|
||||
fileUrl := host + "/ai-chat-files/viewer/"
|
||||
if aiChatFilesReq.FileName != nil {
|
||||
fileUrl += *aiChatFilesReq.FileName
|
||||
}
|
||||
|
||||
if aiChatFilesReq != nil {
|
||||
aiChatFilesRes = &res.AiChatFilesResponse{
|
||||
ID: aiChatFilesReq.ID,
|
||||
MessageId: aiChatFilesReq.MessageId,
|
||||
FilePath: aiChatFilesReq.FilePath,
|
||||
FileUrl: &fileUrl,
|
||||
FileName: aiChatFilesReq.FileName,
|
||||
FileAlt: aiChatFilesReq.FileAlt,
|
||||
Size: aiChatFilesReq.Size,
|
||||
IsActive: aiChatFilesReq.IsActive,
|
||||
CreatedAt: aiChatFilesReq.CreatedAt,
|
||||
UpdatedAt: aiChatFilesReq.UpdatedAt,
|
||||
}
|
||||
}
|
||||
return aiChatFilesRes
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"narasi-ahli-be/app/database"
|
||||
"narasi-ahli-be/app/database/entity"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/request"
|
||||
"narasi-ahli-be/utils/paginator"
|
||||
utilSvc "narasi-ahli-be/utils/service"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type aiChatFilesRepository struct {
|
||||
DB *database.Database
|
||||
}
|
||||
|
||||
// AiChatFilesRepository define interface of IAiChatFilesRepository
|
||||
type AiChatFilesRepository interface {
|
||||
GetAll(req request.AiChatFilesQueryRequest) (aiChatFiles []*entity.AiChatFiles, paging paginator.Pagination, err error)
|
||||
FindOne(id uint) (aiChatFiles *entity.AiChatFiles, err error)
|
||||
FindByAiChat(messageId uint) (aiChatFiles []*entity.AiChatFiles, err error)
|
||||
FindByFilename(filename string) (aiChatFiles *entity.AiChatFiles, err error)
|
||||
Create(aiChatFiles *entity.AiChatFiles) (err error)
|
||||
Update(id uint, aiChatFiles *entity.AiChatFiles) (err error)
|
||||
Delete(id uint) (err error)
|
||||
}
|
||||
|
||||
func NewAiChatFilesRepository(db *database.Database) AiChatFilesRepository {
|
||||
return &aiChatFilesRepository{
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
// implement interface of IAiChatFilesRepository
|
||||
func (_i *aiChatFilesRepository) GetAll(req request.AiChatFilesQueryRequest) (aiChatFiles []*entity.AiChatFiles, paging paginator.Pagination, err error) {
|
||||
var count int64
|
||||
|
||||
query := _i.DB.DB.Model(&entity.AiChatFiles{})
|
||||
query = query.Where("is_active = ?", true)
|
||||
|
||||
if req.MessageId != nil {
|
||||
query = query.Where("message_id = ?", req.MessageId)
|
||||
}
|
||||
if req.FileName != nil && *req.FileName != "" {
|
||||
fileName := strings.ToLower(*req.FileName)
|
||||
query = query.Where("LOWER(file_name) LIKE ?", "%"+strings.ToLower(fileName)+"%")
|
||||
}
|
||||
if req.IsPublish != nil {
|
||||
query = query.Where("is_publish = ?", req.IsPublish)
|
||||
}
|
||||
if req.StatusId != nil {
|
||||
query = query.Where("status_id = ?", req.StatusId)
|
||||
}
|
||||
query.Count(&count)
|
||||
|
||||
if req.Pagination.SortBy != "" {
|
||||
direction := "ASC"
|
||||
if req.Pagination.Sort == "desc" {
|
||||
direction = "DESC"
|
||||
}
|
||||
query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction))
|
||||
}
|
||||
|
||||
req.Pagination.Count = count
|
||||
req.Pagination = paginator.Paging(req.Pagination)
|
||||
|
||||
err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&aiChatFiles).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paging = *req.Pagination
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) FindOne(id uint) (aiChatFiles *entity.AiChatFiles, err error) {
|
||||
query := _i.DB.DB
|
||||
|
||||
if err := query.First(&aiChatFiles, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aiChatFiles, nil
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) FindByAiChat(messageId uint) (aiChatFiles []*entity.AiChatFiles, err error) {
|
||||
query := _i.DB.DB.Where("message_id = ?", messageId)
|
||||
|
||||
if err := query.Find(&aiChatFiles).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aiChatFiles, nil
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) FindByFilename(aiChatFilename string) (aiChatFiles *entity.AiChatFiles, err error) {
|
||||
query := _i.DB.DB.Where("file_name = ?", aiChatFilename)
|
||||
|
||||
if err := query.First(&aiChatFiles).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aiChatFiles, nil
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) Create(aiChatFiles *entity.AiChatFiles) (err error) {
|
||||
return _i.DB.DB.Create(aiChatFiles).Error
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) Update(id uint, aiChatFiles *entity.AiChatFiles) (err error) {
|
||||
aiChatFilesMap, err := utilSvc.StructToMap(aiChatFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return _i.DB.DB.Model(&entity.AiChatFiles{}).
|
||||
Where(&entity.AiChatFiles{ID: id}).
|
||||
Updates(aiChatFilesMap).Error
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesRepository) Delete(id uint) error {
|
||||
return _i.DB.DB.Delete(&entity.AiChatFiles{}, id).Error
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"narasi-ahli-be/app/database/entity"
|
||||
"narasi-ahli-be/utils/paginator"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AiChatFilesGeneric interface {
|
||||
ToEntity()
|
||||
}
|
||||
|
||||
type AiChatFilesQueryRequest struct {
|
||||
MessageId *int `json:"messageId"`
|
||||
FileName *string `json:"fileName"`
|
||||
StatusId *int `json:"statusId"`
|
||||
IsPublish *bool `json:"isPublish"`
|
||||
Pagination *paginator.Pagination `json:"pagination"`
|
||||
}
|
||||
|
||||
type AiChatFilesCreateRequest struct {
|
||||
MessageId uint `json:"messageId" validate:"required"`
|
||||
StatusId int `json:"statusId" validate:"required"`
|
||||
UploadId *string `json:"uploadId"`
|
||||
FilePath *string `json:"filePath"`
|
||||
FileUrl *string `json:"fileUrl"`
|
||||
FileName *string `json:"fileName"`
|
||||
FileThumbnail *string `json:"fileThumbnail"`
|
||||
FileAlt *string `json:"fileAlt"`
|
||||
WidthPixel *string `json:"widthPixel"`
|
||||
HeightPixel *string `json:"heightPixel"`
|
||||
Size *string `json:"size"`
|
||||
}
|
||||
|
||||
func (req AiChatFilesCreateRequest) ToEntity() *entity.AiChatFiles {
|
||||
return &entity.AiChatFiles{
|
||||
MessageId: req.MessageId,
|
||||
UploadID: req.UploadId,
|
||||
FilePath: req.FilePath,
|
||||
FileUrl: req.FileUrl,
|
||||
FileName: req.FileName,
|
||||
FileThumbnail: req.FileThumbnail,
|
||||
FileAlt: req.FileAlt,
|
||||
Size: req.Size,
|
||||
}
|
||||
}
|
||||
|
||||
type AiChatFilesUpdateRequest struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
MessageId uint `json:"messageId" validate:"required"`
|
||||
StatusId int `json:"statusId" validate:"required"`
|
||||
FilePath *string `json:"filePath"`
|
||||
FileUrl *string `json:"fileUrl"`
|
||||
FileName *string `json:"fileName"`
|
||||
FileThumbnail *string `json:"fileThumbnail"`
|
||||
FileAlt *string `json:"fileAlt"`
|
||||
WidthPixel *string `json:"widthPixel"`
|
||||
HeightPixel *string `json:"heightPixel"`
|
||||
Size *string `json:"size"`
|
||||
IsPublish *bool `json:"isPublish" validate:"required"`
|
||||
PublishedAt *time.Time `json:"publishedAt" validate:"required"`
|
||||
}
|
||||
|
||||
func (req AiChatFilesUpdateRequest) ToEntity() *entity.AiChatFiles {
|
||||
return &entity.AiChatFiles{
|
||||
ID: req.ID,
|
||||
MessageId: req.MessageId,
|
||||
FilePath: req.FilePath,
|
||||
FileUrl: req.FileUrl,
|
||||
FileName: req.FileName,
|
||||
FileThumbnail: req.FileThumbnail,
|
||||
FileAlt: req.FileAlt,
|
||||
Size: req.Size,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
type AiChatFilesQueryRequestContext struct {
|
||||
MessageId string `json:"messageId"`
|
||||
FileName string `json:"fileName"`
|
||||
StatusId string `json:"statusId"`
|
||||
IsPublish string `json:"isPublish"`
|
||||
}
|
||||
|
||||
func (req AiChatFilesQueryRequestContext) ToParamRequest() AiChatFilesQueryRequest {
|
||||
var request AiChatFilesQueryRequest
|
||||
|
||||
if messageIdStr := req.MessageId; messageIdStr != "" {
|
||||
messageId, err := strconv.Atoi(messageIdStr)
|
||||
if err == nil {
|
||||
request.MessageId = &messageId
|
||||
}
|
||||
}
|
||||
if fileName := req.FileName; fileName != "" {
|
||||
request.FileName = &fileName
|
||||
}
|
||||
if statusIdStr := req.StatusId; statusIdStr != "" {
|
||||
statusId, err := strconv.Atoi(statusIdStr)
|
||||
if err == nil {
|
||||
request.StatusId = &statusId
|
||||
}
|
||||
}
|
||||
if isPublishStr := req.IsPublish; isPublishStr != "" {
|
||||
isPublish, err := strconv.ParseBool(isPublishStr)
|
||||
if err == nil {
|
||||
request.IsPublish = &isPublish
|
||||
}
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package response
|
||||
|
||||
import "time"
|
||||
|
||||
type AiChatFilesResponse struct {
|
||||
ID uint `json:"id"`
|
||||
MessageId uint `json:"messageId"`
|
||||
FilePath *string `json:"filePath"`
|
||||
FileUrl *string `json:"fileUrl"`
|
||||
FileName *string `json:"fileName"`
|
||||
FileAlt *string `json:"fileAlt"`
|
||||
FileType *string `json:"fileType"`
|
||||
Size *string `json:"size"`
|
||||
IsActive bool `json:"isActive"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/mapper"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/repository"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/request"
|
||||
"narasi-ahli-be/app/module/ai_chat_files/response"
|
||||
config "narasi-ahli-be/config/config"
|
||||
minioStorage "narasi-ahli-be/config/config"
|
||||
"narasi-ahli-be/utils/paginator"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// AiChatFilesService
|
||||
type aiChatFilesService struct {
|
||||
Repo repository.AiChatFilesRepository
|
||||
Log zerolog.Logger
|
||||
Cfg *config.Config
|
||||
MinioStorage *minioStorage.MinioStorage
|
||||
}
|
||||
|
||||
// AiChatFilesService define interface of IAiChatFilesService
|
||||
type AiChatFilesService interface {
|
||||
All(req request.AiChatFilesQueryRequest) (aiChatFiles []*response.AiChatFilesResponse, paging paginator.Pagination, err error)
|
||||
Show(id uint) (aiChatFiles *response.AiChatFilesResponse, err error)
|
||||
Save(c *fiber.Ctx, id uint) error
|
||||
SaveAsync(c *fiber.Ctx, id uint) error
|
||||
Update(id uint, req request.AiChatFilesUpdateRequest) (err error)
|
||||
GetUploadStatus(c *fiber.Ctx) (progress int, err error)
|
||||
Delete(id uint) error
|
||||
Viewer(c *fiber.Ctx) error
|
||||
}
|
||||
|
||||
// NewAiChatFilesService init AiChatFilesService
|
||||
func NewAiChatFilesService(repo repository.AiChatFilesRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) AiChatFilesService {
|
||||
|
||||
return &aiChatFilesService{
|
||||
Repo: repo,
|
||||
Log: log,
|
||||
Cfg: cfg,
|
||||
MinioStorage: minioStorage,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
progressMap = make(map[string]int) // Menyimpan progress upload per UploadID
|
||||
progressLock = sync.Mutex{}
|
||||
)
|
||||
|
||||
type progressWriter struct {
|
||||
uploadID string
|
||||
totalSize int64
|
||||
uploadedSize *int64
|
||||
}
|
||||
|
||||
// All implement interface of AiChatFilesService
|
||||
func (_i *aiChatFilesService) All(req request.AiChatFilesQueryRequest) (aiChatFiless []*response.AiChatFilesResponse, 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 {
|
||||
aiChatFiless = append(aiChatFiless, mapper.AiChatFilesResponseMapper(result, host))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) Show(id uint) (aiChatFiles *response.AiChatFilesResponse, err error) {
|
||||
result, err := _i.Repo.FindOne(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host := _i.Cfg.App.Domain
|
||||
|
||||
return mapper.AiChatFilesResponseMapper(result, host), nil
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) SaveAsync(c *fiber.Ctx, id uint) (err error) {
|
||||
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
form, err := c.MultipartForm()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//filess := 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(),
|
||||
})
|
||||
}
|
||||
|
||||
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("")
|
||||
|
||||
filename := filepath.Base(fileHeader.Filename)
|
||||
filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(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)
|
||||
uploadID := strconv.Itoa(randUniqueId)
|
||||
|
||||
newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId)
|
||||
newFilename := newFilenameWithoutExt + "." + extension
|
||||
|
||||
objectName := fmt.Sprintf("aiChats/upload/%d/%d/%s", now.Year(), now.Month(), newFilename)
|
||||
fileSize := strconv.FormatInt(fileHeader.Size, 10)
|
||||
fileSizeInt := fileHeader.Size
|
||||
|
||||
_i.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top").
|
||||
Interface("Start upload", uploadID).Msg("")
|
||||
|
||||
req := request.AiChatFilesCreateRequest{
|
||||
MessageId: id,
|
||||
UploadId: &uploadID,
|
||||
FilePath: &objectName,
|
||||
FileName: &newFilename,
|
||||
FileAlt: &filenameAlt,
|
||||
Size: &fileSize,
|
||||
}
|
||||
|
||||
err = _i.Repo.Create(req.ToEntity())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
tempFilePath := fmt.Sprintf("/tmp/%s", newFilename)
|
||||
tempFile, err := os.Create(tempFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tempFile.Close()
|
||||
|
||||
// Copy file ke direktori sementara
|
||||
_, err = io.Copy(tempFile, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go uploadToMinIO(ctx, _i.Log, minioClient, uploadID, tempFilePath, bucketName, objectName, fileSizeInt)
|
||||
}
|
||||
}
|
||||
|
||||
_i.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "User:All").
|
||||
Interface("data", "Successfully uploaded").Msg("")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) Save(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()
|
||||
|
||||
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)
|
||||
filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(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("aiChats/upload/%d/%d/%s", now.Year(), now.Month(), newFilename)
|
||||
fileSize := strconv.FormatInt(fileHeader.Size, 10)
|
||||
|
||||
req := request.AiChatFilesCreateRequest{
|
||||
MessageId: id,
|
||||
FilePath: &objectName,
|
||||
FileName: &newFilename,
|
||||
FileAlt: &filenameAlt,
|
||||
Size: &fileSize,
|
||||
}
|
||||
|
||||
err = _i.Repo.Create(req.ToEntity())
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_i.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "User:All").
|
||||
Interface("data", "Successfully uploaded").Msg("")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) Update(id uint, req request.AiChatFilesUpdateRequest) (err error) {
|
||||
_i.Log.Info().Interface("data", req).Msg("")
|
||||
return _i.Repo.Update(id, req.ToEntity())
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) 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 *aiChatFilesService) 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.FilePath
|
||||
|
||||
_i.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:Uploads").
|
||||
Interface("data", objectName).Msg("")
|
||||
|
||||
// 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]
|
||||
}
|
||||
|
||||
func uploadTempFile(log zerolog.Logger, fileHeader *multipart.FileHeader, filePath string) {
|
||||
src, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-0").
|
||||
Interface("err", err).Msg("")
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
tempFile, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-1").
|
||||
Interface("err", err).Msg("")
|
||||
}
|
||||
defer tempFile.Close()
|
||||
|
||||
// Copy file ke direktori sementara
|
||||
_, err = io.Copy(tempFile, src)
|
||||
if err != nil {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-2").
|
||||
Interface("err", err).Msg("")
|
||||
}
|
||||
}
|
||||
|
||||
func uploadToMinIO(ctx context.Context, log zerolog.Logger, minioClient *minio.Client, uploadID, filePath, bucketName string, objectName string, fileSize int64) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-3").
|
||||
Interface("err", err).Msg("")
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Upload file ke MinIO dengan progress tracking
|
||||
uploadProgress := int64(0)
|
||||
reader := io.TeeReader(file, &progressWriter{uploadID: uploadID, totalSize: fileSize, uploadedSize: &uploadProgress})
|
||||
|
||||
_, err = minioClient.PutObject(ctx, bucketName, objectName, reader, fileSize, minio.PutObjectOptions{})
|
||||
if err != nil {
|
||||
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-4").
|
||||
Interface("err", err).Msg("")
|
||||
return
|
||||
}
|
||||
|
||||
// Upload selesai, update progress menjadi 100
|
||||
progressLock.Lock()
|
||||
progressMap[uploadID] = 100
|
||||
progressLock.Unlock()
|
||||
|
||||
go removeFileTemp(log, filePath)
|
||||
}
|
||||
|
||||
func removeFileTemp(log zerolog.Logger, filePath string) {
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-5").
|
||||
Interface("Failed to remove temporary file", err).Msg("")
|
||||
} else {
|
||||
log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "AiChat:uploadToMinIO-6").
|
||||
Interface("err", "Temporary file removed").Msg("")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *progressWriter) Write(data []byte) (int, error) {
|
||||
n := len(data)
|
||||
progressLock.Lock()
|
||||
defer progressLock.Unlock()
|
||||
|
||||
*p.uploadedSize += int64(n)
|
||||
progress := int(float64(*p.uploadedSize) / float64(p.totalSize) * 100)
|
||||
|
||||
// Update progress di map
|
||||
progressMap[p.uploadID] = progress
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (_i *aiChatFilesService) GetUploadStatus(c *fiber.Ctx) (progress int, err error) {
|
||||
uploadID := c.Params("uploadId")
|
||||
|
||||
// Ambil progress dari map
|
||||
progressLock.Lock()
|
||||
progress, _ = progressMap[uploadID]
|
||||
progressLock.Unlock()
|
||||
|
||||
return progress, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/rs/zerolog"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AsyncUploader menangani proses upload secara asynchronous
|
||||
type UploadService interface {
|
||||
UploadFile(ctx context.Context, minioClient *minio.Client, uploadID string, reader io.Reader, bucketName string, objectName string, size int64, contentType string) error
|
||||
}
|
||||
|
||||
type uploadService struct {
|
||||
uploadManager UploadManager
|
||||
Log zerolog.Logger
|
||||
}
|
||||
|
||||
func NewUploadService(uploadManager UploadManager, log zerolog.Logger) UploadService {
|
||||
return &uploadService{
|
||||
uploadManager: uploadManager,
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *uploadService) UploadFile(ctx context.Context, minioClient *minio.Client, uploadID string, reader io.Reader, bucketName string, objectName string, size int64, contentType string) error {
|
||||
status := &UploadStatus{
|
||||
FileName: objectName,
|
||||
Size: size,
|
||||
Progress: 0,
|
||||
Status: "uploading",
|
||||
ObjectName: objectName,
|
||||
BucketName: bucketName,
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
|
||||
u.uploadManager.Add(uploadID, status)
|
||||
|
||||
u.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "UploadService::UploadFile").
|
||||
Interface("add status", status).Msg("")
|
||||
|
||||
// Upload ke Minio
|
||||
_, err := minioClient.PutObject(
|
||||
ctx,
|
||||
bucketName,
|
||||
objectName,
|
||||
reader,
|
||||
size,
|
||||
minio.PutObjectOptions{
|
||||
ContentType: contentType,
|
||||
PartSize: 10 * 1024 * 1024, // 10MB part size
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
u.uploadManager.UpdateStatus(uploadID, "error", err)
|
||||
|
||||
u.Log.Info().Str("timestamp", time.Now().
|
||||
Format(time.RFC3339)).Str("Service:Resource", "UploadService::UploadFile").
|
||||
Interface("error when upload", err).Msg("")
|
||||
}
|
||||
|
||||
u.uploadManager.UpdateStatus(uploadID, "completed", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (au *UploadService) UploadFile() {
|
||||
// // Buat context dengan timeout
|
||||
// ctx, cancel := context.WithTimeout(au.ctx, 30*time.Minute)
|
||||
// defer cancel()
|
||||
//
|
||||
// // Buat reader dari byte slice
|
||||
// reader := bytes.NewReader(au.fileData)
|
||||
// pipeReader, pipeWriter := io.Pipe()
|
||||
//
|
||||
// au.progressMap.Store(au.uploadID, 0.0)
|
||||
//
|
||||
// // Start goroutine to read from reader and write to pipe
|
||||
// go func() {
|
||||
// defer pipeWriter.Close()
|
||||
// buf := make([]byte, au.partSize)
|
||||
//
|
||||
// totalParts := int(reader.Size() / au.partSize)
|
||||
// if reader.Size()%au.partSize != 0 {
|
||||
// totalParts++
|
||||
// }
|
||||
//
|
||||
// for i := 0; i < totalParts; i++ {
|
||||
// n, err := reader.Read(buf)
|
||||
// if err != nil && err != io.EOF {
|
||||
// log.Println("Error reading file:", err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if _, err := pipeWriter.Write(buf[:n]); err != nil {
|
||||
// log.Println("Error writing to pipe:", err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// progress := float64(i+1) / float64(totalParts) * 100
|
||||
// au.progressMap.Store(au.uploadID, progress)
|
||||
// au.uploadManager.UpdateProgress(au.uploadID, int(progress))
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// // Upload ke Minio
|
||||
// _, err := au.minioClient.PutObject(
|
||||
// ctx,
|
||||
// au.bucketName,
|
||||
// au.objectName,
|
||||
// pipeReader,
|
||||
// reader.Size(),
|
||||
// minio.PutObjectOptions{
|
||||
// ContentType: au.contentType,
|
||||
// PartSize: 10 * 1024 * 1024, // 10MB part size
|
||||
// },
|
||||
// )
|
||||
//
|
||||
// if err != nil {
|
||||
// log.Println("Error uploading file:", err)
|
||||
// au.progressMap.Store(au.uploadID, "error")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// fmt.Printf("Uploading process for %s", au.objectName)
|
||||
//
|
||||
// if err != nil {
|
||||
// uploadManager.UpdateStatus(au.uploadID, "error", err)
|
||||
// fmt.Printf("Upload error for %s: %v\n", au.objectName, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// au.progressMap.Store(au.uploadID, 100)
|
||||
// au.uploadManager.UpdateProgress(au.uploadID, 100)
|
||||
// au.uploadManager.UpdateStatus(au.uploadID, "completed", nil)
|
||||
//}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UploadStatus struct {
|
||||
FileName string `json:"fileName"`
|
||||
Size int64 `json:"size"`
|
||||
Progress int `json:"progress"`
|
||||
Status string `json:"status"`
|
||||
ObjectName string `json:"objectName"`
|
||||
BucketName string `json:"bucketName"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type UploadManager interface {
|
||||
Add(uploadID string, status *UploadStatus)
|
||||
UpdateProgress(uploadID string, progress int)
|
||||
UpdateStatus(uploadID string, status string, err error)
|
||||
Get(uploadID string) (*UploadStatus, bool)
|
||||
}
|
||||
|
||||
type uploadManager struct {
|
||||
uploads map[string]*UploadStatus
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewUploadManager() UploadManager {
|
||||
return &uploadManager{
|
||||
uploads: make(map[string]*UploadStatus),
|
||||
}
|
||||
}
|
||||
|
||||
// Add menambahkan status upload baru
|
||||
func (um *uploadManager) Add(uploadID string, status *UploadStatus) {
|
||||
um.mutex.Lock()
|
||||
defer um.mutex.Unlock()
|
||||
um.uploads[uploadID] = status
|
||||
}
|
||||
|
||||
// UpdateProgress memperbarui progress upload
|
||||
func (um *uploadManager) UpdateProgress(uploadID string, progress int) {
|
||||
um.mutex.Lock()
|
||||
defer um.mutex.Unlock()
|
||||
if status, exists := um.uploads[uploadID]; exists {
|
||||
status.Progress = progress
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateStatus memperbarui status upload
|
||||
func (um *uploadManager) UpdateStatus(uploadID string, status string, err error) {
|
||||
um.mutex.Lock()
|
||||
defer um.mutex.Unlock()
|
||||
if upload, exists := um.uploads[uploadID]; exists {
|
||||
upload.Status = status
|
||||
if err != nil {
|
||||
upload.Error = err.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get mendapatkan status upload berdasarkan ID
|
||||
func (um *uploadManager) Get(uploadID string) (*UploadStatus, bool) {
|
||||
um.mutex.RLock()
|
||||
defer um.mutex.RUnlock()
|
||||
status, exists := um.uploads[uploadID]
|
||||
return status, exists
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"narasi-ahli-be/app/module/activity_logs"
|
||||
"narasi-ahli-be/app/module/advertisement"
|
||||
"narasi-ahli-be/app/module/ai_chat"
|
||||
"narasi-ahli-be/app/module/ai_chat_files"
|
||||
"narasi-ahli-be/app/module/article_approvals"
|
||||
"narasi-ahli-be/app/module/article_categories"
|
||||
"narasi-ahli-be/app/module/article_category_details"
|
||||
|
|
@ -68,6 +69,8 @@ type Router struct {
|
|||
EducationHistoryRouter *education_history.EducationHistoryRouter
|
||||
WorkHistoryRouter *work_history.WorkHistoryRouter
|
||||
ResearchJournalsRouter *research_journals.ResearchJournalsRouter
|
||||
AIChatFilesRouter *ai_chat_files.AiChatFilesRouter
|
||||
|
||||
}
|
||||
|
||||
func NewRouter(
|
||||
|
|
@ -102,6 +105,7 @@ func NewRouter(
|
|||
educationHistoryRouter *education_history.EducationHistoryRouter,
|
||||
workHistoryRouter *work_history.WorkHistoryRouter,
|
||||
researchJournalsRouter *research_journals.ResearchJournalsRouter,
|
||||
aiChatFilesRouter *ai_chat_files.AiChatFilesRouter,
|
||||
) *Router {
|
||||
return &Router{
|
||||
App: fiber,
|
||||
|
|
@ -134,6 +138,7 @@ func NewRouter(
|
|||
EducationHistoryRouter: educationHistoryRouter,
|
||||
WorkHistoryRouter: workHistoryRouter,
|
||||
ResearchJournalsRouter: researchJournalsRouter,
|
||||
AIChatFilesRouter: aiChatFilesRouter,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,4 +181,6 @@ func (r *Router) Register() {
|
|||
r.EducationHistoryRouter.RegisterEducationHistoryRoutes()
|
||||
r.WorkHistoryRouter.RegisterWorkHistoryRoutes()
|
||||
r.ResearchJournalsRouter.RegisterResearchJournalsRoutes()
|
||||
r.AIChatFilesRouter.RegisterAiChatFilesRoutes()
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Configuration vars for cmd/app
|
||||
[app]
|
||||
name = "Fiber starter"
|
||||
host = "http://38.47.180.165"
|
||||
host = "http://103.31.38.120"
|
||||
port = ":8800"
|
||||
domain = "https://narasiahli.com/api"
|
||||
external-port = ":8801"
|
||||
|
|
@ -12,7 +12,7 @@ production = false
|
|||
body-limit = 1048576000 # "100 * 1024 * 1024"
|
||||
|
||||
[db.postgres]
|
||||
dsn = "postgresql://narasiahli_user:NarasiAhliDB@2025@38.47.180.165:5432/narasiahli_db" # <driver>://<username>:<password>@<host>:<port>/<database>
|
||||
dsn = "postgresql://narasiahli_user:NarasiAhliDB@2025@157.10.161.198:5432/narasiahli_db" # <driver>://<username>:<password>@<host>:<port>/<database>
|
||||
log-mode = "NONE"
|
||||
migrate = true
|
||||
seed = true
|
||||
|
|
@ -24,7 +24,7 @@ level = 0 # panic -> 5, fatal -> 4, error -> 3, warn -> 2, info -> 1, debug -> 0
|
|||
prettier = true
|
||||
|
||||
[objectstorage.miniostorage]
|
||||
endpoint = "38.47.180.165:9009"
|
||||
endpoint = "is3.cloudhost.id"
|
||||
access-key-id = "lBtjqWidHz1ktBbduwGy"
|
||||
secret-access-key = "nsedJIa2FI7SqsEVcSFqJrlP4JuFRWGLauNpzD0i"
|
||||
use-ssl = false
|
||||
|
|
@ -66,7 +66,7 @@ enable = true
|
|||
retention = 30
|
||||
|
||||
[keycloak]
|
||||
endpoint = "http://38.47.180.165:8008"
|
||||
endpoint = "http://103.31.38.120:8008"
|
||||
realm = "narasi-ahli"
|
||||
client-id = "narasi-ahli-app"
|
||||
client-secret = "IoU4CkzWkWmg6PrC2Ruh8d76QArb0UzJ"
|
||||
|
|
|
|||
|
|
@ -914,6 +914,223 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for getting all AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Get all AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "fileName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "isPublish",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "messageId",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "statusId",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "count",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "nextPage",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "previousPage",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "sort",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "sortBy",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "totalPage",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for getting one AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Get one AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files/{messageId}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for create AiChatFiles",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Upload AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"description": "Upload file",
|
||||
"name": "files",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChat ID",
|
||||
"name": "aiChatId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -1700,6 +1917,223 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/upload-status/{uploadId}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for GetUploadStatus AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "GetUploadStatus AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Upload ID of AiChatFiles",
|
||||
"name": "uploadId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/viewer/{filename}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for Viewer AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Viewer AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "AiChat File Name",
|
||||
"name": "filename",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/{id}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for update AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Update AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Required payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.AiChatFilesUpdateRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for delete AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Delete AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/article-approvals": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -15204,6 +15638,57 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"request.AiChatFilesUpdateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"isPublish",
|
||||
"messageId",
|
||||
"publishedAt",
|
||||
"statusId"
|
||||
],
|
||||
"properties": {
|
||||
"fileAlt": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileName": {
|
||||
"type": "string"
|
||||
},
|
||||
"filePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileThumbnail": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"heightPixel": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"isPublish": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"messageId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "string"
|
||||
},
|
||||
"statusId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"widthPixel": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.ArticleApprovalsCreateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -903,6 +903,223 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for getting all AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Get all AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "fileName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "isPublish",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "messageId",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "statusId",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "count",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "nextPage",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "previousPage",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "sort",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "sortBy",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "totalPage",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for getting one AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Get one AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat-files/{messageId}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for create AiChatFiles",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Upload AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"description": "Upload file",
|
||||
"name": "files",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChat ID",
|
||||
"name": "aiChatId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ai-chat/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -1689,6 +1906,223 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/upload-status/{uploadId}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for GetUploadStatus AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "GetUploadStatus AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Upload ID of AiChatFiles",
|
||||
"name": "uploadId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/viewer/{filename}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for Viewer AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Viewer AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "AiChat File Name",
|
||||
"name": "filename",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/aiChat-files/{id}": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for update AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Update AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Required payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.AiChatFilesUpdateRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "API for delete AiChatFiles",
|
||||
"tags": [
|
||||
"AiChat Files"
|
||||
],
|
||||
"summary": "Delete AiChatFiles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Insert the X-Csrf-Token",
|
||||
"name": "X-Csrf-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "AiChatFiles ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.Response"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.BadRequestError"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.UnauthorizedError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/article-approvals": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -15193,6 +15627,57 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"request.AiChatFilesUpdateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"isPublish",
|
||||
"messageId",
|
||||
"publishedAt",
|
||||
"statusId"
|
||||
],
|
||||
"properties": {
|
||||
"fileAlt": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileName": {
|
||||
"type": "string"
|
||||
},
|
||||
"filePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileThumbnail": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"heightPixel": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"isPublish": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"messageId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"publishedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "string"
|
||||
},
|
||||
"statusId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"widthPixel": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.ArticleApprovalsCreateRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -148,6 +148,41 @@ definitions:
|
|||
- redirectLink
|
||||
- title
|
||||
type: object
|
||||
request.AiChatFilesUpdateRequest:
|
||||
properties:
|
||||
fileAlt:
|
||||
type: string
|
||||
fileName:
|
||||
type: string
|
||||
filePath:
|
||||
type: string
|
||||
fileThumbnail:
|
||||
type: string
|
||||
fileUrl:
|
||||
type: string
|
||||
heightPixel:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
isPublish:
|
||||
type: boolean
|
||||
messageId:
|
||||
type: integer
|
||||
publishedAt:
|
||||
type: string
|
||||
size:
|
||||
type: string
|
||||
statusId:
|
||||
type: integer
|
||||
widthPixel:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- isPublish
|
||||
- messageId
|
||||
- publishedAt
|
||||
- statusId
|
||||
type: object
|
||||
request.ArticleApprovalsCreateRequest:
|
||||
properties:
|
||||
articleId:
|
||||
|
|
@ -1922,6 +1957,142 @@ paths:
|
|||
summary: Viewer Advertisement
|
||||
tags:
|
||||
- Advertisement
|
||||
/ai-chat-files:
|
||||
get:
|
||||
description: API for getting all AiChatFiles
|
||||
parameters:
|
||||
- in: query
|
||||
name: fileName
|
||||
type: string
|
||||
- in: query
|
||||
name: isPublish
|
||||
type: boolean
|
||||
- in: query
|
||||
name: messageId
|
||||
type: integer
|
||||
- in: query
|
||||
name: statusId
|
||||
type: integer
|
||||
- in: query
|
||||
name: count
|
||||
type: integer
|
||||
- in: query
|
||||
name: limit
|
||||
type: integer
|
||||
- in: query
|
||||
name: nextPage
|
||||
type: integer
|
||||
- in: query
|
||||
name: page
|
||||
type: integer
|
||||
- in: query
|
||||
name: previousPage
|
||||
type: integer
|
||||
- in: query
|
||||
name: sort
|
||||
type: string
|
||||
- in: query
|
||||
name: sortBy
|
||||
type: string
|
||||
- in: query
|
||||
name: totalPage
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get all AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/ai-chat-files/{id}:
|
||||
get:
|
||||
description: API for getting one AiChatFiles
|
||||
parameters:
|
||||
- description: AiChatFiles ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get one AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/ai-chat-files/{messageId}:
|
||||
post:
|
||||
description: API for create AiChatFiles
|
||||
parameters:
|
||||
- description: Insert the X-Csrf-Token
|
||||
in: header
|
||||
name: X-Csrf-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: Upload file
|
||||
in: formData
|
||||
name: files
|
||||
required: true
|
||||
type: file
|
||||
- description: AiChat ID
|
||||
in: path
|
||||
name: aiChatId
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Upload AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/ai-chat/logs:
|
||||
get:
|
||||
description: API for getting all AI chat logs for authenticated user
|
||||
|
|
@ -2424,6 +2595,145 @@ paths:
|
|||
summary: Update AI chat message
|
||||
tags:
|
||||
- AI Chat
|
||||
/aiChat-files/{id}:
|
||||
delete:
|
||||
description: API for delete AiChatFiles
|
||||
parameters:
|
||||
- description: Insert the X-Csrf-Token
|
||||
in: header
|
||||
name: X-Csrf-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: AiChatFiles ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Delete AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
put:
|
||||
description: API for update AiChatFiles
|
||||
parameters:
|
||||
- description: Insert the X-Csrf-Token
|
||||
in: header
|
||||
name: X-Csrf-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: Required payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.AiChatFilesUpdateRequest'
|
||||
- description: AiChatFiles ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/aiChat-files/upload-status/{uploadId}:
|
||||
get:
|
||||
description: API for GetUploadStatus AiChatFiles
|
||||
parameters:
|
||||
- description: Upload ID of AiChatFiles
|
||||
in: path
|
||||
name: uploadId
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: GetUploadStatus AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/aiChat-files/viewer/{filename}:
|
||||
get:
|
||||
description: API for Viewer AiChatFiles
|
||||
parameters:
|
||||
- description: AiChat File Name
|
||||
in: path
|
||||
name: filename
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/response.Response'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.BadRequestError'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/response.UnauthorizedError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.InternalServerError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Viewer AiChatFiles
|
||||
tags:
|
||||
- AiChat Files
|
||||
/article-approvals:
|
||||
get:
|
||||
description: API for getting all ArticleApprovals
|
||||
|
|
|
|||
2
main.go
2
main.go
|
|
@ -6,6 +6,7 @@ import (
|
|||
"narasi-ahli-be/app/module/activity_logs"
|
||||
"narasi-ahli-be/app/module/advertisement"
|
||||
"narasi-ahli-be/app/module/ai_chat"
|
||||
"narasi-ahli-be/app/module/ai_chat_files"
|
||||
"narasi-ahli-be/app/module/article_approvals"
|
||||
"narasi-ahli-be/app/module/article_categories"
|
||||
"narasi-ahli-be/app/module/article_category_details"
|
||||
|
|
@ -95,6 +96,7 @@ func main() {
|
|||
education_history.NewEducationHistoryModule,
|
||||
work_history.NewWorkHistoryModule,
|
||||
research_journals.NewResearchJournalsModule,
|
||||
ai_chat_files.NewAiChatFilesModule,
|
||||
|
||||
// start aplication
|
||||
fx.Invoke(webserver.Start),
|
||||
|
|
|
|||
Loading…
Reference in New Issue