2022-08-25 04:17:34 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
2022-08-26 04:21:46 +00:00
|
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/models"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/session"
|
2022-08-25 04:17:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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"`
|
2022-08-30 03:00:15 +00:00
|
|
|
Referrer string `json:"page"`
|
2022-08-25 04:17:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-08-30 03:00:15 +00:00
|
|
|
// 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 = ""
|
|
|
|
}
|
|
|
|
|
2022-08-25 04:17:34 +00:00
|
|
|
// Who do we notify about this like?
|
2022-08-30 03:00:15 +00:00
|
|
|
var (
|
|
|
|
targetUser *models.User
|
|
|
|
notificationMessage string
|
|
|
|
)
|
2022-08-25 04:17:34 +00:00
|
|
|
switch req.TableName {
|
|
|
|
case "photos":
|
|
|
|
if photo, err := models.GetPhoto(req.TableID); err == nil {
|
|
|
|
if user, err := models.GetUser(photo.UserID); err == nil {
|
2023-07-19 05:30:29 +00:00
|
|
|
// Admin safety check: in case the admin clicked 'Like' on a friends-only or private
|
|
|
|
// picture they shouldn't have been expected to see, do not log a like.
|
|
|
|
if currentUser.IsAdmin {
|
|
|
|
if (photo.Visibility == models.PhotoFriends && !models.AreFriends(user.ID, currentUser.ID)) ||
|
|
|
|
(photo.Visibility == models.PhotoPrivate && !models.IsPrivateUnlocked(user.ID, currentUser.ID)) {
|
|
|
|
SendJSON(w, http.StatusForbidden, Response{
|
|
|
|
Error: fmt.Sprintf("You are not allowed to like that photo."),
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-08-25 04:17:34 +00:00
|
|
|
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)
|
|
|
|
}
|
2022-08-30 03:00:15 +00:00
|
|
|
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)
|
|
|
|
}
|
2022-08-25 04:17:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2023-06-16 02:06:16 +00:00
|
|
|
models.RemoveSpecificNotification(targetUser.ID, models.NotificationLike, req.TableName, req.TableID)
|
2022-08-25 04:17:34 +00:00
|
|
|
} 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,
|
2022-08-26 03:36:59 +00:00
|
|
|
AboutUser: *currentUser,
|
2022-08-25 04:17:34 +00:00
|
|
|
Type: models.NotificationLike,
|
|
|
|
TableName: req.TableName,
|
|
|
|
TableID: req.TableID,
|
2022-08-30 03:00:15 +00:00
|
|
|
Message: notificationMessage,
|
|
|
|
Link: req.Referrer,
|
2022-08-25 04:17:34 +00:00
|
|
|
}
|
|
|
|
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),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|