← back to NationalPaperHangers
scripts/deploy-kamatera-https.sh
110 lines
#!/usr/bin/env bash
#
# One-shot prod deploy for nationalpaperhangers.com on Kamatera (45.61.58.125).
#
# What it does, in order:
# 1. Diagnoses why pm2 `national-paper-hangers` is stuck (env / port / DB)
# 2. Syncs the local code tree to /root/Projects/NationalPaperHangers
# 3. Runs migrations 012 + 013 against the prod PG
# 4. Installs / reuses an nginx vhost for nationalpaperhangers.com proxying
# to 127.0.0.1:9765 with /uploads served direct from disk
# 5. Issues a Let's Encrypt cert via certbot --nginx (with --redirect)
# 6. Reloads nginx and restarts the pm2 process under the new env
# 7. Smoke-tests the live URLs
#
# Run this from the LOCAL Mac after SSH access is approved:
# bash ~/Projects/NationalPaperHangers/scripts/deploy-kamatera-https.sh
#
# Env knobs you can override at the top:
# NPH_REMOTE_HOST default: root@45.61.58.125
# NPH_REMOTE_DIR default: /root/Projects/NationalPaperHangers
# NPH_PORT default: 9765
# NPH_DOMAIN default: nationalpaperhangers.com
#
# This script is intentionally idempotent — re-running is safe.
set -euo pipefail
REMOTE="${NPH_REMOTE_HOST:-root@45.61.58.125}"
RDIR="${NPH_REMOTE_DIR:-/root/Projects/NationalPaperHangers}"
PORT="${NPH_PORT:-9765}"
DOMAIN="${NPH_DOMAIN:-nationalpaperhangers.com}"
EMAIL="${NPH_LE_EMAIL:-steve@designerwallcoverings.com}"
LOCAL_DIR="$HOME/Projects/NationalPaperHangers"
say() { printf '\n[deploy] %s\n' "$*"; }
# --- 1. Diagnose ----------------------------------------------------------
say "Diagnosing pm2 + nginx state on $REMOTE..."
ssh "$REMOTE" "pm2 describe national-paper-hangers 2>&1 | head -25; echo '---'; pm2 logs national-paper-hangers --lines 30 --nostream 2>&1 | tail -40 || true; echo '---NGINX VHOSTS---'; ls /etc/nginx/sites-enabled/ | grep -iE 'paper|nph' || echo '(no nph vhost)'"
# --- 2. Sync code ---------------------------------------------------------
say "Rsync'ing local code → $REMOTE:$RDIR (excluding node_modules + .env)..."
rsync -avz --delete \
--exclude node_modules --exclude .env --exclude .env.local \
--exclude tmp --exclude '*.log' --exclude .git \
"$LOCAL_DIR/" "$REMOTE:$RDIR/"
# --- 3. Migrations --------------------------------------------------------
say "Running migrations 012 + 013 against prod PG..."
ssh "$REMOTE" "cd $RDIR && \
source .env 2>/dev/null || true; \
psql \"\$DATABASE_URL\" -f db/migrations/012_installer_templates.sql; \
psql \"\$DATABASE_URL\" -f db/migrations/013_consumer_accounts_and_brief.sql"
# --- 4. nginx vhost -------------------------------------------------------
say "Installing nginx vhost for $DOMAIN → 127.0.0.1:$PORT..."
ssh "$REMOTE" "cat > /etc/nginx/sites-available/$DOMAIN <<'CONF'
server {
listen 80;
listen [::]:80;
server_name $DOMAIN www.$DOMAIN;
# Let's Encrypt webroot challenge (used on first issuance only).
location /.well-known/acme-challenge/ { root /var/www/letsencrypt; }
# Static uploads served directly (avoid passing image bytes through Node).
location /uploads/ {
alias $RDIR/public/uploads/;
access_log off;
expires 30d;
add_header Cache-Control \"public, immutable\";
}
location / {
proxy_pass http://127.0.0.1:$PORT;
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;
client_max_body_size 12m;
}
}
CONF
mkdir -p /var/www/letsencrypt
ln -sf /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/$DOMAIN
# Defensive: remove any *.bak / *.old that might shadow this (per Steve's rule).
find /etc/nginx/sites-enabled/ -maxdepth 1 -type f \( -name '*.bak' -o -name '*.old' \) -delete
nginx -t && systemctl reload nginx"
# --- 5. Restart pm2 (so the new code is live before certbot probes /) -----
say "Restarting pm2 national-paper-hangers under updated env..."
ssh "$REMOTE" "cd $RDIR && pm2 restart national-paper-hangers --update-env || pm2 start ecosystem.kamatera.config.js --only national-paper-hangers --update-env"
sleep 2
ssh "$REMOTE" "curl -s -o /dev/null -w '127.0.0.1:$PORT/ → %{http_code}\n' http://127.0.0.1:$PORT/"
# --- 6. Issue cert --------------------------------------------------------
say "Issuing Let's Encrypt cert for $DOMAIN + www.$DOMAIN..."
ssh "$REMOTE" "certbot --nginx -d $DOMAIN -d www.$DOMAIN \
--non-interactive --agree-tos -m $EMAIL --redirect"
# --- 7. Smoke test --------------------------------------------------------
say "Smoke testing live URLs..."
sleep 1
for path in / /find /installer/atelier-bond-nyc /installer/atelier-bond-nyc/book; do
code=$(curl -s -o /dev/null -w '%{http_code}' "https://$DOMAIN$path" --max-time 10 || echo "TIMEOUT")
printf ' https://%s%s → %s\n' "$DOMAIN" "$path" "$code"
done
say "Done."