const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors');
// Needed this to connect to Firestore, my code not yours
admin.initializeApp();
admin.firestore().settings({ timestampsInSnapshots: true });
// Emulate the transport.sendMail() for debug purposes
let transport = {
sendMail: (options) => {
return new Promise((resolve, reject) => {
console.log(`Sending Mail: ${options}`);
resolve(options);
});
}
}
module.exports.sendMail = functions.https.onRequest((req, res) => {
if (req.method !== 'POST') // won't have a body
return res.status(400).send(`Error: ${req.method} is not Accepted.`);
// extract params from body
const { name, email, doc, state, city, phone, msg, } = req.body
let dbPromises = [];
let firestore = admin.firestore(); // alias to lineup with OP's code
// extract headers
const ip1 = req.headers["fastly-client-ip"];
const ip2 = req.headers["x-forwarded-for"];
// validate input, if bad: emit Client error
if (!ip1 || !ip2 || !email)
return res.status(400).send("Error: Invalid Request.");
// query previous message existence
dbPromises.push(firestore.collection('messages').where('ip1', '==', ip1).get());
dbPromises.push(firestore.collection('messages').where('ip2', '==', ip2).get())
dbPromises.push(firestore.collection('messages').where('email', '==', email).get())
dbPromises.push(firestore.collection('blocked-emails').where('email', '==', email).get())
// Need to return a promise so your function doesn't timeout
return Promise.all(dbPromises)
.then(resultDocs => {
if (resultDocs.length !== 4)
throw new Error("Programmer Error");
// validate if there is any result > 0, if any, throw error to the user
if (resultDocs[0] !== null && resultDocs[0].docs.length !== 0)
throw new Error(`${ip1} already exists`);
if (resultDocs[1] !== null && resultDocs[1].docs.length !== 0)
throw new Error(`${ip2} already exists`);
if (resultDocs[2] !== null && resultDocs[2].docs.length !== 0)
throw new Error(`${email} already exists`);
if (resultDocs[3] !== null && resultDocs[3].docs.length !== 0)
throw new Error(`${email} is blocked`);
return null;
})
.then(() => {
// Build message for mailer
const mailOptions = {
from: `"${name}" <${email}>`,
to: 'a_email@gmail.com',
replyTo: `"${name}" <${email}>`,
subject: `Contact - ${name}`,
html: `<div>${name}</div>
<div>${email}</div>
<div>${doc}</div>
<div>${state}</div>
<div>${city}</div>
<div>${phone}</div>
<div>${msg}</div>`,
}
let innerPromise = null;
// Fix headers for cross-origin
cors()(req, res, () => {
// send mail returns a promise
innerPromise = transport.sendMail(mailOptions);
});
return innerPromise; // return promise or null
})
.then(sendMailResult => {
if (!sendMailResult) {
// not sure if transport.sendMail even returns result
// do validation here if yes
}
// write message to store
return firestore
.collection('messages')
.add({
name: name,
email: email,
doc: doc,
state: state,
city: city,
phone: phone,
msg: msg,
ip1: ip1,
ip2: ip2,
});
})
.then(() => {
return res.status(201).send("Success")
})
.catch(err => {
console.log(err);
res.status(500).send(String(err));
})
})
主要的收获是承诺的结构:总是从内部返回完成的数据或另一个承诺,并使用
.then
. main函数还应该返回一个承诺,这样云函数就不会在一切完成之前超时。