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
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# 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: app, caddy)
|
||||
Dockerfile Multi-stage image build
|
||||
deploy.sh Build & push image to the private registry
|
||||
```
|
||||
|
||||
## Local development
|
||||
|
||||
```bash
|
||||
cd app
|
||||
npm install
|
||||
npm run dev # http://localhost:3000 (uses bundled seed data if PocketBase is down)
|
||||
```
|
||||
|
||||
To run PocketBase locally (optional):
|
||||
|
||||
```bash
|
||||
./.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
|
||||
|
||||
```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
|
||||
(`ansible-galaxy collection install community.docker`), SSH access to the host.
|
||||
|
||||
Requirements on the host (`www.greatbear.in`): Docker and Caddy already installed.
|
||||
|
||||
```bash
|
||||
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.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 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 |
|
||||
Reference in New Issue
Block a user