Wedding Photo Booth · README

Wedding Photo Booth

CI pipeline Next.js TypeScript pnpm Docker

A mobile‑first, QR‑authenticated photo booth for weddings. Guests scan a personal QR on their place card, take a selfie, pick an emoji, choose Team Bride or Team Groom, then snap photos and short videos throughout the night — every upload appears live on the projector slideshow. At the end, a single script stitches everything into a memory video.

Screenshot of the Wedding Photo Booth app — guest dashboard with profile, camera button, and photo gallery

Tech stack

Layer Technology
Front‑end Next.js 16 (App Router), React 19, Tailwind CSS v4, Motion
Back‑end Next.js Route Handlers, Server Components
Database PocketBase 0.39.4 (SQLite + file storage, admin SDK)
Auth HMAC‑signed cookies, per‑guest QR tokens (96‑bit entropy)
Real‑time Server‑Sent Events (Next.js → browser) with live PocketBase subscribe
Reverse proxy Caddy (auto‑TLS, Let’s Encrypt)
CI/CD GitLab CI (7‑stage pipeline, semantic‑release, tag‑driven deploys)
Container Docker Compose (3 services on an internal wedding network)

Architecture

  Internet ──▶ Caddy (:80/:443) ──▶ Next.js (:3000) ──▶ PocketBase (:8090)
                                          │
                                          ▼
                                  PocketBase (:8090)
                                   bind‑mount pb_data
                                   bind‑mount pb_migrations (ro)

The browser never talks to PocketBase directly — every read, write, media proxy, and real‑time connection goes through the Next.js server with superuser‑only API rules.

Features

Quick start (dev)

# 1. Install dependencies
cd app && pnpm install

# 2. Start PocketBase locally
./pocketbase serve --dir=/home/mz/pb_data \
  --migrationsDir=$PWD/pocketbase/pb_migrations

# 3. Create a superuser (on first run)
./pocketbase superuser create <email> <password> --dir=/home/mz/pb_data

# 4. Set env vars
cp .env.example .env.local  # edit WEDDING_SESSION_SECRET, POCKETBASE_ADMIN_*
echo 'POCKETBASE_URL=http://127.0.0.1:8090' > .env.development.local

# 5. Generate QR guest cards
node scripts/generate-qr-codes.mjs names.csv

# 6. Start Next.js
./node_modules/.bin/next dev

Open http://192.168.1.x:3000 on a phone on the same Wi‑Fi (the dev server prints the LAN IP).

Production deployment

Docker Compose

# One‑time VPS setup
docker network create wedding
mkdir -p /opt/wedding/pb_data /opt/wedding-dev/pb_data

# Build and run
docker compose -f infra/compose.yml build
docker compose -f infra/compose.yml up -d

Three containers, all on the internal wedding network: - caddy — TLS termination, ports 80/443. - wedding-app — Next.js standalone build, port 3000 (internal only). - pocketbase — PocketBase 0.39.4, port 8090 (internal only).

A separate compose.develop.yml handles the dev.am-party.pl staging environment on the same host.

CI/CD

GitLab CI

scan ──▶ check ──▶ test ──▶ build ──▶ release (main) ──▶ deploy
  │        │        │         │            │              │
  SAST    Biome   vitest    Docker     semantic‑release   SSH → VPS
  Secret   tsc              registry   GitLab Packages   compose up
  Deps                       tags

Project structure

.
├── app/                        # Next.js application
│   ├── app/                    #   App Router (pages, api routes)
│   ├── lib/                    #   Server utilities (auth, filters, media)
│   ├── pocketbase/
│   │   ├── pb_migrations/      #   PocketBase schema (auto‑applied)
│   │   ├── types.ts            #   DB record types
│   │   └── schema.md           #   Human‑readable schema reference
│   ├── public/                 #   Static assets (bride.png, groom.png, …)
│   ├── scripts/                #   One‑shot Node scripts
│   │   ├── generate-qr-codes.mjs   #   Guest QR generator
│   │   ├── generate-video.mjs      #   Night‑end video stitcher
│   │   └── seed-newlyweds.mjs      #   Bulk upload to Newlyweds collection
│   ├── Dockerfile              #   Multi‑stage standalone Next.js build
│   ├── next.config.ts
│   └── package.json
├── infra/                      # Deployment infrastructure
│   ├── compose.yml             #   Production Docker Compose
│   ├── compose.develop.yml     #   Develop‑environment Compose
│   ├── Caddyfile               #   Reverse proxy + TLS config
│   └── pocketbase/
│       └── Dockerfile          #   PocketBase 0.39.4 container
├── docs/                       # GitLab Pages source
├── .gitlab-ci.yml              # Full CI/CD pipeline (7 stages)
├── .releaserc                  # semantic‑release configuration
└── README.md

Env variables

Variable Purpose
WEDDING_SESSION_SECRET 64‑char random hex — HMAC signing key for auth cookies
POCKETBASE_URL PocketBase base URL — http://pocketbase:8090 in Docker
POCKETBASE_ADMIN_EMAIL PB superuser email
POCKETBASE_ADMIN_PASSWORD PB superuser password
WEDDING_APP_URL Public base URL (for QR generation)
WEDDING_TIMEZONE IANA timezone for day‑boundary calculations
WEDDING_PARTY_CUTOFF Hour (0–23) — photos from the previous day still appear until this local hour

Conventional commits

This project follows Conventional Commits for automated semantic versioning: