From 561f2d69788f6fbf2e051e7cf6ff482858241cc6 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Mon, 30 Mar 2026 15:01:38 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20variant=20testing=20=E2=80=94=206=20sub?= =?UTF-8?q?domains=20+=20Caddy=20wildcard=20cert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - v1-v6.voip.manko.yoga → each maps to a WZP client variant - Caddyfile.test: wildcard *.voip.manko.yoga with CF DNS cert - scripts/test-variants.sh: --setup creates DNS + swaps Caddyfile - --teardown cleans up DNS + restores original - --check verifies all 6 respond HTTP 200 - All variants join same room for cross-variant audio testing Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/deploy/docker/Caddyfile.test | 42 +++++++ warzone/scripts/test-variants.sh | 171 +++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 warzone/deploy/docker/Caddyfile.test create mode 100755 warzone/scripts/test-variants.sh diff --git a/warzone/deploy/docker/Caddyfile.test b/warzone/deploy/docker/Caddyfile.test new file mode 100644 index 0000000..bfec226 --- /dev/null +++ b/warzone/deploy/docker/Caddyfile.test @@ -0,0 +1,42 @@ +{ + email admin@manko.yoga +} + +# Wildcard cert for all variant subdomains +*.voip.manko.yoga { + tls { + dns cloudflare {$CF_API_TOKEN} + } + + # Route each subdomain to wzp-web with the right variant + @v1 host v1.voip.manko.yoga + @v2 host v2.voip.manko.yoga + @v3 host v3.voip.manko.yoga + @v4 host v4.voip.manko.yoga + @v5 host v5.voip.manko.yoga + @v6 host v6.voip.manko.yoga + + # Rewrite root path to include variant param + rewrite @v1 / /?variant=pure + rewrite @v2 / /?variant=hybrid + rewrite @v3 / /?variant=full + rewrite @v4 / /?variant=ws + rewrite @v5 / /?variant=ws-fec + rewrite @v6 / /?variant=ws-full + + # All subdomains proxy to wzp-web + reverse_proxy wzp-web:8080 +} + +# Main domain — featherChat server +voip.manko.yoga { + tls { + dns cloudflare {$CF_API_TOKEN} + } + + handle_path /audio/* { + reverse_proxy wzp-web:8080 + } + + reverse_proxy warzone-server:7700 +} diff --git a/warzone/scripts/test-variants.sh b/warzone/scripts/test-variants.sh new file mode 100755 index 0000000..9e15f22 --- /dev/null +++ b/warzone/scripts/test-variants.sh @@ -0,0 +1,171 @@ +#!/bin/bash +set -euo pipefail + +# Test all 6 WZP web client variants with dedicated subdomains. +# +# Usage: +# ./scripts/test-variants.sh --setup Create DNS + switch Caddyfile +# ./scripts/test-variants.sh --teardown Remove DNS + restore Caddyfile +# ./scripts/test-variants.sh --urls Print all test URLs +# ./scripts/test-variants.sh --check Verify all 6 respond + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +DOCKER_DIR="$PROJECT_DIR/deploy/docker" +CF_TOKEN_FILE="$DOCKER_DIR/cf_api_token.txt" +CF_ZONE="manko.yoga" +BASE_DOMAIN="voip.manko.yoga" + +VARIANTS=(v1 v2 v3 v4 v5 v6) +LABELS=("pure" "hybrid" "full" "ws" "ws-fec" "ws-full") + +get_cf_token() { + cat "$CF_TOKEN_FILE" | tr -d '\n' +} + +get_zone_id() { + curl -4 -s "https://api.cloudflare.com/client/v4/zones?name=$CF_ZONE" \ + -H "Authorization: Bearer $(get_cf_token)" | \ + python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['id'])" +} + +get_my_ip() { + curl -4 -s --connect-timeout 5 https://api.ipify.org 2>/dev/null || \ + ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 | awk '{print $2}' +} + +do_setup() { + local ip zone_id cf_token + ip=$(get_my_ip) + cf_token=$(get_cf_token) + zone_id=$(get_zone_id) + + echo "Setting up variant testing" + echo " IP: $ip" + echo " Zone: $zone_id" + echo "" + + # Create A records for each subdomain + for i in "${!VARIANTS[@]}"; do + local sub="${VARIANTS[$i]}" + local fqdn="${sub}.${BASE_DOMAIN}" + local label="${LABELS[$i]}" + + # Check existing + local rec_id + rec_id=$(curl -4 -s "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?name=$fqdn&type=A" \ + -H "Authorization: Bearer $cf_token" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')" 2>/dev/null || echo "") + + if [ -n "$rec_id" ]; then + curl -4 -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$rec_id" \ + -H "Authorization: Bearer $cf_token" -H "Content-Type: application/json" \ + --data "{\"type\":\"A\",\"name\":\"$fqdn\",\"content\":\"$ip\",\"ttl\":120,\"proxied\":false}" > /dev/null + echo " $fqdn → $ip (updated) [$label]" + else + curl -4 -s -X POST "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records" \ + -H "Authorization: Bearer $cf_token" -H "Content-Type: application/json" \ + --data "{\"type\":\"A\",\"name\":\"$fqdn\",\"content\":\"$ip\",\"ttl\":120,\"proxied\":false}" > /dev/null + echo " $fqdn → $ip (created) [$label]" + fi + done + + # Switch Caddyfile + echo "" + echo "Switching Caddyfile to test mode..." + cp "$DOCKER_DIR/Caddyfile" "$DOCKER_DIR/Caddyfile.backup" + cp "$DOCKER_DIR/Caddyfile.test" "$DOCKER_DIR/Caddyfile" + + echo "Restarting Caddy..." + cd "$DOCKER_DIR" && docker compose restart caddy + + echo "" + echo "=== Ready ===" + do_urls +} + +do_teardown() { + local cf_token zone_id + cf_token=$(get_cf_token) + zone_id=$(get_zone_id) + + echo "Tearing down variant testing..." + + for sub in "${VARIANTS[@]}"; do + local fqdn="${sub}.${BASE_DOMAIN}" + local rec_id + rec_id=$(curl -4 -s "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?name=$fqdn&type=A" \ + -H "Authorization: Bearer $cf_token" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')" 2>/dev/null || echo "") + if [ -n "$rec_id" ]; then + curl -4 -s -X DELETE "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$rec_id" \ + -H "Authorization: Bearer $cf_token" > /dev/null + echo " Deleted $fqdn" + fi + done + + # Restore Caddyfile + if [ -f "$DOCKER_DIR/Caddyfile.backup" ]; then + mv "$DOCKER_DIR/Caddyfile.backup" "$DOCKER_DIR/Caddyfile" + echo "Restored original Caddyfile" + cd "$DOCKER_DIR" && docker compose restart caddy + fi + + echo "Done." +} + +do_urls() { + echo "" + echo "Test URLs (open each in a browser tab, enter same room name):" + echo "" + echo " ┌────────┬──────────┬──────────────────────────────────────────┐" + echo " │ Domain │ Variant │ URL │" + echo " ├────────┼──────────┼──────────────────────────────────────────┤" + for i in "${!VARIANTS[@]}"; do + local sub="${VARIANTS[$i]}" + local label="${LABELS[$i]}" + printf " │ %-6s │ %-8s │ https://%s.%s/test-room │\n" "$sub" "$label" "$sub" "$BASE_DOMAIN" + done + echo " └────────┴──────────┴──────────────────────────────────────────┘" + echo "" + echo "All variants join the same room — test cross-variant audio." + echo "featherChat: https://$BASE_DOMAIN (call via /call command)" +} + +do_check() { + echo "Checking all variant endpoints..." + local pass=0 fail=0 + + for i in "${!VARIANTS[@]}"; do + local sub="${VARIANTS[$i]}" + local label="${LABELS[$i]}" + local url="https://${sub}.${BASE_DOMAIN}/" + local status + status=$(curl -4 -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url" 2>/dev/null || echo "000") + if [ "$status" = "200" ]; then + echo " OK $sub ($label): $status" + pass=$((pass + 1)) + else + echo " FAIL $sub ($label): $status" + fail=$((fail + 1)) + fi + done + + echo "" + echo "Results: $pass passed, $fail failed" +} + +case "${1:-}" in + --setup) do_setup ;; + --teardown) do_teardown ;; + --urls) do_urls ;; + --check) do_check ;; + *) + echo "Test all 6 WZP web client variants" + echo "" + echo "Usage: $0 " + echo "" + echo " --setup Create DNS records + switch Caddyfile" + echo " --teardown Remove DNS records + restore Caddyfile" + echo " --urls Print test URLs" + echo " --check Verify all 6 respond with HTTP 200" + ;; +esac