Staging runs on a private-network VM, not reachable from outside, so the HTTP basic-auth layer was unnecessary defense-in-depth. Removing it also lets the Caddyfile drop the render-around-Compose workaround entirely (that workaround existed specifically because Compose's interpolation mangles a bcrypt hash) — the noindex header and blog_public=0 stay, since those guard against search-engine indexing, a separate concern from network-level access. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
132 lines
5.6 KiB
Markdown
132 lines
5.6 KiB
Markdown
# Bookstore
|
||
|
||
WordPress + WooCommerce bookstore. Design reference: see `docs/` for the
|
||
pre-launch plan and the technical design doc (`bookstore-core` schema,
|
||
interfaces, order state machine — the design doc is the source of truth for
|
||
*why* this repo is laid out the way it is).
|
||
|
||
This week's scope (Week 1 of the 5-week plan): environments, deploy
|
||
pipeline, and the WooCommerce/HPOS plugin scaffold. Catalog schema, pricing,
|
||
supplier adapters, and the order state machine are Week 2+ and live under
|
||
`wp-content/plugins/bookstore-core/includes/`, currently empty.
|
||
|
||
## Layout
|
||
|
||
```
|
||
docker-compose.yml base services: db, redis, wordpress, cron
|
||
docker-compose.{dev,staging,production}.yml per-environment overrides (caddy, mailhog)
|
||
docker/php/ app image: WP core image + wp-cli, composer, redis ext
|
||
docker/caddy/ one Caddyfile per environment
|
||
docker/cron/ Action Scheduler driver (system cron has no host to run on in Docker)
|
||
deploy/deploy.sh idempotent bring-up + WP/WooCommerce config
|
||
deploy/poll-deploy.sh host-crontab script: redeploys when its branch moves on Gitea
|
||
deploy/backup.sh, restore.sh off-host backup; restore is staging-only, on purpose
|
||
wp-content/plugins/bookstore-core/ the one plugin that owns business logic
|
||
wp-content/mu-plugins/ local-mail-catcher.php — routes mail to MailHog outside production
|
||
```
|
||
|
||
Same code runs in every environment; only `.env.<env>` and which
|
||
`docker-compose.<env>.yml` you layer in differ (design doc §01).
|
||
|
||
## Local development
|
||
|
||
```
|
||
cp .env.example .env.dev # fill in DB_PASSWORD etc.; localhost values are fine
|
||
make up ENV=dev
|
||
make deploy ENV=dev # installs WP, WooCommerce, activates bookstore-core, enables HPOS
|
||
```
|
||
|
||
Site is at `http://localhost:8080`. `make wp ENV=dev ARGS="plugin list"` runs
|
||
any wp-cli command; `make logs ENV=dev` tails everything; `make shell ENV=dev`
|
||
drops into the app container.
|
||
|
||
## Staging (this is what goes on your Docker server)
|
||
|
||
```
|
||
cp .env.example .env.staging
|
||
# fill in SITE_DOMAIN, SITE_URL, DB_*
|
||
|
||
make deploy ENV=staging
|
||
```
|
||
|
||
Staging is `noindex`'d (`blog_public=0` plus the `X-Robots-Tag` header in
|
||
Caddyfile.staging) so search engines won't index it — there's no basic-auth
|
||
wall on top of that, since this box is LAN-only and not reachable from
|
||
outside. If that ever changes (a public domain, port-forwarding, etc.),
|
||
basic-auth is worth adding back before that happens, not after. Mail never
|
||
leaves the box: it's caught by MailHog, viewable at `:8025`.
|
||
|
||
### Theme: Blocksy + the Book Store starter site
|
||
|
||
`deploy.sh` installs and activates the **Blocksy** theme and the free
|
||
**Blocksy Companion** plugin from wordpress.org automatically — nothing to
|
||
do here, in any environment.
|
||
|
||
The **Book Store starter site** itself is a paid Companion Pro template
|
||
(Business plan, $99/year — design doc Appendix A), so it can't be scripted
|
||
against a public API the way the free theme can. One-time manual step per
|
||
environment, in wp-admin:
|
||
|
||
1. Purchase/retrieve the Blocksy Business license key.
|
||
2. **Blocksy → General → License** → activate it.
|
||
3. **Blocksy → Extensions → Starter Sites** (Companion Pro) → import
|
||
**Book Store**.
|
||
|
||
After that, the starter site's content and Customizer settings persist in
|
||
the database like any other WordPress content — a redeploy or a fresh
|
||
`deploy.sh` run doesn't touch it or need to repeat it.
|
||
|
||
## Deploy pipeline
|
||
|
||
The Git server (Gitea) and the staging/production Docker hosts are separate
|
||
machines, so instead of a webhook receiver listening for an inbound POST
|
||
from Gitea, each box just polls its branch and redeploys when it moves —
|
||
no inbound port to expose or secure.
|
||
|
||
1. Clone this repo onto the box (e.g. into `/srv/bookstore`), `cp
|
||
.env.example .env.staging` (or `.env.production`) and fill it in.
|
||
2. Add a host crontab entry (not inside a container — `crontab -e` on the
|
||
box itself):
|
||
```
|
||
*/2 * * * * /srv/bookstore/deploy/poll-deploy.sh staging >> /var/log/bookstore-deploy.log 2>&1
|
||
```
|
||
(`production` + `main` on the production box.)
|
||
3. `poll-deploy.sh` fetches, compares against the last-deployed commit, and
|
||
— only if it moved — checks out the branch and runs `deploy.sh`. Silent
|
||
otherwise, so it's safe to run every couple of minutes.
|
||
4. The box needs its own git credentials to fetch from Gitea (a read-only
|
||
access token works well) — set those up once via a credential helper or
|
||
an embedded token in that box's `origin` remote.
|
||
|
||
You can also just run `./deploy/deploy.sh <env>` by hand at any time; the
|
||
poll script is only automation on top of the same idempotent script.
|
||
|
||
## Backups
|
||
|
||
```
|
||
./deploy/backup.sh staging # or production
|
||
```
|
||
|
||
Dumps the database and archives `wp-content/uploads`, gzips both, and syncs
|
||
to `$BACKUP_REMOTE` via `rclone` if set (configure `rclone config` on the
|
||
server first — this repo doesn't manage remote credentials). Before launch,
|
||
run a real restore drill:
|
||
|
||
```
|
||
./deploy/restore.sh ./backups/production/db-<ts>.sql.gz ./backups/production/uploads-<ts>.tar.gz
|
||
```
|
||
|
||
`restore.sh` only ever targets staging — there's no production argument, so
|
||
a restore drill can't accidentally overwrite the live site.
|
||
|
||
## What's deliberately not here yet
|
||
|
||
- Catalog tables, pricing engine, supplier adapters, order state machine —
|
||
Week 2–4, see the design doc.
|
||
- A real TLS-terminating public domain — Caddy will auto-provision certs
|
||
once `SITE_DOMAIN` points at a real host with 80/443 reachable; until
|
||
then `docker-compose.dev.yml` is the only one that works from
|
||
`localhost`.
|
||
- CI test/lint automation — nothing here runs tests yet because there's no
|
||
application code to test yet.
|