package handlers import ( "encoding/json" "net/http" "strconv" "github.com/rede5/gohorsejobs/backend/internal/models" "github.com/rede5/gohorsejobs/backend/internal/services" ) type NotificationHandler struct { service *services.NotificationService } func NewNotificationHandler(service *services.NotificationService) *NotificationHandler { return &NotificationHandler{service: service} } // GetNotifications lists notifications for the authenticated user // @Summary List Notifications // @Description Get all notifications for the current user // @Tags Notifications // @Produce json // @Param limit query int false "Limit results (default 50)" // @Param offset query int false "Offset for pagination" // @Param unreadOnly query bool false "Only show unread" // @Success 200 {array} models.Notification // @Router /api/v1/notifications [get] func (h *NotificationHandler) GetNotifications(w http.ResponseWriter, r *http.Request) { // Get user ID from context (set by auth middleware) userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) offset, _ := strconv.Atoi(r.URL.Query().Get("offset")) unreadOnly := r.URL.Query().Get("unreadOnly") == "true" if limit == 0 { limit = 50 } notifications, err := h.service.GetByUserID(userID, limit, offset, unreadOnly) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(notifications) } // GetUnreadCount returns the count of unread notifications // @Summary Get Unread Count // @Description Get count of unread notifications // @Tags Notifications // @Produce json // @Success 200 {object} map[string]int // @Router /api/v1/notifications/unread-count [get] func (h *NotificationHandler) GetUnreadCount(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } count, err := h.service.GetUnreadCount(userID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]int{"count": count}) } // MarkAsRead marks a notification as read // @Summary Mark as Read // @Description Mark a specific notification as read // @Tags Notifications // @Param id path int true "Notification ID" // @Success 200 // @Router /api/v1/notifications/{id}/read [put] func (h *NotificationHandler) MarkAsRead(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid notification ID", http.StatusBadRequest) return } if err := h.service.MarkAsRead(id, userID); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } // MarkAllAsRead marks all notifications as read // @Summary Mark All as Read // @Description Mark all notifications as read for the current user // @Tags Notifications // @Success 200 // @Router /api/v1/notifications/read-all [put] func (h *NotificationHandler) MarkAllAsRead(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if err := h.service.MarkAllAsRead(userID); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } // DeleteNotification deletes a notification // @Summary Delete Notification // @Description Delete a specific notification // @Tags Notifications // @Param id path int true "Notification ID" // @Success 204 // @Router /api/v1/notifications/{id} [delete] func (h *NotificationHandler) DeleteNotification(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid notification ID", http.StatusBadRequest) return } if err := h.service.Delete(id, userID); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // RegisterFCMToken registers a device token for push notifications // @Summary Register FCM Token // @Description Register a device token for push notifications // @Tags Notifications // @Accept json // @Param token body models.RegisterFCMTokenRequest true "FCM Token" // @Success 200 // @Router /api/v1/notifications/fcm-token [post] func (h *NotificationHandler) RegisterFCMToken(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req models.RegisterFCMTokenRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } if req.Token == "" { http.Error(w, "Token is required", http.StatusBadRequest) return } if err := h.service.RegisterFCMToken(userID, req); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } // UnregisterFCMToken removes a device token // @Summary Unregister FCM Token // @Description Remove a device token from push notifications // @Tags Notifications // @Accept json // @Param token body object{token string} true "Token to remove" // @Success 200 // @Router /api/v1/notifications/fcm-token [delete] func (h *NotificationHandler) UnregisterFCMToken(w http.ResponseWriter, r *http.Request) { userID := getUserIDFromContext(r) if userID == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req struct { Token string `json:"token"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } if err := h.service.UnregisterFCMToken(userID, req.Token); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } // getUserIDFromContext extracts user ID from context (set by auth middleware) func getUserIDFromContext(r *http.Request) int { // Check for user ID in context, set by auth middleware if userID, ok := r.Context().Value("userID").(int); ok { return userID } // Fallback: check header for testing if userIDStr := r.Header.Get("X-User-ID"); userIDStr != "" { if id, err := strconv.Atoi(userIDStr); err == nil { return id } } return 0 }