- playbook.yml (hosts: monoco) + vars/default.yml, requirements.yml - roles: network (shared docker network), greatbear (registry pull, bind mount /var/lib/greatbear/pb_data, .env upload, health wait), caddy (blockinfile snippet into the shared Caddyfile + docker exec reload) - image honors PB_DATA_DIR and runs as root to match the bind-mount pattern - frontend on www.greatbear.in (+apex) with /api/* and /_* proxied to PocketBase; admin.greatbear.in redirects to the admin UI
150 lines
6.5 KiB
Markdown
150 lines
6.5 KiB
Markdown
# 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 `:<git-sha>`.
|
|
|
|
### 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 |
|