From 521de7ea0084d23d8fc0b893450d41e8975ed2a7 Mon Sep 17 00:00:00 2001 From: lendry Date: Tue, 30 Jun 2026 17:30:07 +0300 Subject: [PATCH] fix and update --- install.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/install.sh b/install.sh index 9cbf5ad..91817ec 100644 --- a/install.sh +++ b/install.sh @@ -2150,11 +2150,38 @@ render_proxy_rewrite() { printf ' rewrite ^.*$ %s break;\n proxy_pass %s;' "$target" "$up" } -# proxy_pass для /idp-api/ — prefix ^~ выше regex; rewrite снимает префикс перед api-gateway. +# proxy_pass для /idp-api/ — trailing slash снимает префикс без rewrite (надёжнее под HTTP/2). render_proxy_idp_api() { local up up="$(nginx_upstream api)" - printf ' rewrite ^/idp-api/?(.*)$ /$1 break;\n proxy_pass %s;' "$up" + printf ' proxy_pass %s/;' "$up" +} + +# WebSocket /idp-api/ws → media-ws /ws (без rewrite break). +render_proxy_idp_ws() { + local up + up="$(nginx_upstream ws)" + printf ' proxy_pass %s/ws;' "$up" +} + +# Старые конфиги с $request_uri дают 502 «no resolver defined» на location /. +patch_legacy_nginx_proxy_pass() { + [[ "$NGINX_MODE" == "docker" ]] || return 0 + local conf patched=0 + shopt -s nullglob + for conf in "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-"*.conf; do + [[ -f "$conf" ]] || continue + if grep -q 'proxy_pass.*\$request_uri' "$conf" 2>/dev/null; then + log "Патч legacy proxy_pass \$request_uri → $(basename "$conf")" + sed -i -E 's|proxy_pass (http://[^;]+)\$request_uri;|proxy_pass \1;|g' "$conf" + patched=1 + fi + done + shopt -u nullglob + if [[ $patched -eq 1 ]]; then + ok "Legacy proxy_pass \$request_uri исправлен в nginx/conf.d" + reload_docker_nginx || true + fi } nginx_write_conf() { @@ -2327,7 +2354,7 @@ EOF build_nginx_idp_api_proxy_block() { local proxy_ws proxy_idp proxy_oauth proxy_wk proxy_fedcm - proxy_ws="$(render_proxy_rewrite ws /ws)" + proxy_ws="$(render_proxy_idp_ws)" proxy_idp="$(render_proxy_idp_api)" proxy_oauth="$(render_proxy_root api)" proxy_wk="$(render_proxy_root api)" @@ -2371,7 +2398,13 @@ EOF } nginx_docker_server_dns_block() { - return 0 + if [[ "$NGINX_MODE" != "docker" ]]; then + return 0 + fi + cat <<'EOF' + # Docker embedded DNS — страховка для variable proxy_pass и после recreate контейнеров. + resolver 127.0.0.11 valid=10s ipv6=off; +EOF } write_nginx_site_frontend() { @@ -2672,15 +2705,25 @@ validate_docker_frontend_nginx_config() { if ! grep -q 'lendry-id-api-gateway:3000' "$conf" 2>/dev/null; then fail "Конфиг ${conf} без proxy_pass на lendry-id-api-gateway:3000 — выполните ./install.sh --fix-all" fi - if grep -q 'proxy_pass http://lendry-id-frontend:3000\$request_uri' "$conf" 2>/dev/null; then - fail "Конфиг ${conf}: proxy_pass с \$request_uri на frontend — 502 «no resolver defined». Выполните ./install.sh --fix-all" - fi if ! grep -q 'location \^~ /idp-api/ws' "$conf" 2>/dev/null; then warn "Конфиг ${conf}: location ^~ /idp-api/ws отсутствует — WebSocket может идти на api-gateway вместо media-ws" fi ok "Конфиг SSO-домена: /idp-api → lendry-id-api-gateway:3000" } +validate_all_docker_nginx_proxy_pass() { + [[ "$NGINX_MODE" == "docker" ]] || return 0 + local conf + shopt -s nullglob + for conf in "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-"*.conf; do + [[ -f "$conf" ]] || continue + if grep -q 'proxy_pass.*\$request_uri' "$conf" 2>/dev/null; then + fail "Конфиг $(basename "$conf") содержит proxy_pass \$request_uri — 502 «no resolver defined» на /. Выполните ./install.sh --fix-all" + fi + done + shopt -u nullglob +} + verify_nginx_reaches_api_gateway() { docker_nginx_container_running || return 0 build_compose_stack_cmd @@ -2709,7 +2752,7 @@ verify_nginx_reaches_api_gateway() { return 1 } -# Тот же путь, что в браузере: location ^~ /idp-api/ + rewrite + proxy_pass $idp_up_api +# Тот же путь, что в браузере: location ^~ /idp-api/ → api-gateway. verify_nginx_idp_api_location() { docker_nginx_container_running || return 0 load_env @@ -2893,6 +2936,34 @@ check_sso_idp_api_proxy() { fi } +# Несколько подряд проб /idp-api/health — ловит «curl OK, браузер 502» после recreate контейнеров. +verify_sso_idp_api_burst() { + load_env + local frontend_domain="$1" ssl_type i code ok_count=0 + frontend_domain="${frontend_domain:-$(env_get DOMAIN_FRONTEND "")}" + [[ -n "$frontend_domain" ]] || return 0 + ssl_type="$(env_get SSL_TYPE none)" + + for i in 1 2 3 4 5; do + if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then + code="$(curl -sk --max-time 4 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)" + else + code="$(curl -fsS --max-time 4 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)" + fi + [[ "$code" == "200" ]] && ok_count=$((ok_count + 1)) + sleep 0.25 + done + + if [[ $ok_count -ge 4 ]]; then + ok "SSO /idp-api/health стабилен (${ok_count}/5 → 200)" + return 0 + fi + + warn "SSO /idp-api/health нестабилен (${ok_count}/5 → 200) — возможны 502 в браузере" + warn "Выполните: docker compose --env-file .env --profile proxy exec nginx nginx -s reload" + return 1 +} + write_all_nginx_configs() { ensure_nginx_mode_for_config_write local ssl_type="${1:-$(env_get SSL_TYPE none)}" @@ -2931,6 +3002,7 @@ write_all_nginx_configs() { validate_docker_api_nginx_config validate_docker_frontend_nginx_config + validate_all_docker_nginx_proxy_pass if [[ "$NGINX_MODE" == "host" ]]; then if run_root nginx -t; then @@ -3341,6 +3413,15 @@ diagnose_common_issues() { else echo -e " ${GREEN}✔${NC} Docker Nginx: запущен (режим docker)" fi + if [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then + shopt -s nullglob + if grep -q 'proxy_pass.*\$request_uri' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-"*.conf 2>/dev/null; then + echo -e " ${RED}✘${NC} nginx/conf.d: proxy_pass \$request_uri — 502 на / (no resolver defined to resolve lendry-id-frontend)" + echo " Решение: ./install.sh --fix-all" + issues=$((issues + 1)) + fi + shopt -u nullglob + fi elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] && compose_uses_proxy_profile; then echo -e " ${YELLOW}!${NC} Docker Nginx не запущен (ожидался в режиме docker)" issues=$((issues + 1)) @@ -3786,6 +3867,8 @@ action_fix_all_errors() { log "frontend NEXT_PUBLIC_API_URL=${baked_api:-не задан} (браузер SPA использует /idp-api на SSO)" log "Шаг 5/6: Docker Nginx — конфиги в ./nginx/conf.d и контейнер lendry-id-nginx..." + ensure_nginx_gateway_watch_script + patch_legacy_nginx_proxy_pass local ssl_type ssl_type="$(env_get SSL_TYPE none)" case "$ssl_type" in @@ -3814,6 +3897,7 @@ action_fix_all_errors() { verify_nginx_reaches_frontend 12 3 || fail "Nginx не проксирует / на frontend — см. docker compose logs nginx frontend" check_sso_idp_api_proxy || fail "SSO /idp-api/health недоступен — см. nginx/conf.d/${NGINX_PREFIX}-frontend.conf" check_sso_frontend_root || fail "SSO / → 502 — главная страница недоступна (502 Bad Gateway в браузере)" + verify_sso_idp_api_burst || warn "Нестабильный /idp-api/health — при 502 в браузере: docker compose --profile proxy exec nginx nginx -s reload" if ! docker_nginx_container_running; then fail "Контейнер lendry-id-nginx не запущен. Проверьте: docker compose --profile proxy logs nginx" fi