Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing remove tweet and like/upvote functionality in Firebase

Continuing from this thread, on HN: https://news.ycombinator.com/item?id=5462769

Reading through the firefeed rules file answered a lot of questions for me, except for these two:

  1. Editing an existing tweet isn't allowed (".write": "!data.exists()"). How can you make it not editable, but deletable by the author?
  2. How would you securely handle liking/unliking or upvoting/downvoting? write if authenticated, validate for the increase/decrease by one, if the user hasn't modified this before? How would that work? Would there have to be a child list of people who edited this? I'm just really curious about this specific use case as it seems pretty common in many apps, yet seems to me, would be really complicated to implement in firebase?
like image 579
Kirill Avatar asked Mar 31 '13 10:03

Kirill


1 Answers

1. Not editable but deletable by the author

".write": "!data.exists() || (!newData.exists() && data.child('author') === auth.id)"

2. Liking/Upvoting

On the client, use a transaction which allows you to increment the value safely:

ref.transaction(function(currentValue) {
   return (currentValue||0)+1;
}, function(error) {
   if( error ) /* failed too many times */
   else /* it worked */
});

Security is also straightforward:

".validate": "newData.isNumber() && newData.val() === data.val()+1"

2.5 Ensuring Unique Votes

I'm not sure what this means; the records can't be edited and presumably if they could, only the author would be able to do so; so I don't really understand "modified" in this context: "if the user hasn't modified this before? How would that work?"

To ensure votes are unique, you just store them by user ID. The user can remove their vote by deleting the record.

I'd recommend storing these in a separate path than the sparks and still maintaining a simple increment (the messages that are getting voted up/down) as you don't want to have to retrieve the entire list of voters each time you fetch the spark.

The security rules would look like so:

"votes": {
    "$spark_id": {
       "$vote": {
          ".read": "$vote === auth.id",
          ".write": "$vote === auth.id", 
          // to allow downvoting in addition to up or delete, just add -1 here
          ".validate": "newData.val() === 1 || newData.val() === null"
       }
    }
}

And now add a check to the validate rule for the increment:

".validate": "!root.child('votes').child($spark_id).child(auth.id).exists() && newData.isNumber() && newData.val() === data.val()+1"
like image 80
Kato Avatar answered Sep 20 '22 11:09

Kato