package circle import ( "log" "strconv" "time" auth "donetick.com/core/internal/authorization" "donetick.com/core/internal/chore" chRepo "donetick.com/core/internal/chore/repo" cModel "donetick.com/core/internal/circle/model" cRepo "donetick.com/core/internal/circle/repo" uModel "donetick.com/core/internal/user/model" uRepo "donetick.com/core/internal/user/repo" "donetick.com/core/logging" jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" ) type Handler struct { circleRepo *cRepo.CircleRepository userRepo *uRepo.UserRepository choreRepo *chRepo.ChoreRepository } func NewHandler(cr *cRepo.CircleRepository, ur *uRepo.UserRepository, c *chRepo.ChoreRepository) *Handler { return &Handler{ circleRepo: cr, userRepo: ur, choreRepo: c, } } func (h *Handler) GetCircleMembers(c *gin.Context) { // Get the circle ID from the JWT log := logging.FromContext(c) currentUser, ok := auth.CurrentUser(c) if !ok { log.Error("Error getting current user") c.JSON(500, gin.H{ "error": "Error getting current user", }) return } // Get all the members of the circle members, err := h.circleRepo.GetCircleUsers(c, currentUser.CircleID) if err != nil { log.Error("Error getting circle members:", err) c.JSON(500, gin.H{ "error": "Error getting circle members", }) return } c.JSON(200, gin.H{ "res": members, }) } func (h *Handler) JoinCircle(c *gin.Context) { // Get the circle ID from the JWT log := logging.FromContext(c) log.Debug("handlder.go: JoinCircle") currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } requestedCircleID := c.Query("invite_code") if requestedCircleID == "" { c.JSON(400, gin.H{ "error": "Invalid request", }) return } circle, err := h.circleRepo.GetCircleByInviteCode(c, requestedCircleID) if circle.ID == currentUser.CircleID { c.JSON(409, gin.H{ "error": "You are already a member of this circle", }) return } // Add the user to the circle err = h.circleRepo.AddUserToCircle(c, &cModel.UserCircle{ CircleID: circle.ID, UserID: currentUser.ID, Role: "member", IsActive: false, }) if err != nil { log.Error("Error adding user to circle:", err) c.JSON(500, gin.H{ "error": "Error adding user to circle", }) return } c.JSON(200, gin.H{ "res": "User Requested to join circle successfully", }) } func (h *Handler) LeaveCircle(c *gin.Context) { log := logging.FromContext(c) log.Debug("handler.go: LeaveCircle") currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } rawCircleID := c.Query("circle_id") circleID, err := strconv.Atoi(rawCircleID) if err != nil { c.JSON(400, gin.H{ "error": "Invalid request", }) return } orginalCircleID, err := h.circleRepo.GetUserOriginalCircle(c, currentUser.ID) if err != nil { log.Error("Error getting user original circle:", err) c.JSON(500, gin.H{ "error": "Error getting user original circle", }) return } // START : HANDLE USER LEAVING CIRCLE // bulk update chores: if err := handleUserLeavingCircle(h, c, currentUser, orginalCircleID); err != nil { log.Error("Error handling user leaving circle:", err) c.JSON(500, gin.H{ "error": "Error handling user leaving circle", }) return } // END: HANDLE USER LEAVING CIRCLE err = h.circleRepo.LeaveCircleByUserID(c, circleID, currentUser.ID) if err != nil { log.Error("Error leaving circle:", err) c.JSON(500, gin.H{ "error": "Error leaving circle", }) return } if err := h.userRepo.UpdateUserCircle(c, currentUser.ID, orginalCircleID); err != nil { log.Error("Error updating user circle:", err) c.JSON(500, gin.H{ "error": "Error updating user circle", }) return } c.JSON(200, gin.H{ "res": "User left circle successfully", }) } func handleUserLeavingCircle(h *Handler, c *gin.Context, leavingUser *uModel.User, orginalCircleID int) error { userAssignedCircleChores, err := h.choreRepo.GetChores(c, leavingUser.CircleID, leavingUser.ID) if err != nil { return err } for _, ch := range userAssignedCircleChores { if ch.CreatedBy == leavingUser.ID && ch.AssignedTo != leavingUser.ID { ch.AssignedTo = leavingUser.ID ch.UpdatedAt = time.Now() ch.UpdatedBy = leavingUser.ID ch.CircleID = orginalCircleID } else if ch.CreatedBy != leavingUser.ID && ch.AssignedTo == leavingUser.ID { chore.RemoveAssigneeAndReassign(ch, leavingUser.ID) } } h.choreRepo.UpdateChores(c, userAssignedCircleChores) h.choreRepo.RemoveChoreAssigneeByCircleID(c, leavingUser.ID, leavingUser.CircleID) return nil } func (h *Handler) DeleteCircleMember(c *gin.Context) { log := logging.FromContext(c) log.Debug("handler.go: DeleteCircleMember") currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } rawCircleID := c.Param("id") circleID, err := strconv.Atoi(rawCircleID) if err != nil { c.JSON(400, gin.H{ "error": "Invalid request", }) return } rawMemeberIDToDeleted := c.Query("member_id") memberIDToDeleted, err := strconv.Atoi(rawMemeberIDToDeleted) if err != nil { c.JSON(400, gin.H{ "error": "Invalid request", }) return } admins, err := h.circleRepo.GetCircleAdmins(c, circleID) if err != nil { log.Error("Error getting circle admins:", err) c.JSON(500, gin.H{ "error": "Error getting circle admins", }) return } isAdmin := false for _, admin := range admins { if admin.UserID == currentUser.ID { isAdmin = true break } } if !isAdmin { c.JSON(403, gin.H{ "error": "You are not an admin of this circle", }) return } orginalCircleID, err := h.circleRepo.GetUserOriginalCircle(c, memberIDToDeleted) if handleUserLeavingCircle(h, c, &uModel.User{ID: memberIDToDeleted, CircleID: circleID}, orginalCircleID) != nil { log.Error("Error handling user leaving circle:", err) c.JSON(500, gin.H{ "error": "Error handling user leaving circle", }) return } err = h.circleRepo.DeleteMemberByID(c, circleID, memberIDToDeleted) if err != nil { log.Error("Error deleting circle member:", err) c.JSON(500, gin.H{ "error": "Error deleting circle member", }) return } c.JSON(200, gin.H{ "res": "User deleted from circle successfully", }) } func (h *Handler) GetUserCircles(c *gin.Context) { log := logging.FromContext(c) currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } circles, err := h.circleRepo.GetUserCircles(c, currentUser.ID) if err != nil { log.Error("Error getting user circles:", err) c.JSON(500, gin.H{ "error": "Error getting user circles", }) return } c.JSON(200, gin.H{ "res": circles, }) } func (h *Handler) GetPendingCircleMembers(c *gin.Context) { log := logging.FromContext(c) currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } currentMemebers, err := h.circleRepo.GetCircleUsers(c, currentUser.CircleID) if err != nil { log.Error("Error getting circle members:", err) c.JSON(500, gin.H{ "error": "Error getting circle members", }) return } // confirm that the current user is an admin: isAdmin := false for _, member := range currentMemebers { if member.UserID == currentUser.ID && member.Role == "admin" { isAdmin = true break } } if !isAdmin { c.JSON(403, gin.H{ "error": "You are not an admin of this circle", }) return } members, err := h.circleRepo.GetPendingJoinRequests(c, currentUser.CircleID) if err != nil { log.Error("Error getting pending circle members:", err) c.JSON(500, gin.H{ "error": "Error getting pending circle members", }) return } c.JSON(200, gin.H{ "res": members, }) } func (h *Handler) AcceptJoinRequest(c *gin.Context) { log := logging.FromContext(c) currentUser, ok := auth.CurrentUser(c) if !ok { c.JSON(500, gin.H{ "error": "Error getting current user", }) return } rawRequestID := c.Query("requestId") requestID, err := strconv.Atoi(rawRequestID) if err != nil { c.JSON(400, gin.H{ "error": "Invalid request", }) return } currentMemebers, err := h.circleRepo.GetCircleUsers(c, currentUser.CircleID) if err != nil { log.Error("Error getting circle members:", err) c.JSON(500, gin.H{ "error": "Error getting circle members", }) return } // confirm that the current user is an admin: isAdmin := false for _, member := range currentMemebers { if member.UserID == currentUser.ID && member.Role == "admin" { isAdmin = true break } } if !isAdmin { c.JSON(403, gin.H{ "error": "You are not an admin of this circle", }) return } pendingRequests, err := h.circleRepo.GetPendingJoinRequests(c, currentUser.CircleID) if err != nil { log.Error("Error getting pending circle members:", err) c.JSON(500, gin.H{ "error": "Error getting pending circle members", }) return } isActiveRequest := false var requestedCircle *cModel.UserCircleDetail for _, request := range pendingRequests { if request.ID == requestID { requestedCircle = request isActiveRequest = true break } } if !isActiveRequest { c.JSON(400, gin.H{ "error": "Invalid request", }) return } err = h.circleRepo.AcceptJoinRequest(c, currentUser.CircleID, requestID) if err != nil { log.Error("Error accepting join request:", err) c.JSON(500, gin.H{ "error": "Error accepting join request", }) return } if err := h.userRepo.UpdateUserCircle(c, requestedCircle.UserID, currentUser.CircleID); err != nil { log.Error("Error updating user circle:", err) c.JSON(500, gin.H{ "error": "Error updating user circle", }) return } c.JSON(200, gin.H{ "res": "Join request accepted successfully", }) } func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) { log.Println("Registering routes") circleRoutes := router.Group("circles") circleRoutes.Use(auth.MiddlewareFunc()) { circleRoutes.GET("/members", h.GetCircleMembers) circleRoutes.GET("/members/requests", h.GetPendingCircleMembers) circleRoutes.PUT("/members/requests/accept", h.AcceptJoinRequest) circleRoutes.GET("/", h.GetUserCircles) circleRoutes.POST("/join", h.JoinCircle) circleRoutes.DELETE("/leave", h.LeaveCircle) circleRoutes.DELETE("/:id/members/delete", h.DeleteCircleMember) } }