diff --git a/apps/api/README.md b/apps/api/README.md index b0f3ca5..fe4bf14 100644 --- a/apps/api/README.md +++ b/apps/api/README.md @@ -57,3 +57,23 @@ this directory: Deployed to Fly.io (`fly.toml`). After a domain changes, re-run `src/scripts/register-webhooks.ts` with production env vars to point Printful's and Webflow's webhooks at the new domain — see issue #3 for the full checklist. + +### Running migrations/seed against production + +The deployed image only contains the compiled binary (see `Dockerfile`) — no +source, no `bun_modules`, no migration files — so these can't be run from +`fly ssh console` on the API app itself. Instead, use `migrate.sh` at the repo +root, which tunnels to the Postgres app (`blade-and-brawn-db`, legacy/unmanaged +Fly Postgres) via `fly proxy`, fetches the production `DATABASE_URL` for you, +and runs the scripts against it: + +```bash +./migrate.sh migrate # db:migrate:latest (default if no argument given) +./migrate.sh seed # db:seed +./migrate.sh both # both, in order +``` + +Both underlying scripts prompt for a `y/N` confirmation before touching the +database, and `db:seed` is idempotent (skips seeding if the default rows +already exist). If the tunneled connection fails on TLS, legacy Postgres +sometimes needs `?sslmode=disable` appended — edit `migrate.sh` if so. diff --git a/migrate.sh b/migrate.sh new file mode 100755 index 0000000..2c8b089 --- /dev/null +++ b/migrate.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" + +PG_APP="blade-and-brawn-db" +API_APP="blade-and-brawn-api" +LOCAL_PORT=5433 + +proxy_pid="" +cleanup() { + if [[ -n "$proxy_pid" ]]; then + kill "$proxy_pid" 2>/dev/null || true + fi +} +trap cleanup EXIT + +echo "Fetching production DATABASE_URL from $API_APP..." >&2 +prod_url=$(fly ssh console -a "$API_APP" -C "printenv DATABASE_URL" 2>/dev/null | grep -m1 '^postgres') +if [[ -z "$prod_url" ]]; then + echo "Failed to fetch DATABASE_URL from $API_APP" >&2 + exit 1 +fi +tunneled_url=$(echo "$prod_url" | sed -E "s#@[^/]+/#@localhost:${LOCAL_PORT}/#") + +echo "Starting tunnel to $PG_APP on localhost:${LOCAL_PORT}..." >&2 +fly proxy "${LOCAL_PORT}:5432" -a "$PG_APP" & +proxy_pid=$! + +echo "Waiting for tunnel..." >&2 +for _ in $(seq 1 30); do + nc -z localhost "$LOCAL_PORT" 2>/dev/null && break + sleep 0.5 +done + +target="${1:-migrate}" + +case "$target" in + migrate) + DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:migrate:latest + ;; + seed) + DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:seed + ;; + both) + DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:migrate:latest + DATABASE_URL="$tunneled_url" bun run --filter @blade-and-brawn/api db:seed + ;; + *) + echo "Usage: $0 [migrate|seed|both]" >&2 + exit 1 + ;; +esac