fix and update

This commit is contained in:
lendry
2026-07-01 21:30:22 +03:00
parent 2a488f2ab6
commit cb82544905

View File

@@ -10,7 +10,7 @@
set -euo pipefail set -euo pipefail
SCRIPT_VERSION="2.6.3" SCRIPT_VERSION="2.6.4"
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR" cd "$ROOT_DIR"
@@ -432,6 +432,7 @@ action_fix_docker_nginx() {
log "Восстановление Docker Nginx (lendry-id-nginx)..." log "Восстановление Docker Nginx (lendry-id-nginx)..."
ensure_docker_nginx_mode_for_fix_all ensure_docker_nginx_mode_for_fix_all
ensure_docker_nginx_configs
prepare_compose_stack prepare_compose_stack
ensure_nginx_after_stack_rebuild || fail "Не удалось запустить lendry-id-nginx" ensure_nginx_after_stack_rebuild || fail "Не удалось запустить lendry-id-nginx"
sync_nginx_after_api_gateway_restart || true sync_nginx_after_api_gateway_restart || true
@@ -471,6 +472,7 @@ wait_for_api_gateway_http() {
# После recreate api-gateway nginx должен сбросить DNS/keepalive — иначе 502 на старый IP. # После recreate api-gateway nginx должен сбросить DNS/keepalive — иначе 502 на старый IP.
sync_nginx_after_api_gateway_restart() { sync_nginx_after_api_gateway_restart() {
docker_nginx_container_running || return 0 docker_nginx_container_running || return 0
ensure_docker_nginx_configs || true
log "Синхронизация Docker Nginx с api-gateway после пересборки..." log "Синхронизация Docker Nginx с api-gateway после пересборки..."
wait_for_service "api-gateway" 120 || warn "api-gateway не стал healthy в срок" wait_for_service "api-gateway" 120 || warn "api-gateway не стал healthy в срок"
wait_for_api_gateway_http 90 || warn "api-gateway /health на :3000 не отвечает" wait_for_api_gateway_http 90 || warn "api-gateway /health на :3000 не отвечает"
@@ -522,6 +524,7 @@ ensure_nginx_after_stack_rebuild() {
if [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then if [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then
log "Запуск и синхронизация Docker Nginx (lendry-id-nginx)..." log "Запуск и синхронизация Docker Nginx (lendry-id-nginx)..."
ensure_docker_nginx_configs
ensure_nginx_gateway_watch_script ensure_nginx_gateway_watch_script
patch_legacy_nginx_proxy_pass || true patch_legacy_nginx_proxy_pass || true
@@ -572,10 +575,19 @@ nginx_probe_idp_api_health_from_container() {
build_compose_stack_cmd build_compose_stack_cmd
if [[ "$use_ssl" == "true" || "$ssl_type" != "none" ]]; then if [[ "$use_ssl" == "true" || "$ssl_type" != "none" ]]; then
docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \ body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
wget -qO- --timeout=6 --no-check-certificate \ wget -qO- --timeout=6 --no-check-certificate \
--header="Host: ${frontend_domain}" \ --header="Host: ${frontend_domain}" \
"https://127.0.0.1/idp-api/health" 2>/dev/null || true "https://127.0.0.1/idp-api/health" 2>/dev/null || true)"
if [[ "$body" == *'"status"'* ]]; then
printf '%s' "$body"
return 0
fi
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
wget -qO- --timeout=6 \
--header="Host: ${frontend_domain}" \
"http://127.0.0.1/idp-api/health" 2>/dev/null || true)"
printf '%s' "$body"
return 0 return 0
fi fi
@@ -1635,6 +1647,7 @@ action_migrate_rebuild() {
wait_for_service docs 60 wait_for_service docs 60
log "Шаг 4/4: Nginx, прокси и проверка..." log "Шаг 4/4: Nginx, прокси и проверка..."
ensure_docker_nginx_configs
ensure_nginx_after_stack_rebuild || warn "Nginx не восстановлен — выполните: ./install.sh --fix-docker-nginx" ensure_nginx_after_stack_rebuild || warn "Nginx не восстановлен — выполните: ./install.sh --fix-docker-nginx"
post_deploy_proxy_verify 0 || warn "HTTPS-проверка не прошла — выполните: ./install.sh --fix-all" post_deploy_proxy_verify 0 || warn "HTTPS-проверка не прошла — выполните: ./install.sh --fix-all"
diagnose_common_issues || true diagnose_common_issues || true
@@ -3014,7 +3027,7 @@ verify_nginx_reaches_api_gateway() {
wget -qO- --timeout=5 http://api-gateway:3000/health 2>/dev/null || true)" wget -qO- --timeout=5 http://api-gateway:3000/health 2>/dev/null || true)"
if [[ "$body" == *'"status":"ok"'* ]] || [[ "$body" == *'"status": "ok"'* ]]; then if [[ "$body" == *'"status":"ok"'* ]] || [[ "$body" == *'"status": "ok"'* ]]; then
ok "Docker Nginx → api-gateway:3000/health OK (IP api-gateway: ${gw_ip:-?})" ok "Docker Nginx → api-gateway:3000/health OK (IP api-gateway: ${gw_ip:-?})"
verify_nginx_idp_api_location || return 1 verify_nginx_idp_api_location || warn "location /idp-api/ — проверьте SSL и nginx/conf.d (upstream OK)"
return 0 return 0
fi fi
attempt=$((attempt + 1)) attempt=$((attempt + 1))
@@ -3051,17 +3064,22 @@ verify_nginx_idp_api_location() {
return 0 return 0
fi fi
# Fallback: проверка с хоста (как curl из браузера / DevTools) # Fallback: проверка через локальный Docker Nginx (127.0.0.1), без DNS на сервере
if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then
code="$(curl -sk --max-time 6 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)" code="$(curl_local_nginx_code "$frontend_domain" "/idp-api/health" 1 6)"
else else
code="$(curl -fsS --max-time 6 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)" code="$(curl_local_nginx_code "$frontend_domain" "/idp-api/health" 0 6)"
fi fi
if [[ "$code" == "200" ]]; then if [[ "$code" == "200" ]]; then
ok "SSO /idp-api/health → 200 (проверка с хоста; внутри контейнера — см. SSL/301)" ok "SSO /idp-api/health → 200 (через lendry-id-nginx на 127.0.0.1)"
return 0 return 0
fi fi
if ! docker_nginx_configs_present; then
warn "nginx/conf.d пуст — выполните: ./install.sh --fix-all"
return 1
fi
warn "Nginx location /idp-api/ не проксирует на api-gateway (Host: ${frontend_domain}, HTTP ${code:-?})" warn "Nginx location /idp-api/ не проксирует на api-gateway (Host: ${frontend_domain}, HTTP ${code:-?})"
warn "Проверьте: curl -sk https://${frontend_domain}/idp-api/health" warn "Проверьте: curl -sk https://${frontend_domain}/idp-api/health"
warn " и: docker compose --env-file .env --profile proxy exec nginx wget -qO- --no-check-certificate --header=\"Host: ${frontend_domain}\" https://127.0.0.1/idp-api/health" warn " и: docker compose --env-file .env --profile proxy exec nginx wget -qO- --no-check-certificate --header=\"Host: ${frontend_domain}\" https://127.0.0.1/idp-api/health"
@@ -3086,7 +3104,7 @@ verify_nginx_reaches_frontend() {
if [[ -n "$body" ]]; then if [[ -n "$body" ]]; then
ok "Docker Nginx → frontend:3000/ OK (IP frontend: ${fe_ip:-?})" ok "Docker Nginx → frontend:3000/ OK (IP frontend: ${fe_ip:-?})"
if [[ -n "$frontend_domain" ]]; then if [[ -n "$frontend_domain" ]]; then
verify_nginx_frontend_root_location "$frontend_domain" || return 1 verify_nginx_frontend_root_location "$frontend_domain" || warn "location / — проверьте SSL и nginx/conf.d (upstream OK)"
fi fi
return 0 return 0
fi fi
@@ -3116,16 +3134,30 @@ verify_nginx_frontend_root_location() {
return 0 return 0
fi fi
body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \
wget -qO- --timeout=8 \
--header="Host: ${frontend_domain}" \
"http://127.0.0.1/" 2>/dev/null | head -c 256 || true)"
if [[ -n "$body" ]]; then
ok "Docker Nginx location / → frontend OK по HTTP (Host: ${frontend_domain})"
return 0
fi
if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then 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)" code="$(curl_local_nginx_code "$frontend_domain" "/" 1 8)"
else else
code="$(curl -fsS --max-time 8 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/" 2>/dev/null || echo 000)" code="$(curl_local_nginx_code "$frontend_domain" "/" 0 8)"
fi fi
if [[ "$code" == "200" || "$code" == "304" ]]; then if [[ "$code" == "200" || "$code" == "304" ]]; then
ok "SSO / → HTTP ${code} (проверка с хоста)" ok "SSO / → HTTP ${code} (через lendry-id-nginx на 127.0.0.1)"
return 0 return 0
fi fi
if ! docker_nginx_configs_present; then
warn "nginx/conf.d пуст — выполните: ./install.sh --fix-all"
return 1
fi
warn "Nginx location / не проксирует на frontend (Host: ${frontend_domain}, HTTP ${code:-?})" warn "Nginx location / не проксирует на frontend (Host: ${frontend_domain}, HTTP ${code:-?})"
warn "Проверьте: curl -sk -o /dev/null -w '%{http_code}' https://${frontend_domain}/" warn "Проверьте: curl -sk -o /dev/null -w '%{http_code}' https://${frontend_domain}/"
return 1 return 1
@@ -3139,9 +3171,9 @@ check_sso_frontend_root() {
ssl_type="$(env_get SSL_TYPE none)" ssl_type="$(env_get SSL_TYPE none)"
if [[ "$(env_get USE_NGINX_SSL false)" == "true" || "$ssl_type" != "none" ]]; then 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)" code="$(curl_local_nginx_code "$frontend_domain" "/" 1 8)"
else else
code="$(curl -fsS --max-time 8 -o /dev/null -w '%{http_code}' "http://${frontend_domain}/" 2>/dev/null || echo 000)" code="$(curl_local_nginx_code "$frontend_domain" "/" 0 8)"
fi fi
if [[ "$code" == "200" || "$code" == "304" ]]; then if [[ "$code" == "200" || "$code" == "304" ]]; then
@@ -3193,9 +3225,9 @@ check_sso_idp_api_proxy() {
fi fi
if [[ "$(env_get USE_NGINX_SSL false)" != "true" ]]; then 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)" code="$(curl_local_nginx_code "$frontend_domain" "/idp-api/health" 0 5)"
else else
code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' "https://${frontend_domain}/idp-api/health" 2>/dev/null || echo 000)" code="$(curl_local_nginx_code "$frontend_domain" "/idp-api/health" 1 5)"
fi fi
if [[ "$code" == "200" ]]; then if [[ "$code" == "200" ]]; then
@@ -3711,6 +3743,66 @@ refresh_host_nginx_configs() {
esac esac
} }
docker_nginx_configs_present() {
[[ -f "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" ]] \
&& [[ -f "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-api.conf" ]]
}
# Генерирует ./nginx/conf.d при переходе с host Nginx или первом запуске Docker Nginx.
ensure_docker_nginx_configs() {
load_env
[[ "$(env_get NGINX_MODE docker)" == "docker" ]] || return 0
[[ -n "$(env_get DOMAIN_API "")" && -n "$(env_get DOMAIN_FRONTEND "")" ]] || return 0
[[ "$(env_get INSTALL_MODE local)" == "local" ]] && return 0
docker_nginx_configs_present && return 0
warn "Конфиги Docker Nginx отсутствуют в ${LOCAL_CONF_DIR} — генерируем..."
DOMAIN_API="$(env_get DOMAIN_API "")"
DOMAIN_FRONTEND="$(env_get DOMAIN_FRONTEND "")"
DOMAIN_DOCS="$(env_get DOMAIN_DOCS "")"
DOMAIN_WS="$(env_get DOMAIN_WS "")"
DOMAIN_MINIO="$(env_get DOMAIN_MINIO "")"
DOMAIN_MINIO_CONSOLE="$(env_get DOMAIN_MINIO_CONSOLE "")"
NGINX_MODE="docker"
FORCE_DOCKER_NGINX=1
local ssl_type
ssl_type="$(env_get SSL_TYPE none)"
case "$ssl_type" in
none)
write_nginx_http_only
;;
custom)
install_custom_certificates || warn "Не удалось установить custom-сертификаты"
write_all_nginx_configs "custom"
;;
selfsigned)
generate_all_self_signed_certs || warn "Не удалось создать self-signed сертификаты"
write_all_nginx_configs "selfsigned"
;;
letsencrypt)
write_all_nginx_configs "letsencrypt"
;;
*)
write_nginx_http_only
;;
esac
ok "Конфиги Docker Nginx записаны в ${LOCAL_CONF_DIR}"
}
# HTTP-код через локальный lendry-id-nginx (--resolve 127.0.0.1, без DNS домена на сервере).
curl_local_nginx_code() {
local domain="$1" path="$2" use_ssl="${3:-1}" timeout="${4:-8}"
local port=443
local -a curl_opts=(-sk --max-time "$timeout" -o /dev/null -w '%{http_code}')
if [[ "$use_ssl" != "1" ]]; then
port=80
curl_opts=(-fsS --max-time "$timeout" -o /dev/null -w '%{http_code}')
fi
curl "${curl_opts[@]}" --resolve "${domain}:${port}:127.0.0.1" \
"$([[ "$use_ssl" == "1" ]] && echo "https" || echo "http")://${domain}${path}" 2>/dev/null || echo 000
}
check_local_upstream() { check_local_upstream() {
local label="$1" url="$2" local label="$1" url="$2"
if curl -fsS --max-time 3 "$url" >/dev/null 2>&1; then if curl -fsS --max-time 3 "$url" >/dev/null 2>&1; then
@@ -3745,7 +3837,8 @@ check_https_api_with_origin() {
[[ "$(env_get USE_NGINX_SSL false)" == "true" ]] || return 0 [[ "$(env_get USE_NGINX_SSL false)" == "true" ]] || return 0
origin="https://${frontend_domain}" origin="https://${frontend_domain}"
code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -H "Origin: ${origin}" "https://${api_domain}/health" 2>/dev/null || echo 000)" code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -H "Origin: ${origin}" \
--resolve "${api_domain}:443:127.0.0.1" "https://${api_domain}/health" 2>/dev/null || echo 000)"
if [[ "$code" == "200" ]]; then if [[ "$code" == "200" ]]; then
echo -e " ${GREEN}${NC} HTTPS API + Origin: https://${api_domain}/health → 200" echo -e " ${GREEN}${NC} HTTPS API + Origin: https://${api_domain}/health → 200"
elif [[ "$code" == "502" ]]; then elif [[ "$code" == "502" ]]; then
@@ -3757,7 +3850,8 @@ check_https_api_with_origin() {
return 1 return 1
fi fi
code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -H "Origin: ${origin}" "https://${api_domain}/notifications/unread-count" 2>/dev/null || echo 000)" code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -H "Origin: ${origin}" \
--resolve "${api_domain}:443:127.0.0.1" "https://${api_domain}/notifications/unread-count" 2>/dev/null || echo 000)"
if [[ "$code" == "401" || "$code" == "403" ]]; then if [[ "$code" == "401" || "$code" == "403" ]]; then
echo -e " ${GREEN}${NC} HTTPS API + Origin (без токена): /notifications/unread-count → ${code}" echo -e " ${GREEN}${NC} HTTPS API + Origin (без токена): /notifications/unread-count → ${code}"
elif [[ "$code" == "502" ]]; then elif [[ "$code" == "502" ]]; then
@@ -3772,6 +3866,7 @@ check_https_api_with_origin() {
code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -X OPTIONS \ code="$(curl -sk --max-time 5 -o /dev/null -w '%{http_code}' -X OPTIONS \
-H "Origin: ${origin}" \ -H "Origin: ${origin}" \
-H "Access-Control-Request-Method: GET" \ -H "Access-Control-Request-Method: GET" \
--resolve "${api_domain}:443:127.0.0.1" \
"https://${api_domain}/notifications/unread-count" 2>/dev/null || echo 000)" "https://${api_domain}/notifications/unread-count" 2>/dev/null || echo 000)"
if [[ "$code" == "204" || "$code" == "200" ]]; then if [[ "$code" == "204" || "$code" == "200" ]]; then
echo -e " ${GREEN}${NC} HTTPS API preflight (OPTIONS): /notifications/unread-count → ${code}" echo -e " ${GREEN}${NC} HTTPS API preflight (OPTIONS): /notifications/unread-count → ${code}"
@@ -3840,12 +3935,24 @@ diagnose_common_issues() {
fi fi
if [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then if [[ "$(env_get NGINX_MODE docker)" == "docker" ]]; then
shopt -s nullglob shopt -s nullglob
if grep -q 'proxy_pass.*\$request_uri' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-"*.conf 2>/dev/null; then local conf legacy_conf=0
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
legacy_conf=1
break
fi
done
shopt -u nullglob
if [[ "$legacy_conf" -eq 1 ]]; then
echo -e " ${RED}${NC} nginx/conf.d: proxy_pass \$request_uri — 502 на / (no resolver defined to resolve lendry-id-frontend)" 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" echo " Решение: ./install.sh --fix-all"
issues=$((issues + 1)) issues=$((issues + 1))
elif ! docker_nginx_configs_present; then
echo -e " ${RED}${NC} nginx/conf.d пуст — Docker Nginx без vhost (502 в браузере)"
echo " Решение: ./install.sh --fix-all"
issues=$((issues + 1))
fi fi
shopt -u nullglob
fi fi
elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] && compose_uses_proxy_profile; then elif [[ "$(env_get NGINX_MODE docker)" == "docker" ]] && compose_uses_proxy_profile; then
echo -e " ${YELLOW}!${NC} Docker Nginx не запущен (ожидался в режиме docker)" echo -e " ${YELLOW}!${NC} Docker Nginx не запущен (ожидался в режиме docker)"
@@ -4271,14 +4378,15 @@ action_fix_all_errors() {
echo " (502 Bad Gateway, /idp-api, Docker Nginx lendry-id-nginx, порты upstream)" echo " (502 Bad Gateway, /idp-api, Docker Nginx lendry-id-nginx, порты upstream)"
echo "" echo ""
log "Шаг 1/7: диагностика..." log "Шаг 1/7: Docker Nginx — остановка system nginx, конфиги, освобождение 80/443..."
diagnose_common_issues || true
log "Шаг 2/7: Docker Nginx — остановка system nginx, освобождение 80/443..."
ensure_docker_nginx_mode_for_fix_all ensure_docker_nginx_mode_for_fix_all
ensure_docker_nginx_configs
write_compose_override write_compose_override
ensure_split_domain_public_urls ensure_split_domain_public_urls
log "Шаг 2/7: диагностика..."
diagnose_common_issues || true
log "Шаг 3/7: миграции PostgreSQL (Prisma)..." log "Шаг 3/7: миграции PostgreSQL (Prisma)..."
compose_build_and_up postgres 2>/dev/null || true compose_build_and_up postgres 2>/dev/null || true
run_prisma_migrations || warn "Миграции Prisma не применены — sso-core попробует при старте" run_prisma_migrations || warn "Миграции Prisma не применены — sso-core попробует при старте"