← back to Dw Pairs Well

deploy/kamatera-bootstrap.sh

107 lines

#!/usr/bin/env bash
# Idempotent Kamatera bootstrap for dw-pairs-well.
# Run AS ROOT on 45.61.58.125 after rsync has placed code at
# /root/Projects/dw-pairs-well/ and after DNS pairs.designerwallcoverings.com
# resolves to 45.61.58.125.
#
#     ssh root@45.61.58.125 'bash /root/Projects/dw-pairs-well/deploy/kamatera-bootstrap.sh'

set -euo pipefail

PROJECT_DIR=/root/Projects/dw-pairs-well
DOMAIN=pairs.designerwallcoverings.com
PORT=9813
PM2_NAME=dw-pairs-well
NGINX_CONF=/etc/nginx/sites-available/${DOMAIN}
NGINX_LINK=/etc/nginx/sites-enabled/${DOMAIN}

echo "==[1/7] dependencies =================================================="
node -v >/dev/null 2>&1 || { echo "ERROR: node not installed"; exit 1; }
command -v pm2 >/dev/null 2>&1 || npm i -g pm2
command -v certbot >/dev/null 2>&1 || { apt-get update && apt-get install -y certbot python3-certbot-nginx; }

echo "==[2/7] npm install (production) ====================================="
cd "$PROJECT_DIR"
[ -f .env ] || { echo "ERROR: $PROJECT_DIR/.env missing — copy .env.example and edit"; exit 1; }
npm install --omit=dev --silent --no-audit --no-fund

echo "==[3/7] nginx vhost (HTTP only, certbot will add the TLS block) ======"
cat > "${NGINX_CONF}" <<NGINX
server {
    listen 80;
    listen [::]:80;
    server_name ${DOMAIN};

    # HTTP is left open here ONLY long enough for certbot's HTTP-01 challenge.
    # Certbot will rewrite this file to redirect → HTTPS after issuance.
    location /.well-known/acme-challenge/ { root /var/www/html; }

    location / {
        proxy_pass         http://127.0.0.1:${PORT};
        proxy_http_version 1.1;
        proxy_set_header   Host              \$host;
        proxy_set_header   X-Real-IP         \$remote_addr;
        proxy_set_header   X-Forwarded-For   \$proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto \$scheme;
        proxy_connect_timeout 5s;
        proxy_read_timeout    30s;
    }

    access_log /var/log/nginx/${DOMAIN}.access.log;
    error_log  /var/log/nginx/${DOMAIN}.error.log;
}
NGINX
ln -sfn "${NGINX_CONF}" "${NGINX_LINK}"
nginx -t
systemctl reload nginx

echo "==[4/7] DNS sanity check (must already point at 45.61.58.125) ========"
RESOLVED=$(dig +short "${DOMAIN}" @1.1.1.1 | head -1 || true)
if [ "${RESOLVED}" != "45.61.58.125" ] && [ -n "${RESOLVED}" ]; then
  # CF proxy returns CF IPs (104.x / 172.67.x) — that's fine
  case "${RESOLVED}" in
    104.*|172.67.*|172.64.*|108.162.*) echo "    → Cloudflare-proxied: ${RESOLVED} (ok)";;
    *) echo "    !! WARN: ${DOMAIN} → ${RESOLVED} (expected 45.61.58.125 or CF). Continuing anyway." ;;
  esac
elif [ -z "${RESOLVED}" ]; then
  echo "    !! ERROR: ${DOMAIN} does not resolve. Add the DNS record first."
  exit 1
else
  echo "    → resolves direct: 45.61.58.125 (ok)"
fi

echo "==[5/7] Let's Encrypt cert ==========================================="
if [ ! -d "/etc/letsencrypt/live/${DOMAIN}" ]; then
  certbot --nginx --non-interactive --agree-tos \
    --email steve@designerwallcoverings.com \
    -d "${DOMAIN}" --redirect
else
  echo "    → cert already exists for ${DOMAIN}, skipping issuance"
fi
nginx -t && systemctl reload nginx

echo "==[6/7] pm2 start ===================================================="
cd "${PROJECT_DIR}"
if pm2 describe "${PM2_NAME}" >/dev/null 2>&1; then
  pm2 reload "${PM2_NAME}"
else
  PORT=${PORT} pm2 start server.js --name "${PM2_NAME}" --update-env
fi
pm2 save

echo "==[7/7] smoke test ==================================================="
sleep 2
echo "--- local :${PORT}/healthz ---"
curl -sS "http://127.0.0.1:${PORT}/healthz" || true
echo
echo "--- https://${DOMAIN}/healthz ---"
curl -sS "https://${DOMAIN}/healthz" || true
echo
echo "--- https://${DOMAIN}/api/pairs?dw_sku=DWRW-74991 (first pair only) ---"
curl -sS "https://${DOMAIN}/api/pairs?dw_sku=DWRW-74991" \
  | python3 -c 'import sys,json; d=json.load(sys.stdin); p=d["pairs"][0] if d.get("pairs") else None; print(p["title"], "—", "score", p["score"]) if p else print("no pairs")' \
  || true

echo
echo "✅ done. http://${DOMAIN}/ should now redirect to https://${DOMAIN}/"