package api import ( "fmt" "net/http" "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" ) // Likes API. func Likes() http.HandlerFunc { // Request JSON schema. type Request struct { TableName string `json:"name"` TableID uint64 `json:"id"` Unlike bool `json:"unlike,omitempty"` Referrer string `json:"page"` } // Response JSON schema. type Response struct { OK bool `json:"OK"` Error string `json:"error,omitempty"` Likes int64 `json:"likes"` } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { SendJSON(w, http.StatusNotAcceptable, Response{ Error: "POST method only", }) return } // Get the current user. currentUser, err := session.CurrentUser(r) if err != nil { SendJSON(w, http.StatusBadRequest, Response{ Error: "Couldn't get current user!", }) return } // Parse request payload. var req Request if err := ParseJSON(r, &req); err != nil { SendJSON(w, http.StatusBadRequest, Response{ Error: fmt.Sprintf("Error with request payload: %s", err), }) return } // Sanity check things. The page= param (Referrer) must be a relative URL, the path // is useful for "liked your comment" notifications to supply the Link URL for the // notification. if len(req.Referrer) > 0 && req.Referrer[0] != '/' { req.Referrer = "" } // Who do we notify about this like? var ( targetUser *models.User notificationMessage string ) switch req.TableName { case "photos": if photo, err := models.GetPhoto(req.TableID); err == nil { if user, err := models.GetUser(photo.UserID); err == nil { targetUser = user } } else { log.Error("For like on photos table: didn't find photo %d: %s", req.TableID, err) } case "users": log.Error("subject is users, find %d", req.TableID) if user, err := models.GetUser(req.TableID); err == nil { targetUser = user log.Warn("found user %s", targetUser.Username) } else { log.Error("For like on users table: didn't find user %d: %s", req.TableID, err) } case "comments": log.Error("subject is users, find %d", req.TableID) if comment, err := models.GetComment(req.TableID); err == nil { targetUser = &comment.User notificationMessage = comment.Message log.Warn("found user %s", targetUser.Username) } else { log.Error("For like on users table: didn't find user %d: %s", req.TableID, err) } } // Is the table likeable? if _, ok := models.LikeableTables[req.TableName]; !ok { SendJSON(w, http.StatusBadRequest, Response{ Error: fmt.Sprintf("Can't like table %s: not allowed.", req.TableName), }) return } // Put in a like. if req.Unlike { if err := models.Unlike(currentUser, req.TableName, req.TableID); err != nil { SendJSON(w, http.StatusBadRequest, Response{ Error: fmt.Sprintf("Error unliking: %s", err), }) return } // Remove the target's notification about this like. models.RemoveNotification(req.TableName, req.TableID) } else { if err := models.AddLike(currentUser, req.TableName, req.TableID); err != nil { SendJSON(w, http.StatusBadRequest, Response{ Error: fmt.Sprintf("Error liking: %s", err), }) return } // Notify the recipient of the like. log.Info("Added like on %s:%d, notifying owner %+v", req.TableName, req.TableID, targetUser) if targetUser != nil { notif := &models.Notification{ UserID: targetUser.ID, AboutUser: *currentUser, Type: models.NotificationLike, TableName: req.TableName, TableID: req.TableID, Message: notificationMessage, Link: req.Referrer, } if err := models.CreateNotification(notif); err != nil { log.Error("Couldn't create Likes notification: %s", err) } } } // Send success response. SendJSON(w, http.StatusOK, Response{ OK: true, Likes: models.CountLikes(req.TableName, req.TableID), }) }) }