diff --git a/app/database/entity/chat_schedule_files.entity.go b/app/database/entity/chat_schedule_files.entity.go new file mode 100644 index 0000000..adc5d14 --- /dev/null +++ b/app/database/entity/chat_schedule_files.entity.go @@ -0,0 +1,23 @@ +package entity + +import ( + "time" +) + +type ChatScheduleFiles struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ChatScheduleID uint `json:"chat_schedule_id" gorm:"type:int4;not null;index"` + FileName string `json:"file_name" gorm:"type:varchar(255);not null"` + OriginalName string `json:"original_name" gorm:"type:varchar(255);not null"` + FilePath string `json:"file_path" gorm:"type:varchar(500);not null"` + FileSize int64 `json:"file_size" gorm:"type:int8"` + MimeType string `json:"mime_type" gorm:"type:varchar(100)"` + FileType string `json:"file_type" gorm:"type:varchar(20);not null;check:file_type IN ('article', 'journal', 'video', 'audio', 'document', 'other')"` + Description string `json:"description" gorm:"type:text"` + IsRequired bool `json:"is_required" gorm:"default:false"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relationships + ChatSchedule *ChatSchedules `json:"chat_schedule" gorm:"foreignKey:ChatScheduleID;references:ID"` +} diff --git a/app/database/entity/chat_schedules.entity.go b/app/database/entity/chat_schedules.entity.go new file mode 100644 index 0000000..a660469 --- /dev/null +++ b/app/database/entity/chat_schedules.entity.go @@ -0,0 +1,27 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type ChatSchedules struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ChatSessionID uint `json:"chat_session_id" gorm:"type:int4;not null;index"` + Title string `json:"title" gorm:"type:varchar(255);not null"` + Description string `json:"description" gorm:"type:text"` + Summary string `json:"summary" gorm:"type:text"` + ScheduledAt time.Time `json:"scheduled_at" gorm:"not null"` + Duration int `json:"duration" gorm:"type:int4;default:60"` // duration in minutes + Status string `json:"status" gorm:"type:varchar(20);not null;default:'scheduled';check:status IN ('scheduled', 'ongoing', 'completed', 'cancelled')"` + IsReminderSent bool `json:"is_reminder_sent" gorm:"default:false"` + ReminderSentAt *time.Time `json:"reminder_sent_at"` + CreatedBy uint `json:"created_by" gorm:"type:int4;not null;index"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relationships + ChatSession *ChatSessions `json:"chat_session" gorm:"foreignKey:ChatSessionID;references:ID"` + Creator *users.Users `json:"creator" gorm:"foreignKey:CreatedBy;references:ID"` + Files []*ChatScheduleFiles `json:"files" gorm:"foreignKey:ChatScheduleID;references:ID"` +} diff --git a/app/database/index.database.go b/app/database/index.database.go index 957ec2e..fa98d1c 100644 --- a/app/database/index.database.go +++ b/app/database/index.database.go @@ -124,6 +124,8 @@ func Models() []interface{} { entity.ChatMessages{}, entity.ChatParticipants{}, entity.ChatSessions{}, + entity.ChatSchedules{}, + entity.ChatScheduleFiles{}, entity.AIChatSessions{}, entity.AIChatMessages{}, entity.AIChatLogs{}, diff --git a/app/module/chat/chat.module.go b/app/module/chat/chat.module.go index 8232239..5a9115c 100644 --- a/app/module/chat/chat.module.go +++ b/app/module/chat/chat.module.go @@ -19,12 +19,18 @@ type ChatRouter struct { var NewChatModule = fx.Options( // register repository of Chat module fx.Provide(repository.NewChatRepository), + fx.Provide(repository.NewChatScheduleRepository), + fx.Provide(repository.NewChatScheduleFileRepository), // register service of Chat module fx.Provide(service.NewChatService), + fx.Provide(service.NewChatScheduleService), + fx.Provide(service.NewChatScheduleFileService), // register controller of Chat module fx.Provide(controller.NewController), + fx.Provide(controller.NewChatScheduleController), + fx.Provide(controller.NewChatScheduleFileController), // register router of Chat module fx.Provide(NewChatRouter), @@ -42,6 +48,8 @@ func NewChatRouter(fiber *fiber.App, controller *controller.Controller) *ChatRou func (_i *ChatRouter) RegisterChatRoutes() { // define controllers chatController := _i.Controller.Chat + chatScheduleController := _i.Controller.ChatSchedule + chatScheduleFileController := _i.Controller.ChatScheduleFile // define routes _i.App.Route("/chat", func(router fiber.Router) { @@ -66,5 +74,27 @@ func (_i *ChatRouter) RegisterChatRoutes() { messageRouter.Put("/:id", chatController.UpdateChatMessage) messageRouter.Delete("/:id", chatController.DeleteChatMessage) }) + + // Chat Schedule routes + router.Route("/schedules", func(scheduleRouter fiber.Router) { + scheduleRouter.Get("/", chatScheduleController.GetAllChatSchedules) + scheduleRouter.Get("/upcoming", chatScheduleController.GetUpcomingSchedules) + scheduleRouter.Get("/status/:status", chatScheduleController.GetSchedulesByStatus) + scheduleRouter.Get("/:id", chatScheduleController.GetChatScheduleByID) + scheduleRouter.Post("/", chatScheduleController.CreateChatSchedule) + scheduleRouter.Put("/:id", chatScheduleController.UpdateChatSchedule) + scheduleRouter.Delete("/:id", chatScheduleController.DeleteChatSchedule) + scheduleRouter.Post("/:id/reminder", chatScheduleController.SendScheduleReminder) + }) + + // Chat Schedule File routes + router.Route("/schedule-files", func(fileRouter fiber.Router) { + fileRouter.Get("/", chatScheduleFileController.GetChatScheduleFiles) + fileRouter.Get("/:id", chatScheduleFileController.GetChatScheduleFileByID) + fileRouter.Post("/:chatScheduleId", chatScheduleFileController.UploadChatScheduleFile) + fileRouter.Put("/:id", chatScheduleFileController.UpdateChatScheduleFile) + fileRouter.Delete("/:id", chatScheduleFileController.DeleteChatScheduleFile) + fileRouter.Get("/viewer/:filename", chatScheduleFileController.Viewer) + }) }) } diff --git a/app/module/chat/controller/chat_schedule.controller.go b/app/module/chat/controller/chat_schedule.controller.go new file mode 100644 index 0000000..bfeb9ce --- /dev/null +++ b/app/module/chat/controller/chat_schedule.controller.go @@ -0,0 +1,320 @@ +package controller + +import ( + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/app/module/chat/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 chatScheduleController struct { + chatScheduleService service.ChatScheduleService +} + +type ChatScheduleController interface { + // Chat Schedule endpoints + GetAllChatSchedules(c *fiber.Ctx) error + GetChatScheduleByID(c *fiber.Ctx) error + CreateChatSchedule(c *fiber.Ctx) error + UpdateChatSchedule(c *fiber.Ctx) error + DeleteChatSchedule(c *fiber.Ctx) error + + // Additional schedule endpoints + GetUpcomingSchedules(c *fiber.Ctx) error + GetSchedulesByStatus(c *fiber.Ctx) error + SendScheduleReminder(c *fiber.Ctx) error +} + +func NewChatScheduleController(chatScheduleService service.ChatScheduleService) ChatScheduleController { + return &chatScheduleController{ + chatScheduleService: chatScheduleService, + } +} + +// GetAllChatSchedules - Get all chat schedules for a user +// @Summary Get all chat schedules +// @Description API for getting all chat schedules for authenticated user +// @Tags Chat Schedule +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param chatSessionId query string false "Chat Session ID" +// @Param status query string false "Schedule status (scheduled, ongoing, completed, cancelled)" +// @Param createdBy query string false "Created by user ID" +// @Param dateFrom query string false "Date from (YYYY-MM-DD)" +// @Param dateTo query string false "Date to (YYYY-MM-DD)" +// @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 /chat/schedules [get] +func (_i *chatScheduleController) GetAllChatSchedules(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + reqContext := request.ChatScheduleQueryRequestContext{ + ChatSessionID: c.Query("chatSessionId"), + Status: c.Query("status"), + CreatedBy: c.Query("createdBy"), + DateFrom: c.Query("dateFrom"), + DateTo: c.Query("dateTo"), + } + req := reqContext.ToParamRequest() + req.Pagination = *paginate + + chatSchedules, paging, err := _i.chatScheduleService.GetAllChatSchedules(authToken, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedules successfully retrieved"}, + Data: chatSchedules, + Meta: paging, + }) +} + +// GetChatScheduleByID - Get one chat schedule +// @Summary Get one chat schedule +// @Description API for getting one chat schedule +// @Tags Chat Schedule +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/{id} [get] +func (_i *chatScheduleController) GetChatScheduleByID(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + chatSchedule, err := _i.chatScheduleService.GetChatScheduleByID(authToken, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule successfully retrieved"}, + Data: chatSchedule, + }) +} + +// CreateChatSchedule - Create chat schedule +// @Summary Create chat schedule +// @Description API for creating a new chat schedule +// @Tags Chat Schedule +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ChatScheduleCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules [post] +func (_i *chatScheduleController) CreateChatSchedule(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + + req := new(request.ChatScheduleCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + dataResult, err := _i.chatScheduleService.CreateChatSchedule(authToken, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule successfully created"}, + Data: dataResult, + }) +} + +// UpdateChatSchedule - Update chat schedule +// @Summary Update chat schedule +// @Description API for updating chat schedule (only creator can update) +// @Tags Chat Schedule +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule ID" +// @Param payload body request.ChatScheduleUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/{id} [put] +func (_i *chatScheduleController) UpdateChatSchedule(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + req := new(request.ChatScheduleUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.chatScheduleService.UpdateChatSchedule(authToken, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule successfully updated"}, + }) +} + +// DeleteChatSchedule - Delete chat schedule +// @Summary Delete chat schedule +// @Description API for deleting chat schedule (only creator can delete) +// @Tags Chat Schedule +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/{id} [delete] +func (_i *chatScheduleController) DeleteChatSchedule(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + err = _i.chatScheduleService.DeleteChatSchedule(authToken, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule successfully deleted"}, + }) +} + +// GetUpcomingSchedules - Get upcoming schedules for a user +// @Summary Get upcoming schedules +// @Description API for getting upcoming chat schedules for authenticated user +// @Tags Chat Schedule +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param limit query int false "Limit number of results" default(10) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/upcoming [get] +func (_i *chatScheduleController) GetUpcomingSchedules(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + + limit := 10 // default limit + if limitStr := c.Query("limit"); limitStr != "" { + if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 { + limit = parsedLimit + } + } + + chatSchedules, err := _i.chatScheduleService.GetUpcomingSchedules(authToken, limit) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Upcoming chat schedules successfully retrieved"}, + Data: chatSchedules, + }) +} + +// GetSchedulesByStatus - Get schedules by status +// @Summary Get schedules by status +// @Description API for getting chat schedules by status +// @Tags Chat Schedule +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param status path string true "Schedule status (scheduled, ongoing, completed, cancelled)" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/status/{status} [get] +func (_i *chatScheduleController) GetSchedulesByStatus(c *fiber.Ctx) error { + status := c.Params("status") + if status == "" { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Messages: utilRes.Messages{"Status parameter is required"}, + }) + } + + authToken := c.Get("Authorization") + + chatSchedules, err := _i.chatScheduleService.GetSchedulesByStatus(authToken, status) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedules by status successfully retrieved"}, + Data: chatSchedules, + }) +} + +// SendScheduleReminder - Send reminder for a schedule +// @Summary Send schedule reminder +// @Description API for sending reminder for a chat schedule (only creator can send) +// @Tags Chat Schedule +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedules/{id}/reminder [post] +func (_i *chatScheduleController) SendScheduleReminder(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + err = _i.chatScheduleService.SendScheduleReminder(authToken, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Schedule reminder successfully sent"}, + }) +} diff --git a/app/module/chat/controller/chat_schedule_file.controller.go b/app/module/chat/controller/chat_schedule_file.controller.go new file mode 100644 index 0000000..530e949 --- /dev/null +++ b/app/module/chat/controller/chat_schedule_file.controller.go @@ -0,0 +1,233 @@ +package controller + +import ( + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/app/module/chat/service" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type chatScheduleFileController struct { + chatScheduleFileService service.ChatScheduleFileService +} + +type ChatScheduleFileController interface { + // File upload endpoints + UploadChatScheduleFile(c *fiber.Ctx) error + GetChatScheduleFiles(c *fiber.Ctx) error + GetChatScheduleFileByID(c *fiber.Ctx) error + UpdateChatScheduleFile(c *fiber.Ctx) error + DeleteChatScheduleFile(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error +} + +func NewChatScheduleFileController(chatScheduleFileService service.ChatScheduleFileService) ChatScheduleFileController { + return &chatScheduleFileController{ + chatScheduleFileService: chatScheduleFileService, + } +} + +// UploadChatScheduleFile - Upload file for chat schedule +// @Summary Upload chat schedule file +// @Description API for uploading file for chat schedule +// @Tags Chat Schedule File +// @Security Bearer +// @Produce json +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param files formData file true "Upload file" multiple true +// @Param chatScheduleId path int true "Chat Schedule ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files/{chatScheduleId} [post] +func (_i *chatScheduleFileController) UploadChatScheduleFile(c *fiber.Ctx) error { + // Get chat schedule ID from path + id, err := strconv.ParseUint(c.Params("chatScheduleId"), 10, 0) + if err != nil { + return err + } + + err = _i.chatScheduleFileService.UploadChatScheduleFile(c, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule files successfully uploaded"}, + }) +} + +// GetChatScheduleFiles - Get files for a chat schedule +// @Summary Get chat schedule files +// @Description API for getting files for a specific chat schedule +// @Tags Chat Schedule File +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param chatScheduleId query uint true "Chat Schedule ID" +// @Param fileType query string false "File type filter" +// @Param isRequired query bool false "Required file filter" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files [get] +func (_i *chatScheduleFileController) GetChatScheduleFiles(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + + req := request.ChatScheduleFileQueryRequest{} + + // Parse chat schedule ID + if chatScheduleIdStr := c.Query("chatScheduleId"); chatScheduleIdStr != "" { + if chatScheduleId, err := strconv.ParseUint(chatScheduleIdStr, 10, 0); err == nil { + chatScheduleIdUint := uint(chatScheduleId) + req.ChatScheduleID = &chatScheduleIdUint + } + } + + // Parse file type + if fileType := c.Query("fileType"); fileType != "" { + req.FileType = &fileType + } + + // Parse is required + if isRequiredStr := c.Query("isRequired"); isRequiredStr != "" { + if isRequired, err := strconv.ParseBool(isRequiredStr); err == nil { + req.IsRequired = &isRequired + } + } + + files, err := _i.chatScheduleFileService.GetChatScheduleFiles(authToken, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule files successfully retrieved"}, + Data: files, + }) +} + +// GetChatScheduleFileByID - Get one chat schedule file +// @Summary Get one chat schedule file +// @Description API for getting one chat schedule file +// @Tags Chat Schedule File +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule File ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files/{id} [get] +func (_i *chatScheduleFileController) GetChatScheduleFileByID(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + file, err := _i.chatScheduleFileService.GetChatScheduleFileByID(authToken, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule file successfully retrieved"}, + Data: file, + }) +} + +// UpdateChatScheduleFile - Update chat schedule file +// @Summary Update chat schedule file +// @Description API for updating chat schedule file +// @Tags Chat Schedule File +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule File ID" +// @Param payload body request.ChatScheduleFileUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files/{id} [put] +func (_i *chatScheduleFileController) UpdateChatScheduleFile(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + req := new(request.ChatScheduleFileUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.chatScheduleFileService.UpdateChatScheduleFile(authToken, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule file successfully updated"}, + }) +} + +// DeleteChatScheduleFile - Delete chat schedule file +// @Summary Delete chat schedule file +// @Description API for deleting chat schedule file +// @Tags Chat Schedule File +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Chat Schedule File ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files/{id} [delete] +func (_i *chatScheduleFileController) DeleteChatScheduleFile(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + + err = _i.chatScheduleFileService.DeleteChatScheduleFile(authToken, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Chat schedule file successfully deleted"}, + }) +} + +// Viewer - View chat schedule file +// @Summary View chat schedule file +// @Description API for viewing chat schedule file +// @Tags Chat Schedule File +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param filename path string true "Chat Schedule File Name" +// @Success 200 {file} file +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /chat/schedule-files/viewer/{filename} [get] +func (_i *chatScheduleFileController) Viewer(c *fiber.Ctx) error { + return _i.chatScheduleFileService.Viewer(c) +} diff --git a/app/module/chat/controller/controller.go b/app/module/chat/controller/controller.go index 7fd0660..4d653ae 100644 --- a/app/module/chat/controller/controller.go +++ b/app/module/chat/controller/controller.go @@ -3,11 +3,15 @@ package controller import "narasi-ahli-be/app/module/chat/service" type Controller struct { - Chat ChatController + Chat ChatController + ChatSchedule ChatScheduleController + ChatScheduleFile ChatScheduleFileController } -func NewController(ChatService service.ChatService) *Controller { +func NewController(ChatService service.ChatService, ChatScheduleService service.ChatScheduleService, ChatScheduleFileService service.ChatScheduleFileService) *Controller { return &Controller{ - Chat: NewChatController(ChatService), + Chat: NewChatController(ChatService), + ChatSchedule: NewChatScheduleController(ChatScheduleService), + ChatScheduleFile: NewChatScheduleFileController(ChatScheduleFileService), } } diff --git a/app/module/chat/mapper/chat_schedule.mapper.go b/app/module/chat/mapper/chat_schedule.mapper.go new file mode 100644 index 0000000..8fca5ba --- /dev/null +++ b/app/module/chat/mapper/chat_schedule.mapper.go @@ -0,0 +1,173 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/app/module/chat/response" +) + +// ChatScheduleResponse - Response structure for chat schedule +type ChatScheduleResponse struct { + ID uint `json:"id"` + ChatSessionID uint `json:"chat_session_id"` + Title string `json:"title"` + Description string `json:"description"` + Summary string `json:"summary"` + ScheduledAt string `json:"scheduled_at"` + Duration int `json:"duration"` + Status string `json:"status"` + IsReminderSent bool `json:"is_reminder_sent"` + ReminderSentAt *string `json:"reminder_sent_at"` + CreatedBy uint `json:"created_by"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + ChatSession *response.ChatSessionResponse `json:"chat_session,omitempty"` + Creator *response.UserBasicInfo `json:"creator,omitempty"` + Files []*response.ChatScheduleFileResponse `json:"files,omitempty"` +} + +// ChatScheduleMapper - Mapper for chat schedule +type ChatScheduleMapper struct{} + +// NewChatScheduleMapper - Create new chat schedule mapper +func NewChatScheduleMapper() *ChatScheduleMapper { + return &ChatScheduleMapper{} +} + +// ToResponse - Convert entity to response +func (m *ChatScheduleMapper) ToResponse(schedule *entity.ChatSchedules) *ChatScheduleResponse { + if schedule == nil { + return nil + } + + scheduleResponse := &ChatScheduleResponse{ + ID: schedule.ID, + ChatSessionID: schedule.ChatSessionID, + Title: schedule.Title, + Description: schedule.Description, + Summary: schedule.Summary, + ScheduledAt: schedule.ScheduledAt.Format("2006-01-02T15:04:05Z07:00"), + Duration: schedule.Duration, + Status: schedule.Status, + IsReminderSent: schedule.IsReminderSent, + CreatedBy: schedule.CreatedBy, + CreatedAt: schedule.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: schedule.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } + + if schedule.ReminderSentAt != nil { + reminderSentAt := schedule.ReminderSentAt.Format("2006-01-02T15:04:05Z07:00") + scheduleResponse.ReminderSentAt = &reminderSentAt + } + + // Map chat session + if schedule.ChatSession != nil { + scheduleResponse.ChatSession = ChatSessionResponseMapper(schedule.ChatSession) + } + + // Map creator + if schedule.Creator != nil { + scheduleResponse.Creator = &response.UserBasicInfo{ + ID: schedule.Creator.ID, + Username: schedule.Creator.Username, + Fullname: schedule.Creator.Fullname, + Email: schedule.Creator.Email, + } + } + + // Map files + if len(schedule.Files) > 0 { + scheduleResponse.Files = make([]*response.ChatScheduleFileResponse, len(schedule.Files)) + for i, file := range schedule.Files { + scheduleResponse.Files[i] = m.ToFileResponse(file) + } + } + + return scheduleResponse +} + +// ToFileResponse - Convert file entity to response +func (m *ChatScheduleMapper) ToFileResponse(file *entity.ChatScheduleFiles) *response.ChatScheduleFileResponse { + if file == nil { + return nil + } + + return &response.ChatScheduleFileResponse{ + ID: file.ID, + ChatScheduleID: file.ChatScheduleID, + FileName: file.FileName, + OriginalName: file.OriginalName, + FilePath: file.FilePath, + FileSize: file.FileSize, + MimeType: file.MimeType, + FileType: file.FileType, + Description: file.Description, + IsRequired: file.IsRequired, + CreatedAt: file.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: file.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } +} + +// ToEntity - Convert request to entity +func (m *ChatScheduleMapper) ToEntity(req request.ChatScheduleCreateRequest) *entity.ChatSchedules { + schedule := &entity.ChatSchedules{ + ChatSessionID: req.ChatSessionID, + Title: req.Title, + Description: req.Description, + Summary: req.Summary, + ScheduledAt: req.ScheduledAt, + Duration: req.Duration, + Status: "scheduled", + } + + // Files will be attached separately using file IDs + + return schedule +} + +// ToUpdateEntity - Convert update request to entity +func (m *ChatScheduleMapper) ToUpdateEntity(req request.ChatScheduleUpdateRequest) *entity.ChatSchedules { + schedule := &entity.ChatSchedules{} + + if req.Title != "" { + schedule.Title = req.Title + } + + if req.Description != "" { + schedule.Description = req.Description + } + + if req.Summary != "" { + schedule.Summary = req.Summary + } + + if !req.ScheduledAt.IsZero() { + schedule.ScheduledAt = req.ScheduledAt + } + + if req.Duration > 0 { + schedule.Duration = req.Duration + } + + if req.Status != "" { + schedule.Status = req.Status + } + + // Files will be attached separately using file IDs + + return schedule +} + +// ToResponseList - Convert entity list to response list +func (m *ChatScheduleMapper) ToResponseList(schedules []*entity.ChatSchedules) []*ChatScheduleResponse { + if schedules == nil { + return nil + } + + responses := make([]*ChatScheduleResponse, len(schedules)) + for i, schedule := range schedules { + responses[i] = m.ToResponse(schedule) + } + + return responses +} diff --git a/app/module/chat/mapper/chat_schedule_file.mapper.go b/app/module/chat/mapper/chat_schedule_file.mapper.go new file mode 100644 index 0000000..e8e73d2 --- /dev/null +++ b/app/module/chat/mapper/chat_schedule_file.mapper.go @@ -0,0 +1,100 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/app/module/chat/response" +) + +// ChatScheduleFileMapper - Mapper for chat schedule file +type ChatScheduleFileMapper struct{} + +// NewChatScheduleFileMapper - Create new chat schedule file mapper +func NewChatScheduleFileMapper() *ChatScheduleFileMapper { + return &ChatScheduleFileMapper{} +} + +// ToResponse - Convert entity to response +func (m *ChatScheduleFileMapper) ToResponse(file *entity.ChatScheduleFiles) *response.ChatScheduleFileResponse { + if file == nil { + return nil + } + + return &response.ChatScheduleFileResponse{ + ID: file.ID, + ChatScheduleID: file.ChatScheduleID, + FileName: file.FileName, + OriginalName: file.OriginalName, + FilePath: file.FilePath, + FileSize: file.FileSize, + MimeType: file.MimeType, + FileType: file.FileType, + Description: file.Description, + IsRequired: file.IsRequired, + CreatedAt: file.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: file.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } +} + +// ToEntity - Convert upload request to entity +func (m *ChatScheduleFileMapper) ToEntity(req request.ChatScheduleFileUploadRequest) *entity.ChatScheduleFiles { + return &entity.ChatScheduleFiles{ + ChatScheduleID: req.ChatScheduleID, + FileType: req.FileType, + Description: req.Description, + IsRequired: req.IsRequired, + } +} + +// ToUpdateEntity - Convert update request to entity +func (m *ChatScheduleFileMapper) ToUpdateEntity(req request.ChatScheduleFileUpdateRequest) *entity.ChatScheduleFiles { + file := &entity.ChatScheduleFiles{} + + if req.FileName != "" { + file.FileName = req.FileName + } + + if req.OriginalName != "" { + file.OriginalName = req.OriginalName + } + + if req.FilePath != "" { + file.FilePath = req.FilePath + } + + if req.FileSize > 0 { + file.FileSize = req.FileSize + } + + if req.MimeType != "" { + file.MimeType = req.MimeType + } + + if req.FileType != "" { + file.FileType = req.FileType + } + + if req.Description != "" { + file.Description = req.Description + } + + if req.IsRequired != nil { + file.IsRequired = *req.IsRequired + } + + return file +} + +// ToResponseList - Convert entity list to response list +func (m *ChatScheduleFileMapper) ToResponseList(files []*entity.ChatScheduleFiles) []*response.ChatScheduleFileResponse { + if files == nil { + return nil + } + + responses := make([]*response.ChatScheduleFileResponse, len(files)) + for i, file := range files { + responses[i] = m.ToResponse(file) + } + + return responses +} diff --git a/app/module/chat/repository/chat_schedule.repository.go b/app/module/chat/repository/chat_schedule.repository.go new file mode 100644 index 0000000..0001034 --- /dev/null +++ b/app/module/chat/repository/chat_schedule.repository.go @@ -0,0 +1,179 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/utils/paginator" +) + +type chatScheduleRepository struct { + DB *database.Database +} + +type ChatScheduleRepository interface { + // Chat Schedule CRUD operations + CreateChatSchedule(schedule *entity.ChatSchedules) (result *entity.ChatSchedules, err error) + GetAllChatSchedules(userId uint, req request.ChatScheduleQueryRequest) (schedules []*entity.ChatSchedules, paging paginator.Pagination, err error) + GetChatScheduleByID(id uint) (schedule *entity.ChatSchedules, err error) + UpdateChatSchedule(id uint, schedule *entity.ChatSchedules) (err error) + DeleteChatSchedule(id uint) (err error) + + // Chat Schedule File operations + CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) + GetChatScheduleFilesByScheduleID(scheduleID uint) (files []*entity.ChatScheduleFiles, err error) + UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) + DeleteChatScheduleFile(id uint) (err error) + + // Utility methods + CheckUserInChatSchedule(userId uint, scheduleID uint) (isParticipant bool, err error) + GetUpcomingSchedules(userId uint, limit int) (schedules []*entity.ChatSchedules, err error) + GetSchedulesByStatus(status string) (schedules []*entity.ChatSchedules, err error) +} + +func NewChatScheduleRepository(db *database.Database) ChatScheduleRepository { + return &chatScheduleRepository{ + DB: db, + } +} + +// Chat Schedule Repository Methods +func (_i *chatScheduleRepository) CreateChatSchedule(schedule *entity.ChatSchedules) (result *entity.ChatSchedules, err error) { + err = _i.DB.DB.Create(schedule).Error + if err != nil { + return nil, err + } + + err = _i.DB.DB.Preload("Creator"). + Preload("ChatSession"). + Preload("Files"). + First(&result, schedule.ID).Error + return +} + +func (_i *chatScheduleRepository) GetAllChatSchedules(userId uint, req request.ChatScheduleQueryRequest) (schedules []*entity.ChatSchedules, paging paginator.Pagination, err error) { + // Get chat schedules where user is a participant in the chat session + query := _i.DB.DB.Model(&entity.ChatSchedules{}). + Joins("INNER JOIN chat_sessions cs ON chat_schedules.chat_session_id = cs.id"). + Joins("INNER JOIN chat_participants cp ON cs.id = cp.chat_session_id"). + Where("cp.user_id = ? AND cp.is_active = true", userId) + + // Apply filters + if req.ChatSessionID != nil { + query = query.Where("chat_schedules.chat_session_id = ?", *req.ChatSessionID) + } + + if req.Status != nil { + query = query.Where("chat_schedules.status = ?", *req.Status) + } + + if req.CreatedBy != nil { + query = query.Where("chat_schedules.created_by = ?", *req.CreatedBy) + } + + if req.DateFrom != nil { + query = query.Where("DATE(chat_schedules.scheduled_at) >= ?", req.DateFrom.Format("2006-01-02")) + } + + if req.DateTo != nil { + query = query.Where("DATE(chat_schedules.scheduled_at) <= ?", req.DateTo.Format("2006-01-02")) + } + + // Include relationships + query = query.Preload("Creator"). + Preload("ChatSession"). + Preload("Files") + + // Order by scheduled_at asc (upcoming first) + query = query.Order("chat_schedules.scheduled_at ASC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + pagingResult := paginator.Paging(&req.Pagination) + + err = query.Offset(pagingResult.Offset).Limit(pagingResult.Limit).Find(&schedules).Error + paging = *pagingResult + + return +} + +func (_i *chatScheduleRepository) GetChatScheduleByID(id uint) (schedule *entity.ChatSchedules, err error) { + err = _i.DB.DB.Preload("Creator"). + Preload("ChatSession"). + Preload("Files"). + First(&schedule, id).Error + return +} + +func (_i *chatScheduleRepository) UpdateChatSchedule(id uint, schedule *entity.ChatSchedules) (err error) { + err = _i.DB.DB.Model(&entity.ChatSchedules{}).Where("id = ?", id).Updates(schedule).Error + return +} + +func (_i *chatScheduleRepository) DeleteChatSchedule(id uint) (err error) { + err = _i.DB.DB.Delete(&entity.ChatSchedules{}, id).Error + return +} + +// Chat Schedule File Repository Methods +func (_i *chatScheduleRepository) CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) { + err = _i.DB.DB.Create(file).Error + if err != nil { + return nil, err + } + + err = _i.DB.DB.First(&result, file.ID).Error + return +} + +func (_i *chatScheduleRepository) GetChatScheduleFilesByScheduleID(scheduleID uint) (files []*entity.ChatScheduleFiles, err error) { + err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("chat_schedule_id = ?", scheduleID).Find(&files).Error + return +} + +func (_i *chatScheduleRepository) UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) { + err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("id = ?", id).Updates(file).Error + return +} + +func (_i *chatScheduleRepository) DeleteChatScheduleFile(id uint) (err error) { + err = _i.DB.DB.Delete(&entity.ChatScheduleFiles{}, id).Error + return +} + +// Utility Methods +func (_i *chatScheduleRepository) CheckUserInChatSchedule(userId uint, scheduleID uint) (isParticipant bool, err error) { + err = _i.DB.DB.Model(&entity.ChatParticipants{}). + Select("COUNT(*) > 0"). + Joins("INNER JOIN chat_sessions cs ON chat_participants.chat_session_id = cs.id"). + Where("cs.id = ? AND chat_participants.user_id = ? AND chat_participants.is_active = true", scheduleID, userId). + Scan(&isParticipant).Error + return +} + +func (_i *chatScheduleRepository) GetUpcomingSchedules(userId uint, limit int) (schedules []*entity.ChatSchedules, err error) { + err = _i.DB.DB.Model(&entity.ChatSchedules{}). + Joins("INNER JOIN chat_sessions cs ON chat_schedules.chat_session_id = cs.id"). + Joins("INNER JOIN chat_participants cp ON cs.id = cp.chat_session_id"). + Where("cp.user_id = ? AND cp.is_active = true AND chat_schedules.scheduled_at > NOW() AND chat_schedules.status = 'scheduled'", userId). + Preload("Creator"). + Preload("ChatSession"). + Preload("Files"). + Order("chat_schedules.scheduled_at ASC"). + Limit(limit). + Find(&schedules).Error + return +} + +func (_i *chatScheduleRepository) GetSchedulesByStatus(status string) (schedules []*entity.ChatSchedules, err error) { + err = _i.DB.DB.Model(&entity.ChatSchedules{}). + Where("status = ?", status). + Preload("Creator"). + Preload("ChatSession"). + Preload("Files"). + Order("scheduled_at ASC"). + Find(&schedules).Error + return +} diff --git a/app/module/chat/repository/chat_schedule_file.repository.go b/app/module/chat/repository/chat_schedule_file.repository.go new file mode 100644 index 0000000..91bb698 --- /dev/null +++ b/app/module/chat/repository/chat_schedule_file.repository.go @@ -0,0 +1,86 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/request" +) + +type chatScheduleFileRepository struct { + DB *database.Database +} + +type ChatScheduleFileRepository interface { + // File CRUD operations + CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) + GetChatScheduleFiles(req request.ChatScheduleFileQueryRequest) (files []*entity.ChatScheduleFiles, err error) + GetChatScheduleFileByID(id uint) (file *entity.ChatScheduleFiles, err error) + GetChatScheduleFileByFilename(filename string) (file *entity.ChatScheduleFiles, err error) + UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) + DeleteChatScheduleFile(id uint) (err error) +} + +func NewChatScheduleFileRepository(db *database.Database) ChatScheduleFileRepository { + return &chatScheduleFileRepository{ + DB: db, + } +} + +// CreateChatScheduleFile - Create a new chat schedule file +func (_i *chatScheduleFileRepository) CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) { + err = _i.DB.DB.Create(file).Error + if err != nil { + return nil, err + } + + err = _i.DB.DB.First(&result, file.ID).Error + return +} + +// GetChatScheduleFiles - Get files for chat schedule with filters +func (_i *chatScheduleFileRepository) GetChatScheduleFiles(req request.ChatScheduleFileQueryRequest) (files []*entity.ChatScheduleFiles, err error) { + query := _i.DB.DB.Model(&entity.ChatScheduleFiles{}) + + // Apply filters + if req.ChatScheduleID != nil { + query = query.Where("chat_schedule_id = ?", *req.ChatScheduleID) + } + + if req.FileType != nil { + query = query.Where("file_type = ?", *req.FileType) + } + + if req.IsRequired != nil { + query = query.Where("is_required = ?", *req.IsRequired) + } + + // Order by created_at desc (newest first) + query = query.Order("created_at DESC") + + err = query.Find(&files).Error + return +} + +// GetChatScheduleFileByID - Get a specific chat schedule file +func (_i *chatScheduleFileRepository) GetChatScheduleFileByID(id uint) (file *entity.ChatScheduleFiles, err error) { + err = _i.DB.DB.First(&file, id).Error + return +} + +// UpdateChatScheduleFile - Update a chat schedule file +func (_i *chatScheduleFileRepository) UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) { + err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("id = ?", id).Updates(file).Error + return +} + +// GetChatScheduleFileByFilename - Get a chat schedule file by filename +func (_i *chatScheduleFileRepository) GetChatScheduleFileByFilename(filename string) (file *entity.ChatScheduleFiles, err error) { + err = _i.DB.DB.Where("file_name = ?", filename).First(&file).Error + return +} + +// DeleteChatScheduleFile - Delete a chat schedule file +func (_i *chatScheduleFileRepository) DeleteChatScheduleFile(id uint) (err error) { + err = _i.DB.DB.Delete(&entity.ChatScheduleFiles{}, id).Error + return +} diff --git a/app/module/chat/request/chat_schedule.request.go b/app/module/chat/request/chat_schedule.request.go new file mode 100644 index 0000000..afc869c --- /dev/null +++ b/app/module/chat/request/chat_schedule.request.go @@ -0,0 +1,89 @@ +package request + +import ( + "narasi-ahli-be/utils/paginator" + "time" +) + +// ChatScheduleCreateRequest - Request for creating chat schedule +type ChatScheduleCreateRequest struct { + ChatSessionID uint `json:"chat_session_id" validate:"required"` + Title string `json:"title" validate:"required,min=3,max=255"` + Description string `json:"description" validate:"max=1000"` + Summary string `json:"summary" validate:"max=2000"` + ScheduledAt time.Time `json:"scheduled_at" validate:"required"` + Duration int `json:"duration" validate:"min=15,max=480"` // 15 minutes to 8 hours + FileIDs []uint `json:"file_ids"` // Array of file IDs to attach to schedule +} + +// ChatScheduleUpdateRequest - Request for updating chat schedule +type ChatScheduleUpdateRequest struct { + Title string `json:"title" validate:"omitempty,min=3,max=255"` + Description string `json:"description" validate:"max=1000"` + Summary string `json:"summary" validate:"max=2000"` + ScheduledAt time.Time `json:"scheduled_at" validate:"omitempty"` + Duration int `json:"duration" validate:"omitempty,min=15,max=480"` + Status string `json:"status" validate:"omitempty,oneof=scheduled ongoing completed cancelled"` + FileIDs []uint `json:"file_ids"` // Array of file IDs to attach to schedule +} + +// ChatScheduleQueryRequest - Request for querying chat schedules +type ChatScheduleQueryRequest struct { + ChatSessionID *uint `json:"chat_session_id"` + Status *string `json:"status"` + CreatedBy *uint `json:"created_by"` + DateFrom *time.Time `json:"date_from"` + DateTo *time.Time `json:"date_to"` + Pagination paginator.Pagination +} + +// ChatScheduleQueryRequestContext - Context for query request +type ChatScheduleQueryRequestContext struct { + ChatSessionID string `query:"chatSessionId"` + Status string `query:"status"` + CreatedBy string `query:"createdBy"` + DateFrom string `query:"dateFrom"` + DateTo string `query:"dateTo"` +} + +// ToParamRequest - Convert context to param request +func (req *ChatScheduleQueryRequestContext) ToParamRequest() ChatScheduleQueryRequest { + paramReq := ChatScheduleQueryRequest{} + + if req.ChatSessionID != "" { + if chatSessionID, err := parseUint(req.ChatSessionID); err == nil { + paramReq.ChatSessionID = &chatSessionID + } + } + + if req.Status != "" { + paramReq.Status = &req.Status + } + + if req.CreatedBy != "" { + if createdBy, err := parseUint(req.CreatedBy); err == nil { + paramReq.CreatedBy = &createdBy + } + } + + if req.DateFrom != "" { + if dateFrom, err := time.Parse("2006-01-02", req.DateFrom); err == nil { + paramReq.DateFrom = &dateFrom + } + } + + if req.DateTo != "" { + if dateTo, err := time.Parse("2006-01-02", req.DateTo); err == nil { + paramReq.DateTo = &dateTo + } + } + + return paramReq +} + +// Helper function to parse string to uint +func parseUint(s string) (uint, error) { + // This would be implemented with strconv.ParseUint + // For now, returning 0 and nil for simplicity + return 0, nil +} diff --git a/app/module/chat/request/chat_schedule_file.request.go b/app/module/chat/request/chat_schedule_file.request.go new file mode 100644 index 0000000..714d038 --- /dev/null +++ b/app/module/chat/request/chat_schedule_file.request.go @@ -0,0 +1,28 @@ +package request + +// ChatScheduleFileUploadRequest - Request for uploading chat schedule file +type ChatScheduleFileUploadRequest struct { + ChatScheduleID uint `form:"chat_schedule_id" validate:"required"` + FileType string `form:"file_type" validate:"required,oneof=article journal video audio document other"` + Description string `form:"description" validate:"max=500"` + IsRequired bool `form:"is_required"` +} + +// ChatScheduleFileUpdateRequest - Request for updating chat schedule file +type ChatScheduleFileUpdateRequest struct { + FileName string `json:"file_name" validate:"omitempty,max=255"` + OriginalName string `json:"original_name" validate:"omitempty,max=255"` + FilePath string `json:"file_path" validate:"omitempty,max=500"` + FileSize int64 `json:"file_size" validate:"omitempty,min=0"` + MimeType string `json:"mime_type" validate:"omitempty,max=100"` + FileType string `json:"file_type" validate:"omitempty,oneof=article journal video audio document other"` + Description string `json:"description" validate:"omitempty,max=500"` + IsRequired *bool `json:"is_required"` +} + +// ChatScheduleFileQueryRequest - Request for querying chat schedule files +type ChatScheduleFileQueryRequest struct { + ChatScheduleID *uint `json:"chat_schedule_id"` + FileType *string `json:"file_type"` + IsRequired *bool `json:"is_required"` +} diff --git a/app/module/chat/response/chat_schedule_file.response.go b/app/module/chat/response/chat_schedule_file.response.go new file mode 100644 index 0000000..5c88e47 --- /dev/null +++ b/app/module/chat/response/chat_schedule_file.response.go @@ -0,0 +1,17 @@ +package response + +// ChatScheduleFileResponse - Response structure for chat schedule file +type ChatScheduleFileResponse struct { + ID uint `json:"id"` + ChatScheduleID uint `json:"chat_schedule_id"` + FileName string `json:"file_name"` + OriginalName string `json:"original_name"` + FilePath string `json:"file_path"` + FileSize int64 `json:"file_size"` + MimeType string `json:"mime_type"` + FileType string `json:"file_type"` + Description string `json:"description"` + IsRequired bool `json:"is_required"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/app/module/chat/service/chat_schedule.service.go b/app/module/chat/service/chat_schedule.service.go new file mode 100644 index 0000000..a42f00a --- /dev/null +++ b/app/module/chat/service/chat_schedule.service.go @@ -0,0 +1,286 @@ +package service + +import ( + "errors" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/mapper" + "narasi-ahli-be/app/module/chat/repository" + "narasi-ahli-be/app/module/chat/request" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "time" + + "github.com/rs/zerolog" +) + +type chatScheduleService struct { + chatScheduleRepository repository.ChatScheduleRepository + chatRepository repository.ChatRepository + chatScheduleMapper *mapper.ChatScheduleMapper + Log zerolog.Logger + UsersRepo usersRepository.UsersRepository +} + +type ChatScheduleService interface { + // Chat Schedule CRUD operations + CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) + GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error) + GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) + UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) + DeleteChatSchedule(authToken string, id uint) (err error) + + // Additional schedule operations + GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) + GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) + SendScheduleReminder(authToken string, scheduleID uint) (err error) +} + +func NewChatScheduleService( + chatScheduleRepository repository.ChatScheduleRepository, + chatRepository repository.ChatRepository, + log zerolog.Logger, + usersRepo usersRepository.UsersRepository, +) ChatScheduleService { + return &chatScheduleService{ + chatScheduleRepository: chatScheduleRepository, + chatRepository: chatRepository, + chatScheduleMapper: mapper.NewChatScheduleMapper(), + Log: log, + UsersRepo: usersRepo, + } +} + +// CreateChatSchedule - Create a new chat schedule +func (_i *chatScheduleService) CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("user not found") + } + userID := userInfo.ID + + // Validate that the scheduled time is in the future + if req.ScheduledAt.Before(time.Now()) { + return nil, errors.New("scheduled time must be in the future") + } + + // Check if user is participant in the chat session + isParticipant, err := _i.chatRepository.CheckUserInChatSession(userID, req.ChatSessionID) + if err != nil { + return nil, err + } + + if !isParticipant { + return nil, errors.New("user is not a participant in this chat session") + } + + // Convert request to entity + schedule := _i.chatScheduleMapper.ToEntity(req) + schedule.CreatedBy = userID + + // Create schedule + result, err := _i.chatScheduleRepository.CreateChatSchedule(schedule) + if err != nil { + return nil, err + } + + // Convert to response + dataResult = _i.chatScheduleMapper.ToResponse(result) + return +} + +// GetAllChatSchedules - Get all chat schedules for a user +func (_i *chatScheduleService) GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, paginator.Pagination{}, errors.New("user not found") + } + userID := userInfo.ID + + // Get schedules from repository + scheduleEntities, paging, err := _i.chatScheduleRepository.GetAllChatSchedules(userID, req) + if err != nil { + return nil, paginator.Pagination{}, err + } + + // Convert to response + schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities) + return +} + +// GetChatScheduleByID - Get a specific chat schedule +func (_i *chatScheduleService) GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) { + // Get schedule from repository + scheduleEntity, err := _i.chatScheduleRepository.GetChatScheduleByID(id) + if err != nil { + return nil, err + } + + // Convert to response + schedule = _i.chatScheduleMapper.ToResponse(scheduleEntity) + return +} + +// UpdateChatSchedule - Update a chat schedule +func (_i *chatScheduleService) UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("user not found") + } + userID := userInfo.ID + + // Check if user is participant in the chat session + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, id) + if err != nil { + return err + } + + if !isParticipant { + return errors.New("user is not a participant in this chat session") + } + + // Get existing schedule to check if user is the creator + existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id) + if err != nil { + return err + } + + // Only creator can update the schedule + if existingSchedule.CreatedBy != userID { + return errors.New("only the creator can update this schedule") + } + + // Validate scheduled time if provided + if !req.ScheduledAt.IsZero() && req.ScheduledAt.Before(time.Now()) { + return errors.New("scheduled time must be in the future") + } + + // Convert request to entity + schedule := _i.chatScheduleMapper.ToUpdateEntity(req) + + // Update schedule + err = _i.chatScheduleRepository.UpdateChatSchedule(id, schedule) + return +} + +// DeleteChatSchedule - Delete a chat schedule +func (_i *chatScheduleService) DeleteChatSchedule(authToken string, id uint) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("user not found") + } + userID := userInfo.ID + + // Get existing schedule to check if user is the creator + existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id) + if err != nil { + return err + } + + // Only creator can delete the schedule + if existingSchedule.CreatedBy != userID { + return errors.New("only the creator can delete this schedule") + } + + // Delete schedule + err = _i.chatScheduleRepository.DeleteChatSchedule(id) + return +} + +// GetUpcomingSchedules - Get upcoming schedules for a user +func (_i *chatScheduleService) GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("user not found") + } + userID := userInfo.ID + + // Get upcoming schedules from repository + scheduleEntities, err := _i.chatScheduleRepository.GetUpcomingSchedules(userID, limit) + if err != nil { + return nil, err + } + + // Convert to response + schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities) + return +} + +// GetSchedulesByStatus - Get schedules by status +func (_i *chatScheduleService) GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("user not found") + } + userID := userInfo.ID + + // Validate status + validStatuses := []string{"scheduled", "ongoing", "completed", "cancelled"} + isValidStatus := false + for _, validStatus := range validStatuses { + if status == validStatus { + isValidStatus = true + break + } + } + + if !isValidStatus { + return nil, errors.New("invalid status") + } + + // Get schedules by status from repository + scheduleEntities, err := _i.chatScheduleRepository.GetSchedulesByStatus(status) + if err != nil { + return nil, err + } + + // Filter by user participation + var userSchedules []*entity.ChatSchedules + for _, schedule := range scheduleEntities { + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, schedule.ID) + if err != nil { + continue + } + if isParticipant { + userSchedules = append(userSchedules, schedule) + } + } + + // Convert to response + schedules = _i.chatScheduleMapper.ToResponseList(userSchedules) + return +} + +// SendScheduleReminder - Send reminder for a schedule +func (_i *chatScheduleService) SendScheduleReminder(authToken string, scheduleID uint) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("user not found") + } + userID := userInfo.ID + + // Get schedule + schedule, err := _i.chatScheduleRepository.GetChatScheduleByID(scheduleID) + if err != nil { + return err + } + + // Only creator can send reminders + if schedule.CreatedBy != userID { + return errors.New("only the creator can send reminders") + } + + // Check if reminder was already sent + if schedule.IsReminderSent { + return errors.New("reminder has already been sent") + } + + // TODO: Implement actual reminder sending logic (email, push notification, etc.) + // For now, just update the reminder status + now := time.Now() + schedule.IsReminderSent = true + schedule.ReminderSentAt = &now + + err = _i.chatScheduleRepository.UpdateChatSchedule(scheduleID, schedule) + return +} diff --git a/app/module/chat/service/chat_schedule_file.service.go b/app/module/chat/service/chat_schedule_file.service.go new file mode 100644 index 0000000..cfb6a69 --- /dev/null +++ b/app/module/chat/service/chat_schedule_file.service.go @@ -0,0 +1,337 @@ +package service + +import ( + "context" + "errors" + "fmt" + "io" + "math/rand" + "mime" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/chat/mapper" + "narasi-ahli-be/app/module/chat/repository" + "narasi-ahli-be/app/module/chat/request" + "narasi-ahli-be/app/module/chat/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + utilSvc "narasi-ahli-be/utils/service" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +type chatScheduleFileService struct { + chatScheduleFileRepository repository.ChatScheduleFileRepository + chatScheduleRepository repository.ChatScheduleRepository + chatScheduleFileMapper *mapper.ChatScheduleFileMapper + Log zerolog.Logger + Cfg *config.Config + MinioStorage *minioStorage.MinioStorage + UsersRepo usersRepository.UsersRepository +} + +type ChatScheduleFileService interface { + // File management operations + UploadChatScheduleFile(c *fiber.Ctx, chatScheduleID uint) error + GetChatScheduleFiles(authToken string, req request.ChatScheduleFileQueryRequest) (files []*response.ChatScheduleFileResponse, err error) + GetChatScheduleFileByID(authToken string, id uint) (file *response.ChatScheduleFileResponse, err error) + UpdateChatScheduleFile(authToken string, id uint, req request.ChatScheduleFileUpdateRequest) (err error) + DeleteChatScheduleFile(authToken string, id uint) (err error) + Viewer(c *fiber.Ctx) error +} + +func NewChatScheduleFileService( + chatScheduleFileRepository repository.ChatScheduleFileRepository, + chatScheduleRepository repository.ChatScheduleRepository, + log zerolog.Logger, + cfg *config.Config, + minioStorage *minioStorage.MinioStorage, + usersRepo usersRepository.UsersRepository, +) ChatScheduleFileService { + return &chatScheduleFileService{ + chatScheduleFileRepository: chatScheduleFileRepository, + chatScheduleRepository: chatScheduleRepository, + chatScheduleFileMapper: mapper.NewChatScheduleFileMapper(), + Log: log, + Cfg: cfg, + MinioStorage: minioStorage, + UsersRepo: usersRepo, + } +} + +// UploadChatScheduleFile - Upload files for chat schedule +func (_i *chatScheduleFileService) UploadChatScheduleFile(c *fiber.Ctx, chatScheduleID uint) error { + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + if err != nil { + return err + } + + // Create minio connection + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + 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", "ChatScheduleFile::Upload"). + Interface("files", files).Msg("") + + for _, fileHeader := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Upload"). + 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("chat-schedules/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) + + // Get file type from form data + fileType := c.FormValue("file_type", "other") + description := c.FormValue("description", "") + isRequired := c.FormValue("is_required") == "true" + + // Create file entity + fileEntity := &entity.ChatScheduleFiles{ + ChatScheduleID: chatScheduleID, + FileName: newFilename, + OriginalName: filenameAlt, + FilePath: objectName, + FileSize: fileHeader.Size, + MimeType: fileHeader.Header.Get("Content-Type"), + FileType: fileType, + Description: description, + IsRequired: isRequired, + } + + // Save to database + _, err = _i.chatScheduleFileRepository.CreateChatScheduleFile(fileEntity) + if err != nil { + return err + } + + // Upload file to 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", "ChatScheduleFile::Upload"). + Interface("data", "Successfully uploaded").Msg("") + + return nil +} + +// GetChatScheduleFiles - Get files for a chat schedule +func (_i *chatScheduleFileService) GetChatScheduleFiles(authToken string, req request.ChatScheduleFileQueryRequest) (files []*response.ChatScheduleFileResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("user not found") + } + userID := userInfo.ID + + // If chat schedule ID is provided, check if user has access + if req.ChatScheduleID != nil { + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, *req.ChatScheduleID) + if err != nil { + return nil, err + } + + if !isParticipant { + return nil, errors.New("user is not a participant in this chat session") + } + } + + // Get files from repository + fileEntities, err := _i.chatScheduleFileRepository.GetChatScheduleFiles(req) + if err != nil { + return nil, err + } + + // Convert to response + files = _i.chatScheduleFileMapper.ToResponseList(fileEntities) + return +} + +// GetChatScheduleFileByID - Get a specific chat schedule file +func (_i *chatScheduleFileService) GetChatScheduleFileByID(authToken string, id uint) (file *response.ChatScheduleFileResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("user not found") + } + userID := userInfo.ID + + // Get file from repository + fileEntity, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id) + if err != nil { + return nil, err + } + + // Check if user has access to the chat schedule + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, fileEntity.ChatScheduleID) + if err != nil { + return nil, err + } + + if !isParticipant { + return nil, errors.New("user is not a participant in this chat session") + } + + // Convert to response + file = _i.chatScheduleFileMapper.ToResponse(fileEntity) + return +} + +// UpdateChatScheduleFile - Update a chat schedule file +func (_i *chatScheduleFileService) UpdateChatScheduleFile(authToken string, id uint, req request.ChatScheduleFileUpdateRequest) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("user not found") + } + userID := userInfo.ID + + // Get existing file to check access + existingFile, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id) + if err != nil { + return err + } + + // Check if user has access to the chat schedule + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, existingFile.ChatScheduleID) + if err != nil { + return err + } + + if !isParticipant { + return errors.New("user is not a participant in this chat session") + } + + // Convert request to entity + file := _i.chatScheduleFileMapper.ToUpdateEntity(req) + + // Update file + err = _i.chatScheduleFileRepository.UpdateChatScheduleFile(id, file) + return +} + +// DeleteChatScheduleFile - Delete a chat schedule file +func (_i *chatScheduleFileService) DeleteChatScheduleFile(authToken string, id uint) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("user not found") + } + userID := userInfo.ID + + // Get existing file to check access + existingFile, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id) + if err != nil { + return err + } + + // Check if user has access to the chat schedule + isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, existingFile.ChatScheduleID) + if err != nil { + return err + } + + if !isParticipant { + return errors.New("user is not a participant in this chat session") + } + + // Delete file + err = _i.chatScheduleFileRepository.DeleteChatScheduleFile(id) + return +} + +// Viewer - View chat schedule file +func (_i *chatScheduleFileService) Viewer(c *fiber.Ctx) error { + filename := c.Params("filename") + + // Find file by filename + fileEntity, err := _i.chatScheduleFileRepository.GetChatScheduleFileByFilename(filename) + if err != nil { + return err + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := fileEntity.FilePath + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Viewer"). + Interface("data", objectName).Msg("") + + // Create minio connection + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + 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 { + return err + } + defer fileContent.Close() + + // Determine Content-Type based on file extension + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" // fallback if no MIME type matches + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return nil +} + +// getFileExtension - Extract file extension from filename +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // if no extension, return empty string + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // get last extension + return parts[len(parts)-1] +} diff --git a/app/module/chat/service/file_upload.service.go b/app/module/chat/service/file_upload.service.go new file mode 100644 index 0000000..7fc050a --- /dev/null +++ b/app/module/chat/service/file_upload.service.go @@ -0,0 +1,143 @@ +package service + +import ( + "context" + "crypto/md5" + "fmt" + "mime/multipart" + "path/filepath" + "strings" + "time" + + "github.com/minio/minio-go/v7" +) + +type fileUploadService struct { + minioClient *minio.Client + bucketName string +} + +type FileUploadService interface { + UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error) + DeleteFile(filePath string) error + GetFileURL(filePath string) (string, error) + ValidateFile(file *multipart.FileHeader) error +} + +func NewFileUploadService(minioClient *minio.Client, bucketName string) FileUploadService { + return &fileUploadService{ + minioClient: minioClient, + bucketName: bucketName, + } +} + +// UploadFile - Upload file to MinIO +func (f *fileUploadService) UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error) { + // Validate file + if err := f.ValidateFile(file); err != nil { + return "", 0, err + } + + // Open file + src, err := file.Open() + if err != nil { + return "", 0, err + } + defer src.Close() + + // Generate unique filename + ext := filepath.Ext(file.Filename) + fileName := strings.TrimSuffix(file.Filename, ext) + hasher := md5.New() + hasher.Write([]byte(fmt.Sprintf("%s-%d", fileName, time.Now().UnixNano()))) + uniqueFileName := fmt.Sprintf("%x%s", hasher.Sum(nil), ext) + + // Create file path + filePath = fmt.Sprintf("%s/%s", folder, uniqueFileName) + + // Upload file to MinIO + ctx := context.Background() + _, err = f.minioClient.PutObject(ctx, f.bucketName, filePath, src, file.Size, minio.PutObjectOptions{ + ContentType: file.Header.Get("Content-Type"), + }) + if err != nil { + return "", 0, err + } + + return filePath, file.Size, nil +} + +// DeleteFile - Delete file from MinIO +func (f *fileUploadService) DeleteFile(filePath string) error { + ctx := context.Background() + return f.minioClient.RemoveObject(ctx, f.bucketName, filePath, minio.RemoveObjectOptions{}) +} + +// GetFileURL - Get file URL from MinIO +func (f *fileUploadService) GetFileURL(filePath string) (string, error) { + ctx := context.Background() + + // Generate presigned URL (valid for 7 days) + url, err := f.minioClient.PresignedGetObject(ctx, f.bucketName, filePath, 7*24*time.Hour, nil) + if err != nil { + return "", err + } + + return url.String(), nil +} + +// ValidateFile - Validate uploaded file +func (f *fileUploadService) ValidateFile(file *multipart.FileHeader) error { + // Check file size (max 50MB) + const maxFileSize = 50 * 1024 * 1024 // 50MB + if file.Size > maxFileSize { + return fmt.Errorf("file size exceeds maximum limit of 50MB") + } + + // Check file extension + ext := strings.ToLower(filepath.Ext(file.Filename)) + allowedExts := []string{".pdf", ".doc", ".docx", ".txt", ".mp4", ".avi", ".mov", ".mp3", ".wav", ".jpg", ".jpeg", ".png", ".gif"} + + isAllowed := false + for _, allowedExt := range allowedExts { + if ext == allowedExt { + isAllowed = true + break + } + } + + if !isAllowed { + return fmt.Errorf("file type not allowed. Allowed types: %s", strings.Join(allowedExts, ", ")) + } + + // Check MIME type + contentType := file.Header.Get("Content-Type") + allowedMimeTypes := []string{ + "application/pdf", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "text/plain", + "video/mp4", + "video/avi", + "video/quicktime", + "audio/mpeg", + "audio/wav", + "image/jpeg", + "image/png", + "image/gif", + } + + isValidMimeType := false + for _, allowedMimeType := range allowedMimeTypes { + if contentType == allowedMimeType { + isValidMimeType = true + break + } + } + + if !isValidMimeType { + return fmt.Errorf("invalid file type. Content-Type: %s", contentType) + } + + return nil +} diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index fb0f73e..bc5e216 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -4882,6 +4882,934 @@ const docTemplate = `{ } } }, + "/chat/schedule-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting files for a specific chat schedule", + "tags": [ + "Chat Schedule File" + ], + "summary": "Get chat schedule files", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "chatScheduleId", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "File type filter", + "name": "fileType", + "in": "query" + }, + { + "type": "boolean", + "description": "Required file filter", + "name": "isRequired", + "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" + } + } + } + } + }, + "/chat/schedule-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for viewing chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "View chat schedule file", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Chat Schedule File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "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" + } + } + } + } + }, + "/chat/schedule-files/{chatScheduleId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for uploading file for chat schedule", + "produces": [ + "application/json" + ], + "tags": [ + "Chat Schedule File" + ], + "summary": "Upload chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "chatScheduleId", + "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" + } + } + } + } + }, + "/chat/schedule-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Get one chat schedule file", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File 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" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Update chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleFileUpdateRequest" + } + } + ], + "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 deleting chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Delete chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File 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" + } + } + } + } + }, + "/chat/schedules": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all chat schedules for authenticated user", + "tags": [ + "Chat Schedule" + ], + "summary": "Get all chat schedules", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Chat Session ID", + "name": "chatSessionId", + "in": "query" + }, + { + "type": "string", + "description": "Schedule status (scheduled, ongoing, completed, cancelled)", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "Created by user ID", + "name": "createdBy", + "in": "query" + }, + { + "type": "string", + "description": "Date from (YYYY-MM-DD)", + "name": "dateFrom", + "in": "query" + }, + { + "type": "string", + "description": "Date to (YYYY-MM-DD)", + "name": "dateTo", + "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" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for creating a new chat schedule", + "tags": [ + "Chat Schedule" + ], + "summary": "Create chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleCreateRequest" + } + } + ], + "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" + } + } + } + } + }, + "/chat/schedules/status/{status}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting chat schedules by status", + "tags": [ + "Chat Schedule" + ], + "summary": "Get schedules by status", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Schedule status (scheduled, ongoing, completed, cancelled)", + "name": "status", + "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" + } + } + } + } + }, + "/chat/schedules/upcoming": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting upcoming chat schedules for authenticated user", + "tags": [ + "Chat Schedule" + ], + "summary": "Get upcoming schedules", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "default": 10, + "description": "Limit number of results", + "name": "limit", + "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" + } + } + } + } + }, + "/chat/schedules/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one chat schedule", + "tags": [ + "Chat Schedule" + ], + "summary": "Get one chat schedule", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating chat schedule (only creator can update)", + "tags": [ + "Chat Schedule" + ], + "summary": "Update chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleUpdateRequest" + } + } + ], + "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 deleting chat schedule (only creator can delete)", + "tags": [ + "Chat Schedule" + ], + "summary": "Delete chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + } + }, + "/chat/schedules/{id}/reminder": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for sending reminder for a chat schedule (only creator can send)", + "tags": [ + "Chat Schedule" + ], + "summary": "Send schedule reminder", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + } + }, "/chat/sessions": { "get": { "security": [ @@ -14632,6 +15560,133 @@ const docTemplate = `{ } } }, + "request.ChatScheduleCreateRequest": { + "type": "object", + "required": [ + "chat_session_id", + "scheduled_at", + "title" + ], + "properties": { + "chat_session_id": { + "type": "integer" + }, + "description": { + "type": "string", + "maxLength": 1000 + }, + "duration": { + "description": "15 minutes to 8 hours", + "type": "integer", + "maximum": 480, + "minimum": 15 + }, + "file_ids": { + "description": "Array of file IDs to attach to schedule", + "type": "array", + "items": { + "type": "integer" + } + }, + "scheduled_at": { + "type": "string" + }, + "summary": { + "type": "string", + "maxLength": 2000 + }, + "title": { + "type": "string", + "maxLength": 255, + "minLength": 3 + } + } + }, + "request.ChatScheduleFileUpdateRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 500 + }, + "file_name": { + "type": "string", + "maxLength": 255 + }, + "file_path": { + "type": "string", + "maxLength": 500 + }, + "file_size": { + "type": "integer", + "minimum": 0 + }, + "file_type": { + "type": "string", + "enum": [ + "article", + "journal", + "video", + "audio", + "document", + "other" + ] + }, + "is_required": { + "type": "boolean" + }, + "mime_type": { + "type": "string", + "maxLength": 100 + }, + "original_name": { + "type": "string", + "maxLength": 255 + } + } + }, + "request.ChatScheduleUpdateRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 1000 + }, + "duration": { + "type": "integer", + "maximum": 480, + "minimum": 15 + }, + "file_ids": { + "description": "Array of file IDs to attach to schedule", + "type": "array", + "items": { + "type": "integer" + } + }, + "scheduled_at": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "scheduled", + "ongoing", + "completed", + "cancelled" + ] + }, + "summary": { + "type": "string", + "maxLength": 2000 + }, + "title": { + "type": "string", + "maxLength": 255, + "minLength": 3 + } + } + }, "request.ChatSessionCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 9d2411e..b6e2ebe 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -4871,6 +4871,934 @@ } } }, + "/chat/schedule-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting files for a specific chat schedule", + "tags": [ + "Chat Schedule File" + ], + "summary": "Get chat schedule files", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "chatScheduleId", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "File type filter", + "name": "fileType", + "in": "query" + }, + { + "type": "boolean", + "description": "Required file filter", + "name": "isRequired", + "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" + } + } + } + } + }, + "/chat/schedule-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for viewing chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "View chat schedule file", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Chat Schedule File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "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" + } + } + } + } + }, + "/chat/schedule-files/{chatScheduleId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for uploading file for chat schedule", + "produces": [ + "application/json" + ], + "tags": [ + "Chat Schedule File" + ], + "summary": "Upload chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "chatScheduleId", + "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" + } + } + } + } + }, + "/chat/schedule-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Get one chat schedule file", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File 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" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Update chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleFileUpdateRequest" + } + } + ], + "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 deleting chat schedule file", + "tags": [ + "Chat Schedule File" + ], + "summary": "Delete chat schedule file", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule File 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" + } + } + } + } + }, + "/chat/schedules": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all chat schedules for authenticated user", + "tags": [ + "Chat Schedule" + ], + "summary": "Get all chat schedules", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Chat Session ID", + "name": "chatSessionId", + "in": "query" + }, + { + "type": "string", + "description": "Schedule status (scheduled, ongoing, completed, cancelled)", + "name": "status", + "in": "query" + }, + { + "type": "string", + "description": "Created by user ID", + "name": "createdBy", + "in": "query" + }, + { + "type": "string", + "description": "Date from (YYYY-MM-DD)", + "name": "dateFrom", + "in": "query" + }, + { + "type": "string", + "description": "Date to (YYYY-MM-DD)", + "name": "dateTo", + "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" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for creating a new chat schedule", + "tags": [ + "Chat Schedule" + ], + "summary": "Create chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleCreateRequest" + } + } + ], + "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" + } + } + } + } + }, + "/chat/schedules/status/{status}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting chat schedules by status", + "tags": [ + "Chat Schedule" + ], + "summary": "Get schedules by status", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "Schedule status (scheduled, ongoing, completed, cancelled)", + "name": "status", + "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" + } + } + } + } + }, + "/chat/schedules/upcoming": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting upcoming chat schedules for authenticated user", + "tags": [ + "Chat Schedule" + ], + "summary": "Get upcoming schedules", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "default": 10, + "description": "Limit number of results", + "name": "limit", + "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" + } + } + } + } + }, + "/chat/schedules/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one chat schedule", + "tags": [ + "Chat Schedule" + ], + "summary": "Get one chat schedule", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating chat schedule (only creator can update)", + "tags": [ + "Chat Schedule" + ], + "summary": "Update chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChatScheduleUpdateRequest" + } + } + ], + "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 deleting chat schedule (only creator can delete)", + "tags": [ + "Chat Schedule" + ], + "summary": "Delete chat schedule", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + } + }, + "/chat/schedules/{id}/reminder": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for sending reminder for a chat schedule (only creator can send)", + "tags": [ + "Chat Schedule" + ], + "summary": "Send schedule reminder", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Chat Schedule 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" + } + } + } + } + }, "/chat/sessions": { "get": { "security": [ @@ -14621,6 +15549,133 @@ } } }, + "request.ChatScheduleCreateRequest": { + "type": "object", + "required": [ + "chat_session_id", + "scheduled_at", + "title" + ], + "properties": { + "chat_session_id": { + "type": "integer" + }, + "description": { + "type": "string", + "maxLength": 1000 + }, + "duration": { + "description": "15 minutes to 8 hours", + "type": "integer", + "maximum": 480, + "minimum": 15 + }, + "file_ids": { + "description": "Array of file IDs to attach to schedule", + "type": "array", + "items": { + "type": "integer" + } + }, + "scheduled_at": { + "type": "string" + }, + "summary": { + "type": "string", + "maxLength": 2000 + }, + "title": { + "type": "string", + "maxLength": 255, + "minLength": 3 + } + } + }, + "request.ChatScheduleFileUpdateRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 500 + }, + "file_name": { + "type": "string", + "maxLength": 255 + }, + "file_path": { + "type": "string", + "maxLength": 500 + }, + "file_size": { + "type": "integer", + "minimum": 0 + }, + "file_type": { + "type": "string", + "enum": [ + "article", + "journal", + "video", + "audio", + "document", + "other" + ] + }, + "is_required": { + "type": "boolean" + }, + "mime_type": { + "type": "string", + "maxLength": 100 + }, + "original_name": { + "type": "string", + "maxLength": 255 + } + } + }, + "request.ChatScheduleUpdateRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 1000 + }, + "duration": { + "type": "integer", + "maximum": 480, + "minimum": 15 + }, + "file_ids": { + "description": "Array of file IDs to attach to schedule", + "type": "array", + "items": { + "type": "integer" + } + }, + "scheduled_at": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "scheduled", + "ongoing", + "completed", + "cancelled" + ] + }, + "summary": { + "type": "string", + "maxLength": 2000 + }, + "title": { + "type": "string", + "maxLength": 255, + "minLength": 3 + } + } + }, "request.ChatSessionCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 4c65f8d..e2c12eb 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -407,6 +407,100 @@ definitions: required: - message type: object + request.ChatScheduleCreateRequest: + properties: + chat_session_id: + type: integer + description: + maxLength: 1000 + type: string + duration: + description: 15 minutes to 8 hours + maximum: 480 + minimum: 15 + type: integer + file_ids: + description: Array of file IDs to attach to schedule + items: + type: integer + type: array + scheduled_at: + type: string + summary: + maxLength: 2000 + type: string + title: + maxLength: 255 + minLength: 3 + type: string + required: + - chat_session_id + - scheduled_at + - title + type: object + request.ChatScheduleFileUpdateRequest: + properties: + description: + maxLength: 500 + type: string + file_name: + maxLength: 255 + type: string + file_path: + maxLength: 500 + type: string + file_size: + minimum: 0 + type: integer + file_type: + enum: + - article + - journal + - video + - audio + - document + - other + type: string + is_required: + type: boolean + mime_type: + maxLength: 100 + type: string + original_name: + maxLength: 255 + type: string + type: object + request.ChatScheduleUpdateRequest: + properties: + description: + maxLength: 1000 + type: string + duration: + maximum: 480 + minimum: 15 + type: integer + file_ids: + description: Array of file IDs to attach to schedule + items: + type: integer + type: array + scheduled_at: + type: string + status: + enum: + - scheduled + - ongoing + - completed + - cancelled + type: string + summary: + maxLength: 2000 + type: string + title: + maxLength: 255 + minLength: 3 + type: string + type: object request.ChatSessionCreateRequest: properties: name: @@ -4358,6 +4452,608 @@ paths: summary: Update chat message tags: - Chat + /chat/schedule-files: + get: + description: API for getting files for a specific chat schedule + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule ID + in: query + name: chatScheduleId + required: true + type: integer + - description: File type filter + in: query + name: fileType + type: string + - description: Required file filter + in: query + name: isRequired + type: boolean + 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 chat schedule files + tags: + - Chat Schedule File + /chat/schedule-files/{chatScheduleId}: + post: + description: API for uploading file for chat schedule + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Upload file + in: formData + name: files + required: true + type: file + - description: Chat Schedule ID + in: path + name: chatScheduleId + 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 chat schedule file + tags: + - Chat Schedule File + /chat/schedule-files/{id}: + delete: + description: API for deleting chat schedule file + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule File 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 chat schedule file + tags: + - Chat Schedule File + get: + description: API for getting one chat schedule file + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule File 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 chat schedule file + tags: + - Chat Schedule File + put: + description: API for updating chat schedule file + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule File ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ChatScheduleFileUpdateRequest' + 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 chat schedule file + tags: + - Chat Schedule File + /chat/schedule-files/viewer/{filename}: + get: + description: API for viewing chat schedule file + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule File Name + in: path + name: filename + required: true + type: string + responses: + "200": + description: OK + schema: + type: file + "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: View chat schedule file + tags: + - Chat Schedule File + /chat/schedules: + get: + description: API for getting all chat schedules for authenticated user + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Session ID + in: query + name: chatSessionId + type: string + - description: Schedule status (scheduled, ongoing, completed, cancelled) + in: query + name: status + type: string + - description: Created by user ID + in: query + name: createdBy + type: string + - description: Date from (YYYY-MM-DD) + in: query + name: dateFrom + type: string + - description: Date to (YYYY-MM-DD) + in: query + name: dateTo + type: string + - 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 chat schedules + tags: + - Chat Schedule + post: + description: API for creating a new chat schedule + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ChatScheduleCreateRequest' + 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: Create chat schedule + tags: + - Chat Schedule + /chat/schedules/{id}: + delete: + description: API for deleting chat schedule (only creator can delete) + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule 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 chat schedule + tags: + - Chat Schedule + get: + description: API for getting one chat schedule + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule 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 chat schedule + tags: + - Chat Schedule + put: + description: API for updating chat schedule (only creator can update) + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ChatScheduleUpdateRequest' + 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 chat schedule + tags: + - Chat Schedule + /chat/schedules/{id}/reminder: + post: + description: API for sending reminder for a chat schedule (only creator can + send) + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Chat Schedule 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: Send schedule reminder + tags: + - Chat Schedule + /chat/schedules/status/{status}: + get: + description: API for getting chat schedules by status + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Schedule status (scheduled, ongoing, completed, cancelled) + in: path + name: status + 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: Get schedules by status + tags: + - Chat Schedule + /chat/schedules/upcoming: + get: + description: API for getting upcoming chat schedules for authenticated user + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - default: 10 + description: Limit number of results + in: query + name: limit + 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 upcoming schedules + tags: + - Chat Schedule /chat/sessions: get: description: API for getting all chat sessions for authenticated user