#!/usr/bin/env bash # setup-notify.sh - One-time host and provider setup for the notify service. # # Creates the threesix.ai host, adds Resend as provider, registers noreply@threesix.ai, # and adds Resend DNS records to Cloudflare for domain verification. # # Idempotent: safe to run multiple times. # # Usage: # ./scripts/setup-notify.sh # NOTIFY_URL=... NOTIFY_ADMIN_KEY=... RESEND_API_KEY=... ./scripts/setup-notify.sh set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${BLUE}[STEP]${NC} $1"; } # ─── Load secrets ──────────────────────────────────────────────────────────── SECRETS_FILE="${SECRETS_FILE:-.secrets}" if [[ -f "$SECRETS_FILE" ]]; then while IFS='=' read -r key val || [[ -n "$key" ]]; do [[ -z "$key" || "$key" == \#* ]] && continue export "$key"="${val}" done < "$SECRETS_FILE" fi NOTIFY_URL="${NOTIFY_URL:-}" NOTIFY_ADMIN_KEY="${NOTIFY_ADMIN_KEY:-}" RESEND_API_KEY="${RESEND_API_KEY:-}" CF_TOKEN="${CLOUDFLARE_API_TOKEN:-}" CF_ZONE="${CLOUDFLARE_ZONE_ID:-}" if [[ -z "$NOTIFY_URL" ]]; then log_error "NOTIFY_URL required"; exit 1; fi if [[ -z "$NOTIFY_ADMIN_KEY" ]]; then log_error "NOTIFY_ADMIN_KEY required"; exit 1; fi if [[ -z "$RESEND_API_KEY" ]]; then log_error "RESEND_API_KEY required"; exit 1; fi HOST=threesix.ai FROM=noreply@threesix.ai log_info "Notify URL: $NOTIFY_URL" log_info "Host: $HOST" log_info "From: $FROM" # ─── Helpers ───────────────────────────────────────────────────────────────── notify() { local method="$1" path="$2" body="${3:-}" local args=(-s -X "$method" "$NOTIFY_URL$path" -H "Authorization: Bearer $NOTIFY_ADMIN_KEY" -H "Content-Type: application/json") [[ -n "$body" ]] && args+=(-d "$body") curl "${args[@]}" } resend_api() { local method="$1" path="$2" body="${3:-}" local args=(-s -X "$method" "https://api.resend.com$path" -H "Authorization: Bearer $RESEND_API_KEY" -H "Content-Type: application/json") [[ -n "$body" ]] && args+=(-d "$body") curl "${args[@]}" } cf_dns() { local method="$1" path="$2" body="${3:-}" local args=(-s -X "$method" "https://api.cloudflare.com/client/v4/zones/$CF_ZONE$path" -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json") [[ -n "$body" ]] && args+=(-d "$body") curl "${args[@]}" } # ─── Step 1: Create host ────────────────────────────────────────────────────── log_step "1. Setting up notify host: $HOST" existing=$(notify GET "/admin/hosts" | python3 -c "import sys,json; items=json.load(sys.stdin).get('items',[]); print(next((x['host'] for x in items if x['host']=='$HOST'),''))" 2>/dev/null || true) if [[ "$existing" == "$HOST" ]]; then log_info " Host already exists — skipping" else notify POST "/admin/hosts" "{\"host\":\"$HOST\",\"strategy\":\"failover\"}" | python3 -m json.tool log_info " Host created" fi # ─── Step 2: Add Resend provider ───────────────────────────────────────────── log_step "2. Adding Resend provider" providers=$(notify GET "/admin/hosts/$HOST/providers" | python3 -c "import sys,json; items=json.load(sys.stdin); print(next((str(x['id']) for x in items if x['provider']=='resend'),''))" 2>/dev/null || true) if [[ -n "$providers" ]]; then log_info " Resend provider already configured (id: $providers) — skipping" else notify POST "/admin/hosts/$HOST/providers" \ "{\"provider\":\"resend\",\"config\":{\"api_key\":\"$RESEND_API_KEY\"},\"priority\":1,\"retry_attempts\":3,\"retry_backoff_ms\":1000}" | python3 -m json.tool log_info " Resend provider added" fi # ─── Step 3: Register from-address ─────────────────────────────────────────── log_step "3. Registering from-address: $FROM" addrs=$(notify GET "/admin/hosts/$HOST/from-addresses" | python3 -c "import sys,json; items=json.load(sys.stdin).get('items',[]); print(next((x['email'] for x in items if x['email']=='$FROM'),''))" 2>/dev/null || true) if [[ "$addrs" == "$FROM" ]]; then log_info " From-address already registered — skipping" else notify POST "/admin/hosts/$HOST/from-addresses" \ "{\"email\":\"$FROM\",\"display_name\":\"threesix.ai\"}" | python3 -m json.tool log_info " From-address registered" fi # ─── Step 4: Resend domain + Cloudflare DNS ─────────────────────────────────── log_step "4. Setting up Resend domain for $HOST" existing_domain=$(resend_api GET "/domains" | python3 -c "import sys,json; data=json.load(sys.stdin); print(next((x['id'] for x in data.get('data',[]) if x['name']=='$HOST'),''))" 2>/dev/null || true) if [[ -n "$existing_domain" ]]; then log_info " Resend domain already exists (id: $existing_domain)" DOMAIN_ID="$existing_domain" DOMAIN_RECORDS=$(resend_api GET "/domains/$DOMAIN_ID" | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin).get('records',[])))") else log_info " Creating Resend domain..." domain_resp=$(resend_api POST "/domains" "{\"name\":\"$HOST\",\"region\":\"us-east-1\"}") DOMAIN_ID=$(echo "$domain_resp" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") DOMAIN_RECORDS=$(echo "$domain_resp" | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin).get('records',[])))") log_info " Domain created (id: $DOMAIN_ID)" fi # Add DNS records if Cloudflare is configured if [[ -n "$CF_TOKEN" && -n "$CF_ZONE" ]]; then log_step "5. Adding Resend DNS records to Cloudflare" echo "$DOMAIN_RECORDS" | python3 -c " import sys, json records = json.load(sys.stdin) for r in records: print(r['type'], r['name'], r.get('value',''), r.get('priority','')) " | while read -r rtype rname rvalue rpriority; do # Check if record already exists existing_rec=$(cf_dns GET "/dns_records?type=$rtype&name=$rname.$HOST" | python3 -c "import sys,json; result=json.load(sys.stdin).get('result',[]); print(result[0]['id'] if result else '')" 2>/dev/null || true) if [[ -n "$existing_rec" ]]; then log_info " $rtype $rname already exists — skipping" else if [[ "$rtype" == "MX" ]]; then cf_dns POST "/dns_records" "{\"type\":\"MX\",\"name\":\"$rname\",\"content\":\"$rvalue\",\"priority\":$rpriority,\"ttl\":1}" > /dev/null else cf_dns POST "/dns_records" "{\"type\":\"$rtype\",\"name\":\"$rname\",\"content\":\"$rvalue\",\"ttl\":1,\"proxied\":false}" > /dev/null fi log_info " Added $rtype $rname" fi done # Trigger verification log_step "6. Triggering Resend domain verification" resend_api POST "/domains/$DOMAIN_ID/verify" > /dev/null log_info " Verification triggered (DNS propagation takes ~60s)" else log_warn " CLOUDFLARE_API_TOKEN or CLOUDFLARE_ZONE_ID not set — add DNS records manually:" echo "$DOMAIN_RECORDS" | python3 -m json.tool fi echo "" log_info "Setup complete." log_info "Check Resend domain status: curl -s https://api.resend.com/domains/$DOMAIN_ID -H 'Authorization: Bearer \$RESEND_API_KEY' | python3 -m json.tool"