Auto Delete Mongo Document if Email is Not Confirmed in NodeJS - node.js

I have a working user registration system in NodeJS using Express and Mongoose. At the moment when the user creates an account, a confirmation email will be sent to that email and when the link is clicked, it will confirm their account and let them set a password. Though I want to delete the entire mongo document if the user does not confirm their account within 60 minutes. What is the best method on going about this?

Well mongoose is a javascript wrapper over mongoDB so you should just use regular javascript to do this.
I would take advantage of the pre hook:
https://mongoosejs.com/docs/middleware.html#pre
I would set a timeout of 60 minutes and run a delete query to mongoose and set the timer id you get in response of the setTimeout function to the actual document that gets created.
I would then, in case of validation of the account, clear the timer and remove the entry in the new user's document.
In case nothing happens, the document gets programmatically deleted.
schema.pre("save", async function(next) {
const id = setTimeout(function() {
const document = await Model.findOne(/* use some unique value with this */);
await document.remove()
}, 3600000);
this.timerId = id;
next();
}
Then in case of validation you run:
const document = await Model.findOne(/* find the document */);
clearTimeout(document.id);
This should be enough to handle the whole flow.
Please let me know if it's enough! :)

Related

Bot Framework Node.js ad hoc message TO A SPECIFIC USER

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.
I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.
//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
//REGISTERED MESSAGE
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)
I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?
function startProactiveDialog(address) {
bot.beginDialog(address, "A notification!");
}
This is how simple I think it should be. But where do you specify the user then?
You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.
They're registered to listen for the event
Their address has been saved inside of the database.
In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.
Edit:
So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.
You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this:
SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.
So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
session.userData.listenForEvent = 1 // Our property we're going to look for.
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.
So first of all, you should save the entire address json object in cosmosDB for later using.
As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:
Guide user to register & Save the user's address object with the account mapping in your DB
Create a Route in Restify or Expressjs for trigger the proactive message:
server.get('/api/CustomWebApi', (req, res, next) => {
//find the user's address in your DB as `savedAddress`
var msg = new builder.Message().address(savedAddress);
msg.text('Hello, this is a notification');
bot.send(msg);
res.send('triggered');
next();
}
);
or if you want to leverage loadSession
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('Hello, this is a notification')
session.endConversation();
}
})
res.send('triggered');
next();
});
I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

How to iterate through every document in every collection in Firestore?

Let's say my app has a collection for every restaurant that uses it. Each document for each collection is basically a type of food that has an expiration time stamp on it. What I need to do it query through every 'food' in every 'restaurant' and delete each 'food' if the current time that I get from my node js server is past the expiration time stamp. What is an efficient way to do this?
First you need a HTTP Request trigger Cloud Function that you can invoke by cron. ie. https://cron-job.org/
With this cloud function you should use the Admin JS SDK to loop through collections and fetch all documents.
You can get all collections with the function getCollections() See https://cloud.google.com/nodejs/docs/reference/firestore/0.9.x/Firestore#getCollections
import * as admin from 'firebase-admin'
try {
admin.initializeApp(functions.config().firebase)
} catch (e) {}
const db = admin.firestore()
export const getCollections = functions.https.onRequest(async(req, res) => {
const collections = await db.getCollections()
res.status(200).send(collections.map(collection => collection.id))
})

Make data pulled from a remote source available in NodeJS App

I'm just getting going with NodeJS and am trying to use jsforce (salesforce) to populate a dropdown on a form.
I've written a module the requires jsforce, sets login params, and connects.
``modules/sftools.js
const jsforce = require('jsforce')
const conn = new jsforce.Connection({
loginUrl: process.env.SF_LOGINURL
})
conn.login(process.env.SF_USER, process.env.SF_PASS)
exports.metaDropDown = async (field) =>
conn.sobject...describe..
return arrayOfValues
}
I want to make the value returned available throughout my app, so in index.js I've got
``index.js
const sftools= require('../modules/sftools')
const roles = sftools.metaDropDown(process.env.SF_ROLES)
and then I use some middleware to always set req.roles = roles.
I think the problem is that I'm requesting the roles before the connection is established, but I can't figure out the flow.
I tried logging in before the exports code, but I get an Invalid URL error, presumably because it isn't logged in yet.
I tried to put the login code directly into the metaDropdown export, which got rid of the error, but there is still no data returned.
Any suggestions?
I think the issue your having is that for the login function is expecting a callback as the third argument.
conn.login(process.env.SF_USER, process.env.SF_PASS, function() {
// do your role logic here.
})
Hope this helps.

i want to connect db dynamically when user login nodejs mongodb

please help me to get out of it.
with login api i want to set mongoose connection string.
is it possible that with login api i set connection string and it works for other api also(which will call after login api)??
like first user login at that time i set db and for further api call works for that db?
i had tried
How to connect multiple mongodb database dynamically using mongoose?
but in this solution in each api they specifies requested param.
Can we set connection string of mongo by api call?
this is my app.js
var mongoose = require('mongoose');
var connections = [test = mongoose.createConnection('mongodb://localhost:27017/test'),
demo = mongoose.createConnection('mongodb://localhost:27017/demo')];
exports.getDatabaseConnection = function (dbName) {
if (connections[dbName]) {
//database connection already exist. Return connection object
return connections['dbName'];
} else {
connections[dbName] = mongoose.createConnection('mongodb://localhost:27017/' + dbName);
return connections['dbName'];
}
}
and this is my user.js
exports.authenticate = function (req, res) {
var db = app.getDatabaseConnection(req.body.keydb);
var userDetail = db.model('userdetail', UserSchema);
userDetail.findOne({
email: req.body.email,
userType: req.body.userType
}, function (err, user) {
if (err) throw err;
if (!user) {
res.send({ status: 400, message: req.i18n.__("user.authIncorrectEmail") });
} else {...}
});
}
when i am requesting keydb value as demo..then it should select demo string which is in connection array.
but it is not working.what i am doing wrong??
i am trying to connect mongoDb as per user login request comes. i want to set db once only not on each api call. if user logout then db should be disconnect and when again user login it should again connect.
You need to keep a list of database connections, one per session. One way to do it would be to have an object where the keys are the session IDs and the values are the database connections. Alternatively you can keep one connection per user ID if you want to allow a situation where the same user has multiple sessions and they each share the database connection. Now when you create a session on user login you also create a new database connection. When you destroy a session on user logout you have to close the connection and remove it from the object where it was stored. For all other endpoints except login and logout you just look up the connection and use it.
This is how you can achieve your goal but keep in mind that keeping a separate database connection per logged in user has many disadvantages and wastes a lot of resources, both in your application and in the database.
Can we set connection string of mongo by api call?
Yes, of course. But this will mean that people will be able to trick your app into e.g. breaking into other people's databases and you will be responsible for all of the legal consequences of that.

Unable to make PUT request to Cloudant-bluemix database using Nodejs-express

I'm trying to implement a Password reset. So I'm taking the phone number of the user, getting the document from the database using the phone number to find it, and I'm taking the new password and trying to update the corresponding document using a PUT request in my Cloudant database.
app.post('/pass_rst', function(req,response){
var log='';
//log is just for me to see what's happening
var phone= req.body.phone;
log+=phone+'\n';
db.find({selector:{'phone':phone}}, function(err, result){
if(err){
throw err;
}
if(result.docs.length==0){
response.send('User doesnt exist');
}else{
var existing_data=result.docs[0];
log+=JSON.stringify(existing_data)+'\n';
var upd_pswd= req.body.new_password;
log+=upd_pswd+'\n';
var new_data=existing_data;
new_data.password=upd_pswd;
log+=JSON.stringify(new_data)+'\n';
var id= result.docs[0]._id;
log+=id+'\n';
//make PUT request to db
var options={
host:dbCredentials.host,
port:dbCredentials.port,
path:'/'+dbCredentials.dbName+'/'+id,
//url: dbCredentials.url+'/'+dbCredentials.dbName+'/'+id,
method:'PUT',
json:new_data,
headers:{
'Content-Type':'application/json',
'accept':'*/*'
}
};
log+=JSON.stringify(options)+'\n';
var httpreq= http.request(options);
//log+=JSON.stringify(httpreq);
httpreq.on('error', function(e){
response.send('Error'+e.message);
});
response.send(log+'\n\n\nUpdated');
}
});
});
dbCredentials is defined above as follows:
dbCredentials.host = vcapServices.cloudantNoSQLDB[0].credentials.host;
dbCredentials.port = vcapServices.cloudantNoSQLDB[0].credentials.port;
dbCredentials.user = vcapServices.cloudantNoSQLDB[0].credentials.username;
dbCredentials.password = vcapServices.cloudantNoSQLDB[0].credentials.password;
dbCredentials.url = vcapServices.cloudantNoSQLDB[0].credentials.url;
I've tried tinkering around with it, but in the best case scenario, I don't get an error and I see "Updated" but nothing actually happens in the database. Sometimes I get an error saying : 502 Bad Gateway: Registered endpoint failed to handle the request.
If you see what's going wrong, please let me know. Thank you.
This is the documentation on how to update documents in cloudant
UPDATE
Updating a document
PUT /$DATABASE/$DOCUMENT_ID HTTP/1.1 { "_id": "apple", "_rev":
"1-2902191555", "item": "Malus domestica", "prices": {
"Fresh Mart": 1.59,
"Price Max": 5.99,
"Apples Express": 0.79,
"Gentlefop's Shackmart": 0.49 } }
To update (or create) a document, make a PUT request with the updated
JSON content and the latest _rev value (not needed for creating new
documents) to https://$USERNAME.cloudant.com/$DATABASE/$DOCUMENT_ID.
If you fail to provide the latest _rev, Cloudant responds with a 409
error. This error prevents you overwriting data changed by other
processes. If the write quorum cannot be met, a 202 response is
returned.
Example response: { "ok":true, "id":"apple",
"rev":"2-9176459034" }
The response contains the ID and the new revision of the document or
an error message in case the update failed.
I am using bluemix -nodejs and cloudant. The best way that worked for me to do the update is to use the nano package for db interaction from node.js.
You can refer to the post here:
The summary is - By making use of nano api, you can easily update the record. You need to make sure to use - the _id and the right _rev number, while you use nano. This inturn uses PUT Method underneath.
Updating and Deleting documents using NodeJS on Cloudant DB
When you are including nano, make sure to update the package.json to have the nano dependency added. Let me know if you have further questions on the update/delete
When using the cloudant node.js module there is no separate update function.
You need to use the db.insert function also for the update with the right doc revision, so you need to read the latest revision before the insert.
https://github.com/apache/couchdb-nano#document-functions
"and also used to update an existing document, by including the _rev token in the document being saved:"
// read existing document from db
db.get(key, function(error, existing) {
if (!error)
// use revision of existing doc for new doc to update
obj._rev = existing._rev;
// call db insert
db.insert(obj, key, cb);
});

Resources