I was trying to follow up on a Tutorial. To examine an Image on a Document Creation, here is the Firestore information:
The image is stored as a link inside this document on the field image_url.
I have created a Cloud Function using the Dependency (Node Module):
"#google-cloud/vision": "^2.4.0",
My index File looks like this:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import algoliasearch from 'algoliasearch';
import vision from '#google-cloud/vision';
const env = functions.config();
//Function to check Images on User Create.
exports.checkUserAvatarImage = functions.firestore.document('users/{uid}').onCreate(async (userSnap, context) => {
const content = userSnap.data();
if(content.startsWith("http")){
return (async () => {
const client = new vision.ImageAnnotatorClient();
const [result] = await client.safeSearchDetection(content);
var detections = result.safeSearchAnnotation;
if(detections?.adult == 'LIKELY' || detections?.adult == 'VERY_LIKELY' || detections?.adult == 'POSSIBLE'){
return userSnap.ref.update({
'image_url': 'https://firebasestorage.googleapis.com/v0/b/realstatestb-ad581.appspot.com/o/user_images%2FCasaVerde.png?alt=media&token=ae018b20-858c-4ead-a64a-1871b4e22652'
});
}
return;
})()
.then(() => {
return;
}).catch(err => console.log("An error has ocurred " + err));
}
return;
});
The problem is that when I upload an Adult Content image doesn't change it for the one shown in the code, however when I check the logs on Functions always says it starts and ends with "Ok".
Any ideas?
Thank you for any guidence.
Hey Guys after the valuable input of Marc, I understood the problem the issue wasn't here but the way I sent the data to the Firestore, as when user gets created the Image isn't uploaded yet.
Here is the code:
case "Signup":
{
authResult = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
final ref = FirebaseStorage.instance
.ref()
.child('user_images')
.child(authResult.user.uid + '.jpg');
await ref.putFile(image);
final url = await ref.getDownloadURL();
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user.uid)
.set({
'username': username,
'email': email,
'image_url': url,
'name': fullname,
'phone': 0,
}).then((_) => {
// Provider.of<UserProvider>(context, listen: false).getUserInfo(
// url, fullname, email, username, authResult.user.uid);
Navigator.of(context).pushNamed(ExploreSellScreen
.routeName) //NewMainScreen.routeName) //ListingOverviewScreen.routeName)
});
}
break;
So I think now I have to find a way to upload this on Create and not as an Update. Thank you for those who read this question, and sorry for this detail.
Also if you have any suggestions please let me know.
Related
I have a React-Ionic web app doing some queries to a custom NodeJS server. I can read users but I cant create one unless I refresh my page. All of my GET and POST queries are working properly after I refresh my page.
Here is my function to create a User. Every log are showing except 'USR SUCESSFULLY LAUNCH...'
export async function createUser(p_user: StudentSkeletonDB) {
//Normalize name guid familyname and comments
//Removing all specials characters
console.log("USR STARTING TO CREATE :")
let newUser: StudentSkeletonDB = p_user;
newUser.firstname = newUser.firstname.replace(/[~`!##$%^&*()+={}\[\];:\'\"<>.,\/\\]/g, '');
newUser.guid_id = newUser.guid_id.replace(/[~`!##$%^&*()+={}\[\];:\'\"<>.,\/\\]/g, '');
newUser.familyname = newUser?.familyname != undefined ? newUser.familyname.replace(/[~`!##$%^&*()+={}\[\];:\'\"<>.,\/\\]/g, '') : "";
newUser.comments = newUser?.comments != undefined ? newUser.comments.replace(/[~`!##$%^&*()+={}\[\];:\'\"<>.,\/\\]/g, '') : "";
console.log("USR NORMALIZED :")
console.log(newUser)
var myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
myHeaders.append('Content-Lenght', '' + JSON.stringify(newUser).length);
var data = new FormData();
data.append("json", JSON.stringify(newUser));
console.log("USR SENDING... :")
return await fetch("http://127.0.0.1:8083/signup", {
headers: myHeaders,
method: 'POST',
body: JSON.stringify(newUser)
}).then((s) => {
console.log("USR SUCCESSFULLY LAUNCH... :")
return s;
});
}
On my NodeJS Server, even the first log is not showing.
exports.postSignup = async(req, res, next) => {
//getting user data from request body
console.log("STARTING TO CREATE USER")
const { guid_id, firstname, familyname, _password, birthday, usertype, class_id, login, filenumber, avatar, language, isactive, ef_sessionpid, comments, connected, created, simu_password, gender, driving_school_code, nb_connection } = req.body;
try {
const user = new User({
guid_id,
firstname,
familyname,
_password,
birthday,
usertype,
class_id,
login,
filenumber,
avatar,
language,
isactive,
ef_sessionpid,
comments,
connected,
created,
simu_password,
gender,
driving_school_code,
nb_connection
});
const result = await user.createUser();
result.send(user);
} catch (error) {
const errorToThrow = new Error();
switch (error.code) {
case '23505':
errorToThrow.message = 'User already exists';
errorToThrow.statusCode = 403;
break;
default:
errorToThrow.statusCode = 500;
}
//pass error to next()
next(errorToThrow);
}
};
I repeat it, but if I refresh any page of my web app, then create a User, everything works properly and al my logs (even NodeJS) are showing. My newUser object is fine even without refreshing, it look like fetch itself doesnt work.
I tried to change fetch url to something totally wrong like return await fetch("http://127.0.0.1:8083/dzedzededze/dzedze)" and it doesn't even raise an error.
FIXED IT !
The previous request on my NodeJS server was crashing something.
All POST requests were in a queue.
There was multiples error in my code like Content-Lenght. I'm not very good in english : 'ght' and 'gth' are a big weakness of mine.
data was not used either.
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 have to extract data from the user using auth by phone Number.
I have an array of user's phone number.
I have written a solution and it gives the required output but it doesn't terminate. How can I solve this problem?
"phoneNumberArray" is the array of user's phone number. It is mostly related to HTTP background trigger functions.I have solved this but this is a firebase issue so please see this
const user = async () => {
const getAuth = async phoneNumber => {
return auth.getUserByPhoneNumber(phoneNumber).catch(() => {
return {
phoneNumber,
uid: null,
email: '',
displayName: '',
emailVerified: false,
disabled: false,
};
});
};
const userRecord = await Promise.all(
phoneNumberArray.map(phoneNumber => {
return getAuth(phoneNumber);
}),
);
//when i console.log(userRecord) it's print the output
const x = {
'userRecord Length': userRecord.length
}
console.log(x)
return;
}
module.exports = {user}
I have used this way to solve the function.
in the main file i have to return
return x,admin;
and the calling file works like this
const {user} = require('./filename');
user().then(e => {
console.log(e.x)
e.admin.apps[0].delete();
})catch(error){
console.log(error)}
I have created a Firebase cloud function that will trigger on update of the data. When I go into Firebase console and change the node to either true or false, it triggers and I receive an email from my SendGrid set up. The problem is I am not able to obtain the users e-mail information.
I have spent over a week pouring over the documentation and it says I should be able to use context.auth, however, that is always "undefined" when printed out in console.
I have been trying to get the user data from the users actual info in Firebase as well as in /users/{uid}/email. I can't seem to figure out how to get the e-mail since the snapshot is in a different spot.
I need to somehow extract the users first name and email, which are in in:
/users/uid/first_name and /users/uid/email
I want those two things put into this function, so then I can tell SendGrid to use the email and name. The Sendgrid portion is working fine.
context.params.uid gives me the users firebase ID, but does nothing for me. I can't seem to use that to get the data I need
I tried authVar = context.auth and when I print it out it says 'undefined' and my function stops working.
exports.myFunctionPending =
functions.database.ref('/users/{uid}/profile/isPending')
.onUpdate(async (change, context) => {
const snapshot = change.after;
const val = snapshot.val();
const userid = context.params.uid; //shows userid but is useless
const authVar = context.auth; //says undefined and does nothing
console.log(val);
console.log(userid);
const msg = {
to: 'myemail#mydomain.com',
from: 'noreply#mydomain.com',
// custom templates
templateId: 'd-b7aakjsdgwq7d798wq7d8',
substitutionWrappers: ['{{', '}}'],
//substitutions: {
dynamic_template_data: {
//name: user.displayName
name: 'My Name'
}
};
try {
await sgMail.send(msg);
console.log('This was sucessful');
} catch(error) {
console.error('There was an error while sending the email:', error);
}
return null;
});
I had the code in the incorrect spot, I changed the logic and now it's working as intended.
exports.myFunction = functions.database.ref('/users/{uid}/user_area/pending')
.onUpdate(async (change, context) => {
const triggersnapshot = change.after;
const val = triggersnapshot.val();
const userid = context.params.uid;
console.log(val);
console.log(userid);
return admin.database().ref('users/' + userid).once('value').then(function (snapshot) {
var email = snapshot.child('email');
var name = snapshot.child('first_name');
console.log(snapshot.val());
console.log(email.val());
const msg = {
to: [email],
from: {
email: 'noreply#noreply.com',
name: 'No Name'
},
// custom templates
templateId: 'd-8347983274983u483242',
substitutionWrappers: ['{{', '}}'],
dynamic_template_data: {
name: name
}
};
return sgMail.send(msg);
});
I'm developing a Firebase Function, which is triggered when a new order is added to the Realtime Database. The first thing it does is to creat a pdf and pipe it to a google cloud storage bucket.
On the .on("finish") event of the bucket stream, the next function gets started, which should send the piped pdf via email to the customer.
Everything seems to work, at least a bit.
First I had the problem, that the attached pdf always was empty. (Not just blank. I also opened it in notepad++ and it really was all empty). When I checked the doc and bucketFileSream vars inside the bucketFileStream.on("finished") function both had a length of 0. A check of the doc var directly after doc.end showed a length of somewhat 612.
I then changed the flow, that in the sendOrderEmail function I also open a new Read Stream from the newly created File in the bucket.
Now I get at least some stuff of the PDF in the attachement, but never the whole content.
When I check the PDF uploaded to the bucket, it looks like it should.
I googled alot and found some answers that were also targeting this topic, but as also seen in comments on these questions, they were not completly helpful.
PDF Attachment NodeMailer
Where to generate a PDF of Firebase Database data - mobile app, or Firebase Hosting web app
How to attach file to an email with nodemailer
I also checked with the nodemailer documentation how to pass the attachement correctly and implemented it as documented. But no success.
I think that the mail gets sent before the Read Stream has finished.
Here the Package Versions I use:
"#google-cloud/storage": "1.5.2"
"#types/pdfkit": "^0.7.35",
"firebase-admin": "5.8.0",
"firebase-functions": "^0.7.3"
"nodemailer": "4.4.1",
Can anyone tell me what I'm doing wrong or provide a working example, which uses current package versions, for this usecase?
Here is the code which drives me crazy...
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require("nodemailer");
const pdfkit = require("pdfkit");
const storage = require("#google-cloud/storage")({projectId: `${PROJECT_ID}`})
const mailTransport = nodemailer.createTransport({
host: "smtp.office365.com",
port: 587,
secureConnection: false,
auth: {
user: "userName",
pass: "userPassword"
},
tls: {
ciphers: "SSLv3",
}
});
exports.added = function(event) {
const order = event.data.val();
const userId = event.params.userId;
// Load User Data by userId
return admin
.database()
.ref("/users/" +userId)
.once("value")
.then(function (snapshot) {
return generateOrderPDF(snapshot.val(), userId);
});
};
function generateOrderPDF(user, userId) {
const doc = new pdfkit();
const bucket = storage.bucket(functions.config().bucket);
const filename = `/${userId}/test-` + Date.now() + ".pdf";
const file = bucket.file(filename);
const bucketFileStream = file.createWriteStream();
// Pipe its output to the bucket
doc.pipe(bucketFileStream);
// Do creation Stuff....
doc.end();
bucketFileStream.on("finish", function () {
return sendOrderEmail(user, filename);
});
bucketFileStream.on("error", function(err) {
console.error(err);
});
}
function sendOrderEmail(user, filename) {
const email = user.email;
const firstname = user.firstName;
const mailOptions = {
from: "test#test.test",
to: email,
subject: "Order"
};
const bucket = storage.bucket(functions.config().bucket);
const file = bucket.file(filename);
mailOptions.html = mailTemplate;
mailOptions.attachments = [{
filename: "test.pdf",
content: file.createReadStream()
}];
return mailTransport.sendMail(mailOptions).then(() => {
console.log("New order email sent to:", email);
}).catch(error => {
console.error(error);
});
}
The problem in my appraoch was inside the pdfkit library and not inside nodemailer or firebase. The lines below seem to trigger the end event. So the pdf got sent after these lines. After out commenting them everything worked as it should. It was not that finish was never reached like Hari mentioned.
/* doc.lineCap("underline")
.moveTo(72, 321)
.lineTo(570, 321)
.stroke();*/
After finishing the MVP I will take a root cause analysis and post the final answer as comment below this answer.
This is a working sample of Source-Code for this UseCase. It also ensures, that the firebase function won't finish before all work is done. That is handled by wrapping the event driven doc.on() function into a promise, that is resolved when doc.on("end") is called.
exports.added = function(event) {
const order = event.data.val();
const userId = event.params.userId;
// Load User Data by userId
return admin.database().ref("/users/" + userId).once("value").then(function (snapshot) {
return generatePDF(snapshot.val(), userId);
});
};
function generatePDF(user, userId) {
const doc = new pdfkit();
const bucket = admin.storage().bucket(functions.config().moost.orderbucket);
const filename = "/${userId}/attachement.pdf";
const file = bucket.file(filename);
const bucketFileStream = file.createWriteStream();
var buffers = [];
let p = new Promise((resolve, reject) => {
doc.on("end", function() {
resolve(buffers);
});
doc.on("error", function () {
reject();
});
});
doc.pipe(bucketFileStream);
doc.on('data', buffers.push.bind(buffers));
//Add Document Text and stuff
doc.end();
return p.then(function(buffers) {
return sendMail(buffers);
});
}
function sendMail(buffers) {
const pdfData = Buffer.concat(buffers);
const mailOptions = {
from: "FromName <from#example.com>",
to: "to#example.com",
subject: "Subject",
html: mailTemplate,
attachments: [{
filename: 'attachment.pdf',
content: pdfData
}]
};
return mailTransport.sendMail(mailOptions).then(() => {
console.log("New email sent to:", "to#example.com");
}).catch(error => {
console.error(error);
});
}
The main problem in your code is that the stream.on('finish') never completes. I've also encountered the same issue.
Instead of streaming, convert the pdf into buffer and send the same as attachment.
The following works fine for me,
const doc = new pdfkit()
const filename = '/${userId}/test-' + Date.now() + ".pdf"
const file = bucket.file(filename);
const bucketFileStream = file.createWriteStream();
doc.pipe(bucketFileStream);
doc.end();
var buffers = []
doc.on('data', buffers.push.bind(buffers));
doc.on('end',function(){
let pdfData = Buffer.concat(buffers);
'<<nodemailer stuffs goes here>
'attach the doc as content
});