Import verified AzerothAI snapshot from GitHub
Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s
Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s
Source branch: feature/ai-brain-router Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
STATIC_ROOT="$REPO_ROOT/dist/client"
|
||||
PUBLIC_ROOT="/var/www/azerothai"
|
||||
NGINX_SITE="/etc/nginx/sites-available/azerothai-tailnet"
|
||||
NGINX_LINK="/etc/nginx/sites-enabled/azerothai-tailnet"
|
||||
LOCAL_PROXY_PORT="${AZEROTHAI_TAILNET_PROXY_PORT:-8788}"
|
||||
APP_PORT="${AZEROTHAI_APP_PORT:-8787}"
|
||||
|
||||
need(){ command -v "$1" >/dev/null 2>&1 || { echo "Missing required command: $1" >&2; exit 1; }; }
|
||||
|
||||
if [[ ! -d "$STATIC_ROOT" ]]; then
|
||||
echo "Missing built client assets at $STATIC_ROOT" >&2
|
||||
echo "Run: cd $REPO_ROOT && pnpm build" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
need tailscale
|
||||
need curl
|
||||
|
||||
TAILSCALE_IP="$(tailscale ip -4 2>/dev/null | head -n1 || true)"
|
||||
if [[ -z "$TAILSCALE_IP" ]]; then
|
||||
echo "No active Tailscale IPv4 address found. Run: sudo tailscale up --hostname=azerothai-lab" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "==> Checking AzerothAI backend"
|
||||
curl -fsS "http://127.0.0.1:$APP_PORT/api/health" >/dev/null || {
|
||||
echo "AzerothAI is not responding on 127.0.0.1:$APP_PORT" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "==> Installing nginx if needed"
|
||||
if ! command -v nginx >/dev/null 2>&1; then
|
||||
sudo apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
|
||||
fi
|
||||
|
||||
echo "==> Publishing built client assets to an nginx-readable directory"
|
||||
sudo rm -rf "$PUBLIC_ROOT"
|
||||
sudo mkdir -p "$PUBLIC_ROOT"
|
||||
sudo cp -a "$STATIC_ROOT"/. "$PUBLIC_ROOT"/
|
||||
sudo find "$PUBLIC_ROOT" -type d -exec chmod 755 {} +
|
||||
sudo find "$PUBLIC_ROOT" -type f -exec chmod 644 {} +
|
||||
|
||||
echo "==> Configuring a static-asset-safe reverse proxy"
|
||||
sudo tee "$NGINX_SITE" >/dev/null <<EOF
|
||||
server {
|
||||
listen 127.0.0.1:$LOCAL_PROXY_PORT;
|
||||
server_name _;
|
||||
|
||||
root $PUBLIC_ROOT;
|
||||
|
||||
location ~* \\.(?:css|js|mjs|svg|png|ico|woff|woff2)$ {
|
||||
try_files \$uri @azerothai;
|
||||
access_log off;
|
||||
add_header Cache-Control "no-store" always;
|
||||
}
|
||||
|
||||
location / {
|
||||
add_header Cache-Control "no-store" always;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$http_host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_pass http://127.0.0.1:$APP_PORT;
|
||||
}
|
||||
|
||||
location @azerothai {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$http_host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_pass http://127.0.0.1:$APP_PORT;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
sudo ln -sfn "$NGINX_SITE" "$NGINX_LINK"
|
||||
sudo nginx -t
|
||||
sudo systemctl enable --now nginx
|
||||
sudo systemctl reload nginx
|
||||
|
||||
echo "==> Verifying proxied page and repairing asset paths if needed"
|
||||
PAGE="$(curl -fsS -H "Host: $TAILSCALE_IP:$LOCAL_PROXY_PORT" "http://127.0.0.1:$LOCAL_PROXY_PORT/")"
|
||||
|
||||
map_asset_path(){
|
||||
local raw="$1" clean target base match srcdir dstdir
|
||||
clean="${raw%%\?*}"
|
||||
[[ "$clean" == /* ]] || return 0
|
||||
target="$PUBLIC_ROOT$clean"
|
||||
[[ -f "$target" ]] && return 0
|
||||
base="$(basename "$clean")"
|
||||
match="$(find "$PUBLIC_ROOT" "$REPO_ROOT/dist" "$REPO_ROOT/.next" -type f -name "$base" -print -quit 2>/dev/null || true)"
|
||||
[[ -n "$match" ]] || return 0
|
||||
dstdir="$(dirname "$target")"
|
||||
sudo mkdir -p "$dstdir"
|
||||
sudo cp -a "$match" "$target"
|
||||
}
|
||||
|
||||
while IFS= read -r asset; do
|
||||
[[ -n "$asset" ]] || continue
|
||||
map_asset_path "$asset"
|
||||
done < <(printf '%s' "$PAGE" | grep -oE '(href|src)="[^"]+\.(css|js|mjs|svg|png|ico|woff|woff2)(\?[^"]*)?"' | cut -d'"' -f2 | sort -u || true)
|
||||
|
||||
CSS_PATH="$(printf '%s' "$PAGE" | grep -oE 'href="[^"]+\.css[^"]*"' | head -n1 | cut -d'"' -f2 || true)"
|
||||
if [[ -n "$CSS_PATH" ]]; then
|
||||
case "$CSS_PATH" in
|
||||
http://*|https://*) CSS_URL="$CSS_PATH" ;;
|
||||
/*) CSS_URL="http://127.0.0.1:$LOCAL_PROXY_PORT$CSS_PATH" ;;
|
||||
*) CSS_URL="http://127.0.0.1:$LOCAL_PROXY_PORT/$CSS_PATH" ;;
|
||||
esac
|
||||
if curl -fsS -H "Host: $TAILSCALE_IP:$LOCAL_PROXY_PORT" "$CSS_URL" >/dev/null; then
|
||||
echo "Stylesheet reachable through proxy: $CSS_PATH"
|
||||
else
|
||||
echo "Stylesheet still returned 404: $CSS_PATH" >&2
|
||||
echo "Built CSS files found:" >&2
|
||||
find "$PUBLIC_ROOT" -type f -name '*.css' -print | head -20 >&2 || true
|
||||
exit 44
|
||||
fi
|
||||
else
|
||||
echo "Warning: no stylesheet link found in HTML." >&2
|
||||
fi
|
||||
|
||||
echo "==> Verifying all JavaScript bundles through proxy"
|
||||
JS_FAILURES=0
|
||||
while IFS= read -r script; do
|
||||
[[ -n "$script" ]] || continue
|
||||
case "$script" in
|
||||
http://*|https://*) JS_URL="$script" ;;
|
||||
/*) JS_URL="http://127.0.0.1:$LOCAL_PROXY_PORT$script" ;;
|
||||
*) JS_URL="http://127.0.0.1:$LOCAL_PROXY_PORT/$script" ;;
|
||||
esac
|
||||
CODE="$(curl -sS -o /dev/null -w '%{http_code}' -H "Host: $TAILSCALE_IP:$LOCAL_PROXY_PORT" "$JS_URL" || true)"
|
||||
echo "$CODE $script"
|
||||
if [[ "$CODE" != "200" ]]; then JS_FAILURES=$((JS_FAILURES+1)); fi
|
||||
done < <(printf '%s' "$PAGE" | grep -oE 'src="[^"]+\\.(js|mjs)(\\?[^"]*)?"' | cut -d'"' -f2 | sort -u || true)
|
||||
|
||||
STATE_CODE="$(curl -sS -o /dev/null -w '%{http_code}' -H "Host: $TAILSCALE_IP:$LOCAL_PROXY_PORT" "http://127.0.0.1:$LOCAL_PROXY_PORT/api/state" || true)"
|
||||
echo "$STATE_CODE /api/state"
|
||||
if [[ "$STATE_CODE" != "200" ]]; then
|
||||
echo "Dashboard API state is not reachable through the proxy." >&2
|
||||
exit 45
|
||||
fi
|
||||
if [[ "$JS_FAILURES" -ne 0 ]]; then
|
||||
echo "$JS_FAILURES JavaScript bundle(s) are still unavailable." >&2
|
||||
exit 46
|
||||
fi
|
||||
|
||||
echo "==> Publishing privately with Tailscale Serve"
|
||||
sudo tailscale serve --bg "$LOCAL_PROXY_PORT"
|
||||
|
||||
DNS_NAME="$(tailscale status --json 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); print((d.get("Self",{}).get("DNSName") or "").rstrip("."))' 2>/dev/null || true)"
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " AzerothAI private dashboard proxy is ready"
|
||||
echo "============================================================"
|
||||
if [[ -n "$DNS_NAME" ]]; then
|
||||
echo "Open on any device in your tailnet:"
|
||||
echo " https://$DNS_NAME/"
|
||||
else
|
||||
echo "Run: tailscale serve status"
|
||||
echo "Then open the HTTPS URL it prints."
|
||||
fi
|
||||
echo
|
||||
echo "Direct raw-IP access to :8787 can still be used for diagnostics,"
|
||||
echo "but the Tailscale Serve HTTPS URL is the recommended remote dashboard."
|
||||
Reference in New Issue
Block a user