How to check when firebase user was created? - javascript

Im using a schedule function to delete unverified users. The schedule function runs every 24 hours. Now the Problem that I have is : Lets say a user created his account 3 minutes ago and did not verify his email address yet. And then the functions runs and delete his/her/it Account. What I want is to give the user lets say 5 hours to verify his account . So my question is how can I check that in my function ?
Here's my function
import * as functions from "firebase-functions";
import admin from "firebase-admin";
export default functions.pubsub.schedule('every 24 hours').onRun(async(context) => {
console.log('This will be run every 24 hours!');
const users = []
const listAllUsers = (nextPageToken) => {
// List batch of users, 1000 at a time.
return admin.auth().listUsers(1000, nextPageToken).then((listUsersResult) => {
listUsersResult.users.forEach((userRecord) => {
users.push(userRecord)
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
}).catch((error) => {
const functions = require("firebase-functions");
functions.logger.error("Hello from info. Here's an problem:", error);
});
};
// Start listing users from the beginning, 1000 at a time.
await listAllUsers();
const unVerifiedUsers = users.filter((user) => !user.emailVerified).map((user) => user.uid)
//DELETING USERS
return admin.auth().deleteUsers(unVerifiedUsers).then((deleteUsersResult) => {
console.log(`Successfully deleted ${deleteUsersResult.successCount} users`);
console.log(`Failed to delete ${deleteUsersResult.failureCount} users`);
deleteUsersResult.errors.forEach((err) => {
console.log(err.error.toJSON());
});
return true
}).catch((error) => {
const functions = require("firebase-functions");
functions.logger.error("Hello from info. Here's an problem:", error);
return false
});
});
If any question please let me know !

That's not possible with the Firebase Auth APIs. You will have to record the time on your own in a separate database, and using that database to find out the time you need to know.

Related

Firebase Cloud function not updating data in Firestore database

So I'm writing a scheduled cloud function that is supposed to run every minute and update certain values in my Firestore database. I'm getting a success on the function and I don't see any errors, however, the database isn't updating. My project is made with flutter but I wrote the cloud function with node.js
enter image description here
This is my cloud function code.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.myScheduledCloudFunction = functions.pubsub.schedule('* * * * *').timeZone('Asia/Kuala_Lumpur').onRun(async (context) => {
admin.firestore().collection('users').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
admin.firestore().collection('users').doc(doc.id).collection('habits').get().then(function(querySnapshot) {
querySnapshot.forEach(function(habitDoc) {
admin.firestore().collection('users').doc(doc.uid).collection('habits').doc(habitDoc.id).ref.update({
'iscompleted' : false,
'completedcount': 0
})
})
})
})
})
return null;
});
I feel like it might be related to the rules but I'm not sure, for now my rules are default I haven't changed them. I hope someone can help
Thanks to #Doug Stevenson I've learned that I wasn't giving any time for the async functions to take its course. I have fixed my work and now it is working.
here's the fixed code:
exports.myScheduledCloudFunction = functions.pubsub.schedule('* * * * *').timeZone('Asia/Kuala_Lumpur').onRun(async (context) => {
const querySnapshot = await admin.firestore().collection('users').get();
querySnapshot.forEach(async (doc) => {
const habitQuerySnapshot = await admin.firestore().collection('users').doc(doc.id).collection('habits').get();
habitQuerySnapshot.forEach(async (habitDoc) => {
if (doc && doc.ref) {
await habitDoc.ref.update({
'iscompleted' : false,
'completedcount': 0
});
}
});
});
return null;
});

My firebase function update document takes MINUTES to execute the first time

I am developping an app to order food online. As backend service I am using firestore to store the data and files. The user can order dishes and there are limited stocks. So every time a user order a dish and create a basket I update the stock of the corresponding ordered dishes. I am using a firebase function in order to perform this action. To be honest it is the first I am creating firebase function.
Into the Basket object, there is a list of ordered Dishes with the corresponding database DishID. When the basket is created, I go through the DishID list and I update the Quantity in the firestore database. On my local emulator it works perfectly and very fast. But online it takes minutes to perform the first update. I can deal with some seconds. Even if it takes a few seconds (like for cold restart) it's okay. But sometimes it can take 3 minutes and someone else can order a dish during this time.
Here is my code:
//Update the dishes quantities when a basket is created
exports.updateDishesQuantity = functions.firestore.document('/Baskets/{documentId}').onCreate(async (snap, context) => {
try{
//Get the created basket
const originalBasket = snap.data();
originalBasket.OrderedDishes.forEach(async dish => {
const doc = await db.collection('Dishes').doc(dish.DishID);
console.log('Doc created');
return docRef = doc.get()
.then((result) =>{
console.log('DocRef created');
if(result.exists){
console.log('Result exists');
const dishAvailableOnDataBase = result.data().Available;
console.log('Data created');
const newQuantity = { Available: Math.max(dishAvailableOnDataBase - dish.Quantity, 0)};
console.log('Online doc updated');
return result.ref.set(newQuantity, { merge: true });
}else{
console.log("doc doesnt exist");
}
})
.catch(error =>{
console.log(error);
return null;
});
});
}catch(error){
console.log(error);
}
});
I have a couple of logs output to debug the outputs on the server. It's the doc.get() function that takes 2 minutes to execute as you can see on the logger below:
Firebase logger
Thanks for your help,
Thansk for your help. I just edited a little bit your code to make it work. I post my edited code. Thanks a lot, now it takes just 4 seconds to update the quantities.
Kid regards
//Update the dishes quantities when a basket is created
exports.updateDishesQuantity = functions.firestore.document('/Baskets/{documentId}').onCreate(async (snap, context) => {
try {
//Get the created basket
const originalBasket = snap.data();
const promises = [];
const quantities = [];
originalBasket.OrderedDishes.forEach(dish => {
promises.push(db.collection('Dishes').doc(dish.DishID).get());
quantities.push(dish.Quantity);
});
const docSnapshotsArray = await Promise.all(promises);
console.log("Promises", promises);
const promises1 = [];
var i = 0;
docSnapshotsArray.forEach(result => {
if (result.exists) {
const dishAvailableOnDataBase = result.data().Available;
const newQuantity = { Available: Math.max(dishAvailableOnDataBase - quantities[i], 0) };
promises1.push(result.ref.set(newQuantity, { merge: true }));
}
i++;
})
return Promise.all(promises1)
} catch (error) {
console.log(error);
return null;
}
});
You should not use async/await within a forEach() loop, see "JavaScript: async/await with forEach()" and "Using async/await with a forEach loop".
And since your code executes, in parallel, a variable number of calls to the asynchronous Firebase get() and set() methods, you should use Promise.all().
You should refactor your Cloud Function along the following lines:
//Update the dishes quantities when a basket is created
exports.updateDishesQuantity = functions.firestore.document('/Baskets/{documentId}').onCreate(async (snap, context) => {
try {
//Get the created basket
const originalBasket = snap.data();
const promises = [];
originalBasket.OrderedDishes.forEach(dish => {
promises.push(db.collection('Dishes').doc(dish.DishID).get());
});
const docSnapshotsArray = await Promise.all(promises);
const promises1 = [];
docSnapshotsArray.forEach(snap => {
if (result.exists) {
const dishAvailableOnDataBase = result.data().Available;
const newQuantity = { Available: Math.max(dishAvailableOnDataBase - dish.Quantity, 0) };
promises1.push(result.ref.set(newQuantity, { merge: true }));
}
})
return Promise.all(promises1)
} catch (error) {
console.log(error);
return null;
}
});
Note that instead of looping and calling push() you could use the map() method for a much concise code. However, for SO answers, I like the clarity brought by creating an empty array, populating it with a forEach() loop and passing it to Promise.all()...
Also note that since you are updating quantities in a basket you may need to use a Transaction.

Cloud Firestore - prevent overbooking of packages or products

We have a booking app that uses Vuejs and Cloud Firestore (client library web version 9).
Scenario: Two customers come to our booking site at the same time and try to book the last available package at the same time. To save the reservation, we are using firestore transactions. However, if we try to book the last package from two devices and hit the submit button "simultaneously", the code below allows both orders to be accepted. Also, the package "sold" field is only incremented. Is there any way we can improve the following code to prevent overbooking?
const packageDocRef = doc(db, ...);
try {
const orderId = await runTransaction(db, async (transaction) => {
const packageDoc = await transaction.get(packageDocRef);
if (!packageDoc.exists()) {
throw "Package does not exist!";
}
const quantity = packageDoc.data().quantity;
const sold = parseInt(packageDoc.data().sold + 1);
if (quantity >= sold) {
transaction.update(packageDocRef, { sold: increment(1) });
const orderRef = await addDoc(collection(db, collections.ORDERS), reservation);
if(orderRef.id){
dispatch ("saveTravellerDetails", { orderId: orderRef.id });
return orderRef.id;
}else{
throw "Error saving reservation!";
}
} else {
return Promise.reject("Sorry! Not enough spots available.");
}
});
console.log("Reservation created ");
return { orderId: orderId };
} catch (e) {
// This will be a "Not enough spots available" error.
console.error(e);
return { error: e };
}
Ok, I saw the error in my ways. I'm posting here in the event it can help someone else.
Instead of await addDoc(collection(db, collections.ORDERS), reservation);
I needed to use transaction.set(orderDocRef, reservation);

Send a notification every 15 minutes during the day

I want to send a notification within 1 hour of the data I added to Firebase and I want to cancel the notification 1 day after adding the data. Because I don't know JavaScript, and I'm new to the software world yet, I couldn't quite figure out its algorithm, and I wrote something like that. The addedNewCard method works, but I couldn't adjust the time.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.addedNewCard =
functions.firestore.document('Users/{userID}/Cards/{cardID}').onCreate(async
(snapshot, context) => {
const cardID = context.params.cardID;
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const cardAddedDate = newData.cardAddedDate;
const deviceIdTokens = await admin
.firestore()
.collection('DeviceToken')
.get();
var tokens = [];
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().deviceToken);
}
var payload = {
notification: {
title: 'Tekrar vakti',
body: 'Tekrar etmen gereken kelimeler var!!!',
sound: 'default',
},
data: {
click_action: 'FLUTTER_NOTIFICATIoN_CLICK',
sendCardID: cardID,
}
};
const options = {
priority: "high",
};
try {
await admin.messaging().sendToDevice(tokens, payload, options);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
})
exports.timeSettings = functions.pubsub.schedule('every 1 mins').onRun(async
(context) => {
console.log(context.timestamp);
let now = new Date();
const finishWorking = now.setDate(now.getDate + 1);
const finishWorkingStamp = admin.firestore.Timestamp.fromDate(finishWorking);
db.collection('Users/{userID}/Cards').where('cardAddedDate', '<',
finishWorkingStamp).get().then((snap) => {
if (snap.exist) {
snap.forEach(() => {
return addedNewCard();
}).catch((e) => {
return console.log(e);
});
}
});
})
Thanks to your comment, I would recommend you to use Cloud Task. With Cloud Task you can delay an execution in the futur.
When a user send a data, you can plan the 24H notification in advance (with 1 notification every 15 minutes, for 1 day -> create 96 tasks, the next one with 15 minutes more in the delay than the previous one).
You have code sample here. Have a look on this part, to change the delay
if (inSeconds) {
// The time when the task is scheduled to be attempted.
task.scheduleTime = {
seconds: inSeconds + Date.now() / 1000,
};
}
I wouldn't do the schedule notification in client side, instead, config and send schedule by server side. Try to create thread for client for processing the notifications.
You have to create a firebase cloud function where you need to upgrade your firebase account subscription and use pub-sub.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.scheduledFunctionCrontab = functions.pubsub.schedule('*/15 * * * *').timeZone('Asia/Kolkata').onRun(async (context) => {
console.log('This will be run every 15 minutes');
return null});

Cloud Functions for Firebase: how to use a Transaction promise?

I am trying to write a function in Cloud Functions that triggers every time a user gets created and which then saves that user into a list of users and finally increments a user counter.
However I am not sure if I am using promises correctly.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
const saveUserToListPromise = db.collection("users").doc(userId).set({
"userId" : userId
})
var userCounterRef = db.collection("users").doc("userCounter");
const transactionPromise = db.runTransaction(t => {
return t.get(userCounterRef)
.then(doc => {
// Add one user to the userCounter
var newUserCounter = doc.data().userCounter + 1;
t.update(userCounterRef, { userCounter: newUserCounter });
});
})
.then(result => {
console.log('Transaction success!');
})
.catch(err => {
console.log('Transaction failure:', err);
});
return Promise.all([saveUserToListPromise, transactionPromise])
})
I want to make sure that even if many users register at once that my userCounter is still correct and that the saveUser function won't be terminated before the transaction and the save to the list has happened.
So I tried this out and it works just fine however I don't know if this is the correct way of achieving the functionality that I want and I also don't know if this still works when there are actually many users triggering that function at once.
Hope you can help me.
Thanks in advance.
The correct way to perform multiple writes atomically in a transaction is to perform all the writes with the Transaction object (t here) inside the transaction block. This ensures at all of the writes succeed, or none.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
return db.runTransaction(t => {
const userCounterRef = db.collection("users").doc("userCounter")
return t.get(userCounterRef).then(doc => {
// Add one user to the userCounter
t.update(userCounterRef, { userCounter: FirebaseFirestore.FieldValue.increment(1) })
// And update the user's own doc
const userDoc = db.collection("users").doc(userId)
t.set(userDoc, { "userId" : userId })
})
})
.then(result => {
console.info('Transaction success!')
})
.catch(err => {
console.error('Transaction failure:', err)
})
})

Categories

Resources