fix and update

This commit is contained in:
lendry
2026-06-30 17:01:19 +03:00
parent 31251be877
commit 209036c036
2 changed files with 173 additions and 29 deletions

View File

@@ -1,52 +1,72 @@
#!/bin/sh
# После recreate api-gateway Docker выдаёт новый IP; без reload nginx шлёт на старый адрес → 502.
# После recreate edge-сервисов Docker выдаёт новый IP; без reload nginx шлёт на старый адрес → 502.
set -eu
GW_HOST="${IDP_API_GATEWAY_HOST:-lendry-id-api-gateway}"
GW_PORT="${IDP_API_GATEWAY_PORT:-3000}"
WATCH_INTERVAL="${IDP_NGINX_WATCH_INTERVAL:-5}"
WATCH_HOSTS="${IDP_NGINX_WATCH_HOSTS:-lendry-id-api-gateway:3000/health lendry-id-frontend:3000/}"
log() {
echo "[idp-nginx-watch] $*"
}
wait_for_gateway() {
probe_url() {
target="$1"
host="${target%%:*}"
rest="${target#*:}"
port="${rest%%/*}"
path="/${rest#*/}"
[ "$path" = "/${rest}" ] && path="/"
wget -qO- --timeout=3 "http://${host}:${port}${path}" 2>/dev/null | head -c 64 | grep -q .
}
wait_for_upstreams() {
attempt=0
while [ "$attempt" -lt 120 ]; do
if wget -qO- --timeout=3 "http://${GW_HOST}:${GW_PORT}/health" 2>/dev/null | grep -q '"status"'; then
log "api-gateway доступен (${GW_HOST}:${GW_PORT})"
ok=1
for target in $WATCH_HOSTS; do
if ! probe_url "$target"; then
ok=0
break
fi
done
if [ "$ok" -eq 1 ]; then
log "upstream-ы доступны"
return 0
fi
attempt=$((attempt + 1))
sleep 1
done
log "WARN: api-gateway не ответил за 120с — nginx стартует без ожидания"
log "WARN: не все upstream-ы ответили за 120с — nginx стартует без ожидания"
return 0
}
watch_gateway() {
last_ip=""
watch_upstreams() {
last_ips=""
while true; do
sleep "$WATCH_INTERVAL"
current_ip="$(getent hosts "$GW_HOST" 2>/dev/null | awk '{print $1}' | head -n1 || true)"
healthy=0
if wget -qO- --timeout=3 "http://${GW_HOST}:${GW_PORT}/health" 2>/dev/null | grep -q '"status"'; then
healthy=1
reload=0
current_ips=""
for target in $WATCH_HOSTS; do
host="${target%%:*}"
ip="$(getent hosts "$host" 2>/dev/null | awk '{print $1}' | head -n1 || true)"
current_ips="${current_ips}${host}=${ip};"
if ! probe_url "$target"; then
log "${host} недоступен — reload nginx"
reload=1
break
fi
done
if [ "$reload" -eq 0 ] && [ -n "$last_ips" ] && [ "$current_ips" != "$last_ips" ]; then
log "IP upstream изменился — reload nginx"
reload=1
fi
if [ "$healthy" -eq 0 ]; then
log "api-gateway недоступен — reload nginx"
nginx -s reload 2>/dev/null || true
continue
fi
if [ -n "$current_ip" ] && [ -n "$last_ip" ] && [ "$current_ip" != "$last_ip" ]; then
log "IP ${GW_HOST} изменился (${last_ip}${current_ip}) — reload nginx"
if [ "$reload" -eq 1 ]; then
nginx -s reload 2>/dev/null || true
fi
if [ -n "$current_ip" ]; then
last_ip="$current_ip"
fi
last_ips="$current_ips"
done
}
wait_for_gateway
watch_gateway &
wait_for_upstreams
watch_upstreams &

View File

@@ -426,6 +426,20 @@ sync_nginx_after_api_gateway_restart() {
fi
}
# После recreate frontend/docs nginx должен перерезолвить lendry-id-frontend — иначе 502 на / (корень SSO).
sync_nginx_after_edge_restart() {
docker_nginx_container_running || return 0
log "Синхронизация Docker Nginx с frontend после пересборки..."
wait_for_service "frontend" 120 || warn "frontend не стал healthy в срок"
sleep 2
reload_docker_nginx
if ! verify_nginx_reaches_frontend 12 3; then
warn "Nginx → frontend: reload не помог — пересоздаём lendry-id-nginx..."
recreate_docker_nginx
verify_nginx_reaches_frontend 12 3 || warn "Nginx → frontend: проверка не прошла, см. docker compose logs nginx frontend"
fi
}
recreate_docker_nginx() {
compose_uses_proxy_profile || return 0
ensure_nginx_gateway_watch_script
@@ -483,11 +497,19 @@ post_deploy_proxy_verify() {
}
}
verify_nginx_reaches_media_ws || true
verify_nginx_reaches_frontend 12 3 || {
[[ "$strict" -eq 1 ]] && fail "Docker Nginx не проксирует / на frontend — см. docker compose logs nginx frontend"
warn "Nginx → frontend: проверка не прошла"
}
if [[ "$(env_get USE_NGINX_SSL false)" == "true" ]]; then
check_sso_idp_api_proxy || {
[[ "$strict" -eq 1 ]] && fail "SSO /idp-api/health → 502. Выполните: ./install.sh --fix-all"
warn "SSO /idp-api/health не прошёл — выполните ./install.sh --fix-all"
}
check_sso_frontend_root || {
[[ "$strict" -eq 1 ]] && fail "SSO / → 502. Nginx не проксирует главную на frontend. Выполните: ./install.sh --fix-all"
warn "SSO / (главная) не прошла — выполните ./install.sh --fix-all"
}
fi
}
@@ -844,6 +866,10 @@ compose_build_and_up() {
for svc in "${services[@]}"; do
case "$svc" in
api-gateway) sync_nginx_after_api_gateway_restart ;;
frontend|docs)
wait_for_service "$svc" 90
sync_nginx_after_edge_restart
;;
media-ws)
wait_for_service "media-ws" 60
reload_docker_nginx
@@ -2716,6 +2742,96 @@ verify_nginx_idp_api_location() {
return 1
}
verify_nginx_reaches_frontend() {
docker_nginx_container_running || return 0
build_compose_stack_cmd
load_env
local max_attempts="${1:-1}"
local delay_sec="${2:-0}"
local attempt=0
local body fe_ip frontend_domain code
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
while [[ $attempt -lt $max_attempts ]]; do
fe_ip="$(docker_cmd inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lendry-id-frontend 2>/dev/null || true)"
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
wget -qO- --timeout=6 http://lendry-id-frontend:3000/ 2>/dev/null | head -c 256 || true)"
if [[ -n "$body" ]]; then
ok "Docker Nginx → lendry-id-frontend:3000/ OK (IP frontend: ${fe_ip:-?})"
if [[ -n "$frontend_domain" ]]; then
verify_nginx_frontend_root_location "$frontend_domain" || return 1
fi
return 0
fi
attempt=$((attempt + 1))
if [[ $attempt -lt $max_attempts ]]; then
[[ "$delay_sec" -gt 0 ]] && sleep "$delay_sec"
fi
done
warn "Docker Nginx не достучался до lendry-id-frontend:3000/ (IP: ${fe_ip:-?}, попыток: ${max_attempts})"
warn "Проверьте: docker compose --env-file .env --profile proxy logs --tail=40 frontend nginx"
return 1
}
# location / → frontend (путь браузера на https://sso.../)
verify_nginx_frontend_root_location() {
local frontend_domain="$1"
local body code ssl_type
ssl_type="$(env_get SSL_TYPE none)"
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
wget -qO- --timeout=8 --no-check-certificate \
--header="Host: ${frontend_domain}" \
"https://127.0.0.1/" 2>/dev/null | head -c 256 || true)"
if [[ -n "$body" ]]; then
ok "Docker Nginx location / → frontend OK (Host: ${frontend_domain})"
return 0
fi
if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then
code="$(curl -sk --max-time 8 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/" 2>/dev/null || echo 000)"
else
code="$(curl -fsS --max-time 8 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/" 2>/dev/null || echo 000)"
fi
if [[ "$code" == "200" || "$code" == "304" ]]; then
ok "SSO / → HTTP ${code} (проверка с хоста)"
return 0
fi
warn "Nginx location / не проксирует на frontend (Host: ${frontend_domain}, HTTP ${code:-?})"
warn "Проверьте: curl -sk -o /dev/null -w '%{http_code}' https://${frontend_domain}/"
return 1
}
check_sso_frontend_root() {
load_env
local frontend_domain code ssl_type
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
[[ -n "$frontend_domain" ]] || return 0
ssl_type="$(env_get SSL_TYPE none)"
if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then
code="$(curl -sk --max-time 8 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/" 2>/dev/null || echo 000)"
else
code="$(curl -fsS --max-time 8 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/" 2>/dev/null || echo 000)"
fi
if [[ "$code" == "200" || "$code" == "304" ]]; then
echo -e " ${GREEN}${NC} SSO / → ${code} (главная страница SPA)"
return 0
elif [[ "$code" == "502" ]]; then
echo -e " ${RED}${NC} SSO / → 502 — Nginx не проксирует / на frontend (502 Bad Gateway в браузере)"
echo " Проверьте: docker compose --env-file .env --profile proxy exec nginx wget -qO- http://lendry-id-frontend:3000/"
echo " Решение: docker compose --env-file .env --profile proxy restart nginx frontend"
return 1
else
echo -e " ${YELLOW}!${NC} SSO / → HTTP ${code} (ожидалось 200)"
return 1
fi
}
verify_nginx_reaches_media_ws() {
docker_nginx_container_running || return 0
build_compose_stack_cmd
@@ -3205,10 +3321,14 @@ diagnose_common_issues() {
echo -e " ${RED}${NC} Конфликт: системный Nginx на 80/443 и lendry-id-nginx одновременно — браузер может ходить не туда (502)"
echo " Решение: sudo systemctl stop nginx && ./install.sh --fix-all"
issues=$((issues + 1))
elif [[ "$(env_get NGINX_MODE docker)" == "host" ]] \
|| port_in_use "${NGINX_HTTP_PORT:-80}" \
|| port_in_use "${NGINX_HTTPS_PORT:-443}"; then
echo -e " ${RED}${NC} Конфликт: контейнер lendry-id-nginx запущен при занятых портах 80/443 или NGINX_MODE=host"
elif [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then
echo -e " ${RED}${NC} Конфликт: NGINX_MODE=host, но запущен lendry-id-nginx"
echo " Решение: ./install.sh --fix-all"
issues=$((issues + 1))
elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] \
&& ! docker_nginx_container_running \
&& ( port_in_use "${NGINX_HTTP_PORT:-80}" || port_in_use "${NGINX_HTTPS_PORT:-443}" ); then
echo -e " ${RED}${NC} Порты 80/443 заняты, но lendry-id-nginx не запущен"
echo " Решение: ./install.sh --fix-all"
issues=$((issues + 1))
else
@@ -3299,6 +3419,7 @@ diagnose_common_issues() {
&& [[ -n "$(env_get DOMAIN_API "")" && -n "$(env_get DOMAIN_FRONTEND "")" ]]; then
check_https_api_with_origin || issues=$((issues + 1))
check_sso_idp_api_proxy || issues=$((issues + 1))
check_sso_frontend_root || issues=$((issues + 1))
fi
local api_domain frontend_domain public_api
@@ -3677,12 +3798,15 @@ action_fix_all_errors() {
apply_intranet_selfsigned_runtime
recreate_docker_nginx
sync_nginx_after_api_gateway_restart
sync_nginx_after_edge_restart
verify_nginx_reaches_api_gateway 24 5 || compose_build_and_up api-gateway
sync_nginx_after_api_gateway_restart
verify_nginx_reaches_media_ws || compose_build_and_up media-ws
reload_docker_nginx
verify_nginx_reaches_api_gateway 24 5 || fail "Nginx не проксирует на api-gateway — см. docker compose logs nginx api-gateway"
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 в браузере)"
if ! docker_nginx_container_running; then
fail "Контейнер lendry-id-nginx не запущен. Проверьте: docker compose --profile proxy logs nginx"
fi