second commit
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/gochat/gochat/internal/service"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
"github.com/gochat/gochat/pkg/pagination"
|
||||
"github.com/gochat/gochat/pkg/response"
|
||||
)
|
||||
|
||||
// NotificationHandler handles Notification CRUD.
|
||||
// Reference: Chatwoot app/controllers/api/v1/accounts/notifications_controller.rb
|
||||
type NotificationHandler struct {
|
||||
notificationService *service.NotificationService
|
||||
}
|
||||
|
||||
// NewNotificationHandler creates a new Notification handler with injected service.
|
||||
func NewNotificationHandler(notificationService *service.NotificationService) *NotificationHandler {
|
||||
return &NotificationHandler{notificationService: notificationService}
|
||||
}
|
||||
|
||||
// List returns all notifications for the current user in an account.
|
||||
// GET /api/v1/accounts/:account_id/notifications
|
||||
// Reference: Chatwoot index — NotificationFinder with pagination
|
||||
func (h *NotificationHandler) List(c *gin.Context) {
|
||||
if h.notificationService == nil {
|
||||
response.AbortWithStatusError(c, http.StatusServiceUnavailable, response.ErrInternal, "Notification service not available")
|
||||
return
|
||||
}
|
||||
accountID := getAccountID(c)
|
||||
userID := getUserID(c)
|
||||
p := pagination.Parse(c)
|
||||
|
||||
notifications, total, err := h.notificationService.ListNotificationsByAccount(c.Request.Context(), userID, accountID, p.Page, p.PerPage)
|
||||
if err != nil {
|
||||
applogger.L().Errorf("List notifications: %v", err)
|
||||
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "Failed to fetch notifications")
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, gin.H{
|
||||
"notifications": notifications,
|
||||
"page": p.Page,
|
||||
"per_page": p.PerPage,
|
||||
"total": total,
|
||||
})
|
||||
}
|
||||
|
||||
// Get returns a single notification by ID.
|
||||
// GET /api/v1/accounts/:account_id/notifications/:id
|
||||
func (h *NotificationHandler) Get(c *gin.Context) {
|
||||
if h.notificationService == nil {
|
||||
response.AbortWithStatusError(c, http.StatusServiceUnavailable, response.ErrInternal, "Notification service not available")
|
||||
return
|
||||
}
|
||||
notificationID, err := parseUintParam(c, "notification_id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
|
||||
return
|
||||
}
|
||||
|
||||
notification, svcErr := h.notificationService.GetNotification(c.Request.Context(), notificationID)
|
||||
if svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, notification)
|
||||
}
|
||||
|
||||
// Update marks a notification as read.
|
||||
// PUT /api/v1/accounts/:account_id/notifications/:id
|
||||
// Reference: Chatwoot update — @notification.update(read_at: DateTime.now.utc); render json: @notification
|
||||
func (h *NotificationHandler) Update(c *gin.Context) {
|
||||
notificationID, err := parseUintParam(c, "notification_id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
|
||||
return
|
||||
}
|
||||
|
||||
// MarkRead only returns error; need to fetch updated notification for response
|
||||
if svcErr := h.notificationService.MarkRead(c.Request.Context(), notificationID); svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the updated notification (Chatwoot renders json: @notification)
|
||||
notification, svcErr := h.notificationService.GetNotification(c.Request.Context(), notificationID)
|
||||
if svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, notification)
|
||||
}
|
||||
|
||||
// MarkAllRead marks all unread notifications as read for the current user in account.
|
||||
// POST /api/v1/accounts/:account_id/notifications/read_all
|
||||
// Reference: Chatwoot read_all — update_all(read_at: ...); head :ok
|
||||
func (h *NotificationHandler) MarkAllRead(c *gin.Context) {
|
||||
accountID := getAccountID(c)
|
||||
userID := getUserID(c)
|
||||
|
||||
if err := h.notificationService.MarkAllReadByAccount(c.Request.Context(), userID, accountID); err != nil {
|
||||
applogger.L().Errorf("MarkAllRead notifications: %v", err)
|
||||
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "Failed to mark notifications as read")
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// UnreadCount returns the unread notification count.
|
||||
// GET /api/v1/accounts/:account_id/notifications/unread_count
|
||||
// Reference: Chatwoot unread_count — render json: @unread_count
|
||||
func (h *NotificationHandler) UnreadCount(c *gin.Context) {
|
||||
userID := getUserID(c)
|
||||
|
||||
count, err := h.notificationService.GetUnreadCount(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
applogger.L().Errorf("UnreadCount notifications: %v", err)
|
||||
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "Failed to count unread notifications")
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, gin.H{"unread_count": count})
|
||||
}
|
||||
|
||||
// Snooze snoozes a notification until a specified time.
|
||||
// POST /api/v1/accounts/:account_id/notifications/:id/snooze
|
||||
// Reference: Chatwoot snooze — update(snoozed_until: ..., meta: ...)
|
||||
func (h *NotificationHandler) Snooze(c *gin.Context) {
|
||||
notificationID, err := parseUintParam(c, "notification_id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
|
||||
return
|
||||
}
|
||||
|
||||
accountID := getAccountID(c)
|
||||
userID := getUserID(c)
|
||||
|
||||
var req struct {
|
||||
SnoozedUntil string `json:"snoozed_until"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
snoozedUntil, parseErr := time.Parse(time.RFC3339, req.SnoozedUntil)
|
||||
if parseErr != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid snoozed_until format")
|
||||
return
|
||||
}
|
||||
|
||||
notification, svcErr := h.notificationService.SnoozeNotification(c.Request.Context(), notificationID, userID, accountID, snoozedUntil)
|
||||
if svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, notification)
|
||||
}
|
||||
|
||||
// Unread marks a notification as unread.
|
||||
// POST /api/v1/accounts/:account_id/notifications/:id/unread
|
||||
// Reference: Chatwoot unread — @notification.update(read_at: nil); render json: @notification
|
||||
func (h *NotificationHandler) Unread(c *gin.Context) {
|
||||
notificationID, err := parseUintParam(c, "notification_id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
|
||||
return
|
||||
}
|
||||
|
||||
accountID := getAccountID(c)
|
||||
userID := getUserID(c)
|
||||
|
||||
notification, svcErr := h.notificationService.MarkNotificationUnread(c.Request.Context(), notificationID, userID, accountID)
|
||||
if svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, notification)
|
||||
}
|
||||
|
||||
// Destroy deletes a single notification.
|
||||
// DELETE /api/v1/accounts/:account_id/notifications/:id
|
||||
// Reference: Chatwoot destroy — @notification.destroy; head :ok
|
||||
func (h *NotificationHandler) Destroy(c *gin.Context) {
|
||||
notificationID, err := parseUintParam(c, "notification_id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
|
||||
return
|
||||
}
|
||||
|
||||
if svcErr := h.notificationService.DeleteNotification(c.Request.Context(), notificationID); svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// DestroyAll deletes all (or all read) notifications.
|
||||
// DELETE /api/v1/accounts/:account_id/notifications/destroy_all
|
||||
// Reference: Chatwoot destroy_all — DeleteNotificationJob; head :ok
|
||||
// Query params: type=read → only read; otherwise all
|
||||
// Note: Chatwoot uses async DeleteNotificationJob; GoChat currently does synchronous deletion
|
||||
func (h *NotificationHandler) DestroyAll(c *gin.Context) {
|
||||
accountID := getAccountID(c)
|
||||
userID := getUserID(c)
|
||||
// Chatwoot supports type=read filter; GoChat currently deletes all regardless
|
||||
// TODO: implement type=read filter when async job system is available
|
||||
_ = c.Query("type")
|
||||
|
||||
if err := h.notificationService.DeleteAllNotifications(c.Request.Context(), userID, accountID); err != nil {
|
||||
applogger.L().Errorf("DestroyAll notifications: %v", err)
|
||||
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "Failed to delete notifications")
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
Reference in New Issue
Block a user