Projects my-advent

my-advent

Live

Digital Advent calendars to give away — marketing site, customer portal, back office and a multi-tenant calendar PWA.

Period
2025 – present
Role
Solo — concept to operations
Status
Live, publicly reachable
Code
Private repository

Giving away a digital Advent calendar means picking a photo for every day until Christmas, writing something to go with it, and getting the whole thing into the hands of the person it is for in time. This project’s predecessor could already do that, but every order ended at a person who started a container by hand, issued a certificate and added an entry to the proxy. Eight orders from a single interested customer were left lying there that way, four of them with the calendar already filled — not one went online.

my-advent is the rebuild: a monorepo with two running applications. One carries the marketing site, the customer portal, the back office and the API; the other serves every customer calendar, and which one is meant is written in the address that was called. Between a paid order and a link that can be sent on there is no longer a person. If one of the automatic checks goes wrong, the customer does not wait in silence — the operator is told.

What stands out is how much fell away: no container management from inside the application, no certificate per customer, no payment service of my own, no database server. What is left is two containers, one database file and a directory of images. What also stayed are three deliberately different ways in, depending on who is arriving: whoever is given a calendar should have to set up nothing at all; whoever buys one, as little as possible; whoever runs the shop signs in properly. Each group gets as much effort as it is fair to ask of them, and none beyond that.

Stack
TypeScriptExpressAstroReact · ViteDrizzleSQLite (better-sqlite3)StripesharpReverse ProxyAnsible

Decisions

ADR 0001

One service for every calendar instead of a container per customer

Every customer used to get a running instance of their own, with a proxy entry and a certificate to match; the back office needed rights for that which amounted to those of the server administrator, and every order was a chain of steps that could break in several places. The isolation bought with all that is needed nowhere: all calendars run the same code, and the order data sits together anyway. Creating a calendar now means writing one row to the database.

ADR 0003

A hosted checkout instead of a payment service of my own

Payment used to run through a service I had built myself: around 3,000 lines for exactly one provider, with the API secret in the log and empty error handling. The hosted checkout charges a lower fee, brings Apple Pay and Google Pay with it — the deciding point for an impulse buy of just under five euros on a phone — and produces the invoice with the tax breakdown at the same time. The price is a dependency on someone else's service; the alternative was a payment service of my own with the secret written to the log.

ADR 0006

No user account for the recipient

The plan had been an account per calendar in a sign-in service of my own. But the complaint from real use — “you have to log in again all the time” — was not a problem of identity, it was the cookie lifetime; accounts would not have fixed it, and they would have sent grandparents who only want to look at a photo through a sign-in flow. So what was actually broken got fixed — the lifetime — and not the thing it looked like.

ADR 0009

Build it first, pay afterwards

The other way round would have been simpler: no unpaid drafts taking up space and inviting abuse. But anyone who has picked 24 photos and written a message for each has put in half an hour, and that work is the strongest argument the product has for buying it — behind a paywall it would be wasted. Drafts are therefore created on the server before any payment, and expire after 30 days.