WASAPhoto/service/api/comments.go

185 lines
5.7 KiB
Go
Raw Permalink Normal View History

package api
import (
"encoding/json"
"net/http"
"strconv"
"github.com/julienschmidt/httprouter"
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
"github.com/notherealmarco/WASAPhoto/service/database"
)
type reqbody struct {
UID string `json:"user_id"`
Comment string `json:"comment"`
}
func (rt *_router) GetComments(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
// get the user id from the url
uid := ps.ByName("user_id")
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)
if err != nil {
helpers.SendBadRequest(w, "Invalid photo id", rt.baseLogger)
return
}
// send 404 if the user does not exist
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
return
}
2022-11-22 15:21:33 +01:00
// get limits, or use defaults
start_index, limit, err := helpers.GetLimits(r.URL.Query())
if err != nil {
helpers.SendBadRequest(w, "Invalid start_index or limit", rt.baseLogger)
return
}
// get the user's comments
2022-11-22 15:21:33 +01:00
success, comments, err := rt.db.GetComments(uid, photo_id, ctx.Auth.GetUserID(), start_index, limit)
if err != nil {
helpers.SendInternalError(err, "Database error: GetComments", w, rt.baseLogger)
return
}
if success == database.ERR_NOT_FOUND {
helpers.SendNotFound(w, "User or photo not found", rt.baseLogger)
return
}
// send the response
2023-01-10 01:21:53 +01:00
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(comments)
if err != nil {
helpers.SendInternalError(err, "Error encoding comments", w, rt.baseLogger)
return
}
}
func (rt *_router) PostComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
uid := ps.ByName("user_id")
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)
if err != nil {
helpers.SendBadRequestError(err, "Bad photo_id", w, rt.baseLogger)
return
}
// get the comment from the request
var request_body reqbody
if !helpers.DecodeJsonOrBadRequest(r.Body, w, &request_body, rt.baseLogger) {
return
}
// check if the user is authorized to post a comment
2022-11-20 19:53:24 +01:00
if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, request_body.UID, rt.db, w, rt.baseLogger, http.StatusBadRequest) {
// It returns 400 Bad Request if the user_id field in the request body is missing or an invalid user_id
// It returns 401 if the user is not logged in
// It returns 403 if the user is not authorized to post a comment as the requested user
return
2022-11-29 16:07:42 +01:00
}
// check if the comment is valid (should not contain newlines and at be between 5 and 255 characters)
2022-12-22 18:08:03 +01:00
if !helpers.MatchCommentOrBadRequest(request_body.Comment, w, rt.baseLogger) {
2022-11-29 16:07:42 +01:00
return
}
// add the comment to the database
success, err := rt.db.PostComment(uid, photo_id, request_body.UID, request_body.Comment)
if err != nil {
helpers.SendInternalError(err, "Database error: PostComment", w, rt.baseLogger)
return
}
// if user or photo does not exist, send 404
if success == database.ERR_NOT_FOUND {
helpers.SendNotFound(w, "User or photo not found", rt.baseLogger)
return
}
// send the response
helpers.SendStatus(http.StatusCreated, w, "Comment created with success", rt.baseLogger)
}
func (rt *_router) DeleteComment(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
uid := ps.ByName("user_id")
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)
if err != nil {
helpers.SendBadRequestError(err, "Bad photo_id", w, rt.baseLogger)
return
}
comment_id, err := strconv.ParseInt(ps.ByName("comment_id"), 10, 64)
if err != nil {
helpers.SendBadRequestError(err, "Bad comment_id", w, rt.baseLogger)
return
}
// Check if the user is authorized to delete that comment
// (only the user who posted the comment or the owner of the photo can delete it)
status, comment_owner, err := rt.db.GetCommentOwner(uid, photo_id, comment_id)
if err != nil {
helpers.SendInternalError(err, "Database error: GetCommentOwner", w, rt.baseLogger)
return
}
if status == database.ERR_NOT_FOUND {
helpers.SendNotFound(w, "Resource not found", rt.baseLogger)
return
}
// The authorized user must be either comment_owner or uid
owner_auth, err := ctx.Auth.UserAuthorized(rt.db, comment_owner)
if err != nil {
helpers.SendInternalError(err, "Error checking authorization", w, rt.baseLogger)
return
}
// If the status is UNAUTHORIZED, this means that the Authorization header is missing or invalid
// We don't need to check if user is 'uid' and we can send the error
if owner_auth == reqcontext.UNAUTHORIZED {
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", rt.baseLogger)
}
if owner_auth != reqcontext.AUTHORIZED {
// Authorized user is not the owner of the comment
// let's check if it's the owner of the photo
2022-11-20 19:53:24 +01:00
if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, uid, rt.db, w, rt.baseLogger, http.StatusForbidden) {
// The authorized user is not the owner of the photo, so we sent an error
return
}
// If it is authorized, the user can delete the comment.
// (else the status must be FORBIDDEN. It can't be UNAUTHORIZED because we already checked it
// and it can't be NOT_FOUND because we used it before to get the comment owner)
}
// Delete the comment
_, err = rt.db.DeleteComment(uid, photo_id, comment_id)
if err != nil {
helpers.SendInternalError(err, "Database error: DeleteComment", w, rt.baseLogger)
return
}
// We don't need to check the status because if the comment didn't exist
// we'd have already returned an error when getting the comment owner
// so we know the comment existed and was deleted, and we can safely send 204
w.WriteHeader(http.StatusNoContent)
}