diff --git a/server/handlers/me/get_unreads.go b/server/handlers/me/get_unreads.go new file mode 100644 index 0000000..3c8495e --- /dev/null +++ b/server/handlers/me/get_unreads.go @@ -0,0 +1,27 @@ +package me + +import ( + "github.com/gofiber/fiber/v2" + "github.com/lareii/copl.uk/server/models" +) + +func GetUnreadNotifications(c *fiber.Ctx) error { + user, ok := c.Locals("user").(models.User) + if !ok { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "message": "User not authenticated.", + }) + } + + unreads, err := models.GetUnreadNotifications(user.ID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Error fetching unread notifications.", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "message": "Unread notifications fetched.", + "unreads": unreads, + }) +} diff --git a/server/handlers/me/update_notifications.go b/server/handlers/me/update_notifications.go new file mode 100644 index 0000000..25976d5 --- /dev/null +++ b/server/handlers/me/update_notifications.go @@ -0,0 +1,66 @@ +package me + +import ( + "github.com/gofiber/fiber/v2" + "github.com/lareii/copl.uk/server/models" + "github.com/lareii/copl.uk/server/utils" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type UpdateNotificationBody struct { + Read *bool `json:"read" validate:"required"` +} + +func UpdateNotification(c *fiber.Ctx) error { + user, ok := c.Locals("user").(models.User) + if !ok { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "message": "User not authenticated.", + }) + } + + var body UpdateNotificationBody + if err := c.BodyParser(&body); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "message": "Invalid request body.", + }) + } + + if err := utils.Validate.Struct(&body); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "message": "Missing or invalid fields.", + }) + } + + id := c.Params("id") + notificationID, err := primitive.ObjectIDFromHex(id) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "message": "Invalid notification ID.", + }) + } + + notification, err := models.GetNotificationByID(notificationID) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ + "message": "Error fetching notification.", + }) + } + + if notification.TargetUserID != user.ID { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "message": "User not authorized.", + }) + } + + err = models.UpdateNotification(notificationID, *body.Read) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Error updating notification.", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "message": "Notification updated.", + }) +} diff --git a/server/models/notifications.go b/server/models/notifications.go index 281a8ee..811aa8c 100644 --- a/server/models/notifications.go +++ b/server/models/notifications.go @@ -8,6 +8,7 @@ import ( "github.com/lareii/copl.uk/server/database" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) @@ -21,6 +22,15 @@ type Notification struct { Read bool `bson:"read" json:"read"` } +func GetNotificationByID(notificationID primitive.ObjectID) (notification Notification, err error) { + err = database.Notifications.FindOne(context.Background(), bson.M{"_id": notificationID}).Decode(¬ification) + if err != nil && err == mongo.ErrNoDocuments { + return notification, nil + } + + return notification, err +} + func GetNotifications(limit, offset int64, userID primitive.ObjectID) ([]Notification, error) { var notifications []Notification cursor, err := database.Notifications.Find(context.Background(), @@ -42,6 +52,18 @@ func GetNotifications(limit, offset int64, userID primitive.ObjectID) ([]Notific return notifications, nil } +func GetUnreadNotifications(userID primitive.ObjectID) (int64, error) { + count, err := database.Notifications.CountDocuments(context.Background(), bson.M{ + "target_user_id": userID, + "read": false, + }) + if err != nil { + return 0, fmt.Errorf("error counting unread notifications: %v", err) + } + + return count, nil +} + func CreateNotification(notification Notification) error { notification.ID = primitive.NewObjectID() notification.CreatedAt = primitive.Timestamp{T: uint32(time.Now().Unix())} @@ -54,3 +76,15 @@ func CreateNotification(notification Notification) error { return nil } + +func UpdateNotification(notificationID primitive.ObjectID, read bool) error { + _, err := database.Notifications.UpdateOne(context.Background(), + bson.M{"_id": notificationID}, + bson.M{"$set": bson.M{"read": read}}, + ) + if err != nil { + return fmt.Errorf("error updating notification: %v", err) + } + + return nil +} diff --git a/server/router/router.go b/server/router/router.go index 058a39f..5c2d12c 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -24,6 +24,8 @@ func SetupRouter(app *fiber.App) { meGroup.Patch("/", middlewares.AuthMiddleware(), middlewares.RateLimiterMiddleware(10, 60), me.UpdateUser) meGroup.Get("/feed", middlewares.AuthMiddleware(), middlewares.RateLimiterMiddleware(20, 60), me.GetFeed) meGroup.Get("/notifications", middlewares.AuthMiddleware(), middlewares.RateLimiterMiddleware(20, 60), me.GetNotifications) + meGroup.Patch("/notifications/:id", middlewares.AuthMiddleware(), me.UpdateNotification) + meGroup.Get("/notifications/unread", middlewares.AuthMiddleware(), me.GetUnreadNotifications) userGroup := app.Group("/users") userGroup.Get("/", middlewares.AuthMiddleware(), middlewares.RateLimiterMiddleware(20, 60), users.GetUsers)