Using Firebase and Email to Send a Custom Diploma to Our Top Performing Users
Fjelltoppjakten(the peak hunt) is an app where you collect peaks. The app includes a map of peaks in Norway, and when you are on the top of a mountain you can check-in, collect points and take a picture.
In less than 10 months, some users have already visited more than 500 peaks!
With the year coming to an end I wanted to send a diploma or “certificate of completion” to all the users that had visited all the peaks in Bergen or Osterøy municipality. The diploma should contain the number of mountains they visited, and which municipalities they have completed(visited all peaks).
We have all the user data in Firestore, use Firebase Authentication and have a bunch of Cloud Functions to massage the data.
TL;DR:
- Create a plain HTML/CSS website that looks like a diploma
- Create an API to modify the text of the diploma
- Find all users who should receive the diploma
- Fetch their data (mail, name, visited mountains)
- Generate the diplomas as PDFs from the website with
html-pdf-node
- Save the PDFs to Cloud Storage
- Send the diplomas using nodemailer
Creating the diploma
I wanted a simple A4 landscape diploma with a “ribbon” and some text, and decided to do it using HTML/CSS.
I won’t focus on this part as it’s quite simple HTML/CSS, but a few things to note:
Creating an A4 landscape sized page with:
Creating fancy corners:
Creating signatures:
We wrote our signatures on paper and used the Mac camera app to create a transparent image of it.
Creating an API to generate dynamic diplomas
I have had hosting at one.com that supports PHP for years, so I figured that would be the simplest solution. I simply replaced some texts in the diploma with PHP to echo out some GET parameters, and then uploaded the html and css to the web server at /diploma.php
With this I got a website that looks like a diploma, and the text is customizable with GET parameters. e.g https://example.com/diploma.php?name=Torkel Velure&achievement=Visited 252 peaks during 2021, and finished:&achievement2=Bergen,Askoy
Creating a PDF from the website
All browsers support CTRL+P to print a web page, and you can customize it to remove margins and header/footer. Naturally there is a Node.js library that can do the same thing, called html-pdf-node
.
Uploading the PDF to Cloud Storage
After creating the PDF I uploaded it to Cloud Storage, and generated a signed URL so I can retrieve the PDF later. I think signed URLs expire in 7 days now(?), but that doesn’t matter in this case, as we’ll use the URL immediately after generating it.
Adding the correct text to the diploma based on a user ID
In the Firestore DB of Fjelltoppjakten we have collections like:
users/$userId {name: String, uniqueMountainsVisited: Number, ...}mountains/$mountainId { county: String, municipality: String, ...}userStatistics/$userId/uniqueMountainsVisited/$county {
Bergen: Array<mountainId: String>
Osterøy: Array<mountainId: String>
...municipalityNames
}userStatistics/$userId/uniqueMountainsVisitedYearly/$year {
mountains: Array<mountainId: String>
}totals/$county {
Bergen: Number // number of mountains in Bergen,
Osterøy: Number,
...municipalityNames
}
Whenever a user checks in on a mountain a Cloud Function runs that populates the statistics collections/fields.
Whenever a mountain is added, the municipality in the totals collection is incremented.
We want to figure out:
- How many mountains a user has visited.
* Get it from the user document - User name
* Get it from the user document - Municipalities where the user has visited all the existing peaks/mountains.
* Find every array inuserStatistics/$userId/uniqueMountainsVisited
and take those arrays that contain as manymountainIds
as the total number found for that municipality in thetotals
collection..
Generating a diploma with a Cloud Function
Now that we have the building blocks, we can put it together in a HTTP cloud function that returns a URL pointing to the generated PDF.
Now we can simply call https://us-central1-<project-id>.cloudfunctions.net/generatePdf?userId=$userid
to generate a diploma for a given user and get a url pointing to the PDF in return.
Be careful though: this function does not require authentication.. You should probably secure it better than I did.
Take note of the timeoutSeconds: 60
and memory: “2GB”
. I experienced some problems with memory and slowness, so I increased both values. Generating a PDF and holding it in memory might require a lot of memory, especially with PDFs that contain images.
Sending the diploma to users through mail
Now that we have the ability to generate diplomas, save them to storage and retrieve a URL to download them, we can send them to our users.
Instead of doing it through a Cloud Function, I wrote a script locally instead.
Using the Firebase Admin SDK locally requires some setup:
npm init
npm install dotenv firebase-admin
- Generate a Firebase service account key and place it in
.keys
folder.
(Firebase Console > gear icon > Project settings > Service accounts > Generate a new private key button.) - Write the path of the json key file in to
.env
like this:GOOGLE_APPLICATION_CREDENTIALS=".keys/MYPROJECT-firebase-adminsdk-asdf-234lkjjfsoi.json"
. We will usedotenv
to load it later.
With the setup done we can proceed with our use case.
- Figure out which users have visited all the peaks in Bergen or Osterøy this year.
- Find the users email-addresses through Firebase Authentication.
- Find the name of the users.
- Create a diploma for each user.
- Send diploma through email to those users
Execute node -r dotenv/config index.js
and all the diplomas are generated and an email is sent to all the users!
Conclusion
That’s a lot of code and very specific to my use case (and perhaps terrible data architecture), but I’m sure I will be using some of these snippets in future projects, and I hope some of it can help you as well!