Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Menu voting — a wedding

Archived. The wedding happened, voting is over and the service has been shut down. This repository is a snapshot of the code as it stood at shutdown; it is not maintained.

Not included: the database of guest entries (personal data, deleted) and the licensed brand face Syndicate. The font fallback chain lands on Overpass or Georgia instead. Names, domains and the venue link are placeholders.

A small site where guests enter a menu choice per person. One guest can enter several people (partner, children). On submit, one confirmation mail goes to the person who voted; the organisers receive a daily summary at a time set by CRON_SCHEDULE.

The couple get a live dashboard at /dashboard, refreshing every 20 s: a tally per course plus one card per submission (who · what · when). It is behind HTTP basic auth — the username is ignored, only the password counts, taken from DASHBOARD_PASSWORD.

The guest form has no password (that was removed); it is reachable by URL only. Search engines are shut out three ways: robots.txt (Disallow: /), an X-Robots-Tag: noindex, nofollow, noarchive header on every response, and <meta name="robots" content="noindex, …"> in the HTML.

  • Host: menue.example.de, behind the shared Traefik stack
  • Stack: Node 22 + Fastify, SQLite (file volume), Nodemailer, node-cron
  • Design: taken from the live wedding site (Kadence theme) — Overpass for body copy, Manrope for labels and nav, accent #a81f24. The name lettering originally used Syndicate, deliberately only for the couple's names — it is unreadable for whole sentences. Those font files are licensed and are not part of this archive.

Layout

src/
  server.js                  HTTP server, /api/submit, robots.txt/noindex, cron registration
  menu.js                    >> CENTRAL menu configuration (edit dishes here) <<
  db.js                      SQLite schema + upsert (voting again overwrites)
  mailer.js                  Nodemailer (an empty SMTP_HOST means dev/console mode)
  evaluation.js              Summary (cron, plus `--once` for testing)
  templates/                 HTML mails (confirmation + summary)
public/                      index.html (the form), app.js, styles.css

The per-course columns in person are named after the course ids in menu.js — the API reads them as person[course.id], so renaming a course means renaming its column.

Configuration (.env)

Copy .env.example and fill it in.

Variable Purpose
DASHBOARD_PASSWORD Password for /dashboard. Required — the server refuses to start without it
VOTING_OPEN true/false — the manual close switch
VOTING_DEADLINE Cut-off YYYY-MM-DD; closes automatically once that day has passed
SMTP_HOST/PORT/USER/PASS, MAIL_FROM Mail delivery. An empty SMTP_HOST logs mails to the container output instead of sending them
ORGANIZER_EMAILS Recipients of the summary, comma separated
CRON_SCHEDULE When the summary goes out; default 0 8 * * 1 (Mondays 08:00, Europe/Berlin)
KIDS_MENU_URL Link to the kids' menu (optional)

Closing the vote

Two ways:

  • Cut-off: VOTING_DEADLINE=2026-07-05 — the site closes on its own once that day is over (end of day, Europe/Berlin). Nothing to do.
  • Immediately: set VOTING_OPEN=false and run docker compose up -d.

Either way the page shows "Voting closed" and submissions are refused server-side as well.

Deployment

  1. DNS: an A record for the host → server IP (Traefik fetches the TLS certificate).
  2. Fill in .env (for mail delivery, SMTP_* plus ORGANIZER_EMAILS).
  3. docker compose up -d --build
  4. docker compose ps → is the container healthy? If not, Traefik gets no cert.

Testing

  • Send or log the summary right away: docker compose exec menue-voting node src/evaluation.js --once
  • Without SMTP_HOST, every mail is written to the logs as JSON (docker compose logs -f menue-voting) — handy for checking before going live.

Changing the menu

Every course, dish and piece of copy lives in src/menu.js. Rebuild the container after editing. Do not change the key values of options once guests have voted — old entries would no longer match their labels.

About

Archived: menu voting for a wedding — Fastify, SQLite, Nodemailer. Guests enter a choice per person, the couple watch a live dashboard, and the organisers get a daily summary by mail.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages