Best approach to moderate posts in express nodejs mongodb - node.js

I am building a project in NodeJS, Mongodb, ExpressJS and at this time I would like to be able to moderate each post someone posts and therefore I am kindly asking what would best approach be to do so ?
I would have some users as Moderators and they would need to approve/moderate each post before the post is being released for public.
The same post could be seen only by the post creator during the time it's under review.
What would the best approach be to do something like this ?
Thnx in advance

Create post schema in mongoose
Keep a field in the schema, say
curated: {
type: Boolean,
default: false
}
This field would determine whether a post is curated and should be visible to the public, or not
Initially, curated = false
Create a front-end panel for the moderators to view the posts.
While querying in the MongoDB for the list of posts to be shown,
add appropriate filter, so that the post is visible only if the user is the creator of post or if his role is, let's say "moderator"
req.user is the current user.
Let's say req.user = {roles:["user", "moderator", "admin"]};
if(req.user.roles.indexOf("moderator")>-1){
db.getCollection("posts").find({user: req.user})
}
This will only return posts which have been created by the current user, or if the current user is a moderator
Now the user can choose to change post.curated to true, and update the post
Now, while fetching for the list of posts to be visible to public, add filter
db.getCollection("posts").find({curated:true})

Related

post middleware in mongoose not atomizing operation? still creates document even though next(err) is called

I have a model, let's call it Client and i have another model called Accounts. They are from difference collections, a client can have many different accounts. I reference the accounts within the client doc, as well as referencing back to the client from the account doc.
const Client = new mongoose.Schema({
accounts: [{
type:mongoose.ObjectId,
ref: 'Accounts',
}],
other....
})
const Accounts = new mongoose.Schema({
name:String,
clientID: mongoose.ObjectId
})
So as we can see they reference each other. I'm doing this for easy access to populating the account and such when requesting client info.
What im trying to do is when i create a new client, i also want to create a new default account and reference it in the accounts array. I tried using a pre hook when i create my new Client to create a new Account, however that doesn't update the Client account array with the newly created Account doc _id. I've tried using this.update()
Client.pre('save',async function(next,args){
if(this.isNew){
await Accounts.create({clientID:this._id})
.then(async doc=>{
console.log(doc) // this logs my account doc just fine, which means it got created
await this.update($push:{accounts:doc._id) // this doesnt seem to do anything
})
.catch(err=>next(err)
}
next()
}
So the pre hook almost did what i wanted it to do, but I can't figure out a way to update my newly created Client doc with the info from the newly created Account doc. It creates the Client doc, and it creates the Account doc. And the beauty of it is if I have an error when creating the Account doc, then since it is atomized then the Client doesn't get created. But alas, no updated accounts array...
So instead, I tried putting it into a post hook.
Client.pre('save',async function(doc, next){
await Accounts.create({clientID:doc._id})
.then(async acc=>{
await doc.update({$push:{accounts:[acc._id]}})
}).catch(err=>next(err)
}
And hey! this works!...kinda... I can create a Client document which creates an Account document, and then updates the Client to include the Account _id in its accounts array.
BUT!!! The issue im having with this approach is that it doesnt seem to be atomizing the operation. So if i deliberately make the account creation fail (by for example passing it a non ObjectID argument), then it calls next(err) which in my http request properly returns the error message, and even says that the operation failed. But in my database, the Client document still got created, unlike in the pre-hook where it stops the whole operation, in the post hook it does not 'undo' the creation of the Client.
SUMMARY AND SOLUTIONS
Basically I need a way to update a brand new doc inside of its pre.('save') hook so it will store any changed data i processed inside the hook.
Or some way to guarantee the atomization of the operation if i use a post hook to update the new doc.
Other things i've tried:
I also tried using save() inside the pre hook after creating the Account doc, but this resulted in an loop that maxed out the doc memory since it just became recursive
I tried using a pre-hook on the Accounts model so it would reference back to the Client model and update it, but this gives me both issues together. It does not update the new client doc (since it's technically not queryable yet) AND if the account creation fails, it still creates the Client.
Sorry for the long question, I appreciate any feedback or recommendations to fix this issue or different approach to achieve my goal. If you made it this far, thanks for reading!
My question was built up of a few questions, but i want to post the solution.
While i still don't know how to guarantee that an error in a post hook will make the whole operation behave atomically, the solution to this was quite simple.
Inside the pre hook,to modify the accounts array i just had to push() into it, no need to try using this.set or this.update or any other actual query, just direct modification of this
{
//inside Client pre hook
//create account doc
await Accounts.create(...).then(doc=>{
this.accounts.push(doc._id)
}).catch(err=>next(err)
}

Permission for all users to post to a specific feed

I have been attempting to implement stream via react native and seem to be struggling with what looks like a permissions issue in relation to all users being unable to post to another 'entities' feed. My use case is essentially several 'topic' pages, where all users of my app can post to and comment etc. The topic page will also have an owner who can manage the page if needs be, so they too are essentially a 'user.
I've read elsewhere that global write permissions are not enabled by default so may need a member of the stream team to look at this for me if possible.
Here is the code I'm trying to use on the client side:
let user = client.feed('user', "bob");
let activity = {
actor: "bob",
verb: 'post',
object: "Hello world",
foreign_id: 'post:1',
to: ['user:topic-page-1'],
};
user
.addActivity(activity)
.then(data => {
console.log('success');
})
.catch(reason => {
alert(reason);
});
Another small issue I can't seem to get to the bottom of, is even when posting to the same user's timeline, the post username is always displaying as "Unknown" no matter what data I attach. What am I getting wrong here please?
For the actor field to be treated as a user entity, you should use a reference, something like client.currentUser or client.user('bob'). This should fix your post username issue.

How to create a referral link in nodejs

Am trying to figure out a way of creating a unique referral link that directs to my site for each user that signs up for my site, but to be sincere i don't really know how it works out for sure, I have tried googling for it, but i can't find a perfect answer for it, any plugin needed or any way around that.
The user model, no much code i don't have any idea of this
var UserSchema = new mongoose.Schema({
username: String,
password: String,
referralLink: String
})
UserSchema.plugin(passortLocalMongoose);
module.exports = mongoose.model("User", UserSchema )
user sign up
router.post("/register", function(req,res){
var newUser = new User({username: req.body.username,referral: req.body.referral});
User.register(newUser, req.body.password, function(error, user){
if(error){
console.log(error)
req.flash("signerror", error.message)
return res.redirect("/register")
}
//Do the referral link creation stuff here, don't know for sure
})
})
if there is a way for this, would be glad if you help out
How to create a referral link?
Let this be a platform/language agnostic how-to:
Every time new User is created, generate unique link for him to be his unique referral link and store it in DB (For simplicity this could be _id which is unique in MongoDB and is auto-generated - in this case you don't have to create another field). Let's also keep count of how many people registered through this person's link, let it be referredCount.
Referral link could look for example like this: /register?referrer=${_id}, where ${_id} is user unique ID.
Send referral link to user, so he can pass it along to other people, or make it visible in his /account page - obvious step
When someone registers via this link, create new User as usual and after succesful creation, get req.query.referrer from the URL, look into DB for user with this particular ID and increase his referredCount.
Note that it's just a way of doing that, I'm sure there might be many different strategies for it.
I suggest that you create a table/collection for referrals.
Schema could contain:
userId (the user who generated the link)
numberOfUses (the number of times the link can be used)
expires at (incase you want to limit the validity to a specific period)
MongoDB will generate a _id aka referralId
You can modify the registration endpoint to accept an optional param referralId
Whenever someone uses a referal you just get the userId of the person who created the invite link.
To address Your question from the comment:
You can follow #Tomasz suggestion however to keep referral link You can use cookies.
A cookie is a small piece of data which is stored in a user’s web browser when they visit a website. When a customer comes to your website via an affiliate referral link (i.e. yoursite.com/?ref=123), application should store two main browser cookies in order to track the referring affiliate, and the visit. Their names are:
aff_ref - this cookie stores the affiliate's unique ID
aff_ref_visit_id - this cookie stores the visit ID (the visit ID increments by 1, each time any referral link is used until the cookie expires or is deleted).
So You are able to detect if user came to website using referral link by checking the cookie. It is worth to mention that
cookies should remain in the browser throughout the purchase or conversion process to track the correct affiliate so a referral can be generated for that affiliate. The cookies should also remain in the customer's browser for a period of time specified by the site admin or until the customer clears their cookies.
in the user schema .. have a field named referral (either be a name, number or takes another user's Id ) ...on the main site. create a button that will generate a user's personal referral link (using Javascript) so that the link will look like "your-domain.com/register?register=${user._id} ... then on the registration page ... there will be an hidden input field ... which will automatically be field with the query param ...
location.search so that when a user register ... the person that referred them will also be registerd ... then the route will be like
app.post('/register',(req,res) =>{
user.update({id:(req.query.register)},{referral:req.body.name})
...remaining registration routing ...
})

Feathersjs filter results

I have created a node/feathers project using this chat application guide as a base. It's working great, but now I would like to filter the results the api is giving. For example, when user makes GET request to /messages I would like the response to include only the messages that the authorized user has created, not anyone else's messages. Auth is working correctly in the api and message items have the userId who created the message, but I just don't understand what and where I'm supposed to do to filter the messages according to the user id. After hours of googling I couldn't find anything related to this or anyone even asking the question, so what am I missing here?
You can do a manual filtering. Both on before and after hooks. How to use hooks.
In before hooks you can create a function that update your query object to only get/find data it owns.
hook.params.query = { ... , ownedBy: hook.params.user._id }
Or do result filtering in after hooks, you have the hook.result which is the only thing you can manipulate in the after hooks. Then you can use Array.prototype.filter() to filter the results the user gets.

Proper way to update mongodb document from REST

simply I have AngularJs client. He wants to use every API using CRUD architecture. For example:
GET /user
GET /user/:id
POST /user
PUT /user/:id
DEL /user/:id
This is all endpoints he want to use for my schema.(using MongoDB database).
I have user schema like (simplified):
{
id : ObjectId("..."),
name: "Foo fooer",
itemIds : [
ObjectId("..."),
ObjectId("..."),
ObjectId("...")
]
}
and schema Items(do not need to show for questions).
We need to add/remove itemsId from user.itemIds.
Client wants to create new schema userItems:
{
id : ObjectId("..."),
userId : ObjectId("..."),
itemID : ObjectId("...")
}
and He want to remove user.itemIds from user schema and create 4 CRUD endpoints /userItems.
I think this wrong approach, normalizing the mongo database.
But I don't know which one of these is better from both sides(client and server).
1) Create 2 endpoints POST /UserItem, DEL /UserItem to update items in user.itemIds.
2) Update user.itemIds using existing API PUT /user, but client needs to send whole Array of itemsIds to update it(if there are many this is probably bad approach.
Client say these 2 approaches are bad, and he only know his SQL REST archictere(where everything is normalized). How can I prove him, that he is wrong? Because he said to me this:
Server to adapt to the client and not vice versa.
Thank you.
This is a good article about RESTful API.
In brief, your RESTful API should:
Focus on resource
Make sense
Consistence
For example:
When you want to add an item to an user: find the user first, not the other way around. So, POST users/:userId/items/ to add a new item to user is good. Or, DELETE users/:userId/items/:itemId to remove an item from the user.
When you want to find all the users who have this specific item: GET /items/:itemId/users/.

Resources