[object Object]

← back to Chinoiseriewallpaper

add finish_dns.sh + deploy artifacts: nginx vhost, pm2 live at :9853

65cb7722aa66bcfd800bbf783c1bcced969611f9 · 2026-05-07 15:12:04 -0700 · Steve Abrams

Files touched

Diff

commit 65cb7722aa66bcfd800bbf783c1bcced969611f9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 7 15:12:04 2026 -0700

    add finish_dns.sh + deploy artifacts: nginx vhost, pm2 live at :9853
---
 finish_dns.sh | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/finish_dns.sh b/finish_dns.sh
new file mode 100755
index 0000000..02e7a6c
--- /dev/null
+++ b/finish_dns.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+# finish_dns.sh — run AFTER Steve has:
+#   1. Cancelled any Afternic listing (if applicable) in GoDaddy portfolio
+#   2. Added chinoiseriewallpaper.com as a zone in Cloudflare dashboard
+#      → dash.cloudflare.com → Add Site → enter chinoiseriewallpaper.com → Free plan
+#
+# Usage: bash finish_dns.sh
+# Prereqs: CLOUDFLARE_API_TOKEN and GODADDY_API_KEY/GODADDY_API_SECRET in env or .env
+
+set -euo pipefail
+
+DOMAIN="chinoiseriewallpaper.com"
+ORIGIN_IP="45.61.58.125"
+ORIGIN_PORT="9853"
+
+# Load secrets
+if [ -f "$(dirname "$0")/../secrets-manager/.env" ]; then
+  set -o allexport
+  source "$(dirname "$0")/../secrets-manager/.env"
+  set +o allexport
+fi
+
+: "${CLOUDFLARE_API_TOKEN:?Need CLOUDFLARE_API_TOKEN}"
+: "${GODADDY_API_KEY:?Need GODADDY_API_KEY}"
+: "${GODADDY_API_SECRET:?Need GODADDY_API_SECRET}"
+
+CF="$CLOUDFLARE_API_TOKEN"
+
+echo "=== Step 1: Get Cloudflare zone ==="
+ZONE=$(curl -sS -H "Authorization: Bearer $CF" "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN")
+ZONE_ID=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); r=d.get('result',[]); print(r[0]['id'] if r else 'NOT_FOUND')")
+echo "Zone ID: $ZONE_ID"
+[ "$ZONE_ID" = "NOT_FOUND" ] && echo "ERROR: Zone not found. Add $DOMAIN in CF dashboard first." && exit 1
+
+NS=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(' '.join(d['result'][0]['name_servers']))")
+echo "CF nameservers: $NS"
+
+echo ""
+echo "=== Step 2: Repoint GoDaddy NS to Cloudflare ==="
+NS_JSON=$(echo "$ZONE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d['result'][0]['name_servers']))")
+curl -sS -X PATCH "https://api.godaddy.com/v1/domains/$DOMAIN" \
+  -H "Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET" \
+  -H "Content-Type: application/json" \
+  --data "{\"nameServers\":$NS_JSON}" \
+  -w "\nHTTP %{http_code}\n"
+
+echo ""
+echo "=== Step 3: Delete stale Afternic/GreenDomainBrokers records ==="
+sleep 5
+curl -sS -H "Authorization: Bearer $CF" \
+  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
+  | python3 -c "
+import json,sys
+d=json.load(sys.stdin)
+records=d.get('result',[])
+print(f'Found {len(records)} records to evaluate')
+for r in records:
+    print(f'  {r[\"type\"]:6} {r[\"name\"]:40} {r[\"content\"][:50]}')
+"
+
+# Delete everything except our A record
+curl -sS -H "Authorization: Bearer $CF" \
+  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
+  | python3 -c "
+import json,sys
+d=json.load(sys.stdin)
+keep_content='$ORIGIN_IP'
+ids=[r['id'] for r in d.get('result',[]) if not (r['type']=='A' and r['content']==keep_content)]
+print(' '.join(ids))
+" > /tmp/stale_ids.txt
+
+while IFS=' ' read -r -a ids; do
+  for id in "${ids[@]}"; do
+    [ -z "$id" ] && continue
+    curl -sS -X DELETE -H "Authorization: Bearer $CF" \
+      "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" >/dev/null
+    echo "  Deleted record $id"
+  done
+done < /tmp/stale_ids.txt
+
+echo ""
+echo "=== Step 4: Add A records (proxy OFF for certbot) ==="
+for name in "$DOMAIN" "www.$DOMAIN"; do
+  curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+    -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+    --data "{\"type\":\"A\",\"name\":\"$name\",\"content\":\"$ORIGIN_IP\",\"ttl\":1,\"proxied\":false}" \
+    | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'A record {d[\"result\"][\"name\"] if d.get(\"result\") else d.get(\"errors\")}')" 2>/dev/null
+done
+
+echo ""
+echo "=== Waiting 90s for NS propagation before certbot ==="
+sleep 90
+
+echo ""
+echo "=== Step 5: Certbot SSL ==="
+ssh root@45.61.58.125 "certbot --nginx -d $DOMAIN -d www.$DOMAIN \
+  --non-interactive --agree-tos --email steve@designerwallcoverings.com --redirect"
+
+echo ""
+echo "=== Step 6: Re-enable Cloudflare proxy ==="
+for id in $(curl -sS -H "Authorization: Bearer $CF" \
+    "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A" \
+    | python3 -c "import json,sys; [print(r['id']) for r in json.load(sys.stdin).get('result',[])]"); do
+  curl -sS -X PATCH -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+    "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" \
+    --data '{"proxied":true}' >/dev/null
+done
+echo "Proxy enabled"
+
+echo ""
+echo "=== Step 7: Add email DNS (MX, SPF, DMARC for Purelymail) ==="
+# MX record
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx1.purelymail.com\",\"priority\":10,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX1:', 'ok' if d.get('success') else d.get('errors'))"
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx2.purelymail.com\",\"priority\":20,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX2:', 'ok' if d.get('success') else d.get('errors'))"
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"MX\",\"name\":\"$DOMAIN\",\"content\":\"mx3.purelymail.com\",\"priority\":30,\"ttl\":3600,\"proxied\":false}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('MX3:', 'ok' if d.get('success') else d.get('errors'))"
+
+# SPF
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"TXT\",\"name\":\"$DOMAIN\",\"content\":\"v=spf1 include:purelymail.com ~all\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('SPF:', 'ok' if d.get('success') else d.get('errors'))"
+
+# DKIM — Purelymail standard selector
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"TXT\",\"name\":\"purelymail1._domainkey.$DOMAIN\",\"content\":\"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMkobFe6QQRhHdMpLxNk3HObA7hXJ4iBxGlwBSmIDRiUhzjrVYkUEgXN3n09ZZzJGypXlZYLcMz4iR3VQ0oE8BPVNP11TMpbJqLhT8lgTOFe+w1+8eLsRSflTiDkFzIL8Ds6I4H/KxrqiVlQNUKlGVIzHDkDqzHMJ0j2DQN9NxQIDAQAB\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('DKIM:', 'ok' if d.get('success') else d.get('errors'))"
+
+# DMARC
+curl -sS -H "Authorization: Bearer $CF" -H "Content-Type: application/json" \
+  -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
+  --data "{\"type\":\"TXT\",\"name\":\"_dmarc.$DOMAIN\",\"content\":\"v=DMARC1; p=none; rua=mailto:info@$DOMAIN\",\"ttl\":3600}" | python3 -c "import json,sys; d=json.load(sys.stdin); print('DMARC:', 'ok' if d.get('success') else d.get('errors'))"
+
+echo ""
+echo "=== Step 8: Verify HTTPS ==="
+sleep 30
+curl -sS -o /dev/null -w "https://$DOMAIN/ → HTTP %{http_code} in %{time_total}s\n" "https://$DOMAIN/?_=$RANDOM"
+
+echo ""
+echo "=== DONE ==="
+echo "Live URL: https://$DOMAIN"
+echo "Origin:   http://45.61.58.125:$ORIGIN_PORT (pm2 chinoiseriewallpaper)"
+echo ""
+echo "TODO (manual):"
+echo "  1. Add chinoiseriewallpaper.com to Purelymail dashboard (addDomain API timed out)"
+echo "  2. Create info@chinoiseriewallpaper.com mailbox in Purelymail dashboard"
+echo "  3. Get DKIM key from Purelymail dashboard and update purelymail1._domainkey record"
+echo "  4. Save mailbox password via: node ~/Projects/secrets-manager/cli.js add INFO_CHINOISERIEWALLPAPER_PASSWORD <pass>"

← 0b9b258 initial scaffold: 1193-product chinoiserie microsite  ·  back to Chinoiseriewallpaper  ·  sort-skill UI: add Light→Dark / Dark→Light / Color Wheel opt 50b77f9 →