package handlers import ( "context" "encoding/json" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" ) func (h *Handler) ConnectSocket(c *fiber.Ctx) error { if !websocket.IsWebSocketUpgrade(c) { h.logger.Warn("WebSocket upgrade required") return fiber.ErrUpgradeRequired } userID, ok := c.Locals("userID").(int64) if !ok || userID == 0 { h.logger.Error("Invalid user ID in context") return fiber.NewError(fiber.StatusUnauthorized, "invalid user identification") } c.Locals("allowed", true) return websocket.New(func(conn *websocket.Conn) { ctx := context.Background() logger := h.logger.With("userID", userID, "remoteAddr", conn.RemoteAddr()) if err := h.notificationSvc.ConnectWebSocket(ctx, userID, conn); err != nil { logger.Error("Failed to connect WebSocket", "error", err) _ = conn.Close() return } logger.Info("WebSocket connection established") defer func() { h.notificationSvc.DisconnectWebSocket(userID) logger.Info("WebSocket connection closed") _ = conn.Close() }() for { if _, _, err := conn.ReadMessage(); err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { logger.Warn("WebSocket unexpected close", "error", err) } break } } })(c) } func (h *Handler) MarkNotificationAsRead(c *fiber.Ctx) error { type Request struct { NotificationID string `json:"notification_id" validate:"required"` } var req Request if err := c.BodyParser(&req); err != nil { h.logger.Error("Failed to parse request body", "error", err) return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } userID, ok := c.Locals("userID").(int64) if !ok || userID == 0 { h.logger.Error("Invalid user ID in context") return fiber.NewError(fiber.StatusUnauthorized, "invalid user identification") } if err := h.notificationSvc.MarkAsRead(context.Background(), req.NotificationID, userID); err != nil { h.logger.Error("Failed to mark notification as read", "notificationID", req.NotificationID, "error", err) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update notification status") } return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Notification marked as read"}) } func (h *Handler) CreateAndSendNotification(c *fiber.Ctx) error { type Request struct { RecipientID int64 `json:"recipient_id" validate:"required_if=DeliveryScheme single"` Type domain.NotificationType `json:"type" validate:"required"` Level domain.NotificationLevel `json:"level" validate:"required"` ErrorSeverity *domain.NotificationErrorSeverity `json:"error_severity"` Reciever domain.NotificationRecieverSide `json:"reciever" validate:"required"` DeliveryScheme domain.NotificationDeliveryScheme `json:"delivery_scheme" validate:"required"` DeliveryChannel domain.DeliveryChannel `json:"delivery_channel" validate:"required"` Payload domain.NotificationPayload `json:"payload" validate:"required"` Priority int `json:"priority"` Metadata json.RawMessage `json:"metadata,omitempty"` } var req Request if err := c.BodyParser(&req); err != nil { h.logger.Error("[NotificationSvc.CreateAndSendNotification] Failed to parse request body", "error", err) return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } userID, ok := c.Locals("userID").(int64) if !ok || userID == 0 { h.logger.Error("[NotificationSvc.CreateAndSendNotification] Invalid user ID in context") return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification") } switch req.DeliveryScheme { case domain.NotificationDeliverySchemeSingle: if req.Reciever == domain.NotificationRecieverSideCustomer && req.RecipientID != userID { h.logger.Warn("[NotificationSvc.CreateAndSendNotification] Unauthorized attempt to send notification", "userID", userID, "recipientID", req.RecipientID) return fiber.NewError(fiber.StatusForbidden, "Unauthorized to send notification to this recipient") } notification := &domain.Notification{ ID: "", RecipientID: req.RecipientID, Type: req.Type, Level: req.Level, ErrorSeverity: req.ErrorSeverity, Reciever: req.Reciever, IsRead: false, DeliveryStatus: domain.DeliveryStatusPending, DeliveryChannel: req.DeliveryChannel, Payload: req.Payload, Priority: req.Priority, Metadata: req.Metadata, } if err := h.notificationSvc.SendNotification(context.Background(), notification); err != nil { h.logger.Error("[NotificationSvc.CreateAndSendNotification] Failed to send single notification", "recipientID", req.RecipientID, "error", err) return fiber.NewError(fiber.StatusInternalServerError, "Failed to send notification") } h.logger.Info("[NotificationSvc.CreateAndSendNotification] Single notification sent successfully", "recipientID", req.RecipientID, "type", req.Type) return c.Status(fiber.StatusCreated).JSON(fiber.Map{"message": "Single notification sent successfully", "notification_id": notification.ID}) case domain.NotificationDeliverySchemeBulk: recipients, err := h.getAllRecipientIDs(context.Background(), req.Reciever) if err != nil { h.logger.Error("[NotificationSvc.CreateAndSendNotification] Failed to fetch recipients for bulk notification", "error", err) return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch recipients") } notificationIDs := make([]string, 0, len(recipients)) for _, recipientID := range recipients { notification := &domain.Notification{ ID: "", RecipientID: recipientID, Type: req.Type, Level: req.Level, ErrorSeverity: req.ErrorSeverity, Reciever: req.Reciever, IsRead: false, DeliveryStatus: domain.DeliveryStatusPending, DeliveryChannel: req.DeliveryChannel, Payload: req.Payload, Priority: req.Priority, Metadata: req.Metadata, } if err := h.notificationSvc.SendNotification(context.Background(), notification); err != nil { h.logger.Error("[NotificationSvc.CreateAndSendNotification] Failed to send bulk notification", "recipientID", recipientID, "error", err) continue } notificationIDs = append(notificationIDs, notification.ID) } h.logger.Info("[NotificationSvc.CreateAndSendNotification] Bulk notification sent successfully", "recipient_count", len(recipients), "type", req.Type) return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "message": "Bulk notification sent successfully", "recipient_count": len(recipients), "notification_ids": notificationIDs, }) default: h.logger.Error("[NotificationSvc.CreateAndSendNotification] Invalid delivery scheme", "delivery_scheme", req.DeliveryScheme) return fiber.NewError(fiber.StatusBadRequest, "Invalid delivery scheme") } } func (h *Handler) getAllRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { return h.notificationSvc.ListRecipientIDs(ctx, receiver) }