committtttt
This commit is contained in:
211
backend/internal/handlers/ai.go
Normal file
211
backend/internal/handlers/ai.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"customer-support-system/internal/ai"
|
||||
"customer-support-system/internal/models"
|
||||
"customer-support-system/pkg/logger"
|
||||
)
|
||||
|
||||
// AIHandler handles AI-related HTTP requests
|
||||
type AIHandler struct {
|
||||
aiService *ai.AIService
|
||||
}
|
||||
|
||||
// NewAIHandler creates a new AI handler
|
||||
func NewAIHandler() *AIHandler {
|
||||
return &AIHandler{
|
||||
aiService: ai.NewAIService(),
|
||||
}
|
||||
}
|
||||
|
||||
// QueryAI handles querying the AI service
|
||||
func (h *AIHandler) QueryAI(c *gin.Context) {
|
||||
// Check if user is authenticated
|
||||
_, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Prompt string `json:"prompt" binding:"required"`
|
||||
ConversationHistory []models.Message `json:"conversationHistory"`
|
||||
Complexity int `json:"complexity"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse query AI request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// If complexity is not provided, analyze it
|
||||
if req.Complexity == 0 {
|
||||
req.Complexity = h.aiService.AnalyzeComplexity(req.Prompt)
|
||||
}
|
||||
|
||||
// Query AI
|
||||
ctx := context.Background()
|
||||
response, err := h.aiService.Query(ctx, req.Prompt, req.ConversationHistory, req.Complexity)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to query AI")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to query AI",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "AI query successful",
|
||||
"data": gin.H{
|
||||
"response": response,
|
||||
"complexity": req.Complexity,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AnalyzeComplexity handles analyzing the complexity of a prompt
|
||||
func (h *AIHandler) AnalyzeComplexity(c *gin.Context) {
|
||||
var req struct {
|
||||
Prompt string `json:"prompt" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse analyze complexity request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Analyze complexity
|
||||
complexity := h.aiService.AnalyzeComplexity(req.Prompt)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Complexity analysis successful",
|
||||
"data": gin.H{
|
||||
"complexity": complexity,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetAvailableModels handles getting available AI models
|
||||
func (h *AIHandler) GetAvailableModels(c *gin.Context) {
|
||||
// Get available models
|
||||
models := h.aiService.GetAvailableModels()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"models": models,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// QueryOpenAI handles querying the OpenAI API
|
||||
func (h *AIHandler) QueryOpenAI(c *gin.Context) {
|
||||
// Check if user is authenticated
|
||||
_, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Prompt string `json:"prompt" binding:"required"`
|
||||
ConversationHistory []models.Message `json:"conversationHistory"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse query OpenAI request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Query OpenAI
|
||||
ctx := context.Background()
|
||||
response, err := h.aiService.QueryOpenAI(ctx, req.Prompt, req.ConversationHistory)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to query OpenAI")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to query OpenAI",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "OpenAI query successful",
|
||||
"data": gin.H{
|
||||
"response": response,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// QueryOllama handles querying the Ollama API
|
||||
func (h *AIHandler) QueryOllama(c *gin.Context) {
|
||||
// Check if user is authenticated
|
||||
_, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Prompt string `json:"prompt" binding:"required"`
|
||||
ConversationHistory []models.Message `json:"conversationHistory"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse query Ollama request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Query Ollama
|
||||
ctx := context.Background()
|
||||
response, err := h.aiService.QueryOllama(ctx, req.Prompt, req.ConversationHistory)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to query Ollama")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to query Ollama",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Ollama query successful",
|
||||
"data": gin.H{
|
||||
"response": response,
|
||||
},
|
||||
})
|
||||
}
|
558
backend/internal/handlers/conversation.go
Normal file
558
backend/internal/handlers/conversation.go
Normal file
@@ -0,0 +1,558 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"customer-support-system/internal/conversation"
|
||||
"customer-support-system/internal/models"
|
||||
"customer-support-system/pkg/logger"
|
||||
)
|
||||
|
||||
// ConversationHandler handles conversation-related HTTP requests
|
||||
type ConversationHandler struct {
|
||||
conversationService *conversation.ConversationService
|
||||
}
|
||||
|
||||
// NewConversationHandler creates a new conversation handler
|
||||
func NewConversationHandler() *ConversationHandler {
|
||||
return &ConversationHandler{
|
||||
conversationService: conversation.NewConversationService(),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateConversation handles creating a new conversation
|
||||
func (h *ConversationHandler) CreateConversation(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.CreateConversationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse create conversation request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Create conversation
|
||||
conv, err := h.conversationService.CreateConversation(userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to create conversation")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to create conversation",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"message": "Conversation created successfully",
|
||||
"data": gin.H{
|
||||
"conversation": conv,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetConversation handles getting a conversation by ID
|
||||
func (h *ConversationHandler) GetConversation(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation
|
||||
conv, err := h.conversationService.GetConversation(uint(conversationID), userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get conversation")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get conversation",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"conversation": conv,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ListConversations handles listing conversations for a user
|
||||
func (h *ConversationHandler) ListConversations(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
status := c.DefaultQuery("status", "")
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// List conversations
|
||||
response, err := h.conversationService.ListConversations(userID.(uint), page, pageSize, status)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to list conversations")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to list conversations",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"conversations": response.Conversations,
|
||||
"total": response.Total,
|
||||
"page": response.Page,
|
||||
"pageSize": response.PageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConversation handles updating a conversation
|
||||
func (h *ConversationHandler) UpdateConversation(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdateConversationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse update conversation request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Update conversation
|
||||
conv, err := h.conversationService.UpdateConversation(uint(conversationID), userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to update conversation")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to update conversation",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Conversation updated successfully",
|
||||
"data": gin.H{
|
||||
"conversation": conv,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteConversation handles deleting a conversation
|
||||
func (h *ConversationHandler) DeleteConversation(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete conversation
|
||||
err = h.conversationService.DeleteConversation(uint(conversationID), userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to delete conversation")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to delete conversation",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Conversation deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// CreateMessage handles creating a new message in a conversation
|
||||
func (h *ConversationHandler) CreateMessage(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.CreateMessageRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse create message request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Create message
|
||||
message, err := h.conversationService.CreateMessage(uint(conversationID), userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to create message")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to create message",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"message": "Message created successfully",
|
||||
"data": gin.H{
|
||||
"message": message,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetMessages handles getting messages in a conversation
|
||||
func (h *ConversationHandler) GetMessages(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// Get messages
|
||||
response, err := h.conversationService.GetMessages(uint(conversationID), userID.(uint), page, pageSize)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get messages")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get messages",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"messages": response.Messages,
|
||||
"total": response.Total,
|
||||
"page": response.Page,
|
||||
"pageSize": response.PageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateMessage handles updating a message
|
||||
func (h *ConversationHandler) UpdateMessage(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get message ID from URL params
|
||||
messageID, err := strconv.ParseUint(c.Param("messageId"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse message ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid message ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdateMessageRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse update message request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Update message
|
||||
message, err := h.conversationService.UpdateMessage(uint(messageID), userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to update message")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to update message",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Message updated successfully",
|
||||
"data": gin.H{
|
||||
"message": message,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteMessage handles deleting a message
|
||||
func (h *ConversationHandler) DeleteMessage(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get message ID from URL params
|
||||
messageID, err := strconv.ParseUint(c.Param("messageId"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse message ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid message ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete message
|
||||
err = h.conversationService.DeleteMessage(uint(messageID), userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to delete message")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to delete message",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Message deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// SendMessageWithAI handles sending a message and getting an AI response
|
||||
func (h *ConversationHandler) SendMessageWithAI(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse send message request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Send message and get AI response
|
||||
userMessage, aiMessage, err := h.conversationService.SendMessageWithAI(uint(conversationID), userID.(uint), req.Content)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to send message with AI")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to send message with AI",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"message": "Message sent and AI response received",
|
||||
"data": gin.H{
|
||||
"userMessage": userMessage,
|
||||
"aiMessage": aiMessage,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetConversationStats handles getting statistics for a conversation
|
||||
func (h *ConversationHandler) GetConversationStats(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation ID from URL params
|
||||
conversationID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse conversation ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid conversation ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get conversation stats
|
||||
stats, err := h.conversationService.GetConversationStats(uint(conversationID), userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get conversation stats")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get conversation stats",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"stats": stats,
|
||||
},
|
||||
})
|
||||
}
|
475
backend/internal/handlers/knowledge.go
Normal file
475
backend/internal/handlers/knowledge.go
Normal file
@@ -0,0 +1,475 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"customer-support-system/internal/knowledge"
|
||||
"customer-support-system/internal/models"
|
||||
"customer-support-system/pkg/logger"
|
||||
)
|
||||
|
||||
// KnowledgeHandler handles knowledge base-related HTTP requests
|
||||
type KnowledgeHandler struct {
|
||||
knowledgeService *knowledge.KnowledgeService
|
||||
}
|
||||
|
||||
// NewKnowledgeHandler creates a new knowledge handler
|
||||
func NewKnowledgeHandler() *KnowledgeHandler {
|
||||
return &KnowledgeHandler{
|
||||
knowledgeService: knowledge.NewKnowledgeService(),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateKnowledgeEntry handles creating a new knowledge base entry
|
||||
func (h *KnowledgeHandler) CreateKnowledgeEntry(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.CreateKnowledgeBaseRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse create knowledge entry request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Create knowledge entry
|
||||
entry, err := h.knowledgeService.CreateKnowledgeEntry(userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to create knowledge entry")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to create knowledge entry",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"message": "Knowledge entry created successfully",
|
||||
"data": gin.H{
|
||||
"entry": entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetKnowledgeEntry handles getting a knowledge base entry by ID
|
||||
func (h *KnowledgeHandler) GetKnowledgeEntry(c *gin.Context) {
|
||||
// Get knowledge entry ID from URL params
|
||||
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse knowledge entry ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid knowledge entry ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get knowledge entry
|
||||
entry, err := h.knowledgeService.GetKnowledgeEntry(uint(entryID))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get knowledge entry")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get knowledge entry",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"entry": entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ListKnowledgeEntries handles listing knowledge base entries
|
||||
func (h *KnowledgeHandler) ListKnowledgeEntries(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
category := c.DefaultQuery("category", "")
|
||||
activeStr := c.DefaultQuery("active", "true")
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// Parse active parameter
|
||||
var active *bool
|
||||
if activeStr != "" {
|
||||
activeVal := activeStr == "true"
|
||||
active = &activeVal
|
||||
}
|
||||
|
||||
// List knowledge entries
|
||||
response, err := h.knowledgeService.ListKnowledgeEntries(page, pageSize, category, active)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to list knowledge entries")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to list knowledge entries",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"entries": response.Entries,
|
||||
"total": response.Total,
|
||||
"page": response.Page,
|
||||
"pageSize": response.PageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateKnowledgeEntry handles updating a knowledge base entry
|
||||
func (h *KnowledgeHandler) UpdateKnowledgeEntry(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get knowledge entry ID from URL params
|
||||
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse knowledge entry ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid knowledge entry ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdateKnowledgeBaseRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse update knowledge entry request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Update knowledge entry
|
||||
entry, err := h.knowledgeService.UpdateKnowledgeEntry(uint(entryID), userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to update knowledge entry")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to update knowledge entry",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Knowledge entry updated successfully",
|
||||
"data": gin.H{
|
||||
"entry": entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteKnowledgeEntry handles deleting a knowledge base entry
|
||||
func (h *KnowledgeHandler) DeleteKnowledgeEntry(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get knowledge entry ID from URL params
|
||||
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse knowledge entry ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid knowledge entry ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete knowledge entry
|
||||
err = h.knowledgeService.DeleteKnowledgeEntry(uint(entryID), userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to delete knowledge entry")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to delete knowledge entry",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Knowledge entry deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// SearchKnowledge handles searching knowledge base entries
|
||||
func (h *KnowledgeHandler) SearchKnowledge(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
query := c.DefaultQuery("query", "")
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
category := c.DefaultQuery("category", "")
|
||||
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// Search knowledge entries
|
||||
response, err := h.knowledgeService.SearchKnowledge(query, page, pageSize, category)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to search knowledge entries")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to search knowledge entries",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"results": response.Results,
|
||||
"total": response.Total,
|
||||
"query": response.Query,
|
||||
"page": response.Page,
|
||||
"pageSize": response.PageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetCategories handles getting all unique categories in the knowledge base
|
||||
func (h *KnowledgeHandler) GetCategories(c *gin.Context) {
|
||||
// Get categories
|
||||
categories, err := h.knowledgeService.GetCategories()
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get knowledge categories")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get knowledge categories",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"categories": categories,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetTags handles getting all unique tags in the knowledge base
|
||||
func (h *KnowledgeHandler) GetTags(c *gin.Context) {
|
||||
// Get tags
|
||||
tags, err := h.knowledgeService.GetTags()
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get knowledge tags")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get knowledge tags",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"tags": tags,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// RateKnowledgeEntry handles rating a knowledge base entry as helpful or not
|
||||
func (h *KnowledgeHandler) RateKnowledgeEntry(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get knowledge entry ID from URL params
|
||||
entryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse knowledge entry ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid knowledge entry ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.CreateKnowledgeBaseFeedbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse rate knowledge entry request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Rate knowledge entry
|
||||
err = h.knowledgeService.RateKnowledgeEntry(uint(entryID), userID.(uint), req.Helpful, req.Comment)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to rate knowledge entry")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to rate knowledge entry",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Knowledge entry rated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// GetPopularKnowledge handles getting popular knowledge base entries
|
||||
func (h *KnowledgeHandler) GetPopularKnowledge(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||
if limit < 1 || limit > 100 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
// Get popular knowledge entries
|
||||
entries, err := h.knowledgeService.GetPopularKnowledge(limit)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get popular knowledge entries")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get popular knowledge entries",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"entries": entries,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetRecentKnowledge handles getting recent knowledge base entries
|
||||
func (h *KnowledgeHandler) GetRecentKnowledge(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||
if limit < 1 || limit > 100 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
// Get recent knowledge entries
|
||||
entries, err := h.knowledgeService.GetRecentKnowledge(limit)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get recent knowledge entries")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get recent knowledge entries",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"entries": entries,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// FindBestMatch handles finding the best matching knowledge base entry for a query
|
||||
func (h *KnowledgeHandler) FindBestMatch(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
query := c.DefaultQuery("query", "")
|
||||
if query == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Query parameter is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Find best match
|
||||
entry, err := h.knowledgeService.FindBestMatch(query)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to find best match")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to find best match",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"entry": entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetKnowledgeStats handles getting statistics for the knowledge base
|
||||
func (h *KnowledgeHandler) GetKnowledgeStats(c *gin.Context) {
|
||||
// Get knowledge stats
|
||||
stats, err := h.knowledgeService.GetKnowledgeStats()
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get knowledge stats")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get knowledge stats",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"stats": stats,
|
||||
},
|
||||
})
|
||||
}
|
453
backend/internal/handlers/user.go
Normal file
453
backend/internal/handlers/user.go
Normal file
@@ -0,0 +1,453 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"customer-support-system/internal/auth"
|
||||
"customer-support-system/internal/database"
|
||||
"customer-support-system/internal/models"
|
||||
"customer-support-system/pkg/logger"
|
||||
)
|
||||
|
||||
// UserHandler handles user-related HTTP requests
|
||||
type UserHandler struct {
|
||||
authService *auth.AuthService
|
||||
}
|
||||
|
||||
// NewUserHandler creates a new user handler
|
||||
func NewUserHandler() *UserHandler {
|
||||
return &UserHandler{
|
||||
authService: auth.NewAuthService(database.GetDB()),
|
||||
}
|
||||
}
|
||||
|
||||
// Register handles user registration
|
||||
func (h *UserHandler) Register(c *gin.Context) {
|
||||
var req models.CreateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse register request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if err := validateRegisterRequest(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Create user
|
||||
user, err := h.authService.Register(&req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to register user")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to register user",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"message": "User registered successfully",
|
||||
"data": gin.H{
|
||||
"user": user,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Login handles user login
|
||||
func (h *UserHandler) Login(c *gin.Context) {
|
||||
var req models.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse login request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if err := validateLoginRequest(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get client IP
|
||||
clientIP := c.ClientIP()
|
||||
|
||||
// Authenticate user
|
||||
response, err := h.authService.Login(req.Username, req.Password, clientIP)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to login user")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid username or password",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "User logged in successfully",
|
||||
"data": gin.H{
|
||||
"user": response.User,
|
||||
"token": response.Token,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetProfile handles getting user profile
|
||||
func (h *UserHandler) GetProfile(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user profile
|
||||
user, err := h.authService.GetUserByID(userID.(uint))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get user profile")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get user profile",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"user": user.ToSafeUser(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateProfile handles updating user profile
|
||||
func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse update profile request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Update user profile
|
||||
user, err := h.authService.UpdateUser(userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to update user profile")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to update user profile",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "User profile updated successfully",
|
||||
"data": gin.H{
|
||||
"user": user,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ChangePassword handles changing user password
|
||||
func (h *UserHandler) ChangePassword(c *gin.Context) {
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.ChangePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse change password request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if err := validateChangePasswordRequest(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Change password
|
||||
err := h.authService.ChangePassword(userID.(uint), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to change password")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to change password",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Password changed successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// validateRegisterRequest validates the register request
|
||||
func validateRegisterRequest(req *models.CreateUserRequest) error {
|
||||
if req.Username == "" {
|
||||
return fmt.Errorf("username is required")
|
||||
}
|
||||
if req.Password == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
if req.Email == "" {
|
||||
return fmt.Errorf("email is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateLoginRequest validates the login request
|
||||
func validateLoginRequest(req *models.LoginRequest) error {
|
||||
if req.Username == "" {
|
||||
return fmt.Errorf("username is required")
|
||||
}
|
||||
if req.Password == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateChangePasswordRequest validates the change password request
|
||||
func validateChangePasswordRequest(req *models.ChangePasswordRequest) error {
|
||||
if req.CurrentPassword == "" {
|
||||
return fmt.Errorf("current password is required")
|
||||
}
|
||||
if req.NewPassword == "" {
|
||||
return fmt.Errorf("new password is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminGetUsers handles getting all users (admin only)
|
||||
func (h *UserHandler) AdminGetUsers(c *gin.Context) {
|
||||
// Parse query parameters
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// Get users
|
||||
var users []models.User
|
||||
var total int64
|
||||
|
||||
db := database.GetDB()
|
||||
|
||||
// Get total count
|
||||
if err := db.Model(&models.User{}).Count(&total).Error; err != nil {
|
||||
logger.WithError(err).Error("Failed to count users")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to count users",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get paginated results
|
||||
offset := (page - 1) * pageSize
|
||||
if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil {
|
||||
logger.WithError(err).Error("Failed to get users")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get users",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to safe users
|
||||
safeUsers := make([]models.SafeUser, len(users))
|
||||
for i, user := range users {
|
||||
safeUsers[i] = user.ToSafeUser()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"users": safeUsers,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AdminGetUser handles getting a user by ID (admin only)
|
||||
func (h *UserHandler) AdminGetUser(c *gin.Context) {
|
||||
// Get target user ID from URL params
|
||||
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse user ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid user ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user
|
||||
user, err := h.authService.GetUserByID(uint(targetUserID))
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to get user")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to get user",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"user": user.ToSafeUser(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AdminUpdateUser handles updating a user (admin only)
|
||||
func (h *UserHandler) AdminUpdateUser(c *gin.Context) {
|
||||
// Get target user ID from URL params
|
||||
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse user ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid user ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.WithError(err).Error("Failed to parse update user request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request body",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Update user
|
||||
updatedUser, err := h.authService.UpdateUser(uint(targetUserID), &req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to update user")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to update user",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "User updated successfully",
|
||||
"data": gin.H{
|
||||
"user": updatedUser,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AdminDeleteUser handles deleting a user (admin only)
|
||||
func (h *UserHandler) AdminDeleteUser(c *gin.Context) {
|
||||
// Get target user ID from URL params
|
||||
targetUserID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Failed to parse user ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid user ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get current user ID from context
|
||||
currentUserID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Failed to get current user ID from context")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "Unauthorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent self-deletion
|
||||
if currentUserID.(uint) == uint(targetUserID) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Cannot delete your own account",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete user
|
||||
db := database.GetDB()
|
||||
if err := db.Delete(&models.User{}, uint(targetUserID)).Error; err != nil {
|
||||
logger.WithError(err).Error("Failed to delete user")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "Failed to delete user",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "User deleted successfully",
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user