我正在学习做一个网站usign nodejs,express,mongoose,barts.无法删除对网页上评论的回复

juzqafwq  于 2023-10-19  发布在  Go
关注(0)|答案(1)|浏览(83)

我正在学习创建一个网站,允许用户发布博客,评论博客和回复这些评论。我在comment模式中使用了replies[this]来处理对comment的回复。但是我很难允许用户删除这些回复。下面是我的服务器端代码来删除评论和回复:

// Submit form to leave comment or reply
app.post('/leaveComment/:id', requireLogin, uploadImage.single('commentImage'), async (req, res) => {
    try {

        const { commentId } = req.query;  // Extract commentId from query parameters
        const post = await Post.findById({_id: req.params.id});
        const commentImage = req.file ? `https://s3.eu-west-2.amazonaws.com/${req.file.originalname}` : null;

        const newComment = {
            commentUser: req.user, // Use the user object directly
            commentBody: req.body.commentBody,
            commentImage: commentImage,
            dateCreated: new Date()
        };
        if (!newComment.commentBody || newComment.commentBody.trim() === '') {
            // Comment body is empty or contains only whitespace
            return res.redirect(`/fullPost/${post._id}`);
        }

        if (commentId) {  // If a commentId exists, it's a reply
            const comment = post.comments.id(commentId);
            comment.replies.push(newComment);
        } else {  // It's a top-level comment
            post.comments.push(newComment);
        }

        await post.save();
        res.redirect(`/fullPost/${post._id}`);
    } catch (err) {
        console.log(err);
        res.status(500).send("An error occurred");
    }
});

// Delete a specific comment or reply
app.get('/deleteComment/:postId/:commentId', requireLogin, async (req, res) => {
    try {
        const currentUser = req.user; 
        const { replyId } = req.query; 
        const foundPost = await Post.findById(req.params.postId);
        
        if (replyId) {  // If it's a reply
            const comment = foundPost.comments.id(req.params.commentId);
            const reply = comment.replies.id(replyId);
            
            // Check if the user is the author of the reply
            if (reply.commentUser.toString() !== currentUser._id.toString()) {
                return res.status(403).send('Permission denied: You can only delete your own replies.');
            }

            reply.remove();
        } else {  // It's a top-level comment
            const comment = foundPost.comments.id(req.params.commentId);

            // Check if the user is the author of the comment
            if (comment.commentUser.toString() !== req.user._id.toString()) {
                return res.status(403).send('Permission denied: You can only delete your own comments.');
            }

        
            comment.remove();
        }
        
        await foundPost.save();
        res.status(200).send('Deleted successfully');
    } catch (err) {
        console.log(err);
        res.status(500).send("An error occurred");
    }
});

这是我的前端,

<head>
  <script src="https://cdn.jsdelivr.net/npm/emoji-mart@latest/dist/browser.js"></script>
</head>
{{#if successMessage}}
<div class="alert alert-success">
  {{successMessage}}
</div>
{{/if}}

<div class="media">
  <a href="/userProfile/{{post.postUser._id}}">
    <img src="{{post.postUser.image}}" class="mr-3" width="64px" alt="">
    <span>{{post.postUser.fullname}}</span>
  </a>
  <div class="media-body">
    <h5 class="mt-0">{{post.title}} <small><i class="{{post.icon}}"></i></small></h5>
    <img src="{{post.image}}" alt="" width="300" height="auto">
    <p>{{post.body}}</p>
    <img src="{{image}}" class="mr-3 responsive-img" alt="">
    <br>
    <small>{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
     
    <div class="comment-and-form" style="display: flex; align-items: center;"></div>
    
     <a href="#" id="comment-icon" onclick="toggleCommentForm('comment_form')">
  <i class="fa-solid fa-comment"></i> 
</a>

      <a href="/deletePost/{{post._id}}"><i class="fa fa-trash post-icons"></i></a>&nbsp &nbsp</a> 
    </h4>

    <form id="comment_form" action="/leaveComment/{{post._id}}" method="POST" enctype="multipart/form-data" class="form-inline" style="display:none; margin-left: 20px;">
      
      <div class="form-group mx-sm-2 mg-2">
         <textarea type="text" name="commentBody" id="commentBody1"  class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
        <span id="charCount1">0/1000</span>
        <button type="button" class="btn btn-warning" id="emoji-picker-btn-new1">😀</button>
    
      </div>

      <br>
      <div class="form-group mx-sm-2 mg-2">
        <input type="file" name="commentImage" id="commentImage" class="form-control-file" placeholder="Leave Comment">
      </div>
      &nbsp <button class="btn btn-primary mb-2 mt-2" type="submit">
        <i class="fa-brands fa-telegram"></i> Post
      </button>
    </form>
    <hr>
    {{#each post.comments}}
    <div class="media mt-3">
      <a class="mr-3" href="/userProfile/{{commentUser._id}}">
        <img src="{{commentUser.image}}" class="mr-3" width="64px"  alt="">
        <span>{{commentUser.fullname}}</span>
      </a>
      
      <div class="media-body">
        <p id="comment-body-{{_id}}">{{commentBody}}</p>
       
        <form id="edit-comment-form-{{_id}}" action="#" method="POST" style="display:none">
          <textarea name="newCommentBody" id="newCommentBody-{{_id}}" class="form-control emoji-input"   rows="4" cols="50" maxlength="1000">{{commentBody}}</textarea>
          <button type="button" class="btn btn-warning" id="emoji-picker-btn-{{_id}}">😀</button>
          <button type="button" onclick="submitEditComment('{{../post._id}}', '{{_id}}')">Save</button>
          <span id="charCount">0/1000</span>
        </form>
      
        {{#if commentImage}}
        <img src="{{commentImage}}" alt="User uploaded image" width="200" height="auto">
        {{/if}}
        <br>
        <small data-comment-date-{{_id}}="{{getLastMoment dateCreated 'x'}}">{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
       
      
          {{#if commentUser}}
           &nbsp
         &nbsp&nbsp
      <a href="/editComment/{{post._id}}/{{comment._id}}" class="edit-button" onclick="showEditForm(event,'{{_id}}','data-comment-date-{{_id}}')">
  <i class="fa fa-pencil post-icons"></i>
</a>

       &nbsp
      &nbsp
     <a href="#" onclick="deleteComment('{{../post._id}}', '{{_id}}')"><i class="fa fa-trash post-icons"></i></a>
{{/if}}
      
        <!-- Reply button -->
    <a href="#" onclick="toggleReplyForm('{{_id}}', '{{../post._id}}')">&nbsp&nbsp&nbsp&nbspReply</a>

    <!-- Reply form -->
    <form id="reply-form-{{_id}}" action="/leaveComment/{{../post._id}}?commentId={{_id}}" method="POST" enctype="multipart/form-data" style="display: none;">
   <textarea type="text" name="commentBody" id="replycommentBody"  class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
        <span id="replycharCount">0/1000</span>
        <button type="button" class="btn btn-warning" id="emoji-picker-btn-reply">😀</button>
       <button class="btn btn-primary mb-2 mt-2" type="submit">
        <i class="fa-brands fa-telegram"></i> Post
      </button>
    </form>

 <div class="expand-replies">
    <!-- Expand/Collapse button for replies -->
    <a href="#" onclick="toggleReplies('{{_id}}')">Expand Replies</a>
    <hr>
</div>


  </div>
</div>

 <div id="replies-container-{{_id}}" style="display: none;" class="replies-container">
  {{#each replies}}
    <div class="media mt-3">
      <a class="mr-3" href="/userProfile/{{commentUser._id}}">
       <img src="{{commentUser.image}}" class="mr-3 user-image" alt="user profile">
        <span>{{commentUser.fullname}}</span>
      
        
      </a>
      <div class="media-body">
        <p id="comment-body-{{_id}}">{{commentBody}}</p>
       
        
        {{#if commentImage}}
         <img src="{{commentUser.image}}" class="mr-3 user-image" alt="user image">
          
        {{/if}}
        
        <br>
        <small data-comment-date-{{_id}}="{{getLastMoment dateCreated 'x'}}">{{getLastMoment dateCreated 'MMM Do YYYY, h:mm:ss a'}}</small>
       

       
          
          
          &nbsp
          &nbsp
        <a href="#" onclick="deleteReply('{{../post._id}}', '{{_id}}', '{{replyId}}')">
     <i class="fa fa-trash post-icons"></i>
 </a>
   
      
      
      </div>
    </div>
    <hr>
  {{/each}}
</div>
       
      </div>
    </div>
    <hr>
    {{/each}}
    {{#if post.allowComments}}
    <form id="comment_form" action="/leaveComment/{{post._id}}" method="POST" enctype="multipart/form-data" class="form-inline" style="display:none">
      <div class="form-group mx-sm-2 mg-2">
       
        <textarea type="text" name="commentBody" id="commentBody2" class="form-control emoji-input" placeholder="Leave Comment..." rows="4" cols="50" maxlength="1000"></textarea>
        <span id="charCount2">0/1000</span>
         <button type="bu
oup mx-sm-2 mg-2">
        <input type="file" name="commentImage" id="commentImage" class="form-control-file">
      </div>
      <button class="btn btn-primary mb-2 mt-2" type="submit">
        <i class="fa fa-comment"></i>
      </button>
    </form>


    <script>
  let isPickerOpen = false; // Track whether the emoji picker is open
  
  
  function toggleCommentForm(formId) {
  const commentForm = document.getElementById(formId);

  if (commentForm.style.display === 'none' || commentForm.style.display === '') {
    // Comment form is closed or not set, so open it
    commentForm.style.display = 'block';
  } else {
    // Comment form is open, so close it
    commentForm.style.display = 'none';
  }
}

  
 function showEditForm(event, commentId, dateAttribute) {
    event.preventDefault();
    const FIFTEEN_MINUTES = 15 * 60 * 1000;
    const commentDateElement = document.querySelector(`[data-comment-date-${commentId}]`);
    const commentDate = parseInt(commentDateElement.getAttribute(`data-comment-date-${commentId}`), 10);
    const now = new Date().getTime();
    const timeDifference = now - commentDate;
    if (timeDifference > FIFTEEN_MINUTES) {
      alert('Time limit exceeded, you cannot edit your comment.');
      return;
    }
    document.getElementById('comment-body-' + commentId).style.display = 'none';
    document.getElementById('edit-comment-form-' + commentId).style.display = 'block';
    document.getElementById(`emoji-picker-btn-${commentId}`).addEventListener('click', function() {
      toggleEmojiPicker(`newCommentBody-${commentId}`, `charCount-${commentId}`);
    });
  }

function submitEditComment(postId, commentId) {
    const newCommentBody = document.getElementById(`newCommentBody-${commentId}`).value;
    const xhr = new XMLHttpRequest();
    xhr.open('POST', `/editComment/${postId}/${commentId}`, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                location.reload(); // Reload the page for a successful edit
            } else if (xhr.status === 403) {
                // Permission denied, display an error message
                alert('Permission denied: You can only edit your own comments.');
            } else {
                // Handle other error cases and display appropriate messages
                alert('An error occurred while editing the comment.');
            }
        }
    };
    xhr.send(`commentBody=${encodeURIComponent(newCommentBody)}`);
}


  function deleteComment(postId, commentId) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', `/deleteComment/${postId}/${commentId}`, true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                location.reload(); // Reload the page for successful deletion
            } else if (xhr.status === 403) {
                // Permission denied, display an error message
                alert('Permission denied: You can only delete your own comments.');
            } else {
                // Handle other error cases and display appropriate messages
                alert('An error occurred while deleting the comment.');
            }
        }
    };
    xhr.send();
}



 function toggleEmojiPicker(inputId, charCountId) {
  if (isPickerOpen) {
    const existingPicker = document.getElementById('emoji-picker');
    if (existingPicker) {
      existingPicker.remove();
    }
    isPickerOpen = false;
  } else {
    const pickerOptions = {
      onEmojiSelect: function(emoji) {
        const emojiInput = document.getElementById(inputId);
        emojiInput.value += emoji.native;
        const charCountSpan = document.getElementById(charCountId);
        charCountSpan.innerText = `${emojiInput.value.length}/1000`;
      }
    };
    const picker = new EmojiMart.Picker(pickerOptions);
    picker.id = 'emoji-picker';
    picker.style.position = 'fixed';
    picker.style.bottom = '0px';
    picker.style.left = '0px';
    document.body.appendChild(picker);
    isPickerOpen = true;

    // Make the picker draggable
    let isDragging = false;
    let offsetX, offsetY;

    picker.addEventListener('mousedown', function(event) {
      isDragging = true;
      offsetX = event.clientX - picker.getBoundingClientRect().left;
      offsetY = event.clientY - picker.getBoundingClientRect().top;
    });

    window.addEventListener('mousemove', function(event) {
      if (isDragging) {
        const x = event.clientX - offsetX;
        const y = event.clientY - offsetY;
        picker.style.left = x + 'px';
        picker.style.top = y + 'px';
        picker.style.bottom = 'auto';  // Reset 'bottom' to allow 'top' to take effect
      }
    });

    window.addEventListener('mouseup', function() {
      isDragging = false;
    });
  }
}
function initializeTextareaAndCharCount(textareaId, charCountId) {
  const textarea = document.getElementById(textareaId);
  const charCountSpan = document.getElementById(charCountId);

  textarea.addEventListener('input', function() {
    const charCount = this.value.length;
    charCountSpan.innerText = `${charCount}/1000`;
  });
}



initializeTextareaAndCharCount('replycommentBody', 'replycharCount');
document.getElementById('emoji-picker-btn-reply').addEventListener('click', function() {
  toggleEmojiPicker('replycommentBody', 'replycharCount');
});


initializeTextareaAndCharCount('commentBody1', 'charCount1');
document.getElementById('emoji-picker-btn-new1').addEventListener('click', function() {
  toggleEmojiPicker('commentBody1', 'charCount1');
});
initializeTextareaAndCharCount('commentBody2', 'charCount2');
document.getElementById('emoji-picker-btn-new2').addEventListener('click', function() {
  toggleEmojiPicker('commentBody2', 'charCount2');
});



// Function to toggle the reply form for a comment
  function toggleReplyForm(commentId, postId) {
    event.preventDefault();
    const replyForm = document.getElementById(`reply-form-${commentId}`);
    replyForm.style.display = replyForm.style.display === 'none' ? 'block' : 'none';
  }

  // Function to toggle the visibility of replies
  function toggleReplies(commentId) {
    event.preventDefault();
    const repliesContainer = document.getElementById(`replies-container-${commentId}`);
    repliesContainer.style.display = repliesContainer.style.display         === 'none' ? 'block' : 'none';
     }





    </script>

    
    
 
    {{else}}
    <p class="red"> Comments are not allowed </p>
    {{/if}}
    </div>
    </div>

每次我试图删除回复时,都会收到404错误。我可以删除评论,这只是回复。我已经尝试了许多事情,如改变路由和功能,包括replyId,但它只是不断给404错误。知道为什么不管用吗

um6iljoc

um6iljoc1#

  • 列表项

下面是我从你提供的代码片段中可以告诉你的:
删除此路由中的注解是可行的,因为/deleteComment/:postId/:commentId接收postIdcommentId,这是删除注解所需的唯一两个参数。当你试图删除一个回复时,你的服务器端代码没有收到所需的replyId,因此出现了404。
此外,当你有一个GET路由时,它需要在前端和后端都完全匹配,才能正常工作。这就是为什么有两个GET请求将解决你的问题。一个删除评论/deleteComment/:postId/:commentId,一个删除回复/deleteReply/:postId/:commentId/:replyId
有多种方法可以做到这一点。这里有一个方法。
服务器端:

app.get('/deleteComment/:postId/:commentId', requireLogin, async (req, res) => {
  try {
      const currentUser = req.user; 
      const foundPost = await Post.findById(req.params.postId);
  
      const comment = foundPost.comments.id(req.params.commentId);

      if(!comment) {
          return res.status(404).send('Comment not found');
      }

      // Check if the user is the author of the comment
      if (comment.commentUser.toString() !== currentUser._id.toString()) {
          return res.status(403).send('Permission denied: You can only delete your own comments.');
      }

      comment.remove();
      
      await foundPost.save();
      res.status(200).send('Deleted successfully');
  } catch (err) {
      console.log(err);
      res.status(500).send("An error occurred");
  }
});

app.get('/deleteReply/:postId/:commentId/:replyId', requireLogin, async (req, res) => {
  try {
      const currentUser = req.user; 
      const foundPost = await Post.findById(req.params.postId);
      

      const comment = foundPost.comments.id(req.params.commentId);
      const reply = comment.replies.id(req.params.replyId);

      if(!reply) {
          return res.status(404).send('Reply not found');
      }
      
      // Check if the user is the author of the reply
      if (reply.commentUser.toString() !== currentUser._id.toString()) {
          return res.status(403).send('Permission denied: You can only delete your own replies.');
      }

      reply.remove();
      
      await foundPost.save();
      res.status(200).send('Deleted successfully');
  } catch (err) {
      console.log(err);
      res.status(500).send("An error occurred");
  }
});

前端:

function deleteReply(postId, commentId, replyId) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', `/deleteReply/${postId}/${commentId}/${replyId}`, true);
  xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
          if (xhr.status === 200) {
              location.reload(); 
          } else if (xhr.status === 403) {
             
              alert('Permission denied: You can only delete your own reply.');
          } else {
             
              alert('An error occurred while deleting the reply.');
          }
      }
  };
  xhr.send();
}

  function deleteComment(postId, commentId) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', `/deleteComment/${postId}/${commentId}`, true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                location.reload(); 
            } else if (xhr.status === 403) {
               
                alert('Permission denied: You can only delete your own comments.');
            } else {
               
                alert('An error occurred while deleting the comment.');
            }
        }
    };
    xhr.send();
}

现在确保你使用两个不同的锚点,一个删除回复,一个删除帖子:
删除备注:

<a href="#" onclick="deleteComment('{{../post._id}}', '{{_id}}')">
     <i class="fa fa-trash post-icons"></i>
 </a>

删除回复(确保使用正确的replyId变量)

<a href="#" onclick="deleteReply('{{../post._id}}', '{{_id}}', '{{replyId}}')">
     <i class="fa fa-trash post-icons"></i>
 </a>

同样,您将面临404错误,因为rpleyId在当前代码中未定义,因为它不是URL查询的一部分。
这也是一个很好的做法,当你使用一个路由来删除一些东西使用一个路由。
如果你需要更多的帮助,我很乐意帮助:)

相关问题