tanshu 7b070fde81 Initial build: Great Bear microbrewery site
- TanStack Start (React 19, Tailwind 4) frontend recreated from Stitch design
- PocketBase backend with schema migration + idempotent content seed
- Single Docker image running both services (non-root, healthchecked)
- deploy.sh to build & push to registry.tanshu.com
- Ansible playbook (roles: app, caddy) deploying to www.greatbear.in
  with Caddy TLS entries for the site and the PocketBase admin
2026-09-03 08:51:47 +05:30

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

cd app
npm install
npm run dev          # http://localhost:3000 (uses bundled seed data if PocketBase is down)

To run PocketBase locally (optional):

./.tmp-pb/pocketbase serve --http=127.0.0.1:8090 --dir=./pb_data --migrationsDir=./pb/migrations

npm run build produces .output/server/index.mjs, started with npm start.

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:

  1. Logs into the registry on the host, pulls the image, and (re)creates the greatbear container with ports bound to loopback (127.0.0.1:3000, 127.0.0.1:8090) and a named volume greatbear_pb_data:/data for the database.

  2. Waits for PocketBase + site health checks.

  3. Writes /etc/caddy/caddy.d/greatbear.conf with both site blocks and reloads Caddy:

    • www.greatbear.in127.0.0.1:3000 (the site)
    • admin.greatbear.in127.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 in ansible/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
S
Description
No description provided
Readme
4.7 MiB
Languages
TypeScript 46.7%
JavaScript 43.8%
Shell 4.5%
CSS 3.6%
Dockerfile 1.2%
Other 0.2%