Pak Jabit: full stack — storefront, Spree customisations, catalogue tooling
A South African men's apparel store on Spree 5.6 + Next.js 15. 160 products
live, three markets, live Malaysian courier rates, customer-owned carts.
What is here:
storefront/ the Next.js app
backend/ ONLY the custom Spree code. Spree itself is a prebuilt image
that mounts no app code, so every file here needs an explicit
volume entry in docker-compose.yml or it is silently ignored.
catalogue/ the scripts that loaded and maintain the products
docs/ manifest, mock registry, napkins
Real .env files are committed deliberately. This lives on the house's private
Gitea, and a clone that runs beats a clone that needs a secrets handover.
LANDMINE.md maps every key, what it opens, and what must be rotated before this
repo goes anywhere less private.
The admin API key used to be a literal in six catalogue scripts. They now read
SPREE_ADMIN_KEY from the environment and refuse to run without it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pull a full Pak Jabit backend backup to the house.
|
||||
# - pg_dump (custom format) of spree_production
|
||||
# - ActiveStorage volume, rsync'd mirror (~208MB, only deltas after the first run)
|
||||
# - .env (SECRET_KEY_BASE lives here — without it every signed image URL and
|
||||
# the admin API token die on restore)
|
||||
#
|
||||
# Usage: backup-pak-jabit.sh # normal run
|
||||
# backup-pak-jabit.sh --verify # also list the dump's table counts
|
||||
set -euo pipefail
|
||||
|
||||
HOST=ijat@35.247.170.3
|
||||
KEY=/root/.ssh/google_compute_engine
|
||||
SSH=(ssh -i "$KEY" -o ConnectTimeout=15 -o BatchMode=yes)
|
||||
DEST=/root/Backups/pak-jabit
|
||||
KEEP=14 # dated dumps to retain
|
||||
STAMP=$(date +%Y%m%d-%H%M)
|
||||
|
||||
STORAGE_REMOTE=/var/lib/docker/volumes/pak-jabit_storage_data/_data/
|
||||
mkdir -p "$DEST"/{db,storage}
|
||||
chmod 700 "$DEST"
|
||||
|
||||
log() { printf '%s %s\n' "$(date +%H:%M:%S)" "$*"; }
|
||||
|
||||
# --- 1. database -------------------------------------------------------------
|
||||
log "pg_dump → db/spree-$STAMP.dump"
|
||||
"${SSH[@]}" "$HOST" \
|
||||
'docker exec pak-jabit-postgres-1 pg_dump -U postgres -Fc spree_production' \
|
||||
> "$DEST/db/spree-$STAMP.dump.part"
|
||||
mv "$DEST/db/spree-$STAMP.dump.part" "$DEST/db/spree-$STAMP.dump"
|
||||
|
||||
# A truncated dump is the classic silent-success backup. Refuse to keep one.
|
||||
if ! pg_restore -l "$DEST/db/spree-$STAMP.dump" >/dev/null 2>&1; then
|
||||
rm -f "$DEST/db/spree-$STAMP.dump"
|
||||
echo "FATAL: dump failed pg_restore -l — not a usable archive, deleted." >&2
|
||||
exit 1
|
||||
fi
|
||||
log " ok $(du -h "$DEST/db/spree-$STAMP.dump" | cut -f1)"
|
||||
|
||||
# --- 2. ActiveStorage --------------------------------------------------------
|
||||
log "rsync storage/"
|
||||
rsync -a --delete --info=stats2 \
|
||||
-e "ssh -i $KEY -o BatchMode=yes" \
|
||||
--rsync-path='sudo rsync' \
|
||||
"$HOST:$STORAGE_REMOTE" "$DEST/storage/" | tail -n 6
|
||||
|
||||
# --- 3. secrets & compose ----------------------------------------------------
|
||||
log "env + compose"
|
||||
"${SSH[@]}" "$HOST" 'cat ~/pak-jabit/.env' > "$DEST/env-$STAMP"
|
||||
"${SSH[@]}" "$HOST" 'cat ~/pak-jabit/docker-compose.yml' > "$DEST/docker-compose.yml"
|
||||
chmod 600 "$DEST"/env-*
|
||||
ln -sfn "env-$STAMP" "$DEST/env.latest"
|
||||
|
||||
# --- 3b. custom app code -----------------------------------------------------
|
||||
# The Spree image is prebuilt and mounts no app code, so every customisation
|
||||
# (Image Studio, the API body-limit initializer, the patched routes.rb) exists
|
||||
# ONLY as bind-mounted files on this VM. The database dump does not contain them
|
||||
# and a rebuilt box would come back without them.
|
||||
log "custom code"
|
||||
rsync -a --delete \
|
||||
-e "ssh -i $KEY -o BatchMode=yes" \
|
||||
--include='config/' --include='config/routes.rb' \
|
||||
--include='config/initializers/***' \
|
||||
--include='app/' --include='app/controllers/***' --include='app/views/***' \
|
||||
--exclude='*' \
|
||||
"$HOST:pak-jabit/" "$DEST/custom/"
|
||||
find "$DEST/custom" -type f | sed "s|$DEST/custom/| |"
|
||||
|
||||
# --- 4. rotate ---------------------------------------------------------------
|
||||
ls -1t "$DEST"/db/spree-*.dump 2>/dev/null | tail -n +$((KEEP + 1)) | xargs -r rm -v
|
||||
ls -1t "$DEST"/env-* 2>/dev/null | tail -n +$((KEEP + 1)) | xargs -r rm -v
|
||||
|
||||
if [[ ${1:-} == --verify ]]; then
|
||||
log "dump contents:"
|
||||
pg_restore -l "$DEST/db/spree-$STAMP.dump" | grep -c 'TABLE DATA' \
|
||||
| xargs printf ' %s tables with data\n'
|
||||
fi
|
||||
|
||||
log "done → $DEST ($(du -sh "$DEST" | cut -f1) total)"
|
||||
Reference in New Issue
Block a user