File: //bigscoots/wpo/extras/shop-monitor.sh
#!/bin/bash
# Bootstrap installer: sets up a per-domain Woo "shop page monitor" and a systemd unit.
# Usage:
# bash /home/nginx/domains/foodprepguide.com/shop-monitor.sh <domain.tld>
# bash /home/nginx/domains/foodprepguide.com/shop-monitor.sh remove <domain.tld>
set -euo pipefail
die() { echo "ERROR: $*" >&2; exit 1; }
ACTION="install"
DOMAIN=""
if [[ $# -eq 1 ]]; then
DOMAIN="$1"
elif [[ $# -eq 2 && "$1" == "remove" ]]; then
ACTION="remove"
DOMAIN="$2"
else
cat >&2 <<USAGE
Usage:
$0 <domain.tld>
$0 remove <domain.tld>
USAGE
exit 1
fi
# ---- Derived paths/names
UNIT_NAME_BASE="$(echo "${DOMAIN}-shop-monitor" | tr '.' '-')"
UNIT_NAME="${UNIT_NAME_BASE}.service"
ROOT="/home/nginx/domains/${DOMAIN}"
WP_PATH="${ROOT}/public"
WORKER="${ROOT}/shop-monitor-worker.sh"
LOG_DIR="${ROOT}/logs"
STATE_FILE="/var/run/shop-monitor.${DOMAIN}.lastfix"
UNIT_FILE="/etc/systemd/system/${UNIT_NAME}"
if [[ "$ACTION" == "remove" ]]; then
systemctl stop "$UNIT_NAME" 2>/dev/null || true
systemctl disable "$UNIT_NAME" 2>/dev/null || true
rm -f "$UNIT_FILE"
systemctl daemon-reload
echo "Removed ${UNIT_NAME}."
exit 0
fi
# ---- Install
[[ -d "$ROOT" ]] || die "Domain root not found: $ROOT"
mkdir -p "$LOG_DIR"
touch "$STATE_FILE" || true
# Per-domain worker (does the monitoring + flush)
cat > "$WORKER" <<'EOF'
#!/bin/bash
set -o nounset
set -o pipefail
DOMAIN="__DOMAIN__"
URL="https://__DOMAIN__/shop/"
WP_PATH="__WP_PATH__"
STATE_FILE="__STATE_FILE__"
COOLDOWN_SECONDS=600
CHECK_INTERVAL=5
serverip=$(ip route get 1 | awk '{print $NF;exit}')
send_slack_alert() {
local message="$1"
bash /bigscoots/general/slack.sh "#woo-errors" ":warning: \n*Hostname:* $(hostname)\n *Server IP:* ${serverip}\n *Message:* ${message}"
}
mkdir -p "$(dirname "$STATE_FILE")"
while true; do
TMPFILE=$(mktemp)
# -L follows redirects, --compressed handles gzip
HTTP_STATUS=$(curl -sS -L --compressed --max-time 10 \
-H "Accept: text/html;q=1.0,*/*;q=0.8" \
-H "User-Agent: shop-monitor/1.0 (+bigscoots)" \
-w "%{http_code}" -o "$TMPFILE" "$URL" || echo "000")
# Count Woo product tiles
PRODUCT_COUNT=$(grep -Eio 'class="[^"]*\btype-product\b[^"]*"' "$TMPFILE" | wc -l | awk '{print $1}')
echo "$(date '+%F %T') domain=${DOMAIN} status=$HTTP_STATUS products=$PRODUCT_COUNT"
# TRIGGER LOGIC: 404 error OR (200 OK but 0 products)
TRIGGER_FIX=false
REASON=""
if [[ "$HTTP_STATUS" == "404" ]]; then
TRIGGER_FIX=true
REASON="Page returned 404 Not Found"
elif [[ "$HTTP_STATUS" == "200" && "$PRODUCT_COUNT" -eq 0 ]]; then
TRIGGER_FIX=true
REASON="Page returned 200 OK but 0 'type-product' entries"
fi
if [ "$TRIGGER_FIX" = true ]; then
now_epoch=$(date +%s)
last_fix=0
[[ -f "$STATE_FILE" ]] && last_fix=$(cat "$STATE_FILE" 2>/dev/null || echo 0)
since=$(( now_epoch - last_fix ))
if (( since >= COOLDOWN_SECONDS )); then
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
msg="[${TIMESTAMP}] ${URL} issue detected: ${REASON}. Running 'wp rewrite flush --hard'."
echo "$msg" >> "__LOG_DIR__/shop-monitor.log"
send_slack_alert "$msg"
# Diagnostics
cp -rf "$TMPFILE" "__LOG_DIR__/shop-monitor-capture-${TIMESTAMP}.html"
wp rewrite list --allow-root --path="$WP_PATH" > "__LOG_DIR__/shop-monitor-rewrites-${TIMESTAMP}.log" 2>&1
# The Fix
wp rewrite flush --hard --allow-root --path="$WP_PATH"
wp cache flush --allow-root --path="$WP_PATH" 2>/dev/null || true
if wp plugin is-active bigscoots-cache --quiet --path="$WP_PATH" 2>/dev/null; then
wp bs_cache purge_cache --path="$WP_PATH" 2>/dev/null
fi
echo "$now_epoch" > "$STATE_FILE"
else
echo "Cooldown active (${since}s < ${COOLDOWN_SECONDS}s); skipping flush."
fi
fi
rm -f "$TMPFILE"
sleep "$CHECK_INTERVAL"
done
EOF
# Fill in template vars in the worker
sed -i \
-e "s#__DOMAIN__#${DOMAIN}#g" \
-e "s#__WP_PATH__#${WP_PATH}#g" \
-e "s#__STATE_FILE__#${STATE_FILE}#g" \
-e "s#__LOG_DIR__#${LOG_DIR}#g" \
"$WORKER"
chmod +x "$WORKER"
# systemd unit
cat > "$UNIT_FILE" <<EOF
[Unit]
Description=Monitor ${DOMAIN} shop page for missing products or 404s and flush permalinks
After=network.target
[Service]
Type=simple
ExecStart=${WORKER}
Restart=always
RestartSec=3
User=root
StandardOutput=append:${LOG_DIR}/shop-monitor.service.log
StandardError=append:${LOG_DIR}/shop-monitor.service.err
[Install]
WantedBy=multi-user.target
EOF
# Reload + enable + start
systemctl daemon-reload
systemctl enable --now "$UNIT_NAME"
echo "Installed and started ${UNIT_NAME}"
systemctl --no-pager --lines=20 status "$UNIT_NAME" || true