Google Cloud Flashcards
How do you create an email sending function with google cloud functions?
You’ll want a file that is formatted like this:
const functions = require(‘@google-cloud/functions-framework’);
const { google } = require(‘googleapis’);
const fs = require(‘fs’);
// Gmail API setup
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REFRESH_TOKEN = process.env.REFRESH_TOKEN;
// Configure OAuth2 client
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET);
oauth2Client.setCredentials({ refresh_token: REFRESH_TOKEN });
async function sendEmailViaGmail({ from, to, subject, finalHtml }) {
const gmail = google.gmail({ version: ‘v1’, auth: oauth2Client });
try { const email = [ `From: ${from}`, `To: ${to}`, `Subject: ${subject}`, `Content-Type: text/html; charset=utf-8`, '', finalHtml, ].join('\n'); const encodedMessage = Buffer.from(email) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); const response = await gmail.users.messages.send({ userId: 'me', requestBody: { raw: encodedMessage, }, }); console.log('Email sent successfully:', response.data); // Log successful email send return response.data; } catch (error) { console.error('Failed to send email:', error); throw new Error('Failed to send email'); // Re-throw the error to be handled by the calling function } }
functions.http(‘sendForgotPasswordEmail’, async (req, res) => {
const allowedMethods = ‘POST, OPTIONS’;
const allowedHeaders = ‘Content-Type’;
// Set CORS headers for all responses res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', allowedMethods); res.set('Access-Control-Allow-Headers', allowedHeaders); res.set('Access-Control-Max-Age', '3600'); // Handle preflight requests if (req.method === 'OPTIONS') { console.log('Handling preflight request'); return res.status(204).send(''); // Respond to preflight request } try { console.log('Request body:', req.body); // Log the request body const { from, to, link, subject = 'Reset Password | National Auto Hub' } = req.body; if (!from || !to) { console.log('Missing required fields'); return res.status(400).json({ message: 'From and To are required.', }); } // Read the HTML file const htmlContent = fs.readFileSync('NAHPassword.html', 'utf8'); // Send email via Gmail API const result = await sendEmailViaGmail({ from, to, subject, finalHtml: htmlContent.replace( 'https://www.nationalautohub.com/reset-password', link ), }); console.log('Email sent successfully'); res.status(200).json({ message: 'Email sent successfully', result, }); } catch (error) { console.error('Failed to send email:', error); res.status(500).json({ message: 'Failed to send email', error: error.toString(), }); } });
Then install the google apis library, fs, and juice:
NOTE: You only need juice if you need to dynamically change the email per the user’s request. Static emails don’t need it, so the htmlContent can be the final html you use. The css file may also not be necessary if you don’t use it additional styles.
npm install googleapis fs juice
Your package.json should look like this:
{
“dependencies”: {
“@google-cloud/functions-framework”: “^3.0.0”,
“fs”: “^0.0.1-security”,
“googleapis”: “^144.0.0”
}
}
Then you’ll need to get the Client ID, Client Secret, and Refresh token.
To get the ID and Secret, go to Google Cloud Console, create a new project if you don’t already have it, then go to credentials and make new OAuth credentials. Set the application type to web application and put in the javascript origins which will looks something like:
http://localhost:3000
http://localhost
https://nationalautohub.com
https://www.nationalautohub.com
You WILL need this redirect URI to create the necessary refresh token:
https://developers.google.com/oauthplayground
Create it and it should provide the Client ID and Secret.
For the refresh token, go to the OAuth 2.0 Playground:
https://developers.google.com/oauthplayground/
Before doing anything, go to the settings in the top right, check “Use your own OAuth credentials”, and put in the client ID and secret that you got before. NOTE: You won’t be able to do this successfully unless you specify that redirect URI.
On step one, copy and paste this into the “Input your own scopes” field and press Authorize APIs:
https://www.googleapis.com/auth/gmail.send
It will have you login. Then you want to press “Exchange authorization code for tokens”, which will then have a refresh token in the response.
You’ll put these RUNTIME environment variables in the Google Cloud Function for use.
Go to Google Cloud Functions:
https://console.cloud.google.com/functions
Click “Create Function”. Name it and select the region that makes the most sense like SLC. You want to allow unauthenticated invocations so that anyone can call it who is using your website.
Runtime is the only tab you really need to mess with here. You need to decide what this particular email needs. For example, a forgot password email could keep the defaults, and even lower the maximum number of instances to 10. Something like an order confirmation email where many people could be calling it at once could do CPU 1, 10 concurrent requests, 1-2 minimum number of instances to keep the function warm, and 100 max instances. Just depends, and the more resources you allocate, the higher the costs.
Set the environment variables and continue on.
Choose the correct runtime, probably the latest version of node. Choose zip folder as the source code. It will ask for a bucket which you may have to create.
I named the bucket nah-bucket for the NAH application. You can keep most defaults. If the website will be seen by people across the US, probably keep it multi-regional. Also keep the “prevent public access” checked because the function will still work with this more secured. You only uncheck it if the bucket is going to be used for images or something people need direct access to.
Finally zip the folder contents and upload it. DO NOT compress the folder that holds all the necessary files though. This will cause there to be an unnecessary folder. You should highlight the contents and compress that and upload it.
If there is an issue there you can also just name the files manually in the inline editor and copy and paste them for more surety.
Deploy the function, get the url, and test it in your application!
If there are CORS issues, there may be an issue with the “unauthenticated invocation”, so you should go to Google Cloud Run here:
https://console.cloud.google.com/run
Go to your function, go to the security tab, and make sure the “Allow unauthenticated invocations” is checked. Even when specifying that on the creation of the function, it didn’t go through for me.
What is a POSSIBLE step you may need to take in order to properly setup your Google Cloud Functions?
You may need to make sure you and your organization have the permissions to edit things properly. This may be necessary for you to invoke the functions from your web app.
Go here:
https://console.cloud.google.com/iam-admin/iam
Make sure the organization is in the top dropdown – NOT a project. Click the edit pencil by the Organization Administrator and add the role “Organization Policy Administrator”. Then go here:
https://console.cloud.google.com/iam-admin/orgpolicies/list
Find “Domain restricted sharing” in the filter, manage policy, delete any existing rules, and add the rule “Allow all”. Then click “Set Policy”.
Then in the permissions settings of your Google Cloud Function, grant access to allUsers as the principle and give them the role of Cloud Function Invoker or something along those lines.
What do you do if your Google Cloud Function sends emails and you need it to send from a particular email?
You need to get the client ID and secret and refresh token while logged into that particular email. In the main account, go to the project you need to give them access to and make them an owner. Then login to the console through that email and make the credentials from there. When using the gmail api, those credentials will be associated with that email and the “From” field will be from that email. (Haven’t tested this fully yet.)