6.2 KiB
The Great Bear — Microbrewery Website
Full-stack site for The Great Bear microbrewery (SCO 32, Sector 26, Chandigarh), built from the Stitch "Industrial Hospitality" design.
| Layer | Tech |
|---|---|
| Frontend | TanStack Start (React 19, Vite 8, Tailwind CSS 4, SSR) |
| Backend | PocketBase v0.40 — CMS content, form intake, admin UI |
| Runtime | Single Docker image running both services |
| Proxy | Caddy (automatic HTTPS) on the target host |
| Deploys | deploy.sh (build + push) → Ansible playbook (pull + run) |
Pages
/— hero, stats, featured brews, experiences, testimonials, quick table booking/our-brews— flagship banner, core lineup, seasonal releases, tasting-flight builder/menu— filterable gastropub menu with beer pairings/visit— reservation form, contact info, map
All structured content (brews, menu, testimonials, stats, contact info) is served from PocketBase and editable in the admin UI. If PocketBase is unreachable, the site falls back to bundled seed content so it never renders empty.
Repository layout
app/ TanStack Start application
src/routes/ Pages (/, /our-brews, /menu, /visit)
src/components/ Shared UI + client forms
src/lib/ Server functions & PocketBase client
src/data/content.json Single source of truth for seed + fallback content
public/images/ Design assets (downloaded from the Stitch export)
pb/migrations/ PocketBase schema (snapshot migration, auto-applied)
scripts/
run-all.mjs Container supervisor: PocketBase + site server
seed.mjs Idempotent content seeder (create-if-missing by uid)
create-collections.sh Dev helper used to build the schema migration
ansible/ Deployment playbook (roles: app, caddy)
Dockerfile Multi-stage image build
deploy.sh Build & push image to the private registry
Local development
Frontend only (renders from bundled seed content; forms return errors):
cd app
npm install
npm run dev # http://localhost:3000
Full stack with PocketBase (recommended — from the repo root):
# Terminal 1 — PocketBase on :8090 (data in ./pb_data, schema auto-applied
# from pb/migrations on first boot)
./.tmp-pb/pocketbase serve --http=127.0.0.1:8090 --dir=./pb_data --migrationsDir=./pb/migrations
# Terminal 2 — one-time setup: create a local admin and seed the content
./.tmp-pb/pocketbase superuser upsert dev@greatbear.in devpass12345 --dir=./pb_data --migrationsDir=./pb/migrations
PB_URL=http://127.0.0.1:8090 PB_SUPERUSER_EMAIL=dev@greatbear.in PB_SUPERUSER_PASSWORD=devpass12345 \
node scripts/seed.mjs
# Terminal 2 — the site (talks to PocketBase on 127.0.0.1:8090 automatically)
cd app && npm run dev
- PocketBase admin UI:
http://127.0.0.1:8090/_/(dev@greatbear.in) — edits show up on the site on the next page load - Dev data lives in
./pb_data(git-ignored); delete it for a factory reset - The PB binary in
.tmp-pb/was fetched during setup — if it's missing, grab a release from https://github.com/pocketbase/pocketbase/releases (or run PocketBase however you like; only the port matters)
npm run build produces .output/server/index.mjs, started with npm start. To run the
production image locally instead:
docker build -t greatbear:dev .
docker run --rm -p 3000:3000 -p 8090:8090 \
-e PB_SUPERUSER_EMAIL=dev@greatbear.in -e PB_SUPERUSER_PASSWORD=devpass12345 \
-v greatbear_dev_data:/data greatbear:dev
Deploying
1. Build and push the image
cp .env.example .env # then fill in registry credentials
./deploy.sh
Tags pushed: registry.tanshu.com/tanshu/greatbear:latest and :<git-sha>.
2. Deploy with Ansible
Requirements on the controller: ansible + community.docker collection
(ansible-galaxy collection install community.docker), SSH access to the host.
Requirements on the host (www.greatbear.in): Docker and Caddy already installed.
cd ansible
# edit group_vars/greatbear/vars.yml if needed (or encrypt it with ansible-vault)
ansible-playbook site.yml # add -i inventory/hosts.yml if not using ansible.cfg
The playbook:
-
Logs into the registry on the host, pulls the image, and (re)creates the
greatbearcontainer with ports bound to loopback (127.0.0.1:3000,127.0.0.1:8090) and a named volumegreatbear_pb_data:/datafor the database. -
Waits for PocketBase + site health checks.
-
Writes
/etc/caddy/caddy.d/greatbear.confwith both site blocks and reloads Caddy:www.greatbear.in→127.0.0.1:3000(the site)admin.greatbear.in→127.0.0.1:8090(PocketBase API + admin UI)
DNS records for both domains must point at the host; Caddy obtains TLS certificates automatically on first request.
PocketBase admin
- URL:
https://admin.greatbear.in/_/ - First container start bootstraps the superuser from
PB_SUPERUSER_EMAIL/PB_SUPERUSER_PASSWORD(set inansible/group_vars/greatbear/vars.yml, idempotent).
Content collections: brews, menu_categories, menu_items, experiences, testimonials,
stats, pairings, settings. Reservation/contact submissions land in reservations
(public create only — readable by admins).
The seed only creates records that are missing (matched by uid), so edits made in the admin
are never overwritten by redeploys. Schema changes belong in pb/migrations/.
Container environment
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
Site server port |
PB_URL |
http://127.0.0.1:8090 |
PocketBase URL used by the site server |
PB_SUPERUSER_EMAIL |
— | Initial admin (bootstrapped at start) |
PB_SUPERUSER_PASSWORD |
— | Initial admin password |