g1t/scripts/setup-custom-domains.sh
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Agents and memory, checks and conflicts, profiles, slug renames, custom domains | 1 | #!/usr/bin/env bash |
| 2 | # Sets up custom domains for deployed apps, once. Safe to run again: each | |
| 3 | # step skips what exists. Run after scripts/setup-deployments.sh, then | |
| 4 | # deploy with scripts/deploy.sh (deployments, then pages). | |
| 5 | # | |
| 6 | # Custom domains are Cloudflare for SaaS custom hostnames on the apps' zone | |
| 7 | # (g1t.page). Turn Cloudflare for SaaS on for the zone first, in the | |
| 8 | # dashboard: SSL/TLS, Custom Hostnames. Until then the API answers with | |
| 9 | # codes 1404 or 1456, and g1t says custom domains are being switched on. | |
| 10 | # | |
| 11 | # This script: | |
| 12 | # 1. creates the g1t-domains KV namespace (hostname -> app) and writes its | |
| 13 | # id into services/deployments and services/pages wrangler.jsonc; | |
| 14 | # 2. adds the fallback origin's DNS record: domains.g1t.page, proxied | |
| 15 | # AAAA 100:: (what every custom domain points at); | |
| 16 | # 3. sets domains.g1t.page as the zone's custom hostname fallback origin. | |
| 17 | # | |
| 18 | # Steps 2 and 3 need CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY (a Global API | |
| 19 | # Key), or CLOUDFLARE_ZONE_TOKEN with DNS: Edit and SSL and Certificates: | |
| 20 | # Edit on the zone; otherwise it says what to do by hand. | |
| 21 | # | |
| 22 | # The deployments service's own token (its CLOUDFLARE_API_TOKEN secret) | |
| 23 | # also needs SSL and Certificates: Edit on the zone, to add custom | |
| 24 | # hostnames. Add that permission to the token in the dashboard. | |
| 25 | set -euo pipefail | |
| 26 | ||
| 27 | ROOT="$(cd "$(dirname "$0")/.." && pwd)" | |
| 28 | export CLOUDFLARE_API_TOKEN="${CLOUDFLARE_DEPLOY_TOKEN:-}" | |
| 29 | export CLOUDFLARE_ACCOUNT_ID="${CLOUDFLARE_ACCOUNT_ID:-1e6f2cffa3f445920836e8ebe446bb58}" | |
| 30 | ZONE_ID="${G1T_APPS_ZONE_ID:-45d1c969bdd9d593756e70d5f45c2cbb}" | |
| 31 | FALLBACK="${G1T_DOMAINS_FALLBACK:-domains.g1t.page}" | |
| 32 | API="https://api.cloudflare.com/client/v4" | |
| 33 | w() { npx wrangler "$@"; } | |
| 34 | ||
| 35 | echo "== KV namespace g1t-domains" | |
| 36 | if grep -q DOMAINS_KV_ID "$ROOT/services/deployments/wrangler.jsonc" "$ROOT/services/pages/wrangler.jsonc"; then | |
| 37 | id=$(cd "$ROOT" && w kv namespace list 2>/dev/null | tr -d '\n ' | grep -oE '"id":"[0-9a-f]{32}","title":"g1t-domains"' | grep -oE '[0-9a-f]{32}' | head -1 || true) | |
| 38 | if [ -z "$id" ]; then | |
| 39 | id=$(cd "$ROOT" && w kv namespace create g1t-domains </dev/null 2>&1 | grep -oE '[0-9a-f]{32}' | head -1 || true) | |
| 40 | fi | |
| 41 | [ -n "$id" ] || { echo "Could not create the namespace; is wrangler logged in? (npx wrangler whoami)"; exit 1; } | |
| 42 | sed -i "s/DOMAINS_KV_ID/$id/" "$ROOT/services/deployments/wrangler.jsonc" "$ROOT/services/pages/wrangler.jsonc" | |
| 43 | echo "Using $id; wrote it into both wrangler.jsonc files. Commit that." | |
| 44 | else | |
| 45 | echo "Already set." | |
| 46 | fi | |
| 47 | ||
| 48 | cf() { | |
| 49 | local method="$1" path="$2" body="${3:-}" | |
| 50 | local auth=() | |
| 51 | if [ -n "${CLOUDFLARE_ZONE_TOKEN:-}" ]; then | |
| 52 | auth=(-H "Authorization: Bearer $CLOUDFLARE_ZONE_TOKEN") | |
| 53 | else | |
| 54 | auth=(-H "X-Auth-Email: $CLOUDFLARE_EMAIL" -H "X-Auth-Key: $CLOUDFLARE_API_KEY") | |
| 55 | fi | |
| 56 | curl -sS -X "$method" "$API$path" "${auth[@]}" -H "Content-Type: application/json" ${body:+--data "$body"} | |
| 57 | } | |
| 58 | ||
| 59 | echo "== Fallback origin $FALLBACK" | |
| 60 | if [ -n "${CLOUDFLARE_ZONE_TOKEN:-}" ] || { [ -n "${CLOUDFLARE_API_KEY:-}" ] && [ -n "${CLOUDFLARE_EMAIL:-}" ]; }; then | |
| 61 | if cf GET "/zones/$ZONE_ID/dns_records?name=$FALLBACK" | grep -q "\"name\":\"$FALLBACK\""; then | |
| 62 | echo "DNS record exists." | |
| 63 | else | |
| 64 | cf POST "/zones/$ZONE_ID/dns_records" \ | |
| 65 | "{\"type\":\"AAAA\",\"name\":\"$FALLBACK\",\"content\":\"100::\",\"proxied\":true,\"comment\":\"g1t custom domains: the Cloudflare for SaaS fallback origin, served by g1t-pages\"}" \ | |
| 66 | | grep -q '"success":true' && echo "Added $FALLBACK AAAA 100:: (proxied)." || { echo "Could not add the DNS record."; exit 1; } | |
| 67 | fi | |
| 68 | answer=$(cf PUT "/zones/$ZONE_ID/custom_hostnames/fallback_origin" "{\"origin\":\"$FALLBACK\"}") | |
| 69 | if echo "$answer" | grep -q '"success":true'; then | |
| 70 | echo "Fallback origin set to $FALLBACK." | |
| 71 | elif echo "$answer" | grep -qE '"code":(1404|1456)'; then | |
| 72 | echo "Cloudflare for SaaS is not on for the zone yet. Turn it on (SSL/TLS, Custom Hostnames), then run this again." | |
| 73 | else | |
| 74 | echo "Could not set the fallback origin: $answer"; exit 1 | |
| 75 | fi | |
| 76 | else | |
| 77 | cat <<EOF | |
| 78 | Set CLOUDFLARE_ZONE_TOKEN (or CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY) to do this for you, or by hand: | |
| 79 | 1. On the g1t.page zone, add a proxied DNS record: type AAAA, name ${FALLBACK%%.*}, content 100:: | |
| 80 | 2. SSL/TLS, Custom Hostnames: set the fallback origin to $FALLBACK | |
| 81 | EOF | |
| 82 | fi | |
| 83 | ||
| 84 | cat <<EOF | |
| 85 | == Remaining by hand | |
| 86 | - Give the deployments service's API token SSL and Certificates: Edit on | |
| 87 | the g1t.page zone (it already has Workers Scripts and Account Analytics). | |
| 88 | - Deploy, which applies each part's migrations first: | |
| 89 | scripts/deploy.sh billing deployments pages web | |
| 90 | EOF |