Repository navigation
Development
Local setup, the check scripts and the seeds.
npm install
# A Postgres to point at — anything will do
docker run -d --name wed-db -e POSTGRES_USER=wed -e POSTGRES_PASSWORD=wed \
-e POSTGRES_DB=wed -p 5432:5432 postgres:15-alpine
DATABASE_URL=postgresql://wed:wed@localhost:5432/wed ADMIN_PASSWORD=dev npm run devThen http://localhost:3000, and /admin with ADMIN_PASSWORD.
The schema creates itself: database/init.sql is idempotent and the container runs
it on every boot. Running locally with npm run dev skips that, so apply it once
by hand — psql … < database/init.sql.
None of these need a network, and only check:finance:db needs a database, so
they are cheap to run before a commit.
| Command | What it covers |
|---|---|
npm run check:types |
tsc --noEmit over the app and the scripts |
npm run lint |
ESLint |
npm run check:honeymoon |
Distances, date maths, URL parsing, the calendar grid, .ics output, travel arcs and overnight legs, stay ranking, search ranking, seed integrity, journeys, time zones, the budget, opening hours, sunrise/sunset, the timeline with travel, the place panel's sections and travel documents — 743 assertions |
npm run check:offline |
The offline worker's rules and the offline page lists — 38 checks, no browser, in CI |
npm run check:offline:ui |
The whole site offline in a real browser against a production build, with the server cut off through a proxy — 49 checks. Manual |
npm run check:honeymoon:ui |
The honeymoon portal in a real browser (53 checks, none of which write): one place panel from every entry point, the itinerary toolbar, travel on the timeline, and the phone layout — no sideways scroll, nothing under 44px. Needs a browser and a server with honeymoon data: BASE=… ADMIN_PASSWORD=…, or DEMO=1 against the demo. Manual, not CI |
npm run check:seating |
Who takes a chair (a party member who declined takes none), seat-index allocation, moves, swaps, gathering a split party, auto-seating, the plan's warnings, and the export: tallies, vendor plates, the grand total, the spreadsheet and the one-page fit maths — 199 assertions |
npm run check:changelog |
The changelog parser against fixtures and the real CHANGELOG.md — 56 assertions |
npm run check:photos |
The photo route: thumbs, resizing, 404s, the traversal guard |
npm run check:finance |
Budget arithmetic |
npm run check:finance:db |
The same against a live database, plus the real route handlers — CRUD, validation, injection safety, and the settings singleton on every verb the UI sends |
npm run check:finance:ui |
The finance UI's contracts |
npm run audit:finance |
A deeper sweep over the finance logic |
check:changelog is the one that catches a hand-editing mistake in
CHANGELOG.md — most usefully, nested backticks inside a code span, which
cannot parse and render as raw ** in the in-app viewer.
| Command | What it does |
|---|---|
npm run seed:honeymoon |
Loads the bundled Bali/Singapore travel guide into the honeymoon portal — 7 regions, 224 places, 12 notes. Idempotent: it matches on place name and skips anything already there, so it never reverts an edit. |
npm run seed:demo -- --yes-wipe |
Fills an instance with a completely fictional wedding. Destructive. See The demo instance. |
DEMO_MODE=true makes an instance read-only and login-free. The flag is refused
unless the database in DATABASE_URL is named demo, so to try it locally the
database has to be called that:
docker run -d --name demo-db -e POSTGRES_USER=demo -e POSTGRES_PASSWORD=demo \
-e POSTGRES_DB=demo -p 5433:5432 postgres:15-alpine
DATABASE_URL=postgresql://demo:demo@localhost:5433/demo \
DEMO_MODE=true ADMIN_PASSWORD=demo npm run devWrites under /api/ are then answered but never stored, /admin needs no
password, and a banner says so. Details in
The demo instance; the flag itself is src/lib/demo.ts
(server) and src/lib/demoClient.ts (browser, via /api/demo-status).
-
CHANGELOG.mdis the source of truth for the version. There is no number inpackage.json. See Versions and the changelog. -
database/init.sqlis the only copy of the schema. Some tables are also created at runtime by the API route that owns them; if you add one of those, add it here too or a fresh install comes up without it. -
Photos are served through
/api/photos/[...], never/photos/…— with standalone output, files written into a volume at runtime are not served statically. See Architecture. -
AGENTS.mdin the repository root carries the working agreements for this project — the deploy loop, the changelog rules, and the traps that have cost time before. It follows agents.md;CLAUDE.mdis a symlink to it, so there is one file and not two to keep in step. (IfCLAUDE.mdever shows up as a 9-byte text file, git was cloned withcore.symlinks=false—git config core.symlinks true, delete it, check it out again.) -
Docker lives in
docker/—Dockerfile, the three compose files (.dev,.prod,.demo) and.env.example. The build context stays the repository root, so builds are-f docker/Dockerfile .. -
Pushing to
mainis the deploy, and it reaches the running servers on its own: GitHub Actions publishes the image and each host's timer pulls it within about five minutes. Nothing is built on a laptop. See Deployment. -
ops/holds the host-side pieces — the two auto-updaters and theDOCKER-USERport rules — with the reasoning that made each necessary. Nothing in there is used by the application or the Docker build; it is version-controlled so it is reviewable, because each one started as a file on exactly one box.
Setting it up
Using the admin panel
- Guests and RSVPs
- Seating and the Export
- Registry
- Finances
- Honeymoon Portal
- Content and Settings
- Offline
How it works