Lorne Caravan — Project Summary
The problem
The family caravan at Lorne Foreshore Caravan Park had a private WordPress site running on a Mac mini at home. It worked until it didn’t — the home router rebooting or Docker misbehaving would take the whole site down, often at the exact moment guests were trying to look up the gate code on arrival. The goal of the rebuild was to get off self-hosted infrastructure entirely and land on something that would just stay up, with no ongoing ops work.
What the site is
A private, passphrase-gated site for family and close friends who use the caravan. Roughly 10 people, nearly all on phones, often on slow rural connections. The site is split between practical utility (codes, calendar, directions) and memory-keeping (a visitor book of photos and stories from past stays).
Stack
- Astro 6 on Cloudflare Workers + Static Assets — one deploy unit, no separate Functions concept
- Neon Postgres (serverless, scale-to-zero) for the Visitor Book database
- Cloudflare R2 for uploaded photos (zero egress fees, served via custom domain)
- Google Calendar API (read-only, server-side, ~5-minute cache) for the booking calendar
The stack was chosen for cost efficiency across a portfolio of micro-sites. No Supabase, no Vercel, no Firebase.
Features
Gate
A shared passphrase wall — the first screen any visitor sees. On success, sets a signed, HTTP-only cookie valid for 90 days (sliding). All pages are gated; photos on R2 are not.
Homepage
Utility-first dashboard: a full-bleed hero photo carousel pulling the latest Visitor Book photos, the toilet code and WiFi password in prominent cards, a horizontal scroll of recent stories, and quick-link tiles to the other pages.
Visitor Book
The heart of the site. A scrollable feed of entries from past stays — each a card with a title, author, caption, and a horizontally-scrolling filmstrip of photos. Filter by author or year, search by title/caption, infinite scroll (10 at a time). A floating + button opens the Add Entry form.
Add Entry
A phone-first modal form: title, author (pre-filled from localStorage), caption, and up to 10 photos. Photos are resized client-side to ~300 KB before upload (HEIC handled, EXIF orientation corrected). Per-photo progress bars during upload, then a single POST to Neon on success.
Calendar
Monthly grid calendar showing who has the caravan booked and when. Bookings render as coloured pills spanning their date range; maintenance events render as small dot icons. Tapping a day opens a bottom sheet with details. Data comes from a single Google Calendar, owner-managed.
Getting There
A decorative SVG illustration (not a real map) and an “Open in Google Maps” CTA that deep-links to the device’s native maps app. Below that, three step cards covering driving from Melbourne, parking in the park, and bus options.
Housekeeping
Codes page: gate code, toilet code, WiFi network and password — all copy-to-clipboard with a toast. Below that, how-to sections for the fridge, stove, hot water, annex zips, power, and water, plus an emergency contact.
Eating
A chip-filtered list of recommended places in Lorne. Data lives in a hand-maintained restaurants.json in the repo. Each place card expands to reveal menu and website links and an “Open in Maps” button. Categories: Breakfast, Lunch, Coffee, Dinner, Casual, Provisions.
Key decisions
- No real maps anywhere. Getting There and Eating use device-native deep links only — no Mapbox, no Google Maps embeds.
- Client-side image resize. Phones produce 4–12 MB HEICs; resizing to 1600px / JPEG 80% / ~300 KB before upload matters on slow rural connections.
- Settings in the database, not env vars. Gate code, toilet code, WiFi details, and booking contact all live in a
settingskey-value table in Neon, rotatable without a redeploy. - No admin UI for v1. Deleting entries or rotating codes is done via the Neon SQL editor directly.
- Boring code wins. I maintain this solo and infrequently. Anything clever enough to forget in six months shouldn’t be there.
Last updated May 10, 2026.