I want to save token generated into the user's confirmed email column. The token is part of the confirmation link that will be sent to the user so that when the user clicks on the link I can check if it matches, then updates it to "activated".
Now the problem is I can't figure out how to save it in the ConfirmEmailLink method .
async register(createDTO: CreateUserDto) {
const { email } = createDTO;
const user = await this.userModel.findOne({ email })
if (user) {
throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
}
const createdUser = new this.userModel(createDTO);
var newUser = await createdUser.save();
await SendEmail(createDTO.email, await **this.ConfirmEmailLink(createdUser._id)**, createDTO.email);
return this.sanitizeUser(createdUser);
//return null;
}
In the above code there is ConfirmEmailLink that is a parameter to SendEmail method
async ConfirmEmailLink(userId: string) {
const id = v4();
var payload = { userId: userId };
var secret = process.env.JWT_SIMPLE_TOKEN;
var token = jwt.encode(payload, secret);
console.log("This is uuid", userId);
var link = `${process.env.HOST}/user/confirm/${token}/${id}`;
let user = await this.userModel.findById(userId);
if (!user) {
throw new HttpException("Registration not complete, try again or contact admin", HttpStatus.NOT_FOUND);
}
**//This is where the problem is, I want to save the token in ConfirmEmail column**
await this.userModel.updateOne({confirmEmail: token});
return link;
}
I will appreciate your suggestions or if there is a better way to do this
Thanks
updateOne needs 2 parameters, a filter to identify which document to modify, and a update indicating what to do.
.updateOnde({"_id":userId},{"$set":{"confirmEmail": token}})
Related
I am making a user Authentication system and it sends users a confirmation email when user registers, the problem I'm facing is that sometimes link contains / and \ .due to this any text after the / was considered as new route. Is there any way to ignore these slashes. btw I'm using hashing to generate the link.
The link is generated by this piece of code.
const hashedEmail = await bcrypt.hash(email,10);
console.log(await bcrypt.compare(email,hashedEmail))
const mailOptions = {
from: 'someone#gmail.com',
to: email,
subject: 'Confirm your email',
text : `Thanks for registering! Please click on the link to confirm your email:
http://localhost:3500/confirm/${hashedEmail}`
}
confirmation Route
const bcrypt = require('bcrypt');
const fsPromises = require('fs').promises;
const path = require('path');
// user data:
const userData = {
users: require('../model/users.json'),
setUsersData : function(data) {this.users = data}
}
async function confirmationRoute(fastify,options,done){
fastify.get('/confirm/:id',async(req,res) => {
const HashedId = req.params.id;
let foundUser;
for (let i = 0; i < userData.users.length; i++) {
if (await bcrypt.compare(userData.users[i].email,HashedId)){
foundUser = userData.users[i];
}
}
if(foundUser){
// change isverified to true
foundUser.isVerified = true;
// write the new user data to the file
userData.setUsersData([...userData.users.filter(user => user.email !== foundUser.email),foundUser]);
await fsPromises.writeFile(path.join(__dirname,'..','model','users.json'),
JSON.stringify(userData.users)
);
res.send({"message":"Account Verified"})
}
else{
res.send({"message":"Invalid Link"})
}
})
done()
}
module.exports = confirmationRoute;
Example Link is : http://localhost:3500/confirm/$2b$10$bWUrZ6d0dQuZ8.e/Hqd..OEIaFN2By3nVE6x6H2sHCyLlA/nTriKO
You need to encode the parameter and decode it before processing it:
const param = encodeURIComponent('hello/world?')
console.log(param)
const data = decodeURIComponent(param)
console.log(data)
I make an edit profile request where is possible to edit one or more fields.
And when i make put request and want change only one field, every undefined field becomes null. How to pass undefined field? Every not mentioned field in request saves its value
//update profile
app.put("/id/:id", async (req,res) => {
try{
const {id} = req.params;
const {username, email, userdescription, photo_url} = req.body;
const ifEmailExists = await isEmailAvailable(email)
const ifUsernameExists = await isUsernameAvailable(username)
if(ifEmailExists && ifUsernameExists){
const updUser = await pool.query(
"UPDATE users SET (username, email, userdescription, photo_url) = ($1, $2, $3, $4) WHERE id = $5",
[username, email, userdescription, photo_url, id]);
res.json(`User data was updated successfully !`);
} else { if(!ifUsernameExists&& !ifEmailExists){
res.json({"msg": "Username and email are already used !"});}
else {
if(!ifEmailExists)
res.json({"msg": "Email is already used !"});
else { if(!ifUsernameExists){
res.json({"msg": "Username is already used !"});}
}
}
}
}catch(err){
console.error(err.message);
}
})
This is my code:
exports.saveUserEmail = functions.region('europe-central2').auth.user().onCreate((user) => {
const email = user.email;
const uid = user.uid;
const dt = dateTime.create();
const formatted = dt.format("Y-m-d H:M:S");
return admin.firestore().collection('users').doc(uid).set({uid: uid, email: email, created_at: formatted});
});
and i tried do update like this:
exports.saveEditedEmail = functions.region('europe-central2').auth.user().updateUser((user, uid) => {
const email = user.email;
return admin.firestore().collection('users').doc(uid).set({uid: uid, email: email,});
})
Where is my mistake?
There isn't any onUpdate() auth trigger for Cloud functions. Instead your can create a callable function and call it directly from client side to update your user.
exports.addMessage = functions.https.onCall((data, context) => {
const { email } = data;
// update email in Firebase Auth and Firestore
});
Alternatively, you can directly update the document in Firestore if the user tries to update their own profile. You can setup the following security rules so a user can update their own profile only:
match /users/{userId} {
allow update: if request.auth.uid == userId;
}
I am creating a cloud firestore function. The last step I need for now is using the userId to retrieve a document.
Here I get the userId
const userId = snap.data().userId; <<< THIS WORKS
console.log('A new transaction has been added');
Here I want insert the value from userId to retrieve the correct document.
const deviceDoc = db.collection('device').doc(**userId**); <<< THIS IS THE PROBLEM
const deviceData = await deviceDoc.get();
const deviceToken = deviceData.data().token;
I don't know how to use the variable, userId, to insert the value into the .doc(userId) to get the data.
If userId = 12345 I want the line to look like this:
const deviceDoc = db.collection('device').doc('12345');
I have tried .doc('userId'), .doc('${userId}'), as well as other things. None of these work.
How do I do this?
As Puf has responded, you can simply use doc(userId). The rest of your code looks fine, so maybe the document you are getting doesn't exist. Try the following:
const deviceRef = db.collection('device').doc(userId);
// you can shorten this to >> const deviceRef = db.doc(`device/${userId}`);
try {
const deviceDoc = await deviceRef.get();
if (!deviceDoc.exists) {
console.log(`The document for user ${userID} does not exist`);
} else {
const {token} = deviceDoc.data();
console.log('The token is:', token);
}
}
catch (err) {
console.error(err);
}
I'm trying to create a system where if the user request a coupon
It will check if the user have already requested a coupon
Then it should be in a collection as a document.
If there is no document it will generate a document.
So what I expect is if I run the code and there is a document in the collection it should cancel the operation? But it doesn't. It just updates the documents fields createdAt and qrCodeId.
export const addCoupon = functions.https.onCall(async (data, context) => {
const key = nanoid();
const today = admin.firestore.FieldValue.serverTimestamp();
const uid = context.auth!.uid;
const couponRef = db
.collection("kuponger")
.doc("newCoupons")
.collection(data.school)
.doc(uid);
const doc = await couponRef.get();
if (!context.auth) {
throw new functions.https.HttpsError(
"failed-precondition",
"Du måste vara inloggad"
);
}
if (!doc.exists) {
couponRef
.set({
createdAt: today,
createdBy: uid,
qrCodeId: key,
})
.catch((error) => {
throw new functions.https.HttpsError("unknown", error);
});
} else {
throw new functions.https.HttpsError("already-exists", "finns redan");
}
});
It looks like it should work, but when I try to run it and a document is already present in the collection, it will update the document and not cancel the operation.
Could you please help me?
If you need more information please comment.
On this line:
const uid = context.auth!.uid;
You want to use:
const uid = context.auth?.uid;
to accomplish optional chaining. However, I think it would be better to move the "precondition code" first, in which case the optional chaining should not be necessary.
export const addCoupon = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
"failed-precondition",
"Du måste vara inloggad"
);
}
const key = nanoid();
const today = admin.firestore.FieldValue.serverTimestamp();
const uid = context.auth.uid;
const couponRef = db
.collection("kuponger")
.doc("newCoupons")
.collection(data.school)
.doc(uid);
const doc = await couponRef.get();
if (!doc.exists) {
couponRef
.set({
createdAt: today,
createdBy: uid,
qrCodeId: key,
})
.catch((error) => {
throw new functions.https.HttpsError("unknown", error);
});
} else {
throw new functions.https.HttpsError("already-exists", "finns redan");
}
});
I fixed the problem by reinstalling firebase function and reinitializing it all again.