From 36f30039ad6ee3f89711adcb49a67ce18a95f761 Mon Sep 17 00:00:00 2001 From: lendry Date: Wed, 24 Jun 2026 16:21:29 +0300 Subject: [PATCH] fix idp on started --- .env.example | 4 + docker-compose.yml | 4 +- install.sh | 309 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 302 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index 03c7397..2ff71e1 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,10 @@ INSTALL_MODE=local # Nginx: auto (Windows → Docker) | host (Linux) | docker NGINX_MODE=auto +# Порты Nginx-контейнера (если 80/443 заняты системным nginx) +NGINX_HTTP_PORT=80 +NGINX_HTTPS_PORT=443 + # SSL: none (HTTP) | selfsigned (HTTPS локально) | letsencrypt (интернет) SSL_TYPE=none diff --git a/docker-compose.yml b/docker-compose.yml index 21446c0..408d1ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -253,8 +253,8 @@ services: container_name: lendry-id-nginx restart: unless-stopped ports: - - "80:80" - - "443:443" + - "${NGINX_HTTP_PORT:-80}:80" + - "${NGINX_HTTPS_PORT:-443}:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/certs:/etc/nginx/certs:ro diff --git a/install.sh b/install.sh index 53ed836..d65f005 100644 --- a/install.sh +++ b/install.sh @@ -10,7 +10,7 @@ set -euo pipefail -SCRIPT_VERSION="2.1.0" +SCRIPT_VERSION="2.2.0" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$ROOT_DIR" @@ -40,6 +40,8 @@ CLI_DOMAIN_MINIO="" CLI_DOMAIN_MINIO_CONSOLE="" CLI_EMAIL="" CLI_SSL_TYPE="" +CLI_NGINX_HTTP_PORT="" +CLI_NGINX_HTTPS_PORT="" RED='\033[0;31m' GREEN='\033[0;32m' @@ -62,6 +64,7 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл ./install.sh --configure-domains Изменить домены / Nginx / SSL ./install.sh --renew-ssl Обновить сертификаты Let's Encrypt ./install.sh --restart Перезапуск контейнеров + ./install.sh --fix-proxy Исправить 502: host Nginx + порты 3000/3002/3003 ./install.sh --status Статус сервисов ./install.sh --reset-db Сброс PostgreSQL (новый пароль из .env, данные удалятся) ./install.sh --remove-nginx Удалить конфиги Nginx IdP @@ -69,7 +72,8 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл ${BOLD}Режим локальной сети (без интернета / AD-домены .local .lpr):${NC} --intranet Локальная сеть: HTTP или самоподписанный HTTPS --ssl TYPE none | selfsigned | letsencrypt (по умолчанию в --intranet: none) - --nginx-mode MODE auto | host | docker (Windows → docker) + --nginx-http-port PORT HTTP-порт Nginx (если 80 занят, например 8080) + --nginx-https-port PORT HTTPS-порт Nginx (если 443 занят, например 8443) --offline Не скачивать Docker из интернета (только если уже установлен) ${BOLD}Домены (можно указывать отдельно):${NC} @@ -118,6 +122,7 @@ parse_args() { --configure-domains) ACTION="configure-domains"; shift ;; --renew-ssl) ACTION="renew-ssl"; shift ;; --restart) ACTION="restart"; shift ;; + --fix-proxy) ACTION="fix-proxy"; shift ;; --status) ACTION="status"; shift ;; --reset-db) ACTION="reset-db"; shift ;; --local) INSTALL_LOCAL=1; shift ;; @@ -125,6 +130,8 @@ parse_args() { --offline) OFFLINE=1; shift ;; --ssl) CLI_SSL_TYPE="${2:-}"; shift 2 ;; --nginx-mode) NGINX_MODE="${2:-auto}"; shift 2 ;; + --nginx-http-port) CLI_NGINX_HTTP_PORT="${2:-}"; shift 2 ;; + --nginx-https-port) CLI_NGINX_HTTPS_PORT="${2:-}"; shift 2 ;; --api-domain) CLI_DOMAIN_API="${2:-}"; shift 2 ;; --frontend-domain) CLI_DOMAIN_FRONTEND="${2:-}"; shift 2 ;; --docs-domain) CLI_DOMAIN_DOCS="${2:-}"; shift 2 ;; @@ -196,6 +203,128 @@ detect_nginx_mode() { log "Режим Nginx: ${NGINX_MODE} (host=системный, docker=контейнер)" } +port_in_use() { + local port="$1" + if has_cmd ss; then + ss -tln 2>/dev/null | grep -qE ":${port}[[:space:]]" + elif has_cmd netstat; then + netstat -tln 2>/dev/null | grep -qE ":${port}[[:space:]]" + else + return 1 + fi +} + +host_nginx_available() { + [[ -d /etc/nginx ]] || has_cmd nginx +} + +system_nginx_is_active() { + has_cmd systemctl && systemctl is-active --quiet nginx 2>/dev/null +} + +lendry_host_nginx_configured() { + [[ -f "${NGINX_ENABLED}/${NGINX_PREFIX}-frontend.conf" ]] \ + || [[ -L "${NGINX_ENABLED}/${NGINX_PREFIX}-frontend.conf" ]] +} + +stop_docker_nginx() { + if docker_cmd ps -q -f name=lendry-id-nginx 2>/dev/null | grep -q .; then + log "Останавливаем контейнер lendry-id-nginx (используется системный Nginx)..." + docker_cmd compose --env-file .env --profile proxy stop nginx 2>/dev/null || true + docker_cmd rm -f lendry-id-nginx 2>/dev/null || true + ok "Docker Nginx остановлен" + fi +} + +prepare_compose_stack() { + resolve_nginx_mode_and_ports + write_compose_override + load_env + if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + stop_docker_nginx + fi +} + +resolve_nginx_mode_and_ports() { + load_env + detect_nginx_mode + + if system_nginx_is_active || lendry_host_nginx_configured; then + if [[ "$NGINX_MODE" == "docker" ]]; then + warn "На сервере активен системный Nginx — переключаем NGINX_MODE на host" + warn "Docker Nginx (lendry-id-nginx) не совместим с системным Nginx на 443" + NGINX_MODE="host" + env_set NGINX_MODE "host" + fi + fi + if [[ -n "$CLI_NGINX_HTTP_PORT" ]]; then + NGINX_HTTP_PORT="$CLI_NGINX_HTTP_PORT" + env_set NGINX_HTTP_PORT "$NGINX_HTTP_PORT" + fi + if [[ -n "$CLI_NGINX_HTTPS_PORT" ]]; then + NGINX_HTTPS_PORT="$CLI_NGINX_HTTPS_PORT" + env_set NGINX_HTTPS_PORT "$NGINX_HTTPS_PORT" + fi + + NGINX_HTTP_PORT="${NGINX_HTTP_PORT:-$(env_get NGINX_HTTP_PORT 80)}" + NGINX_HTTPS_PORT="${NGINX_HTTPS_PORT:-$(env_get NGINX_HTTPS_PORT 443)}" + + if [[ "$NGINX_MODE" == "docker" ]]; then + if port_in_use "$NGINX_HTTP_PORT" || port_in_use "$NGINX_HTTPS_PORT"; then + if host_nginx_available && [[ "$NGINX_HTTP_PORT" == "80" ]]; then + warn "Порт ${NGINX_HTTP_PORT}/${NGINX_HTTPS_PORT} занят — переключаемся на системный Nginx (host)" + NGINX_MODE="host" + env_set NGINX_MODE "host" + elif [[ "$NGINX_HTTP_PORT" == "80" && "$NON_INTERACTIVE" -eq 1 ]]; then + warn "Порт 80 занят — Docker Nginx будет на 8080/8443" + NGINX_HTTP_PORT="8080" + NGINX_HTTPS_PORT="8443" + env_set NGINX_HTTP_PORT "8080" + env_set NGINX_HTTPS_PORT "8443" + elif [[ "$NGINX_HTTP_PORT" == "80" ]]; then + echo "" + warn "Порт 80 или 443 уже занят на этом сервере." + echo " 1) Системный Nginx (рекомендуется, если nginx уже установлен)" + echo " 2) Docker Nginx на портах 8080 / 8443" + echo " 3) Отмена — освободите 80/443 и повторите" + local port_choice + read -r -p "Выберите [1-3] [1]: " port_choice + port_choice="${port_choice:-1}" + case "$port_choice" in + 1) + NGINX_MODE="host" + env_set NGINX_MODE "host" + ;; + 2) + NGINX_HTTP_PORT="8080" + NGINX_HTTPS_PORT="8443" + env_set NGINX_HTTP_PORT "8080" + env_set NGINX_HTTPS_PORT "8443" + ;; + *) + fail "Освободите порт 80: sudo systemctl stop nginx или укажите --nginx-mode host" + ;; + esac + else + fail "Порт ${NGINX_HTTP_PORT} или ${NGINX_HTTPS_PORT} занят. Освободите его или укажите другие: --nginx-http-port 8080" + fi + fi + fi + + env_set NGINX_MODE "$NGINX_MODE" + env_set NGINX_HTTP_PORT "${NGINX_HTTP_PORT:-80}" + env_set NGINX_HTTPS_PORT "${NGINX_HTTPS_PORT:-443}" + log "Nginx: mode=${NGINX_MODE}, HTTP=${NGINX_HTTP_PORT}, HTTPS=${NGINX_HTTPS_PORT}" +} + +compose_uses_proxy_profile() { + load_env + [[ -n "$(env_get DOMAIN_API "")" ]] || return 1 + [[ "$(env_get INSTALL_MODE local)" == "local" ]] && return 1 + [[ "$(env_get NGINX_MODE docker)" == "docker" ]] || return 1 + return 0 +} + install_packages_apt() { run_root apt-get update -qq run_root apt-get install -y -qq ca-certificates curl git openssl gnupg lsb-release jq @@ -797,8 +926,9 @@ persist_domain_env() { } write_compose_override() { + resolve_nginx_mode_and_ports load_env - local mode="${NGINX_MODE:-$(env_get NGINX_MODE auto)}" + local mode="${NGINX_MODE:-$(env_get NGINX_MODE host)}" local has_domains has_domains="$(env_get DOMAIN_API "")" @@ -823,6 +953,14 @@ write_compose_override() { fi if [[ "$mode" == "docker" ]]; then + local http_port https_port nginx_ports + http_port="$(env_get NGINX_HTTP_PORT 80)" + https_port="$(env_get NGINX_HTTPS_PORT 443)" + nginx_ports=" + nginx: + ports: !reset + - \"${http_port}:80\" + - \"${https_port}:443\"" cat >"$COMPOSE_OVERRIDE" <"$COMPOSE_OVERRIDE" </dev/null | sed 's/^/ /' || true + docker_cmd port lendry-id-frontend 2>/dev/null | sed 's/^/ frontend: /' || echo " frontend: порты не опубликованы" + echo "" + fail "Порт ${port} недоступен. Проверьте: grep NGINX_MODE .env && cat docker-compose.override.yml && docker compose logs frontend" + fi + done + ok "Порты 3000/3002/3003 доступны на localhost" + fi +} + nginx_upstream() { case "$1" in api) @@ -1382,8 +1582,11 @@ setup_nginx_proxy() { [[ -n "${DOMAIN_API:-}" ]] || return 0 [[ "$INSTALL_LOCAL" -eq 1 ]] && return 0 - detect_nginx_mode - env_set NGINX_MODE "$NGINX_MODE" + resolve_nginx_mode_and_ports + write_compose_override + if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + stop_docker_nginx + fi install_nginx_certbot local ssl_type @@ -1441,23 +1644,24 @@ deploy_stack() { export DOCKER_BUILDKIT=1 export COMPOSE_DOCKER_CLI_BUILD=1 - write_compose_override - load_env + prepare_compose_stack verify_postgres_auth local compose_cmd=(compose --env-file .env) - if [[ -n "$(env_get DOMAIN_API "")" && "$(env_get INSTALL_MODE local)" != "local" ]]; then + if compose_uses_proxy_profile; then compose_cmd+=(--profile proxy) fi log "Сборка и запуск контейнеров (10–20 минут при первом запуске)..." docker_cmd "${compose_cmd[@]}" up -d --build + recreate_host_port_services + ensure_host_upstreams wait_for_service "sso-core" 120 wait_for_service "api-gateway" 90 wait_for_service "frontend" 60 wait_for_service "docs" 60 - if [[ " ${compose_cmd[*]} " == *" proxy "* ]]; then + if compose_uses_proxy_profile; then wait_for_service "nginx" 30 fi } @@ -1517,6 +1721,43 @@ print_summary() { fi } +refresh_host_nginx_configs() { + load_env + [[ "$(env_get NGINX_MODE docker)" == "host" ]] || return 0 + [[ -n "$(env_get DOMAIN_API "")" ]] || return 0 + [[ "$(env_get INSTALL_MODE local)" == "local" ]] && return 0 + + local ssl_type + ssl_type="$(env_get SSL_TYPE none)" + case "$ssl_type" in + none) write_nginx_http_only ;; + selfsigned) write_all_nginx_configs "selfsigned" ;; + letsencrypt) write_all_nginx_configs "letsencrypt" ;; + esac +} + +check_local_upstream() { + local label="$1" url="$2" + if curl -fsS --max-time 3 "$url" >/dev/null 2>&1; then + echo -e " ${GREEN}✔${NC} ${label}: ${url}" + else + echo -e " ${RED}✘${NC} ${label}: ${url} — недоступен (502 Bad Gateway)" + fi +} + +show_upstream_health() { + load_env + [[ "$(env_get NGINX_MODE docker)" != "host" ]] && return 0 + [[ "$(env_get INSTALL_MODE local)" == "local" ]] && return 0 + [[ -n "$(env_get DOMAIN_API "")" ]] || return 0 + + echo " Upstream (host Nginx → localhost):" + check_local_upstream "API" "http://127.0.0.1:3000/api" + check_local_upstream "Frontend" "http://127.0.0.1:3002" + check_local_upstream "Docs" "http://127.0.0.1:3003/docs" + echo "" +} + show_status() { load_env echo "" @@ -1533,7 +1774,20 @@ show_status() { echo "" docker_cmd compose ps 2>/dev/null || warn "Контейнеры не запущены" echo "" - if has_cmd nginx && [[ "$(env_get USE_NGINX_SSL false)" == "true" ]]; then + if docker_cmd ps -q -f name=lendry-id-nginx 2>/dev/null | grep -q .; then + if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + warn "КОНФЛИКТ: контейнер lendry-id-nginx запущен при NGINX_MODE=host — выполните: ./install.sh --fix-proxy" + else + echo " Docker Nginx: запущен (режим docker; curl 127.0.0.1:3002 не нужен)" + fi + fi + if [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then + docker_cmd port lendry-id-frontend 3000/tcp 2>/dev/null | sed 's/^/ frontend port: /' || \ + warn "frontend не публикует 3000/tcp на хост — выполните: ./install.sh --fix-proxy" + fi + echo "" + show_upstream_health + if has_cmd nginx && [[ "$(env_get NGINX_MODE docker)" == "host" ]]; then run_root nginx -t 2>&1 | sed 's/^/ /' fi } @@ -1594,14 +1848,42 @@ action_renew_ssl() { esac } +action_fix_host_proxy() { + [[ -f .env ]] || fail "Файл .env не найден — сначала выполните ./install.sh --install" + load_env + NGINX_MODE="host" + env_set NGINX_MODE "host" + NON_INTERACTIVE=1 + + log "Исправление прокси: системный Nginx + проброс портов на 127.0.0.1..." + stop_docker_nginx + prepare_compose_stack + refresh_host_nginx_configs + + local compose_cmd=(compose --env-file .env) + docker_cmd "${compose_cmd[@]}" up -d --force-recreate "${HOST_EDGE_SERVICES[@]}" + ensure_host_upstreams + + if system_nginx_is_active; then + run_root nginx -t + run_root systemctl reload nginx + fi + + ok "Прокси исправлен — проверьте: curl -I http://127.0.0.1:3002" + show_status +} + action_restart() { load_env - write_compose_override + prepare_compose_stack + refresh_host_nginx_configs local compose_cmd=(compose --env-file .env) - if [[ -n "$(env_get DOMAIN_API "")" && "$(env_get INSTALL_MODE local)" != "local" ]]; then + if compose_uses_proxy_profile; then compose_cmd+=(--profile proxy) fi docker_cmd "${compose_cmd[@]}" up -d --build + recreate_host_port_services + ensure_host_upstreams ok "Контейнеры перезапущены" show_status } @@ -1654,6 +1936,7 @@ main() { configure-domains) action_configure_domains ;; renew-ssl) action_renew_ssl ;; restart) action_restart ;; + fix-proxy) action_fix_host_proxy ;; status) show_status ;; reset-db) action_reset_db ;; remove-nginx) remove_nginx_configs ;;