# 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](https://tanstack.com/start) (React 19, Vite 8, Tailwind CSS 4, SSR) | | Backend | [PocketBase](https://pocketbase.io) 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: network, greatbear, 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): ```bash cd app npm install npm run dev # http://localhost:3000 ``` Full stack with PocketBase (recommended — from the repo root): ```bash # 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: ```bash 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:/app/pb_data greatbear:dev ``` ## Deploying ### 1. Build and push the image ```bash cp .env.example .env # then fill in registry credentials ./deploy.sh ``` Tags pushed: `registry.tanshu.com/tanshu/greatbear:latest` and `:`. ### 2. Deploy with Ansible Requirements on the controller: `ansible` + `community.docker` collection (see `ansible/requirements.yml`). Requirements on the host: Docker with a Caddy **container** named `caddy` (config at `/var/lib/caddy/conf/Caddyfile`). ```bash cd ansible ansible-galaxy install -r requirements.yml ansible-playbook playbook.yml # targets inventory host `monoco` — edit playbook.yml if needed ``` All deployment variables live in `ansible/vars/default.yml`. The playbook: 1. Ensures the `greatbear_net` Docker network exists with the Caddy container attached. 2. Pulls `registry.tanshu.com/tanshu/greatbear:latest`, uploads `/var/lib/greatbear/.env`, and (re)creates the `greatbear` container on that network with `/var/lib/greatbear/pb_data` bind-mounted to `/app/pb_data`, then waits for its healthcheck. 3. Inserts a managed snippet into the shared Caddyfile and reloads Caddy via `docker exec`: - `www.greatbear.in` (+ apex `greatbear.in`) — frontend on port 3000, PocketBase API/admin proxied under `/api/*` and `/_/*` - `admin.greatbear.in` — redirects to the PocketBase admin UI No host ports are published; Caddy reaches the containers over the Docker network. DNS records for all three hosts must point at the server; 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_DATA_DIR` | `/data` | PocketBase data directory (volume) | | `PB_SUPERUSER_EMAIL` | — | Initial admin (bootstrapped at start) | | `PB_SUPERUSER_PASSWORD` | — | Initial admin password |