Files
Novault-backend/internal/handler/notification_handler.go

269 lines
7.2 KiB
Go

package handler
import (
"errors"
"strconv"
"accounting-app/internal/models"
"accounting-app/internal/service"
"accounting-app/pkg/api"
"github.com/gin-gonic/gin"
)
// NotificationHandler handles HTTP requests for notification operations
type NotificationHandler struct {
notificationService *service.NotificationService
}
// NewNotificationHandler creates a new NotificationHandler instance
func NewNotificationHandler(notificationService *service.NotificationService) *NotificationHandler {
return &NotificationHandler{
notificationService: notificationService,
}
}
// GetNotifications handles GET /api/v1/notifications
// Returns a list of notifications with pagination and filtering
func (h *NotificationHandler) GetNotifications(c *gin.Context) {
// Get user ID from context
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
// Parse query parameters
input := service.NotificationListInput{}
// Parse type filter
if typeStr := c.Query("type"); typeStr != "" {
notifType := models.NotificationType(typeStr)
input.Type = &notifType
}
// Parse is_read filter
if isReadStr := c.Query("is_read"); isReadStr != "" {
isRead := isReadStr == "true"
input.IsRead = &isRead
}
// Parse pagination
if offsetStr := c.Query("offset"); offsetStr != "" {
offset, err := strconv.Atoi(offsetStr)
if err != nil || offset < 0 {
api.BadRequest(c, "Invalid offset")
return
}
input.Offset = offset
}
if limitStr := c.Query("limit"); limitStr != "" {
limit, err := strconv.Atoi(limitStr)
if err != nil || limit < 0 {
api.BadRequest(c, "Invalid limit")
return
}
input.Limit = limit
}
result, err := h.notificationService.ListNotifications(userID.(uint), input)
if err != nil {
api.InternalError(c, "Failed to get notifications: "+err.Error())
return
}
// Calculate current page (1-indexed)
currentPage := 1
if result.Limit > 0 {
currentPage = (result.Offset / result.Limit) + 1
}
api.Success(c, gin.H{
"notifications": result.Notifications,
"total": result.Total,
"page": currentPage,
"limit": result.Limit,
})
}
// GetUnreadCount handles GET /api/v1/notifications/unread-count
// Returns the count of unread notifications
func (h *NotificationHandler) GetUnreadCount(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
count, err := h.notificationService.GetUnreadCount(userID.(uint))
if err != nil {
api.InternalError(c, "Failed to get unread count: "+err.Error())
return
}
api.Success(c, gin.H{"count": count})
}
// MarkAsRead handles PUT /api/v1/notifications/:id/read
// Marks a notification as read
func (h *NotificationHandler) MarkAsRead(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid notification ID")
return
}
err = h.notificationService.MarkAsRead(userID.(uint), uint(id))
if err != nil {
if errors.Is(err, service.ErrNotificationNotFound) {
api.NotFound(c, "Notification not found")
return
}
api.InternalError(c, "Failed to mark notification as read: "+err.Error())
return
}
api.Success(c, gin.H{"message": "Notification marked as read"})
}
// MarkAllAsRead handles POST /api/v1/notifications/read-all
// Marks all notifications as read
func (h *NotificationHandler) MarkAllAsRead(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
err := h.notificationService.MarkAllAsRead(userID.(uint))
if err != nil {
api.InternalError(c, "Failed to mark all notifications as read: "+err.Error())
return
}
api.Success(c, gin.H{"message": "All notifications marked as read"})
}
// DeleteNotification handles DELETE /api/v1/notifications/:id
// Deletes a notification
func (h *NotificationHandler) DeleteNotification(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
api.Unauthorized(c, "User not authenticated")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
api.BadRequest(c, "Invalid notification ID")
return
}
err = h.notificationService.DeleteNotification(userID.(uint), uint(id))
if err != nil {
if errors.Is(err, service.ErrNotificationNotFound) {
api.NotFound(c, "Notification not found")
return
}
api.InternalError(c, "Failed to delete notification: "+err.Error())
return
}
api.NoContent(c)
}
// CreateNotification handles POST /api/v1/notifications
// Creates a new notification (Admin/System use)
func (h *NotificationHandler) CreateNotification(c *gin.Context) {
// TODO: Add admin authorization check
var input service.CreateNotificationInput
if err := c.ShouldBindJSON(&input); err != nil {
api.BadRequest(c, "Invalid request body: "+err.Error())
return
}
notification, err := h.notificationService.CreateNotification(input)
if err != nil {
api.InternalError(c, "Failed to create notification: "+err.Error())
return
}
api.Created(c, notification)
}
// BroadcastNotification handles POST /api/v1/notifications/broadcast
// Sends a notification to all active users
func (h *NotificationHandler) BroadcastNotification(c *gin.Context) {
// TODO: Add strict admin authorization check
var input service.BroadcastNotificationInput
if err := c.ShouldBindJSON(&input); err != nil {
api.BadRequest(c, "Invalid request body: "+err.Error())
return
}
// Validate input
if input.Title == "" || input.Content == "" {
api.BadRequest(c, "Title and content are required")
return
}
if input.Type == "" {
input.Type = models.NotificationTypeSystem
}
if err := h.notificationService.BroadcastNotification(input); err != nil {
api.InternalError(c, "Failed to broadcast notification: "+err.Error())
return
}
api.Success(c, gin.H{"message": "Notification broadcast started"})
}
// GetBroadcastHistory handles GET /api/v1/notifications/announcements
// Returns a list of system broadcast history
func (h *NotificationHandler) GetBroadcastHistory(c *gin.Context) {
limitStr := c.DefaultQuery("limit", "10")
offsetStr := c.DefaultQuery("offset", "0")
limit, _ := strconv.Atoi(limitStr)
offset, _ := strconv.Atoi(offsetStr)
result, err := h.notificationService.ListAnnouncements(limit, offset)
if err != nil {
api.InternalError(c, "Failed to get broadcast history: "+err.Error())
return
}
api.Success(c, result)
}
// RegisterUserRoutes registers notification routes for regular users
func (h *NotificationHandler) RegisterUserRoutes(rg *gin.RouterGroup) {
notifications := rg.Group("/notifications")
{
notifications.GET("", h.GetNotifications)
notifications.GET("/unread-count", h.GetUnreadCount)
notifications.PUT("/:id/read", h.MarkAsRead)
notifications.POST("/read-all", h.MarkAllAsRead)
notifications.DELETE("/:id", h.DeleteNotification)
}
}
// RegisterAdminRoutes registers notification routes for admin/system use
func (h *NotificationHandler) RegisterAdminRoutes(rg *gin.RouterGroup) {
notifications := rg.Group("/notifications")
{
notifications.POST("", h.CreateNotification)
notifications.POST("/broadcast", h.BroadcastNotification)
notifications.GET("/announcements", h.GetBroadcastHistory)
}
}