fix and update
This commit is contained in:
@@ -625,7 +625,7 @@ export function buildSystemSettingPayload(setting: Pick<SystemSetting, 'key' | '
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
||||||
const GATEWAY_RETRY_ATTEMPTS = 3;
|
const GATEWAY_RETRY_ATTEMPTS = 4;
|
||||||
|
|
||||||
function sleep(ms: number): Promise<void> {
|
function sleep(ms: number): Promise<void> {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
@@ -638,14 +638,14 @@ async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Re
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(url, init);
|
const response = await fetch(url, init);
|
||||||
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||||
await sleep(250 * (attempt + 1));
|
await sleep(400 * (attempt + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastNetworkError = error;
|
lastNetworkError = error;
|
||||||
if (attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
if (attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||||
await sleep(250 * (attempt + 1));
|
await sleep(400 * (attempt + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw mapFetchError(error);
|
throw mapFetchError(error);
|
||||||
|
|||||||
120
install.sh
120
install.sh
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_VERSION="2.3.8"
|
SCRIPT_VERSION="2.3.9"
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
@@ -1533,13 +1533,13 @@ nginx_upstream() {
|
|||||||
[[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-api-gateway:3000" || echo "http://127.0.0.1:3000"
|
[[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-api-gateway:3000" || echo "http://127.0.0.1:3000"
|
||||||
;;
|
;;
|
||||||
frontend)
|
frontend)
|
||||||
[[ "$NGINX_MODE" == "docker" ]] && echo "http://frontend:3000" || echo "http://127.0.0.1:3002"
|
[[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-frontend:3000" || echo "http://127.0.0.1:3002"
|
||||||
;;
|
;;
|
||||||
docs)
|
docs)
|
||||||
[[ "$NGINX_MODE" == "docker" ]] && echo "http://docs:3000" || echo "http://127.0.0.1:3003"
|
[[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-docs:3000" || echo "http://127.0.0.1:3003"
|
||||||
;;
|
;;
|
||||||
ws)
|
ws)
|
||||||
[[ "$NGINX_MODE" == "docker" ]] && echo "http://media-ws:8085" || echo "http://127.0.0.1:8085"
|
[[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-media-ws:8085" || echo "http://127.0.0.1:8085"
|
||||||
;;
|
;;
|
||||||
minio)
|
minio)
|
||||||
[[ "$NGINX_MODE" == "docker" ]] && echo "http://minio:9000" || echo "http://127.0.0.1:9000"
|
[[ "$NGINX_MODE" == "docker" ]] && echo "http://minio:9000" || echo "http://127.0.0.1:9000"
|
||||||
@@ -1871,7 +1871,10 @@ nginx_proxy_headers=" proxy_set_header Host \$host;
|
|||||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
proxy_set_header X-Forwarded-Host \$host;
|
proxy_set_header X-Forwarded-Host \$host;
|
||||||
proxy_read_timeout 300s;
|
proxy_read_timeout 300s;
|
||||||
proxy_connect_timeout 75s;"
|
proxy_connect_timeout 75s;
|
||||||
|
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
|
||||||
|
proxy_next_upstream_tries 2;
|
||||||
|
proxy_next_upstream_timeout 10s;"
|
||||||
|
|
||||||
# Встроенный DNS Docker. В docker-режиме nginx обязан перерезолвлять имена
|
# Встроенный DNS Docker. В docker-режиме nginx обязан перерезолвлять имена
|
||||||
# бэкенд-контейнеров на каждый запрос — иначе после пересборки контейнера
|
# бэкенд-контейнеров на каждый запрос — иначе после пересборки контейнера
|
||||||
@@ -2102,7 +2105,8 @@ build_nginx_idp_api_proxy_block() {
|
|||||||
proxy_wk="$(render_proxy_root api)"
|
proxy_wk="$(render_proxy_root api)"
|
||||||
proxy_fedcm="$(render_proxy_root api)"
|
proxy_fedcm="$(render_proxy_root api)"
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
location /idp-api/ws {
|
# ^~ обязателен: иначе regex ниже перехватывает /idp-api/ws и шлёт WS на api-gateway вместо media-ws
|
||||||
|
location ^~ /idp-api/ws {
|
||||||
${proxy_ws}
|
${proxy_ws}
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade \$http_upgrade;
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
@@ -2112,6 +2116,7 @@ ${proxy_ws}
|
|||||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
proxy_read_timeout 86400s;
|
proxy_read_timeout 86400s;
|
||||||
|
proxy_connect_timeout 75s;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/idp-api/(?<api_path>.*)$ {
|
location ~ ^/idp-api/(?<api_path>.*)$ {
|
||||||
@@ -2420,6 +2425,26 @@ validate_docker_api_nginx_config() {
|
|||||||
ok "Конфиг API-домена: динамический DNS (resolver + idp_up_api)"
|
ok "Конфиг API-домена: динамический DNS (resolver + idp_up_api)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate_docker_frontend_nginx_config() {
|
||||||
|
[[ "$NGINX_MODE" == "docker" ]] || return 0
|
||||||
|
local conf="${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf"
|
||||||
|
[[ -f "$conf" ]] || fail "Не найден ${conf} — конфиг SSO-домена не записан"
|
||||||
|
|
||||||
|
if ! grep -q 'location.*idp-api' "$conf" 2>/dev/null; then
|
||||||
|
fail "Конфиг ${conf} без location /idp-api — браузер SPA получит 502. Выполните ./install.sh --fix-all"
|
||||||
|
fi
|
||||||
|
if ! grep -q 'resolver 127.0.0.11' "$conf" 2>/dev/null; then
|
||||||
|
fail "Конфиг ${conf} без resolver 127.0.0.11 — /idp-api на SSO не переживёт пересборку api-gateway"
|
||||||
|
fi
|
||||||
|
if ! grep -q 'idp_up_api' "$conf" 2>/dev/null; then
|
||||||
|
fail "Конфиг ${conf} без idp_up_api — /idp-api проксируется некорректно. Выполните ./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 с динамическим DNS (resolver + idp_up_api)"
|
||||||
|
}
|
||||||
|
|
||||||
verify_nginx_reaches_api_gateway() {
|
verify_nginx_reaches_api_gateway() {
|
||||||
docker_nginx_container_running || return 0
|
docker_nginx_container_running || return 0
|
||||||
build_compose_stack_cmd
|
build_compose_stack_cmd
|
||||||
@@ -2436,6 +2461,59 @@ verify_nginx_reaches_api_gateway() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verify_nginx_reaches_media_ws() {
|
||||||
|
docker_nginx_container_running || return 0
|
||||||
|
build_compose_stack_cmd
|
||||||
|
local body
|
||||||
|
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
|
||||||
|
wget -qO- --timeout=5 http://lendry-id-media-ws:8085/health 2>/dev/null || true)"
|
||||||
|
if [[ "$body" == *ok* ]] || [[ "$body" == *healthy* ]]; then
|
||||||
|
ok "Docker Nginx → lendry-id-media-ws:8085/health OK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
warn "Docker Nginx не достучался до lendry-id-media-ws:8085"
|
||||||
|
warn "Проверьте: docker compose ps media-ws && docker compose logs --tail=40 media-ws"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка того же пути, что использует браузер после логина: https://sso.../idp-api/*
|
||||||
|
check_sso_idp_api_proxy() {
|
||||||
|
local frontend_domain code body
|
||||||
|
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||||
|
[[ -n "$frontend_domain" ]] || return 0
|
||||||
|
|
||||||
|
if docker_nginx_container_running; then
|
||||||
|
body="$(docker_cmd exec lendry-id-frontend \
|
||||||
|
wget -qO- --timeout=5 http://lendry-id-api-gateway:3000/health 2>/dev/null || true)"
|
||||||
|
if [[ "$body" == *'"status":"ok"'* ]] || [[ "$body" == *'"status": "ok"'* ]]; then
|
||||||
|
echo -e " ${GREEN}✔${NC} frontend → api-gateway:3000/health OK (fallback через Next.js rewrites)"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✘${NC} frontend → api-gateway:3000 недоступен (Next.js rewrites тоже дадут 502)"
|
||||||
|
echo " Проверьте: docker compose exec frontend wget -qO- http://lendry-id-api-gateway:3000/health"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(env_get USE_NGINX_SSL false)" != "true" ]]; then
|
||||||
|
code="$(curl -fsS --max-time 5 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)"
|
||||||
|
else
|
||||||
|
code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$code" == "200" ]]; then
|
||||||
|
echo -e " ${GREEN}✔${NC} SSO /idp-api/health → 200 (путь браузера после логина)"
|
||||||
|
return 0
|
||||||
|
elif [[ "$code" == "502" ]]; then
|
||||||
|
echo -e " ${RED}✘${NC} SSO /idp-api/health → 502 — Nginx не проксирует /idp-api на api-gateway"
|
||||||
|
echo " Проверьте: grep -E 'idp-api|idp_up_api|resolver' nginx/conf.d/${NGINX_PREFIX}-frontend.conf"
|
||||||
|
echo " docker compose --profile proxy exec nginx wget -qO- http://lendry-id-api-gateway:3000/health"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}!${NC} SSO /idp-api/health → HTTP ${code} (ожидалось 200)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
write_all_nginx_configs() {
|
write_all_nginx_configs() {
|
||||||
ensure_nginx_mode_for_config_write
|
ensure_nginx_mode_for_config_write
|
||||||
local ssl_type="${1:-$(env_get SSL_TYPE none)}"
|
local ssl_type="${1:-$(env_get SSL_TYPE none)}"
|
||||||
@@ -2473,6 +2551,7 @@ write_all_nginx_configs() {
|
|||||||
[[ -n "${DOMAIN_MINIO_CONSOLE:-}" ]] && write_nginx_site_minio "$DOMAIN_MINIO_CONSOLE" minio-console minio-console "$ssl_type"
|
[[ -n "${DOMAIN_MINIO_CONSOLE:-}" ]] && write_nginx_site_minio "$DOMAIN_MINIO_CONSOLE" minio-console minio-console "$ssl_type"
|
||||||
|
|
||||||
validate_docker_api_nginx_config
|
validate_docker_api_nginx_config
|
||||||
|
validate_docker_frontend_nginx_config
|
||||||
|
|
||||||
if [[ "$NGINX_MODE" == "host" ]]; then
|
if [[ "$NGINX_MODE" == "host" ]]; then
|
||||||
if run_root nginx -t; then
|
if run_root nginx -t; then
|
||||||
@@ -2916,6 +2995,16 @@ diagnose_common_issues() {
|
|||||||
issues=$((issues + 1))
|
issues=$((issues + 1))
|
||||||
elif docker_cmd ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -q 'lendry-id-api-gateway.*Up'; then
|
elif docker_cmd ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -q 'lendry-id-api-gateway.*Up'; then
|
||||||
echo -e " ${GREEN}✔${NC} api-gateway: контейнер запущен"
|
echo -e " ${GREEN}✔${NC} api-gateway: контейнер запущен"
|
||||||
|
local restart_count started_at
|
||||||
|
restart_count="$(docker_cmd inspect lendry-id-api-gateway --format '{{.RestartCount}}' 2>/dev/null || echo '?')"
|
||||||
|
started_at="$(docker_cmd inspect lendry-id-api-gateway --format '{{.State.StartedAt}}' 2>/dev/null || echo '?')"
|
||||||
|
if [[ "$restart_count" =~ ^[0-9]+$ ]] && [[ "$restart_count" -gt 2 ]]; then
|
||||||
|
echo -e " ${RED}✘${NC} api-gateway RestartCount=${restart_count} (Started=${started_at}) — частые перезапуски дают 502 в браузере"
|
||||||
|
echo " Проверьте: docker compose logs --tail=80 api-gateway sso-core"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✔${NC} api-gateway RestartCount=${restart_count} (Started=${started_at})"
|
||||||
|
fi
|
||||||
local grpc_code
|
local grpc_code
|
||||||
grpc_code="$(curl -fsS --max-time 3 -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/health/ready 2>/dev/null || echo 000)"
|
grpc_code="$(curl -fsS --max-time 3 -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/health/ready 2>/dev/null || echo 000)"
|
||||||
if [[ "$grpc_code" == "200" ]]; then
|
if [[ "$grpc_code" == "200" ]]; then
|
||||||
@@ -2930,6 +3019,7 @@ diagnose_common_issues() {
|
|||||||
if [[ "$(env_get USE_NGINX_SSL false)" == "true" ]] \
|
if [[ "$(env_get USE_NGINX_SSL false)" == "true" ]] \
|
||||||
&& [[ -n "$(env_get DOMAIN_API "")" && -n "$(env_get DOMAIN_FRONTEND "")" ]]; then
|
&& [[ -n "$(env_get DOMAIN_API "")" && -n "$(env_get DOMAIN_FRONTEND "")" ]]; then
|
||||||
check_https_api_with_origin || issues=$((issues + 1))
|
check_https_api_with_origin || issues=$((issues + 1))
|
||||||
|
check_sso_idp_api_proxy || issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local api_domain frontend_domain public_api
|
local api_domain frontend_domain public_api
|
||||||
@@ -2937,15 +3027,15 @@ diagnose_common_issues() {
|
|||||||
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||||
public_api="$(env_get PUBLIC_API_URL "")"
|
public_api="$(env_get PUBLIC_API_URL "")"
|
||||||
if [[ -n "$api_domain" && -n "$frontend_domain" && "$api_domain" != "$frontend_domain" ]]; then
|
if [[ -n "$api_domain" && -n "$frontend_domain" && "$api_domain" != "$frontend_domain" ]]; then
|
||||||
if [[ "$public_api" == *"/idp-api"* ]]; then
|
if [[ "$public_api" == *"${frontend_domain}"*"/idp-api"* ]]; then
|
||||||
echo -e " ${RED}✘${NC} Split-domain: PUBLIC_API_URL=${public_api} — должно быть https://${api_domain} (не /idp-api на SSO)"
|
echo -e " ${GREEN}✔${NC} Split-domain + same-origin SPA: PUBLIC_API_URL=${public_api}, браузер → /idp-api на SSO"
|
||||||
echo " Решение: ./install.sh --fix-all"
|
elif [[ "$public_api" == *"${api_domain}"* ]]; then
|
||||||
issues=$((issues + 1))
|
echo -e " ${GREEN}✔${NC} Split-domain: PUBLIC_API_URL=${public_api} (OAuth issuer), SPA → /idp-api на SSO"
|
||||||
elif [[ "$public_api" == *"${frontend_domain}"* && "$public_api" != *"${api_domain}"* ]]; then
|
elif [[ "$public_api" == *"/idp-api"* ]]; then
|
||||||
echo -e " ${RED}✘${NC} PUBLIC_API_URL указывает на домен SSO (${frontend_domain}), а не API (${api_domain})"
|
echo -e " ${YELLOW}!${NC} PUBLIC_API_URL=${public_api} — для OAuth issuer лучше https://${api_domain}"
|
||||||
issues=$((issues + 1))
|
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}✔${NC} Split-domain: PUBLIC_API_URL=${public_api}"
|
echo -e " ${YELLOW}!${NC} Split-domain: PUBLIC_API_URL=${public_api} — проверьте настройки"
|
||||||
|
issues=$((issues + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -3244,8 +3334,10 @@ action_fix_all_errors() {
|
|||||||
apply_intranet_selfsigned_runtime
|
apply_intranet_selfsigned_runtime
|
||||||
recreate_docker_nginx
|
recreate_docker_nginx
|
||||||
verify_nginx_reaches_api_gateway || compose_build_and_up api-gateway
|
verify_nginx_reaches_api_gateway || compose_build_and_up api-gateway
|
||||||
|
verify_nginx_reaches_media_ws || compose_build_and_up media-ws
|
||||||
recreate_docker_nginx
|
recreate_docker_nginx
|
||||||
verify_nginx_reaches_api_gateway || fail "Nginx не проксирует на api-gateway — см. docker compose logs nginx api-gateway"
|
verify_nginx_reaches_api_gateway || fail "Nginx не проксирует на api-gateway — см. docker compose logs nginx api-gateway"
|
||||||
|
check_sso_idp_api_proxy || fail "SSO /idp-api/health недоступен — см. nginx/conf.d/${NGINX_PREFIX}-frontend.conf"
|
||||||
if ! docker_nginx_container_running; then
|
if ! docker_nginx_container_running; then
|
||||||
fail "Контейнер lendry-id-nginx не запущен. Проверьте: docker compose --profile proxy logs nginx"
|
fail "Контейнер lendry-id-nginx не запущен. Проверьте: docker compose --profile proxy logs nginx"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user