- Python 36.8%
- HTML 35.2%
- CSS 23.5%
- JavaScript 1.7%
- Just 1.6%
- Other 1.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CLAUDE.md | ||
| compose.yml | ||
| content.toml | ||
| Dockerfile | ||
| justfile | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
bodapakuelsybea
Wedding website for Bea y Paco: 4, 5 and 6 June 2027, Finca Casaes (Torralba de Oropesa, Toledo). Spanish only.
A single long landing page with the event info, a public RSVP form and a small admin panel where the couple manages the guest list.
Features
- Landing: dates, countdown, weekend programme, food, how to get there (map), where to sleep, dress code, Spotify playlist, gift IBAN with copy button, FAQ, optional photo album link,
.icscalendar download. - RSVP: one submission per group (adults and babies, phone, allergies/diet, message). After sending, the guest gets a private link (
/rsvp/<token>) to edit their answer until the deadline (rsvp.fecha_limite, 2027-05-01). Spam protection: hidden honeypot field. - Admin (
/admin, one shared password): totals, allergies list, filter by status (confirmado / no viene / pendiente), add/edit/delete groups by hand (e.g. grandma), internal notes, copy a group's private link to resend it, CSV export. - Content lives in
content.toml. No CMS: edit, push, deploy. Sections with no content are hidden.
Stack
FastAPI, Jinja2, stdlib sqlite3, plain CSS and a few lines of vanilla JS. No build step. One container, data in a SQLite file.
Local development
cp .env.example .env # set ADMIN_PASSWORD and SECRET_KEY
just install
just dev # http://localhost:8000
just test
just fix # ruff lint + format
SECURE_COOKIES=false in .env is needed for the admin login over plain http.
Configuration
| Variable | Purpose |
|---|---|
ADMIN_PASSWORD |
Password for /admin/login |
SECRET_KEY |
Signs the admin session cookie (openssl rand -hex 32) |
DATA_DIR |
Directory holding boda.db (/data in Docker) |
PORT |
Host port used by compose.yml (default 8000) |
SECURE_COOKIES |
false only for local http dev |
Editing the content
Everything textual is in content.toml: names, dates, programme, FAQ, IBAN, links. Strings accept inline HTML. Entries marked EJEMPLO are placeholders waiting for the couple's real text.
Commit and push to development; CI builds the image and the server pulls it.
Deploy
CI (.forgejo/workflows/ci.yml) runs ruff and pytest, then on pushes to development builds and pushes forgejo.patilla.es/patillacode/bodapakuelsybea:latest. It needs the REGISTRY_TOKEN secret on the Forgejo repo.
On the server, with compose.yml and a .env next to it:
docker compose pull && docker compose up -d # or: just deploy
The database is ./data/boda.db; include that directory in your backups. /admin/export.csv is a manual fallback.
Put the site behind a reverse proxy with HTTPS and rate-limit the two public POST endpoints, for example in nginx:
limit_req_zone $binary_remote_addr zone=boda:10m rate=10r/m;
location = /rsvp { limit_req zone=boda burst=5 nodelay; proxy_pass http://127.0.0.1:8000; }
location = /admin/login { limit_req zone=boda burst=5 nodelay; proxy_pass http://127.0.0.1:8000; }
(Keep your usual proxy_set_header lines. The container already runs uvicorn with --proxy-headers, so the private links come out with https://.)
Layout
app/main.py routes, auth, CSV, .ics
app/db.py schema and queries
app/templates/ base, index, shared _form.html, rsvp pages, admin/
app/static/ style.css, app.js, fonts
content.toml all site text
tests/test_app.py end-to-end checks of RSVP, admin and CSV
Rules baked into the design
- Adults and babies only (no children). Single menu: the RSVP asks only for allergies/diet.
- The RSVP form closes after
rsvp.fecha_limite.