Merry Belated Christmas

Christmas in July

It’s Independence Day when I’m writ­ing this, which seems like a good time to write about my win­ter hol­i­day cards from last year. In a pre­vi­ous blog post, I wrote about a small pro­ject I made with my nait­ian.hol­i­day do­main name. However, that was­n’t the orig­i­nal pur­pose for the do­main. The first use for the do­main was dig­i­tal hol­i­day cards for some of my friends.

Table of Contents

Planning

AKA all the things I wanted to do but did­n’t.

There were a cou­ple of clear re­quire­ments that were non-ne­go­tiable:

  1. There had to be at least some form of au­then­ti­ca­tion (so not every­one can see every­one else’s card)
  2. Emails would be sent out no­ti­fy­ing peo­ple of their cards.
  3. Cards need to be per­son­al­ized with:
    • Greeting
    • Name (duh)
    • Message

Later, I also de­cided a cou­ple more re­quire­ments, both for fun and for ed­u­ca­tion:

  1. Everything would be com­piled and hosted sta­t­i­cally on S3.
  2. In the theme of us­ing AWS for every­thing, I’d also send email no­ti­fi­ca­tions with Amazon’s Simple Email Service (SES) and han­dle do­main name / certificates with Route53.
  3. Each per­son would only see pretty URLs (e.g. nait­ian.hol­i­day/​nate)
  4. Cards would in­clude these drag­gable Polaroid-style pic­tures from my Google Photos.

Implementation

AKA what I ended up do­ing

Infrastructure

I will be to­tally hon­est with you and say I don’t ac­tu­ally re­mem­ber all the de­tails to the in­fra­struc­ture on AWS, but it was­n’t su­per com­plex. The gist is:

  • All the sta­tic files are hosted with S3′s sta­tic web­site host­ing.
  • I used Cloudfront as the CDN for the S3 files.
  • I have a hosted zone for naitian.holiday in Route53 which points to the Cloud­front end­point (which in turn points to the S3 bucket).
  • Photos were hosted on Google Photos

Design

Aesthetically, I de­cided to go for a fes­tive red with a sub­tle gra­di­ent. I chose a classy serif type­face and drew this freak­ing amaz­ing Christmas non-denominational hol­i­day tree.

Build Process

Each card was de­fined as a yaml file. I wrote a Python script to read these yaml files and used mark­down to ren­der them into HTML files. At the same time, I generated the SES email tem­plate data for bulk send­ing the no­ti­fi­ca­tion emails.

I also had a Makefile which would run the ren­der­ing script, com­pile the SCSS into CSS, minify CSS and JS, and push every­thing to S3. There were also Make steps which would send a test email and send out the ac­tual emails.

Authentication

One of the most in­ter­est­ing parts of this whole ex­er­cise was fig­ur­ing out a good au­then­ti­ca­tion method. I def­i­nitely did not want to make any­one sign in or cre­ate any ac­counts (partly also be­cause I wanted this to be a to­tally sta­tic site).

I ended up com­pro­mis­ing with a good-enough” au­then­ti­ca­tion plan, which I had to al­ter a lit­tle bit to also keep the pretty URLs. Here’s how it ended up work­ing:

  1. When a card is ren­dered, it is ac­tu­ally placed in a di­rec­tory named af­ter its SHA256 hash.
  2. This hash is used as an auth to­ken in the link.
  3. Not only is an HTML file gen­er­ated with the card’s con­tent, an­other HTML page is gen­er­ated with Javascript for au­then­ti­ca­tion and a hid­den in­put field con­tain­ing a SHA256 hash of the to­ken.
  4. When a user nav­i­gates to the site (e.g. nait­ian.holidy/​nate), they are ac­tu­ally nav­i­gat­ing to the page with only the auth code.
    • On the client side, the to­ken (if it’s pre­sent) is hashed and com­pared to the cor­rect to­ken.
    • If in­cor­rect, the user is re­jected.
  5. If cor­rect, there is an XHR re­quest that fetches the ac­tual HTML and dis­plays it.

This means the user would have a hard time even re­al­iz­ing that any au­then­ti­ca­tion had oc­curred in the first place.

To re­cap, for each card, the path struc­ture looked like this:

naitian.holiday/<SHA256>/index.html   <- this is the HTML for the card
naitian.holiday/nate/index.html       <- this is the HTML for authentication

Of course, I also added a robots.txt which dis­al­lowed any crawl­ing. This makes it pretty dif­fi­cult to find the (very much pub­lic) SHA256 paths of any of the cards.

So, while not air­tight, this worked well enough to pre­vent peo­ple from just typ­ing in names of other peo­ple to see their cards.

I also stored the to­kens in lo­cal stor­age, so af­ter the first visit, the to­ken is­n’t ac­tu­ally re­quired.

Nifty.

Email Notifications

Emails were sur­pris­ingly straight­for­ward to set up. I used the Simple Email Service from Amazon. Initially, I planned to do a lot more CSS for the HTML emails, but ul­ti­mately de­cided against it for two rea­sons: it would have been more work to im­ple­ment and try to test, and I felt like it would feel more per­sonal if it was more raw”.

I had a make rule which would send just test emails to my­self and a dif­fer­ent rule to send out the emails for real.