Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 58s
Deploy / Build Web Image (push) Failing after 14s
Deploy / Rollback Production (push) Has been skipped
CI / E2E Tests (push) Has been skipped
Deploy / Build API Image (push) Failing after 3m8s
Deploy / Build AI Services Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 1m21s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
- Add Nginx reverse-proxy configs for api.goodgo.vn and platform.goodgo.vn with SSL, gzip, rate limiting, security headers, and WebSocket support - Add Cloudflare DNS setup script for A/AAAA/CNAME records - Add server-setup.sh for Ubuntu provisioning (Docker, fail2ban, UFW, swap, unattended-upgrades) - Add deploy-production.sh for manual production deployments - Add env.production.example with all required environment variables - Bind container ports to 127.0.0.1 in docker-compose.prod.yml (security: prevent direct access bypassing Nginx) - Fix deploy workflow: add -T flag to exec, sync Nginx configs, copy pgbouncer and backup configs to server Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
193 lines
6.7 KiB
Bash
Executable File
193 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ==============================================================================
|
|
# GoodGo Platform — Cloudflare DNS Setup
|
|
# Adds DNS records for platform.goodgo.vn, api.goodgo.vn, grafana.goodgo.vn
|
|
#
|
|
# Prerequisites:
|
|
# export CF_API_TOKEN="your-cloudflare-api-token"
|
|
# export CF_ZONE_ID="your-goodgo-vn-zone-id"
|
|
#
|
|
# Usage:
|
|
# ./infra/cloudflare-dns.sh
|
|
# ./infra/cloudflare-dns.sh --dry-run # Preview without creating
|
|
# ./infra/cloudflare-dns.sh --delete # Remove records
|
|
# ==============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Configuration ─────────────────────────────────────────────────────────────
|
|
CF_API_TOKEN="${CF_API_TOKEN:?Error: Set CF_API_TOKEN environment variable}"
|
|
CF_ZONE_ID="${CF_ZONE_ID:?Error: Set CF_ZONE_ID environment variable}"
|
|
TARGET_IP="${TARGET_IP:-185.225.232.65}"
|
|
CF_API="https://api.cloudflare.com/client/v4"
|
|
DRY_RUN=false
|
|
DELETE=false
|
|
|
|
# Parse flags
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--dry-run) DRY_RUN=true ;;
|
|
--delete) DELETE=true ;;
|
|
esac
|
|
done
|
|
|
|
# DNS records to manage
|
|
declare -a SUBDOMAINS=("platform" "api" "grafana")
|
|
|
|
# ── Colors ────────────────────────────────────────────────────────────────────
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[DNS]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
cf_api() {
|
|
local method="$1"
|
|
local endpoint="$2"
|
|
shift 2
|
|
curl -s -X "$method" \
|
|
"${CF_API}${endpoint}" \
|
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"$@"
|
|
}
|
|
|
|
get_record_id() {
|
|
local name="$1"
|
|
cf_api GET "/zones/${CF_ZONE_ID}/dns_records?type=A&name=${name}.goodgo.vn" \
|
|
| jq -r '.result[0].id // empty'
|
|
}
|
|
|
|
# ── Verify API Token ─────────────────────────────────────────────────────────
|
|
log "Verifying Cloudflare API token..."
|
|
VERIFY=$(cf_api GET "/user/tokens/verify")
|
|
if [ "$(echo "$VERIFY" | jq -r '.success')" != "true" ]; then
|
|
err "Invalid Cloudflare API token!"
|
|
echo "$VERIFY" | jq .
|
|
exit 1
|
|
fi
|
|
log "API token verified."
|
|
|
|
# ── Verify Zone ──────────────────────────────────────────────────────────────
|
|
ZONE_NAME=$(cf_api GET "/zones/${CF_ZONE_ID}" | jq -r '.result.name')
|
|
if [ "$ZONE_NAME" != "goodgo.vn" ]; then
|
|
err "Zone ID does not match goodgo.vn! Got: ${ZONE_NAME}"
|
|
exit 1
|
|
fi
|
|
log "Zone verified: ${ZONE_NAME}"
|
|
|
|
# ── Process DNS Records ─────────────────────────────────────────────────────
|
|
echo ""
|
|
log "=========================================="
|
|
if $DELETE; then
|
|
log " Deleting DNS records"
|
|
elif $DRY_RUN; then
|
|
log " DRY RUN — no changes will be made"
|
|
else
|
|
log " Creating/Updating DNS records"
|
|
fi
|
|
log " Target IP: ${TARGET_IP}"
|
|
log "=========================================="
|
|
echo ""
|
|
|
|
for sub in "${SUBDOMAINS[@]}"; do
|
|
FQDN="${sub}.goodgo.vn"
|
|
EXISTING_ID=$(get_record_id "$sub")
|
|
|
|
if $DELETE; then
|
|
if [ -n "$EXISTING_ID" ]; then
|
|
if $DRY_RUN; then
|
|
info "[DRY RUN] Would delete: ${FQDN} (ID: ${EXISTING_ID})"
|
|
else
|
|
RESULT=$(cf_api DELETE "/zones/${CF_ZONE_ID}/dns_records/${EXISTING_ID}")
|
|
if [ "$(echo "$RESULT" | jq -r '.success')" = "true" ]; then
|
|
log "Deleted: ${FQDN}"
|
|
else
|
|
err "Failed to delete ${FQDN}: $(echo "$RESULT" | jq -r '.errors[0].message')"
|
|
fi
|
|
fi
|
|
else
|
|
warn "Record not found: ${FQDN} — skipping delete"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
RECORD_DATA=$(cat <<EOF
|
|
{
|
|
"type": "A",
|
|
"name": "${sub}",
|
|
"content": "${TARGET_IP}",
|
|
"ttl": 1,
|
|
"proxied": true,
|
|
"comment": "GoodGo Platform — managed by infra/cloudflare-dns.sh"
|
|
}
|
|
EOF
|
|
)
|
|
|
|
if [ -n "$EXISTING_ID" ]; then
|
|
# Update existing record
|
|
if $DRY_RUN; then
|
|
info "[DRY RUN] Would update: ${FQDN} → ${TARGET_IP} (Proxied, ID: ${EXISTING_ID})"
|
|
else
|
|
RESULT=$(cf_api PUT "/zones/${CF_ZONE_ID}/dns_records/${EXISTING_ID}" -d "$RECORD_DATA")
|
|
if [ "$(echo "$RESULT" | jq -r '.success')" = "true" ]; then
|
|
log "Updated: ${FQDN} → ${TARGET_IP} (Proxied)"
|
|
else
|
|
err "Failed to update ${FQDN}: $(echo "$RESULT" | jq -r '.errors[0].message')"
|
|
fi
|
|
fi
|
|
else
|
|
# Create new record
|
|
if $DRY_RUN; then
|
|
info "[DRY RUN] Would create: ${FQDN} → ${TARGET_IP} (Proxied)"
|
|
else
|
|
RESULT=$(cf_api POST "/zones/${CF_ZONE_ID}/dns_records" -d "$RECORD_DATA")
|
|
if [ "$(echo "$RESULT" | jq -r '.success')" = "true" ]; then
|
|
NEW_ID=$(echo "$RESULT" | jq -r '.result.id')
|
|
log "Created: ${FQDN} → ${TARGET_IP} (Proxied) [ID: ${NEW_ID}]"
|
|
else
|
|
err "Failed to create ${FQDN}: $(echo "$RESULT" | jq -r '.errors[0].message')"
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ── Verify DNS ───────────────────────────────────────────────────────────────
|
|
if ! $DRY_RUN && ! $DELETE; then
|
|
echo ""
|
|
log "Verifying DNS records..."
|
|
echo ""
|
|
for sub in "${SUBDOMAINS[@]}"; do
|
|
FQDN="${sub}.goodgo.vn"
|
|
RECORD=$(cf_api GET "/zones/${CF_ZONE_ID}/dns_records?type=A&name=${FQDN}")
|
|
IP=$(echo "$RECORD" | jq -r '.result[0].content // "NOT FOUND"')
|
|
PROXIED=$(echo "$RECORD" | jq -r '.result[0].proxied // "N/A"')
|
|
info "${FQDN} → ${IP} (proxied: ${PROXIED})"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
log "=========================================="
|
|
log " DNS setup complete!"
|
|
if ! $DELETE; then
|
|
log ""
|
|
log " Cloudflare SSL/TLS settings (manual):"
|
|
log " - SSL mode: Full (Strict)"
|
|
log " - Always Use HTTPS: ON"
|
|
log " - Minimum TLS: 1.2"
|
|
log " - HTTP/2: ON"
|
|
log " - Brotli: ON"
|
|
log ""
|
|
log " Generate Origin Certificate at:"
|
|
log " Cloudflare Dashboard → SSL/TLS → Origin Server"
|
|
log " Save as: /etc/ssl/goodgo/origin.pem"
|
|
log " /etc/ssl/goodgo/origin-key.pem"
|
|
fi
|
|
log "=========================================="
|